Design discussion: SSSD running as a non-root user
by Jakub Hrozek
Hi,
we'd like the SSSD in 1.12.1 to run as a non-privileged user. To
summarize the discussions we had, I created the following design page:
https://fedorahosted.org/sssd/wiki/DesignDocs/NotRootSSSD
For your convenience, the text of the page is also included below.
I'll be glad for comments and another round of discussion.
= Running SSSD as a non-root user =
Related ticket(s):
* https://fedorahosted.org/sssd/ticket/2370
=== Problem statement ===
Currently, all SSSD processes run as the root user. However, if one of the processes was compromised, this might lead to compromising the whole system, especially if additional measures like SELinux were not enabled. It would improve security if instead SSSD was running as its own private user, This design page summarizes what would be needed to run sssd as a non-privileged user and all the cases that currently require a root user.
== Scope of the changes ==
At a higher level, the changes would amount to:
* A new system user would be created. This user must be added in sssd.spec during the `%pre` section.
* Files that were used by sssd and previously owned by root should now be owned as the sssd user. This includes the LDB databases.
* It is important that no code linked from libkrb5 runs as root. This has consequences for accessing the system keytab, which must be readable by the sssd user.
* Responders and back ends would drop privileges and become the sssd user as soon as possible, ideally as the first action after startup.
* Short-lived processes that are spawned by `sssd_be` but might still require elevated privileges would be setuid root.
* Places that blindly check for UID==0 must be converted to handling EPERM/EACCESS gracefully.
The changes to individual binaries and files are described in more detail
below. After the changes are implemented, the code that runs as root will
be reduced to the monitor process and the krb5_child.
== A new system user ==
The sssd will run as a new system user called simply `sssd`. We do not need to have the UID fixed across systems as no files owned by SSSD are shared among different systems. The user will be simply added during the `%pre` phase:
{{{
%pre
getent group sssd >/dev/null || groupadd -r sssd
getent passwd sssd >/dev/null || useradd -r -g sssd -d / -s /sbin/nologin -c "User for sssd" sssd
}}}
As it's common practice for system users, the shell will be `/sbin/nologin` so the user cannot log in into the system.
The name of the user should be configurable at configure-time as different distributions might decide on using different users.
== Dropping privileges of the SSSD processes ==
The goal is for the "worker" processes (that is, both responders and
providers) to drop the root privileges as soon as possible - typically right
after startup, or alternatively after completing any work that requires
root privileges such as opening a file. Because the processes might have to keep the root
privileges after startup, the monitor process would still be running as root.
=== Using libcap-ng to drop the privileges ===
Even though we already have some code to drop privileges in the `krb5`provider, we should leverage the [https://people.redhat.com/sgrubb/libcap-ng/ libcap-ng] project for privilege drop. The added benefits over doing the privilege drop ourselves are:
* libcap-ng is tested and used by many other packages already. For security-sensitive code, it's advisable to re-use existing code rather than hit the same mistakes someone else did already.
* libcap-ng also allows to work with capabilities. For some cases, this might be beneficial in future, for instance, we might need to retain the auditing capability.
The downside is obviously the extra dependency, but libcap-ng has a small footprint and is already used by packages that are present on most, if not all, modern Linux installations, such as dbus.
We should keep the existing code around as a fallback for environments that don't have the libcap-ngs library available, such as non-Linux systems or embedded systems. Because the code wouldn't be enabled by default, it's important to have unit tests for the privilege drop. For unit testing both options (libcap-ng and our own code), [http://cwrap.org/uid_wrapper.html uid_wrapper] and [http://cwrap.org/nss_wrapper.html nss_wrapper] are the best choice.
=== Monitor (sssd) ===
The monitor process would keep running as root. This is in order to be
able to fork and exec processes that are initially privileged without
making them all setuid. As a future enhancement, the process management
functionality of the monitor will be delegated to systemd (see ticket #2243).
=== Responders ===
The responder processes are by nature 'readers' that mostly read data from
cache and request cache updates from the back end processes.
==== NSS responder ====
The NSS responder can drop privileges after startup. The files that the
NSS responder reads (sysdb, confdb, NSS pipe) and writes (memory cache,
debug logs, NSS pipe) will be owned by the sssd user.
==== PAM responder ====
The PAM responder can drop privileges after startup. The files that the PAM
responder reads (sysdb, confdb, PAM public pipe) and writes (debug logs,
PAM pipe) will be owned by the sssd user.
In order to keep the privileged pipe only owned by the root user, we
would open the pipe prior to becoming user and pass the file descriptor.
==== !InfoPipe responder ====
The !InfoPipe responder can drop privileges after startup. The files that the !InfoPipe
responder reads (sysdb, confdb) and writes (debug logs,
PAM pipe) will be owned by the sssd user.
Contrary to other responders, the !InfoPipe responder doesn't have a public
pipe. The !InfoPipe responder also binds to the system bus, we must also
convert the bus policy file to allow the sssd user to bind to the bus.
Moreover, as the !InfoPipe responder allows to change the configuration
(this functionality is used by the OpenLMI SSSD provider), the !InfoPipe
responder should be able to write to the `sssd.conf` file. Therefore, the
`sssd.conf` file should be writable by the sssd user, too. Alternatively,
another setuid helper can be created to write the configuration changes
with augeas calls.
==== Autofs, SUDO and SSH responders ====
The Autofs, SUDO and SSH responders only read from the sysdb, confdb and
their respective UNIX public pipes. These responders also only write to
the debug logs and the public pipe, all of which would be owned by the
sssd user. This means the Autofs, SUDO and SSH responders can drop privileges
right after startup.
=== Providers ===
The providers are dynamically loadable libraries that are loaded by the
`sssd_be` process. After startup, the sssd_be process dlopens the provider
library and dlsyms the handlers. During sssd operation, the `sssd_be` process
mostly unpacks requests arriving on the SBUS and calls the provider-specific
handlers.
We have two options here - either drop the privileges in the provider library
itself or directly in the `sssd_be` process. Dropping privileges in the
`sssd_be` process has the advantage of modifying only one place and being
sure that no matter the back end, the privileges would always be dropped.
On the other hand, becoming user in the library itself might be
beneficial for scenarios where the back end requires root access for
initialization. Also, if some third-party proprietary module absolutely
requires to run as root, we shouldn't enforce the privilege drop.
If we don't care about the third party modules, we could take an approach
where the provider would drop privilege as soon as it can and after all
the initialization was completed, the sssd_be process would ensure the
privileges are indeed dropped. Because there are no third party back ends
so far and there was no attempt to write one, this is currently safe.
=== Short-lived processes ===
The purpose of the short-lived processes is to avoid blocking calls by performing an otherwise blocking action in a completely separate process.
==== ldap_child ====
The ldap_child subprocess primes the credential cache used to establish
GSSAPI-encrypted connection. In order to do so, the ldap_child process needs
to be able to read the keytab, which needs to have its group ownership set
to the sssd group and have the appropriate access rights. The ldap_child
process does not have to be setuid. The resulting ccache would be owned
by the sssd user and thus accessible to the rest of the SSSD.
==== krb5_child ====
The krb5_child would be split into two parts - one that would perform most
of the Kerberos-related tasks, such as reading the keytab or creating the
ccache on behalf of the sssd user. Another part, that would be setuid root
would change the ccache ownership to the user who logs in. The new process
(krb5_child_user perhaps) would have the permissions and ownership set to
4750 and root.sssd respectively. That way only the sssd user would be able
to run the new helper.
==== proxy_child ====
The proxy child performs the PAM conversation with the module being
proxied. Normally there is no restriction on the UID to initiate the PAM
conversation, so the proxy child can run unprivileged.
==== gpo_child ====
The gpo_child process connects to a SMB share, downloads a GPO policy file
and stores it locally, by default in `/var/lib/sss/gpo_cache`. The gpo_child
authenticates to the SMB share using Kerberos; the ccache, as created by
ldap_child is already accessible to the sssd user. Since that directory would
be owned by the sssd user, the gpo_child could run unprivileged.
==== ssh helpers ====
The SSH helpers already run non-privileged. `sss_ssh_knownhostsproxy` runs
as the user who initiated the SSH session. `sss_ssh_authorizedkeys` runs as
the user specified with the !AuthorizedKeysCommandUser directive in sshd_config.
=== Command line tools ===
There are two general kinds of command line tools we ship with the SSSD - tools that manage accounts in the local backend and SSSD management
tools. All tools check if they are executed by root currently. I think
this check makes sense and should stay because all the tool are intended
for administrative purposes only.
==== Local back end tools ====
The tools either write (`sss_useradd, userdel, usermod, sss_groupadd,
groupdel, groupmod`) or read (`sss_groupshow`) the `sssd.ldb` file. Since
the file will be owned by the sssd user, the tools can also run as sssd.
==== sss_seed and sss_cache ====
These two tools function similarly to the local backend management tools,
except they manipulate the domain cache. The cache is also owned and
writable by the sssd user, so it's safe to drop privileges here, too.
==== sss_debuglevel ====
The sss_debuglevel tool changes the debug level of sssd on the fly. The
tool writes new debug level values to the confdb (owned by sssd) and
touches sssd.conf (owned and writable by sssd as well). The tool can drop
privileges to sssd after startup.
==== sss_obfuscate ====
The sss_obfuscate tool is written in Python and manipulates the
sssd.conf file by obfuscating the input and using it as a value of the
`ldap_default_authtok` configuration option. For dropping privileges of
the sss_obfuscate tool, we can use the python bindings of libcap-ng.
== External resources currently requiring root access ==
This part of the design page summarizes which external resources, typically
file system objects currently require SSSD to have elevated privileges.
For filesystem objects, we can either change their owner to the sssd
local user, add an ACL or open them as the privileged process and pass
the file descriptor.
=== SSSD configuration file ===
* Filesystem path: `/etc/sssd/sssd.conf`
* Current owner and permissions: root.root 0600
* Read by: The monitor process
* Written to by: The !InfoPipe responder and users of the configAPI, such as sss_obfuscate or authconfig
* ''Change: In order to allow the !InfoPipe responder to make changes to the config file, the file must be owned by sssd.sssd and made writable by the sssd user''
* ''New owner and permissions: sssd.sssd 0600''
=== Debug logs ===
* Filesystem path: `/var/log/sssd/*.log`
* Current owner and permissions: root.root 0600
* Read by: N/A, only externally by admin
* Written to by: monitor, providers, responders, child processes
* ''New owner and permissions: sssd.sssd 0600''
=== The configuration database ===
* Filesystem path: `/var/lib/sss/db/config.ldb`
* Current owner and permissions: root.root 0600
* Read by: responders, providers, monitor, command-line tools
* Written to by: The monitor process, sssd-ad (a single confdb_set call), sss_debuglevel, sssd_ifp
* ''New owner and permissions: sssd.sssd 0600''
=== The on-disk cache (sysdb) ===
* Filesystem path: `/var/lib/sss/db/cache_$domain.ldb`
* Current owner and permissions: root.root 0600
* Read by: responders, providers, command-line tools
* Written to by: sssd_be, the CLI tools
* ''New owner and permissions: sssd.sssd 0600''
=== Memory Cache ===
* Filesystem path: `/var/lib/sss/mc/{passwd,group}`
* Current owner and permissions: root.root 0644
* Read by: The SSS NSS module
* Written to by: The NSS responder
* ''New owner and permissions: sssd.sssd 0600''
=== Kerberos keytab ===
* Filesystem path: configurable, `/etc/krb5.keytab` by default
* Current owner and permissions: root.root 0600
* Read by: LDAP, KRB5, IPA, AD providers, krb5_child, ldap_child
* Written to by: sssd_be, the CLI tools
* ''Change: Since no Kerberos code should run as root, the keytab must be readable by the sssd group''
* ''New owner and permissions: root.sssd 0640''
* Special care must be taken to make sure other the keytabs as specified in sssd.conf have the right ownership. We already have an upgrade script in %post, we can use it to chown the keytabs
=== Kerberos user credential cache ===
* Filesystem path: Configurable, only if FILE or DIR based cache is used, which is not the default anymore
* Current owner and permissions: the user who logged in, 0600
* Read by: KRB5, AD, IPA, krb5_child, libkrb5 externally
* Written to by: krb5_child
* ''Change: No change, the credential cache will still be written as the user in question''
=== Kerberos LDAP credential cache ===
* Filesystem path: `/var/lib/sss/db/ccache_$domain`
* Current owner and permissions: root.root 0600
* Read by: AD, IPA and LDAP providers (coded up in LDAP provider tree)
* Written to by: ldap_child
* No change needed since ldap_child will run as the sssd user in the new design
* ''New owner and permissions: sssd.sssd 0600''
=== Kerberos kdcinfo files ===
* Filesystem path: `/var/lib/sss/pubconf/*`
* Current owner and permissions: root.root. The directory has permissions of 0755, the files 0644
* Read by: libkrb5
* Written to by: LDAP, KRB5, IPA, AD providers, krb5_child, ldap_child
* ''New owner and permissions: Both directory and files will be owned by sssd.sssd, the permissions will stay the same''
=== SELinux user mappings ===
* Filesystem path: `/etc/selinux/targeted/logins`
* Current owner and permissions: root.root. The directory has permissions of 0755, the files 0644
* Read by: pam_selinux
* Written to by: IPA provider
* ''Change: We need to see if we can use libselinux to set the label first. Either way, the code to set the label needs to run privileged, the SELinux policy mainainer didn't like the idea of changing permissions of the directory''
=== UNIX pipes ===
* Filesystem path: `/var/lib/sss/pipes/`
* Current owner and permissions: root.root. The directory has permissions of 0755, the files 0666. There is one pipe per responder.
* Read by: client modules, all responders except !InfoPipe
* Written to by: client modules, responders
* ''New owner and permissions: Both directory and files will be owned by sssd.sssd, the permissions will stay the same''
=== UNIX PAM private pipe ===
* Filesystem path: `/var/lib/sss/pipes/private/pam`
* Current owner and permissions: root.root. The directory has permissions of 0700, the files 0600. Only the PAM responder uses the private pipe.
* Read by: PAM responder
* Written to by: PAM client module
* ''New owner and permissions: The directory will be owned by sssd.sssd, the file will stay the same''
=== Data Provider private pipes ===
* Filesystem path: `/var/lib/sss/pipes/private/sbus-dp_$domain`
* Current owner and permissions: root.root. The directory has permissions of 0700, the files 0600. Only the PAM responder uses the private pipe.
* Read by: Responders
* Written to by: Data Provider
* ''New owner and permissions: Both directory and files will be owned by sssd.sssd, the permissions will stay the same''
== Kerberos configuration file ==
* Filesystem path: `/etc/krb5.conf`
* Read by: libkrb5
* Written to by: The IPA and AD providers "touch" the file in order to make libkrb5 re-read it
* ''Change: The file can be opened before dropping privileges and we can keep the fd around. Alternatively, the modification can be performed with a setuid helper''
== How to test ==
Test ordinary SSSD operations. Everything must work as it used to before. Pay special attention to operations that involve the short-lived processes, like GSSAPI LDAP provider authentication or Kerberos user authentication.
Upgrade testing must be performed as well.
== Authors ==
* Sumit Bose <sbose(a)redhat.com>
* Jakub Hrozek <jhrozek(a)redhat.com>
* Simo Sorce <simo(a)redhat.com
9 years, 5 months
[PATCH] LDAP: Change defaults for ldap_user/group_objectsid
by Michal Židek
Hi,
this patch solves the ticket:
https://fedorahosted.org/sssd/ticket/2361
From the discussion on bugzilla and the ticket comments, it
looks like Jakub and Lukas were for changes in documentation
only (+ maybe warning/error if user misconfigured SSSD).
I do not want look like I ignore the opinions of others
but I think the best way is to simply change the defaults
to the same as expected by AD backend. While I do agree
that users should set their id_provider option to AD
if they use AD backend, this option is more or less
AD specific, so I think it would be better to have
the same defaults with id_provider ldap. This
would also solve the small regressions that are
mentioned in the bugzilla (by users who do use
id_provider=AD and id_mapping=true).
OTOH if this approach is disliked by other developers
I have no big problems with the man page + warning/error
solution.
Simple patch is attached.
Thanks,
Michal
9 years, 5 months
[PATCH] allowed_shells: using wildcard for any shell
by Denis Kutin
Dear friends,
Using sssd, for a long time, I have come across with a problem recently,
which I would like to solve with your help.
I provide centralized authentication and authorization service for a huge
heterogeneous network. And in my case it would be "nice and easy" if sssd
used only shells(5). I believe this mechanism is sufficient for
identification of an allowed shell.
I take a liberty to offer you this tiny patch, which will let use wildcard
(*) in param allowed_shells in sssd.conf
What do you think about it?
--
Denis Kutin
9 years, 6 months
[PATCHES] IPA: Use libsemanage
by Michal Židek
Hi,
these 3 patches remove the old approach where we directly
wrote the selinux login file. Here, the same approach is used
as in the tools (sss_useradd/sss_usermod) where we already
use libsemanage to manage these tasks.
The advantage is that we remove some lines of code (yay!)
and get the auditing of selinux user changes for free.
1st patch just moves stuff around, so that set_seuser
function can be used from IPA provider.
2nd patch is a very simple change that adds new attribute
to the set_seuser function (mls_range).
The main functionality is in the 3rd patch. Note that to
test the patch, you must run in permissive mode.
$ setenforce Permissive
Otherwise you will get AVC denials (because sssd_be is
not allowed to use libsemanage).
So we will have to change the selinux policy in fedora/rhel
to make it work this way. I will ping the selinux policy maintainer
after the patches are reviewed.
Patches are attached.
Thanks,
Michal
9 years, 6 months
[PATCH] UTIL: Always write capath
by Jakub Hrozek
I had this patch in my branch for a couple of days already so why not
send it out..
In a remote session with a customer we discovered that it's beneficial
for some scenarios to generate the capaths even on clients. The attached
patch does exactly this.
9 years, 6 months
[PATCHES] Add backend part of user views
by Sumit Bose
Hi,
this patches contain the IPA provider part of
https://fedorahosted.org/sssd/ticket/2375 as described in the FreeIPA
design page
http://www.freeipa.org/page/V4/Migrating_existing_environments_to_Trust
Patches 0001, 0006 and 0007 contain new sysdb interfaces for views and
overrides. 0002 and 0003 refactor some already existing calls.
0004 adds the initial support for views and overrides and reads the view
name for the client from the FreeIPA server or sets it to 'default' when
running in ipa-server-mode. It adds some new options which currently
misses man page entries and are not added to the python config API. I
did this on purpose for the time being because I wanted to see first if
the list is complete or if some options ca be dropped.
0005 adds the request to get a specific override from the FreeIPA server
and finally in 0008 the overrides are read during a request and saves or
applied.
This patches must be applied on to of the extdom patch I send yesterday.
To add and manage views and override on the server side please have a
look at Tomas's patch set on the freeipa-devel list
(https://www.redhat.com/archives/freeipa-devel/2014-September/msg00336.html).
bye,
Sumit
9 years, 6 months
[PATCHES] LDAP: SID-Mapping - Store non-POSIX users in cache if they have a SID
by Pavel Reichl
Hello,
please see attached patches.
I have briefly discussed with Jakub how to handle saving users with uid
0 whether to resurrect sysdb_add_fake_user or modify existing fuctions
for storing users. I decided to add wrapper function around existing
ones to minimize changes in code which calls them.
Thanks,
Pavel Reichl
9 years, 6 months