[SSSD] Design discussion: SSSD running as a non-root user

Jakub Hrozek jhrozek at redhat.com
Mon Nov 3 15:48:24 UTC 2014


On Mon, Nov 03, 2014 at 10:29:41AM -0500, Simo Sorce wrote:
> On Mon, 3 Nov 2014 16:08:16 +0100
> Jakub Hrozek <jhrozek at redhat.com> wrote:
> 
> > On Sat, Nov 01, 2014 at 05:21:51PM -0400, Simo Sorce wrote:
> > > On Sat, 1 Nov 2014 17:24:53 +0100
> > > Jakub Hrozek <jhrozek at redhat.com> wrote:
> > > 
> > > > On Wed, Jul 23, 2014 at 03:38:13PM +0200, Jakub Hrozek wrote:
> > > > > === 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.
> > > > 
> > > > After some experimentation and poking at the krb5 code, I think a
> > > > different route is needed. We can't reasonably drop the
> > > > privileges in the provider's setup routines themselves, because
> > > > we don't know if another provider needs root privileges or not.
> > > > For example consider a LDAP + Kerberos combination, the LDAP ID
> > > > provider can't drop privileges until Kerberos provider
> > > > initializes because the Kerberos provider might need root
> > > > privileges to check exisiting ccaches for renewal after startup.
> > > > 
> > > > We can drop privileges after the whole initialization finishes,
> > > > but we should strive to reduce the privileged code even further.
> > > > 
> > > > Michal had an idea of adding another function to the providers
> > > > that would perform privileged init, then sssd_be would drop privs
> > > > and proceed with the rest of the init.
> > > > 
> > > > So the initialization would change from:
> > > >     id_provider = dlopen()
> > > >     id_init = dlsym(id_provider)
> > > >     id_init()
> > > > 
> > > >     auth_provider = dlopen()
> > > >     auth_init = dlsym(auth_provider)
> > > >     auth_init()
> > > > 
> > > >     [...]
> > > > 
> > > >     become_user(sssd)
> > > > 
> > > > to something like:
> > > >     id_provider = dlopen()
> > > >     auth_provider = dlopen()
> > > >     privileged_id_init = dlsym(id_provider)
> > > >     privileged_auth_init = dlsym(auth_provider)
> > > >     privileged_id_init()
> > > >     privileged_auth_init()
> > > > 
> > > >     become_user(sssd)
> > > > 
> > > >     id_init()
> > > >     auth_init()
> > > > 
> > > > The privileged init would initialize private data the provider
> > > > needs and pass it on to the non-privileged init code.
> > > > 
> > > > There are some other alternatives, like a setuid setup process or
> > > > performing the initialization in the monitor, but I like this
> > > > option the best because passing on complex data structures
> > > > between processes is tricky. This way we also keep no knowledge
> > > > about provider internals outside the provider code.
> > > > 
> > > > Simo, I CC-ed you directly because I know you prefer to be aware
> > > > of changes to the data_provider_be.c module. Do you agree with
> > > > this plan?
> > > 
> > > Are we sure we will be able to meaningfully separate "privileged
> > > initialization" from non-privileged items ?
> > 
> > Right now we've identified the places that need root access during
> > initialization because we crawled the whole SSSD codebase while making
> > it rootless -- they're the keytab processing after startup of IPA and
> > AD providers and reading the ccaches of users when initializing the
> > renewal. In general, the only access where we might fail unprivileged
> > atm is keytab access or ccache access.
> > 
> > I can't guarantee other problems in the future, true, but that's not a
> > problem limited to initialization, but we might run into a similar
> > problem during runtime as well.
> 
> Given keytabs can be rotated during the lifetime of sssd I think we
> should rather have a suid helper (or use gss-proxy in conjuction with
> sssd) to access the keytab, or sssd_be will fail to handle keyroation
> in future.
> 
> > > My fear is that we complicate the init system and end up having all
> > > initialization in privileged_*_init() anyway, and *_init() become
> > > empty shells.
> > > 
> > > I am comfortable with keeping the whole init privileged TBH.
> > 
> > Right, the initialization is a small window, at least time-wise,
> > compared to the code that runs during regular SSSD execution.
> > Separating the privileged init would have two benefits from my point
> > of view. 1) Making the surface of the code that runs as root smaller
> > in general. This is mostly interested in downstream groups that aim at
> >     limiting the root code.
> 
> This sounds like it would be better served by a helper though.

That was my first instinct, too, and for keytab access it's certainly
doable, I even started to refactor the code that checks the keytab, so
it's reusable, other tasks just bubbled up.

However, for gathering the list of ccaches that need to be renewed after
startup, I'm not so sure a helper is a good idea. The output from the
renewal function is currently a hash table in the form of:
    user -> struct renew_data

where renew_data contains stuff like ccache end-time but also the
pam_data structure which then contains authtok. Such complex data is not
easy to transfer between processes, that's why I initially opted for
gathering the renew_data while privileged, store it in the global krb5
context and then drop privs (and proceed with the rest of the
initialization)

> 
> >     2) Being able to separate the code that we know is required to run
> >     privileged and be able to unit test SSSD as a whole during make
> >     check, using wrappers.
> 
> Same as above.
>  
> > With 1) we can wait for some feedback from downstream. 2) is more of a
> > convenience -- in order for SSSD to run as a completely different
> > user, we need to do changes in the monitor (like stop requiring that
> > monitor runs as root)
> 
> Hopefully this will not cause issues in monitoring network or
> resolv.conf and other files that is done there.

Yes, currently I think both these cases would work, but I agree there is
a chance that something might not. For regular operation, the monitor
will keep running as root now, it would drop (or rather never acquire
root..) only when unit testing.

> 
> > In conclusion, for now maybe we can file a ticket describing the
> > privileged init, defer it and wait for downstream feedback as well as
> > see how our testing with a custom destdir goes. Revive this ticket if
> > we see a need and keep the backend init privileged now.
> 
> +1
> 
> Simo.
> 
> -- 
> Simo Sorce * Red Hat, Inc * New York



More information about the sssd-devel mailing list