From 2f4dd41289ce0da9ea1ea983b96b3b1f364fd0a5 Mon Sep 17 00:00:00 2001 From: Jakub Hrozek Date: Mar 13 2019 11:09:33 +0000 Subject: Rework the hybrid domain design page to reflect the changed requirements --- diff --git a/design_pages/hybrid_private_groups.rst b/design_pages/hybrid_private_groups.rst index 1291c7d..a2dead6 100644 --- a/design_pages/hybrid_private_groups.rst +++ b/design_pages/hybrid_private_groups.rst @@ -10,63 +10,66 @@ Related ticket(s): Problem statement ----------------- This change will augment the ``auto_private_groups`` option which currently -is a boolean option with a third mode that would, for users who do have -the ``gidNumber`` attribute set return the original group, but for users who -have no ``gidNumber,`` autogenerate a user-private group. +is a boolean option with a third mode that would, for users whose ``uidNumber`` +has the same value as the ``gidNumber`` attribute and no group exists in +LDAP that has the same value of gidNumber, to autogenerate a user-private +group. Use cases --------- -This change is mostly useful for backwards compatibility in environments that -used to manually set a ``gidNumber`` and need to retain the same primary -GID number, but for newly added user, autogenerate the user private group -from the ``uidNumber.`` Please see the "How to test" section for an example. +This change is mostly useful for backwards compatibility in environments +that used to manually create a corresponding group for every user's +``gidNumber`` and need to retain the same primary groups, but for newly +added user, autogenerate the user private group from the ``gidNumber.`` + +To keep backwards compatibility, if a group exists with the same +``gidNumber`` as set a user entry, this "real" group must not be shadowed +by the autogenerated group even if the autogenerated group comes from a +different domain than the user. + +Please see the "How to test" section for a complete example. Overview of the solution ------------------------ -Internally, this hybrid domain would work as a MPG domain, so as if -``auto_private_groups`` was set true. The difference would be when -returning the entries. In the hybrid model, if the user being returned -had an original GID, this original GID would be used. Otherwise, a primary -group generated from the UID would be returned. +Internally, a hybrid domain would work as a usual non-MPG domain. All +logic will be implemented in the NSS responder, to account for the +case where the primary group comes from a different domain than +the user, because in that case, SSSD must iterate over the domains, +which means calling into the ``cache_req`` code. -Care must be taken in the ``getgrnam`` and ``getgrgid`` calls to not return the -autogenerated group if requested for a user who also has the originalGID set. +Care must be taken in the ``getgrnam`` and ``getgrgid`` calls to not +return the autogenerated group if requested for a user whose ``gidNumber`` +is also represented in LDAP with a real group. Implementation details ---------------------- -As said above, for the most part, the hybrid domain would work in a similar -manner as a domain that had ``auto_private_groups=true`` set in the config -file. This means, that the ``uidNumber`` and ``gidNumber`` attributes in -the sysdb cache are always set to the same value, but in case the original -entry in LDAP did have a gidNumber stored, that original value is also -stored in the cache using the ``origPrimaryGroupGidNumber`` attribute. -The purpose of this attribute is to be able to return the original -primary GID as a supplementary group so that any resources owned by this -ID are still accessible to the user. - -We will take advantage of this attribute when returning the user object -during ``getpwnam`` (keep in mind that ``struct passwd`` that the -``getpwnam`` call returns also contains the primary GID number), initgroups -and also for a special case during the ``getgrgid`` and ``getgrnam`` calls. - -During the getpwnam call, the code will check whether the user being returned -has the ``origPrimaryGroupGidNumber`` or not. If it does, its value would -be returned as the GID number, otherwise the ``gidNumber`` attribute would -be used. A similar logic will be implemented in the initgroups call, with -the additional caveat that if the ``origPrimaryGroupGidNumber`` exists, -it wouldn't be returned as another secondary group. - -For group requests in a domain where the user private groups are completely -managed, SSSD would search the whole combined user and group space and -return group objects based on user objects in the cache. The hybrid domain -must behave a little differently here. When a request for a user-private -group comes, the ``getgr*`` call in a hybrid domain should first try -to search group objects only. If there is no match, then also the user -objects need to be searched, but only those user objects that have no -``origPrimaryGroupGidNumber`` set can be returned as a private groups. -Reusing the example above, ``getgrnam("hybriduser")`` would return a group -object inferred from the user object, but ``getgrnam("posixuser")`` would -not return anything. +As said above, the logic will be contained in the NSS responder. If a request +for a group arrives, either by ID or by name, the NSS responder first +issues a request for a group. If that group is not resolvable and the +domain is configured in this special mode, the SSSD retries the same +search in the user ID space or name space. + +If a result is found with this fallback search, the resulting object +would be transformed from a user object to a group object so that the +NSS protocol can create a reply. + +As a last step, if a group is requested by name, the NSS responder must, +in case of returning the user group, verify that this user group is +not shadowed by an entry in another domain. This is important because +if the autogenerated group was returned even as an alias, the result +would have been stored in the memory cache and subsequent ``getgrgid()`` +requests would return this autogenerated group from the memory cache +until it expires, but then return the real group entry from the on-disk +cache. To avoid this confusing state, the NSS responder would also run +a by-GID search and only return the result if the by-GID search returns +nothing. For example, consider that there is a user ``hybrid_with_group`` +whose uid and gid are the same, but there exists a group ``real_group`` +with the same gid as the primary gid of the user entry. A ``getgrnam`` +request arrives for the ``hybrid_with_group`` group, does not match a real +group entry, falls back to the user space. In order to avoid returning the +``hybrid_with_group`` group, the NSS responder would search the group space +again for ``hybrid_with_group``'s primary GID, find out that the group +``real_group`` exists and return ``ENOENT.`` Configuration changes --------------------- @@ -78,9 +81,10 @@ and ``false`` for backwards compatibility but add a third option value How To Test ----------- -Considering these two partial LDIFs:: +Considering these partial LDIFs:: - cn=posixuser,dc=example,dc=com + cn=posixuser,ou=Users,dc=example,dc=com + objectclass: posixUser uidNumber: 1234 gidNumber: 5678 cn: posixuser @@ -88,20 +92,66 @@ Considering these two partial LDIFs:: homeDirectory: /home/posixuser loginShell: /bin/sh - cn=hybriduser,dc=example,dc=com + cn=posixgroup,ou=Groups,dc=example,dc=com + objectclass: posixGroup + gidNumber: 5678 + cn: real_group + + cn=hybriduser,ou=Users,dc=example,dc=com + objectclass: posixUser uidNumber: 2345 + gidNumber: 2345 cn: hybriduser - # note: no gidNumber attribute gecos: hybrid user homeDirectory: /home/hybriduser loginShell: /bin/sh -The getpwnam output for these two users would be:: + cn=hybrid_with_group,ou=Users,dc=example,dc=com + objectclass: posixUser + uidNumber: 3456 + gidNumber: 3456 + cn: hybrid_with_group + gecos: hybrid with group + homeDirectory: /home/hybrid_with_group + loginShell: /bin/sh + + cn=real_group,ou=Groups,dc=example,dc=com + objectclass: posixGroup + gidNumber: 3456 + cn: real_group + +The ``posixuser`` behaves as usual:: $ getent passwd posixuser posixuser:*:1234:5678:posix user:/home/posixuser:/bin/sh + $ getent group 5678 + posixgroup:*:5678: + $ getent group posixuser + returns nothing + $ id posixuser + uid=1234(posixuser) gid=5678(posixgroup) groups=5678(posixgroup) + +The ``hybriduser``'s primary group is autogenerated:: + $ getent passwd hybriduser - hybriduser:*:2345:2345:hybrid user:/home/hybriduser:/bin/sh + hybriduser:*:2345:2345:posix user:/home/hybriduser:/bin/sh + $ getent group 2345 + hybriduser:*:2345: + $ getent group hybriduser + hybriduser:*:2345: + $ id hybriduser + uid=2345(hybriduser) gid=2345(hybriduser) groups=2345(hybriduser) + +The primary group of ``hybrid_with_group`` is still the one stored in LDAP, not autogenerated:: + + $ getent passwd hybrid_with_group + hybrid_with_group:*:3456:3456:posix user:/home/hybrid_with_group:/bin/sh + $ getent group 3456 + real_group:*:3456: + $ getent group hybrid_with_group + returns nothing + $ id hybrid_with_group + uid=3456(hybrid_with_group) gid=3456(real_group) groups=3456(hybrid_with_group) Authors -------