URL: https://github.com/freeipa/freeipa/pull/5438 Author: tiran Title: #5438: [PoC] Add support for subuid and subgid Action: opened
PR body: """ See: https://pagure.io/freeipa/issue/8361 Signed-off-by: Christian Heimes <chei...@redhat.com> """ To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/5438/head:pr5438 git checkout pr5438
From 103464cc299ffc0b7ecea8c054b2aaf25a4a86ee Mon Sep 17 00:00:00 2001 From: Christian Heimes <chei...@redhat.com> Date: Mon, 18 Jan 2021 14:13:43 +0100 Subject: [PATCH] Add support for subuid and subgid See: https://pagure.io/freeipa/issue/8361 Signed-off-by: Christian Heimes <chei...@redhat.com> --- install/share/60basev3.ldif | 5 ++ install/ui/src/freeipa/user.js | 29 +++++++ install/updates/20-indices.update | 16 ++++ ipalib/constants.py | 5 ++ ipaserver/plugins/baseuser.py | 134 +++++++++++++++++++++++++++++- ipaserver/plugins/internal.py | 7 ++ 6 files changed, 194 insertions(+), 2 deletions(-) diff --git a/install/share/60basev3.ldif b/install/share/60basev3.ldif index efc6c8afb60..c5b64195744 100644 --- a/install/share/60basev3.ldif +++ b/install/share/60basev3.ldif @@ -58,6 +58,10 @@ attributeTypes: (2.16.840.1.113730.3.8.11.70 NAME 'ipaPermTargetTo' DESC 'Destin attributeTypes: (2.16.840.1.113730.3.8.11.71 NAME 'ipaPermTargetFrom' DESC 'Source location from where moving an entry IPA permission ACI' EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE X-ORIGIN 'IPA v4.0' ) attributeTypes: ( 2.16.840.1.113730.3.8.11.75 NAME 'ipaNTAdditionalSuffixes' DESC 'Suffix for the user principal name associated with the domain' EQUALITY caseIgnoreMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15) attributeTypes: (2.16.840.1.113730.3.8.11.77 NAME 'ipaDomainResolutionOrder' DESC 'List of domains used to resolve a short name' EQUALITY caseIgnoreIA5Match SYNTAX 1.3.6.1.4.1.1466.115.121.1.26 SINGLE-VALUE X-ORIGIN 'IPA v4.5') +attributeTypes: ( 2.16.840.1.113730.3.8.11.78 NAME 'ipaSubUidNumber' DESC 'numerical subordinate user ID' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') +attributeTypes: ( 2.16.840.1.113730.3.8.11.79 NAME 'ipaSubUidCount' DESC 'numerical subordinate user ID count' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') +attributeTypes: ( 2.16.840.1.113730.3.8.11.80 NAME 'ipaSubGidNumber' DESC 'numerical subordinate user ID' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') +attributeTypes: ( 2.16.840.1.113730.3.8.11.81 NAME 'ipaSubGidCount' DESC 'numerical subordinate user ID count' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') attributeTypes: (2.16.840.1.113730.3.8.18.2.1 NAME 'ipaVaultType' DESC 'IPA vault type' EQUALITY caseExactMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v4.2') attributeTypes: (2.16.840.1.113730.3.8.18.2.2 NAME 'ipaVaultSalt' DESC 'IPA vault salt' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 X-ORIGIN 'IPA v4.2' ) # FIXME: https://bugzilla.redhat.com/show_bug.cgi?id=1267782 @@ -86,5 +90,6 @@ objectClasses: (2.16.840.1.113730.3.8.12.25 NAME 'ipaPrivateKeyObject' DESC 'Wra objectClasses: (2.16.840.1.113730.3.8.12.26 NAME 'ipaSecretKeyObject' DESC 'Wrapped secret keys' SUP top AUXILIARY MUST ( ipaSecretKey $ ipaWrappingKey $ ipaWrappingMech ) X-ORIGIN 'IPA v4.1' ) objectClasses: (2.16.840.1.113730.3.8.12.34 NAME 'ipaSecretKeyRefObject' DESC 'Indirect storage for encoded key material' SUP top AUXILIARY MUST ( ipaSecretKeyRef ) X-ORIGIN 'IPA v4.1' ) objectClasses: (2.16.840.1.113730.3.8.12.39 NAME 'ipaNameResolutionData' DESC 'Data used to resolve short names to fully-qualified form' SUP top AUXILIARY MAY ( ipaDomainResolutionOrder ) X-ORIGIN 'IPA v4.5') +objectClasses: (2.16.840.1.113730.3.8.12.40 NAME 'ipaUserSubordinate' DESC 'Subordinate uid and gid for users' SUP posixAccount AUXILIARY MUST ( ipaSubUidNumber $ ipaSubUidCount $ ipaSubGidNumber $ ipaSubGidCount ) X-ORIGIN 'IPA v4.9') objectClasses: (2.16.840.1.113730.3.8.18.1.1 NAME 'ipaVault' DESC 'IPA vault' SUP top STRUCTURAL MUST ( cn ) MAY ( description $ ipaVaultType $ ipaVaultSalt $ ipaVaultPublicKey $ owner $ member ) X-ORIGIN 'IPA v4.2' ) objectClasses: (2.16.840.1.113730.3.8.18.1.2 NAME 'ipaVaultContainer' DESC 'IPA vault container' SUP top STRUCTURAL MUST ( cn ) MAY ( description $ owner ) X-ORIGIN 'IPA v4.2' ) diff --git a/install/ui/src/freeipa/user.js b/install/ui/src/freeipa/user.js index a4eb390b7d9..d161449fb96 100644 --- a/install/ui/src/freeipa/user.js +++ b/install/ui/src/freeipa/user.js @@ -259,6 +259,35 @@ return { } ] }, + { + name: 'subordinate', + label: '@i18n:objects.subordinate.identity', + fields: [ + { + name: 'ipasubuidnumber', + label: '@i18n:objects.subordinate.subuidnumber', + flags: ['w_if_no_aci'] + }, + { + name: 'ipasubuidcount', + label: '@i18n:objects.subordinate.subuidcount', + hidden_if_empty: true, + read_only: true + + }, + { + name: 'ipasubgidnumber', + label: '@i18n:objects.subordinate.subgidnumber', + flags: ['w_if_no_aci'] + }, + { + name: 'ipasubgidcount', + label: '@i18n:objects.subordinate.subgidcount', + hidden_if_empty: true, + read_only: true + } + ] + }, { name: 'pwpolicy', label: '@i18n:objects.pwpolicy.identity', diff --git a/install/updates/20-indices.update b/install/updates/20-indices.update index 6632f105a98..b47b3851c55 100644 --- a/install/updates/20-indices.update +++ b/install/updates/20-indices.update @@ -272,6 +272,22 @@ add:nsIndexType: eq add:nsIndexType: pres add:nsIndexType: sub +dn: cn=ipaSubGidNumber,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config +only:cn: ipaSubGidNumber +default:objectClass: nsIndex +default:objectClass: top +default:nsSystemIndex: false +add:nsIndexType: eq +add:nsMatchingRule: integerOrderingMatch + +dn: cn=ipaSubUidNumber,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config +only:cn: ipaSubUidNumber +default:objectClass: nsIndex +default:objectClass: top +default:nsSystemIndex: false +add:nsIndexType: eq +add:nsMatchingRule: integerOrderingMatch + dn: cn=ipasudorunasgroup,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config only:cn: ipasudorunasgroup default:objectClass: nsIndex diff --git a/ipalib/constants.py b/ipalib/constants.py index a622f640b24..78f3a0288bc 100644 --- a/ipalib/constants.py +++ b/ipalib/constants.py @@ -340,3 +340,8 @@ # Apache's mod_ssl SSLVerifyDepth value (Maximum depth of CA # Certificates in Client Certificate verification) MOD_SSL_VERIFY_DEPTH = '5' + +# subuid / subgid counts are hard-coded +# An interval of 65536 uids/gids is required to map nobody (65534). +SUBUID_COUNT = 65536 +SUBGID_COUNT = 65536 diff --git a/ipaserver/plugins/baseuser.py b/ipaserver/plugins/baseuser.py index e1b7763f0fd..4edb31a744b 100644 --- a/ipaserver/plugins/baseuser.py +++ b/ipaserver/plugins/baseuser.py @@ -33,7 +33,9 @@ from ipaserver.plugins.service import (validate_realm, normalize_principal) from ipalib.request import context from ipalib import _ -from ipalib.constants import PATTERN_GROUPUSER_NAME +from ipalib.constants import ( + PATTERN_GROUPUSER_NAME, SUBUID_COUNT, SUBGID_COUNT +) from ipapython import kerberos from ipapython.ipautil import ipa_generate_password, TMP_PWD_ENTROPY_BITS from ipapython.ipavalidate import Email @@ -175,13 +177,17 @@ class baseuser(LDAPObject): 'krbprincipalexpiration', 'usercertificate;binary', 'krbprincipalname', 'krbcanonicalname', 'ipacertmapdata', 'ipantlogonscript', 'ipantprofilepath', - 'ipanthomedirectory', 'ipanthomedirectorydrive' + 'ipanthomedirectory', 'ipanthomedirectorydrive', + 'ipasubuidnumber', 'ipasubuidcount', 'ipasubgidnumber', + 'ipasubgidcount', ] search_display_attributes = [ 'uid', 'givenname', 'sn', 'homedirectory', 'krbcanonicalname', 'krbprincipalname', 'loginshell', 'mail', 'telephonenumber', 'title', 'nsaccountlock', 'uidnumber', 'gidnumber', 'sshpubkeyfp', + 'ipasubuidnumber', 'ipasubuidcount', 'ipasubgidnumber', + 'ipasubgidcount', ] uuid_attribute = 'ipauniqueid' attribute_members = { @@ -429,6 +435,32 @@ class baseuser(LDAPObject): 'J:', 'K:', 'L:', 'M:', 'N:', 'O:', 'P:', 'Q:', 'R:', 'S:', 'T:', 'U:', 'V:', 'W:', 'X:', 'Y:', 'Z:'), ), + Int('ipasubuidnumber?', + label=_('SubUID'), + cli_name='subuid', + doc=_('Subordinate user ID'), + ), + Int('ipasubuidcount?', + label=_('SubUID count'), + cli_name='subuidcount', + doc=_('Subordinate user ID count'), + flags=['no_create', 'no_update', 'no_search'], + minvalue=SUBUID_COUNT, + maxvalue=SUBUID_COUNT, + ), + Int('ipasubgidnumber?', + label=_('SubGID'), + cli_name='subgid', + doc=_('Subordinate group ID'), + ), + Int('ipasubgidcount?', + label=_('SubGID count'), + cli_name='subgidcount', + doc=_('Subordinate group ID count'), + flags=['no_create', 'no_update', 'no_search'], + minvalue=SUBGID_COUNT, + maxvalue=SUBGID_COUNT, + ), ) def normalize_and_validate_email(self, email, config=None): @@ -526,6 +558,102 @@ def convert_attribute_members(self, entry_attrs, *keys, **options): except KeyError: pass + def find_subordinate_conflicts( + self, ldap, dn, entry_attrs, extra_filters=(), **kwargs + ): + id_interval_filters = [] + single = entry_attrs.single_value + if 'ipasubuidnumber' in single: + subuid = single['ipasubuidnumber'] + sulow = subuid - SUBUID_COUNT + 1 + suhigh = subuid + SUBUID_COUNT - 1 + + # any uid within our interval + id_interval_filters.append( + f"(&(uidNumber>={subuid})(uidNumber<={suhigh}))" + ) + # any other subuid within our interval + # and our subuid within the interval of any other subuid + id_interval_filters.append( + f"(&(ipaSubUidNumber>={sulow})(ipaSubUidNumber<={suhigh}))", + ) + + if 'ipasubgidnumber' in single: + subgid = single['ipasubgidnumber'] + sglow = subgid - SUBGID_COUNT + 1 + sghigh = subgid + SUBGID_COUNT - 1 + + id_interval_filters.append( + f"(&(gidNumber>={subgid})(gidNumber<={sghigh}))" + ) + id_interval_filters.append( + f"(&(ipaSubGidNumber>={sglow})(ipaSubGidNumber<={sghigh}))", + ) + + if not id_interval_filters: + # no filter + return [] + + filters = [ + ldap.make_filter_from_attr( + "objectClass", + ["posixAccount", "posixGroup", "ipaIDObject"], + ldap.MATCH_ANY + ), + ldap.combine_filters(id_interval_filters, ldap.MATCH_ANY), + # exclude this entry (exclude by DN does not work) + ldap.make_filter_from_attr( + "uid", dn["uid"], rules=ldap.MATCH_NONE + ) + ] + filters.extend(extra_filters) + id_filter = ldap.combine_filters(filters, ldap.MATCH_ALL) + + return ldap.find_entries( + filter=id_filter, + base_dn=DN(api.env.container_accounts, api.env.basedn), + **kwargs + ) + + def handle_subordinate_ids(self, ldap, dn, entry_attrs): + new_subuid = entry_attrs.get('ipasubuidnumber') + new_subgid = entry_attrs.get('ipasubgidnumber') + if new_subuid is None and new_subgid is None: + # nothing to do + return + + # if new_subuid is None: + # raise errors.RequirementError(name='ipasubuidnumber') + # if new_subgid is None: + # raise errors.RequirementError(name='ipasubgidnumber') + + if 'objectclass' in entry_attrs: + obj_classes = entry_attrs['objectclass'] + else: + _entry_attrs = ldap.get_entry(dn, ['objectclass']) + entry_attrs['objectclass'] = _entry_attrs['objectclass'] + if 'ipausersubordinate' not in obj_classes: + obj_classes.append('ipausersubordinate') + + # hard-coded constants + entry_attrs['ipasubuidcount'] = SUBUID_COUNT + entry_attrs['ipasubgidcount'] = SUBGID_COUNT + + try: + conflicts = self.find_subordinate_conflicts( + ldap, dn, entry_attrs, size_limit=10 + ) + except errors.NotFound: + pass + else: + raise errors.ValidationError( + name='ipasubuidnumber', + error=_( + "subuid interval or subgid interval conflicts with " + "existing intervals, users, or groups." + ) + repr(conflicts) + ) + class baseuser_add(LDAPCreate): """ @@ -536,6 +664,7 @@ def pre_common_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, assert isinstance(dn, DN) set_krbcanonicalname(entry_attrs) self.obj.convert_usercertificate_pre(entry_attrs) + self.obj.handle_subordinate_ids(ldap, dn, entry_attrs) if entry_attrs.get('ipatokenradiususername', None): add_missing_object_class(ldap, u'ipatokenradiusproxyuser', dn, entry_attrs, update=False) @@ -683,6 +812,7 @@ def pre_common_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, self.check_objectclass(ldap, dn, entry_attrs) self.obj.convert_usercertificate_pre(entry_attrs) + self.obj.handle_subordinate_ids(ldap, dn, entry_attrs) self.preserve_krbprincipalname_pre(ldap, entry_attrs, *keys, **options) update_samba_attrs(ldap, dn, entry_attrs, **options) diff --git a/ipaserver/plugins/internal.py b/ipaserver/plugins/internal.py index 7b61e527eb7..d6a38944c56 100644 --- a/ipaserver/plugins/internal.py +++ b/ipaserver/plugins/internal.py @@ -1546,6 +1546,13 @@ class i18n_messages(Command): "Drive to mount a home directory" ), }, + "subordinate": { + "identity": _("Subordinate user and group id"), + "subuidnumber": _("Subordinate user id"), + "subuidcount": _("Subordinate user id count"), + "subgidnumber": _("Subordinate group id"), + "subgidcount": _("Subordinate group id count"), + }, "trustconfig": { "options": _("Options"), },
_______________________________________________ FreeIPA-devel mailing list -- freeipa-devel@lists.fedorahosted.org To unsubscribe send an email to freeipa-devel-le...@lists.fedorahosted.org Fedora Code of Conduct: https://docs.fedoraproject.org/en-US/project/code-of-conduct/ List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines List Archives: https://lists.fedorahosted.org/archives/list/freeipa-devel@lists.fedorahosted.org