On 23.11.2015 15:47, Simo Sorce wrote:
On Mon, 2015-11-23 at 15:37 +0100, Jan Cholasta wrote:

Ad alternative is to add the host to ipaservers before the checks are
done and remove it again if any of them fail.

Too error prone, I am ok with the current way in your patches
until/unless I can think of a fail safe way. :-)

Updated patches attached. Note that 520 should be applied between 509 and 510.

--
Jan Cholasta
From 3044955bb8358ca268c61dc19900dcfbc1a91fee Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Tue, 1 Dec 2015 10:42:38 +0100
Subject: [PATCH 1/2] aci: add IPA servers host group 'ipaservers'

https://fedorahosted.org/freeipa/ticket/3416
---
 ACI.txt                                        |  4 ++--
 install/share/bootstrap-template.ldif          | 11 +++++++++++
 install/updates/20-ipaservers_hostgroup.update | 13 +++++++++++++
 install/updates/Makefile.am                    |  1 +
 ipalib/plugins/host.py                         |  6 ++++++
 ipalib/plugins/hostgroup.py                    | 26 ++++++++++++++++++++++++++
 ipaserver/install/krbinstance.py               |  7 +++++++
 7 files changed, 66 insertions(+), 2 deletions(-)
 create mode 100644 install/updates/20-ipaservers_hostgroup.update

diff --git a/ACI.txt b/ACI.txt
index 40fa822..bbc2e66 100644
--- a/ACI.txt
+++ b/ACI.txt
@@ -119,7 +119,7 @@ aci: (targetattr = "usercertificate")(targetfilter = "(objectclass=ipahost)")(ve
 dn: cn=computers,cn=accounts,dc=ipa,dc=example
 aci: (targetattr = "userpassword")(targetfilter = "(objectclass=ipahost)")(version 3.0;acl "permission:System: Manage Host Enrollment Password";allow (write) groupdn = "ldap:///cn=System: Manage Host Enrollment Password,cn=permissions,cn=pbac,dc=ipa,dc=example";)
 dn: cn=computers,cn=accounts,dc=ipa,dc=example
-aci: (targetattr = "krblastpwdchange || krbprincipalkey")(targetfilter = "(objectclass=ipahost)")(version 3.0;acl "permission:System: Manage Host Keytab";allow (write) groupdn = "ldap:///cn=System: Manage Host Keytab,cn=permissions,cn=pbac,dc=ipa,dc=example";)
+aci: (targetattr = "krblastpwdchange || krbprincipalkey")(targetfilter = "(&(!(memberOf=cn=ipaservers,cn=hostgroups,cn=accounts,dc=ipa,dc=example))(objectclass=ipahost))")(version 3.0;acl "permission:System: Manage Host Keytab";allow (write) groupdn = "ldap:///cn=System: Manage Host Keytab,cn=permissions,cn=pbac,dc=ipa,dc=example";)
 dn: cn=computers,cn=accounts,dc=ipa,dc=example
 aci: (targetattr = "createtimestamp || entryusn || ipaallowedtoperform;read_keys || ipaallowedtoperform;write_keys || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipahost)")(version 3.0;acl "permission:System: Manage Host Keytab Permissions";allow (compare,read,search,write) groupdn = "ldap:///cn=System: Manage Host Keytab Permissions,cn=permissions,cn=pbac,dc=ipa,dc=example";)
 dn: cn=computers,cn=accounts,dc=ipa,dc=example
@@ -137,7 +137,7 @@ aci: (targetfilter = "(objectclass=ipahost)")(version 3.0;acl "permission:System
 dn: cn=hostgroups,cn=accounts,dc=ipa,dc=example
 aci: (targetfilter = "(objectclass=ipahostgroup)")(version 3.0;acl "permission:System: Add Hostgroups";allow (add) groupdn = "ldap:///cn=System: Add Hostgroups,cn=permissions,cn=pbac,dc=ipa,dc=example";)
 dn: cn=hostgroups,cn=accounts,dc=ipa,dc=example
-aci: (targetattr = "member")(targetfilter = "(objectclass=ipahostgroup)")(version 3.0;acl "permission:System: Modify Hostgroup Membership";allow (write) groupdn = "ldap:///cn=System: Modify Hostgroup Membership,cn=permissions,cn=pbac,dc=ipa,dc=example";)
+aci: (targetattr = "member")(targetfilter = "(&(!(cn=ipaservers))(objectclass=ipahostgroup))")(version 3.0;acl "permission:System: Modify Hostgroup Membership";allow (write) groupdn = "ldap:///cn=System: Modify Hostgroup Membership,cn=permissions,cn=pbac,dc=ipa,dc=example";)
 dn: cn=hostgroups,cn=accounts,dc=ipa,dc=example
 aci: (targetattr = "cn || description")(targetfilter = "(objectclass=ipahostgroup)")(version 3.0;acl "permission:System: Modify Hostgroups";allow (write) groupdn = "ldap:///cn=System: Modify Hostgroups,cn=permissions,cn=pbac,dc=ipa,dc=example";)
 dn: cn=hostgroups,cn=accounts,dc=ipa,dc=example
diff --git a/install/share/bootstrap-template.ldif b/install/share/bootstrap-template.ldif
index 3570627..628a8e2 100644
--- a/install/share/bootstrap-template.ldif
+++ b/install/share/bootstrap-template.ldif
@@ -261,6 +261,17 @@ description: Limited admins who can edit other users
 cn: editors
 ipaUniqueID: autogenerate
 
+dn: cn=ipaservers,cn=hostgroups,cn=accounts,$SUFFIX
+changetype: add
+objectClass: top
+objectClass: groupOfNames
+objectClass: nestedGroup
+objectClass: ipaobject
+objectClass: ipahostgroup
+description: IPA server hosts
+cn: ipaservers
+ipaUniqueID: autogenerate
+
 dn: cn=sshd,cn=hbacservices,cn=hbac,$SUFFIX
 changetype: add
 objectclass: ipahbacservice
diff --git a/install/updates/20-ipaservers_hostgroup.update b/install/updates/20-ipaservers_hostgroup.update
new file mode 100644
index 0000000..47c9100
--- /dev/null
+++ b/install/updates/20-ipaservers_hostgroup.update
@@ -0,0 +1,13 @@
+dn: cn=ipaservers,cn=hostgroups,cn=accounts,$SUFFIX
+default: objectClass: top
+default: objectClass: groupOfNames
+default: objectClass: nestedGroup
+default: objectClass: ipaobject
+default: objectClass: ipahostgroup
+default: description: IPA server hosts
+default: cn: ipaservers
+default: ipaUniqueID: autogenerate
+
+# Add local host to ipaservers
+dn: cn=ipaservers,cn=hostgroups,cn=accounts,$SUFFIX
+add: member: fqdn=$FQDN,cn=computers,cn=accounts,$SUFFIX
diff --git a/install/updates/Makefile.am b/install/updates/Makefile.am
index 6c8fa11..b04ab48 100644
--- a/install/updates/Makefile.am
+++ b/install/updates/Makefile.am
@@ -14,6 +14,7 @@ app_DATA =				\
 	20-dna.update			\
 	20-host_nis_groups.update	\
 	20-indices.update		\
+	20-ipaservers_hostgroup.update	\
 	20-nss_ldap.update		\
 	20-replication.update		\
 	20-sslciphers.update		\
diff --git a/ipalib/plugins/host.py b/ipalib/plugins/host.py
index bceab31..be7e2f2 100644
--- a/ipalib/plugins/host.py
+++ b/ipalib/plugins/host.py
@@ -395,6 +395,12 @@ class host(LDAPObject):
         },
         'System: Manage Host Keytab': {
             'ipapermright': {'write'},
+            'ipapermtargetfilter': [
+                '(objectclass=ipahost)',
+                '(!(memberOf=%s))' % DN('cn=ipaservers',
+                                        api.env.container_hostgroup,
+                                        api.env.basedn),
+            ],
             'ipapermdefaultattr': {'krblastpwdchange', 'krbprincipalkey'},
             'replaces': [
                 '(targetattr = "krbprincipalkey || krblastpwdchange")(target = "ldap:///fqdn=*,cn=computers,cn=accounts,$SUFFIX";)(version 3.0;acl "permission:Manage host keytab";allow (write) groupdn = "ldap:///cn=Manage host keytab,cn=permissions,cn=pbac,$SUFFIX";)',
diff --git a/ipalib/plugins/hostgroup.py b/ipalib/plugins/hostgroup.py
index 596290f..f3e0d72 100644
--- a/ipalib/plugins/hostgroup.py
+++ b/ipalib/plugins/hostgroup.py
@@ -72,6 +72,8 @@ def get_complete_hostgroup_member_list(hostgroup):
 
 register = Registry()
 
+PROTECTED_HOSTGROUPS = (u'ipaservers',)
+
 
 @register()
 class hostgroup(LDAPObject):
@@ -121,6 +123,10 @@ class hostgroup(LDAPObject):
         },
         'System: Modify Hostgroup Membership': {
             'ipapermright': {'write'},
+            'ipapermtargetfilter': [
+                '(objectclass=ipahostgroup)',
+                '(!(cn=ipaservers))',
+            ],
             'ipapermdefaultattr': {'member'},
             'replaces': [
                 '(targetattr = "member")(target = "ldap:///cn=*,cn=hostgroups,cn=accounts,$SUFFIX";)(version 3.0;acl "permission:Modify Hostgroup membership";allow (write) groupdn = "ldap:///cn=Modify Hostgroup membership,cn=permissions,cn=pbac,$SUFFIX";)',
@@ -229,6 +235,14 @@ class hostgroup_del(LDAPDelete):
 
     msg_summary = _('Deleted hostgroup "%(value)s"')
 
+    def pre_callback(self, ldap, dn, *keys, **options):
+        if keys[0] in PROTECTED_HOSTGROUPS:
+            raise errors.ProtectedEntryError(label=_(u'hostgroup'),
+                                             key=keys[0],
+                                             reason=_(u'privileged hostgroup'))
+
+        return dn
+
 
 @register()
 class hostgroup_mod(LDAPUpdate):
@@ -283,6 +297,18 @@ class hostgroup_add_member(LDAPAddMember):
 class hostgroup_remove_member(LDAPRemoveMember):
     __doc__ = _('Remove members from a hostgroup.')
 
+    def pre_callback(self, ldap, dn, found, not_found, *keys, **options):
+        if keys[0] in PROTECTED_HOSTGROUPS and 'host' in options:
+            result = api.Command.hostgroup_show(keys[0])
+            hosts_left = set(result['result'].get('member_host', []))
+            hosts_deleted = set(options['host'])
+            if hosts_left.issubset(hosts_deleted):
+                raise errors.LastMemberError(key=sorted(hosts_deleted)[0],
+                                             label=_(u'hostgroup'),
+                                             container=keys[0])
+
+        return dn
+
     def post_callback(self, ldap, completed, failed, dn, entry_attrs, *keys, **options):
         assert isinstance(dn, DN)
         self.obj.suppress_netgroup_memberof(ldap, dn, entry_attrs)
diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
index 1dd807c..f928e50 100644
--- a/ipaserver/install/krbinstance.py
+++ b/ipaserver/install/krbinstance.py
@@ -117,6 +117,13 @@ class KrbInstance(service.Service):
             host_entry['krbticketflags'] = service_entry['krbticketflags']
         self.admin_conn.add_entry(host_entry)
 
+        # Add the host to the ipaserver host group
+        hostgroup_dn = DN(('cn', 'ipaservers'), ('cn', 'hostgroups'),
+                          ('cn', 'accounts'), self.suffix)
+        hostgroup_entry = self.admin_conn.get_entry(hostgroup_dn, ['member'])
+        hostgroup_entry.setdefault('member', []).append(host_dn)
+        self.admin_conn.update_entry(hostgroup_entry)
+
     def __common_setup(self, realm_name, host_name, domain_name, admin_password):
         self.fqdn = host_name
         self.realm = realm_name.upper()
-- 
2.4.3

From dfa1962da7fd588891cb9e1140333c65eb97a9f0 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Tue, 1 Dec 2015 10:44:59 +0100
Subject: [PATCH 2/2] aci: replace per-server ACIs with ipaserver-based ACIs

https://fedorahosted.org/freeipa/ticket/3416
---
 install/share/default-aci.ldif       |  11 ----
 install/updates/40-delegation.update |  18 ++++--
 ipaserver/install/replication.py     | 111 -----------------------------------
 3 files changed, 12 insertions(+), 128 deletions(-)

diff --git a/install/share/default-aci.ldif b/install/share/default-aci.ldif
index 7b174e7..dd15cbe 100644
--- a/install/share/default-aci.ldif
+++ b/install/share/default-aci.ldif
@@ -77,17 +77,6 @@ changetype: modify
 add: aci
 aci: (targetattr="userPassword || krbPrincipalKey")(version 3.0; acl "Search existence of password and kerberos keys"; allow(search) userdn = "ldap:///all";;)
 
-# Let host add and update CA renewal certificates
-dn: cn=ipa,cn=etc,$SUFFIX
-changetype: modify
-add: aci
-aci: (target="ldap:///cn=*,cn=ca_renewal,cn=ipa,cn=etc,$SUFFIX";)(version 3.0; acl "Add CA Certificates for renewals"; allow(add) userdn = "ldap:///fqdn=$FQDN,cn=computers,cn=accounts,$SUFFIX";;)
-
-dn: cn=ipa,cn=etc,$SUFFIX
-changetype: modify
-add: aci
-aci: (target="ldap:///cn=*,cn=ca_renewal,cn=ipa,cn=etc,$SUFFIX";)(targetattr="userCertificate")(version 3.0; acl "Modify CA Certificates for renewals"; allow(write) userdn = "ldap:///fqdn=$FQDN,cn=computers,cn=accounts,$SUFFIX";;)
-
 # Let users manage their own tokens
 dn: $SUFFIX
 changetype: modify
diff --git a/install/updates/40-delegation.update b/install/updates/40-delegation.update
index 08906a6..f0431b9 100644
--- a/install/updates/40-delegation.update
+++ b/install/updates/40-delegation.update
@@ -60,8 +60,10 @@ default:cn: SELinux User Map Administrators
 default:description: SELinux User Map Administrators
 
 dn: cn=ipa,cn=etc,$SUFFIX
-add:aci:(target = "ldap:///cn=*,cn=ca_renewal,cn=ipa,cn=etc,$SUFFIX";)(version 3.0; acl "Add CA Certificates for renewals"; allow(add) userdn = "ldap:///fqdn=$FQDN,cn=computers,cn=accounts,$SUFFIX";;)
-add:aci:(target = "ldap:///cn=*,cn=ca_renewal,cn=ipa,cn=etc,$SUFFIX";)(targetattr = "userCertificate")(version 3.0; acl "Modify CA Certificates for renewals"; allow(write) userdn = "ldap:///fqdn=$FQDN,cn=computers,cn=accounts,$SUFFIX";;)
+remove:aci:(target = "ldap:///cn=*,cn=ca_renewal,cn=ipa,cn=etc,$SUFFIX";)(version 3.0; acl "Add CA Certificates for renewals"; allow(add) userdn = "ldap:///fqdn=$FQDN,cn=computers,cn=accounts,$SUFFIX";;)
+remove:aci:(target = "ldap:///cn=*,cn=ca_renewal,cn=ipa,cn=etc,$SUFFIX";)(targetattr = "userCertificate")(version 3.0; acl "Modify CA Certificates for renewals"; allow(write) userdn = "ldap:///fqdn=$FQDN,cn=computers,cn=accounts,$SUFFIX";;)
+add:aci:(target = "ldap:///cn=*,cn=ca_renewal,cn=ipa,cn=etc,$SUFFIX";)(version 3.0; acl "Add CA Certificates for renewals"; allow(add) groupdn = "ldap:///cn=ipaservers,cn=hostgroups,cn=accounts,$SUFFIX";;)
+add:aci:(target = "ldap:///cn=*,cn=ca_renewal,cn=ipa,cn=etc,$SUFFIX";)(targetattr = "userCertificate")(version 3.0; acl "Modify CA Certificates for renewals"; allow(write) groupdn = "ldap:///cn=ipaservers,cn=hostgroups,cn=accounts,$SUFFIX";;)
 
 # Add permissions "Retrieve Certificates from the CA" and "Revoke Certificate"
 # to privilege "Host Administrators"
@@ -72,10 +74,12 @@ dn: cn=Revoke Certificate,cn=permissions,cn=pbac,$SUFFIX
 add: member: cn=Host Administrators,cn=privileges,cn=pbac,$SUFFIX
 
 dn: cn=ipa,cn=etc,$SUFFIX
-add:aci:(target = "ldap:///cn=CAcert,cn=ipa,cn=etc,$SUFFIX";)(targetattr = cACertificate)(version 3.0; acl "Modify CA Certificate"; allow (write) userdn = "ldap:///fqdn=$FQDN,cn=computers,cn=accounts,$SUFFIX";;)
+remove:aci:(target = "ldap:///cn=CAcert,cn=ipa,cn=etc,$SUFFIX";)(targetattr = cACertificate)(version 3.0; acl "Modify CA Certificate"; allow (write) userdn = "ldap:///fqdn=$FQDN,cn=computers,cn=accounts,$SUFFIX";;)
+add:aci:(target = "ldap:///cn=CAcert,cn=ipa,cn=etc,$SUFFIX";)(targetattr = cACertificate)(version 3.0; acl "Modify CA Certificate"; allow (write) groupdn = "ldap:///cn=ipaservers,cn=hostgroups,cn=accounts,$SUFFIX";;)
 
 dn: cn=certificates,cn=ipa,cn=etc,$SUFFIX
-add:aci:(targetfilter = "(&(objectClass=ipaCertificate)(ipaConfigString=ipaCA))")(targetattr = "ipaCertIssuerSerial || cACertificate")(version 3.0; acl "Modify CA Certificate Store Entry"; allow (write) userdn = "ldap:///fqdn=$FQDN,cn=computers,cn=accounts,$SUFFIX";;)
+remove:aci:(targetfilter = "(&(objectClass=ipaCertificate)(ipaConfigString=ipaCA))")(targetattr = "ipaCertIssuerSerial || cACertificate")(version 3.0; acl "Modify CA Certificate Store Entry"; allow (write) userdn = "ldap:///fqdn=$FQDN,cn=computers,cn=accounts,$SUFFIX";;)
+add:aci:(targetfilter = "(&(objectClass=ipaCertificate)(ipaConfigString=ipaCA))")(targetattr = "ipaCertIssuerSerial || cACertificate")(version 3.0; acl "Modify CA Certificate Store Entry"; allow (write) groupdn = "ldap:///cn=ipaservers,cn=hostgroups,cn=accounts,$SUFFIX";;)
 
 # Automember tasks
 dn: cn=Automember Task Administrator,cn=privileges,cn=pbac,$SUFFIX
@@ -197,8 +201,10 @@ default:cn: IPA Masters Readers
 default:description: Read list of IPA masters
 
 dn: cn=masters,cn=ipa,cn=etc,$SUFFIX
-add:aci:(targetfilter = "(objectClass=nsContainer)")(targetattr = "cn || objectClass || ipaConfigString")(version 3.0; acl "Read IPA Masters"; allow (read, search, compare) userdn = "ldap:///fqdn=$FQDN,cn=computers,cn=accounts,$SUFFIX";;)
-add:aci:(targetfilter = "(objectClass=nsContainer)")(targetattr = "ipaConfigString")(version 3.0; acl "Modify IPA Masters"; allow (write) userdn = "ldap:///fqdn=$FQDN,cn=computers,cn=accounts,$SUFFIX";;)
+remove:aci:(targetfilter = "(objectClass=nsContainer)")(targetattr = "cn || objectClass || ipaConfigString")(version 3.0; acl "Read IPA Masters"; allow (read, search, compare) userdn = "ldap:///fqdn=$FQDN,cn=computers,cn=accounts,$SUFFIX";;)
+remove:aci:(targetfilter = "(objectClass=nsContainer)")(targetattr = "ipaConfigString")(version 3.0; acl "Modify IPA Masters"; allow (write) userdn = "ldap:///fqdn=$FQDN,cn=computers,cn=accounts,$SUFFIX";;)
+add:aci:(targetfilter = "(objectClass=nsContainer)")(targetattr = "cn || objectClass || ipaConfigString")(version 3.0; acl "Read IPA Masters"; allow (read, search, compare) groupdn = "ldap:///cn=ipaservers,cn=hostgroups,cn=accounts,$SUFFIX";;)
+add:aci:(targetfilter = "(objectClass=nsContainer)")(targetattr = "ipaConfigString")(version 3.0; acl "Modify IPA Masters"; allow (write) groupdn = "ldap:///cn=ipaservers,cn=hostgroups,cn=accounts,$SUFFIX";;)
 
 # PassSync
 dn: cn=PassSync Service,cn=privileges,cn=pbac,$SUFFIX
diff --git a/ipaserver/install/replication.py b/ipaserver/install/replication.py
index 576dfd3..388b3c0 100644
--- a/ipaserver/install/replication.py
+++ b/ipaserver/install/replication.py
@@ -1266,117 +1266,6 @@ class ReplicationManager(object):
                 err = e
 
         try:
-            entry = self.conn.get_entry(
-                DN(('cn', 'ipa'), ('cn', 'etc'), self.suffix), ['aci'])
-
-            sub = {'suffix': self.suffix, 'fqdn': replica}
-            try:
-                entry.raw['aci'].remove(
-                    b'(target = "ldap:///cn=*,cn=ca_renewal,cn=ipa,cn=etc,'
-                    b'%(suffix)s")(version 3.0; acl "Add CA Certificates for '
-                    b'renewals"; allow(add) userdn = "ldap:///fqdn=%(fqdn)s,'
-                    b'cn=computers,cn=accounts,%(suffix)s";)' % sub)
-            except ValueError:
-                pass
-            try:
-                entry.raw['aci'].remove(
-                    b'(target = "ldap:///cn=*,cn=ca_renewal,cn=ipa,cn=etc,'
-                    b'%(suffix)s")(targetattr = "userCertificate")'
-                    b'(version 3.0; acl "Modify CA Certificates for renewals"; '
-                    b'allow(write) userdn = "ldap:///fqdn=%(fqdn)s,'
-                    b'cn=computers,cn=accounts,%(suffix)s";)' % sub)
-            except ValueError:
-                pass
-            try:
-                entry.raw['aci'].remove(
-                    b'(target = "ldap:///cn=CAcert,cn=ipa,cn=etc,%(suffix)s")'
-                    b'(targetattr = cACertificate)(version 3.0; acl "Modify CA '
-                    b'Certificate"; allow (write) userdn = "ldap:///fqdn='
-                    b'%(fqdn)s,cn=computers,cn=accounts,%(suffix)s";)' % sub)
-            except ValueError:
-                pass
-
-            try:
-                self.conn.update_entry(entry)
-            except errors.EmptyModlist:
-                pass
-        except errors.NotFound:
-            pass
-        except Exception as e:
-            if not force:
-                raise e
-            elif not err:
-                err = e
-
-        try:
-            entry = self.conn.get_entry(
-                DN(('cn', 'masters'), ('cn', 'ipa'), ('cn', 'etc'),
-                   self.suffix),
-                ['aci'])
-
-            sub = {'suffix': self.suffix, 'fqdn': replica}
-            try:
-                entry.raw['aci'].remove(
-                    b'(targetfilter = "(objectClass=nsContainer)")'
-                    b'(targetattr = "cn || objectClass || ipaConfigString")'
-                    b'(version 3.0; acl "Read IPA Masters"; allow (read, '
-                    b'search, compare) userdn = "ldap:///fqdn=%(fqdn)s,'
-                    b'cn=computers,cn=accounts,%(suffix)s";)' % sub)
-            except ValueError:
-                pass
-            try:
-                entry.raw['aci'].remove(
-                    b'(targetfilter = "(objectClass=nsContainer)")'
-                    b'(targetattr = "ipaConfigString")(version 3.0; acl '
-                    b'"Modify IPA Masters"; allow (write) userdn = '
-                    b'"ldap:///fqdn=%(fqdn)s,cn=computers,cn=accounts,'
-                    b'%(suffix)s";)' % sub)
-            except ValueError:
-                pass
-
-            try:
-                self.conn.update_entry(entry)
-            except errors.EmptyModlist:
-                pass
-        except errors.NotFound:
-            pass
-        except Exception as e:
-            if not force:
-                raise e
-            elif not err:
-                err = e
-
-        try:
-            entry = self.conn.get_entry(
-                DN(('cn', 'certificates'), ('cn', 'ipa'), ('cn', 'etc'),
-                   self.suffix),
-                ['aci'])
-
-            sub = {'suffix': self.suffix, 'fqdn': replica}
-            try:
-                entry.raw['aci'].remove(
-                    b'(targetfilter = "(&(objectClass=ipaCertificate)'
-                    b'(ipaConfigString=ipaCA))")(targetattr = '
-                    b'"ipaCertIssuerSerial || cACertificate")(version 3.0; acl '
-                    b'"Modify CA Certificate Store Entry"; allow (write) '
-                    b'userdn = "ldap:///fqdn=%(fqdn)s,cn=computers,cn=accounts,'
-                    b'%(suffix)s";)' % sub)
-            except ValueError:
-                pass
-
-            try:
-                self.conn.update_entry(entry)
-            except errors.EmptyModlist:
-                pass
-        except errors.NotFound:
-            pass
-        except Exception as e:
-            if not force:
-                raise e
-            elif not err:
-                err = e
-
-        try:
             basedn = DN(('cn', 'etc'), self.suffix)
             filter = '(dnaHostname=%s)' % replica
             entries = self.conn.get_entries(
-- 
2.4.3

From 709a3b8cbf9df19606970a330dec6d73d4842a7e Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Fri, 13 Nov 2015 08:15:55 +0100
Subject: [PATCH 1/5] aci: allow members of ipaservers to set up replication

Add ACIs which allow the members of the ipaservers host group to set up
replication. This allows IPA hosts to perform replica promotion on
themselves.

A number of checks which need read access to certain LDAP entries is done
during replica promotion. Add ACIs to allow these checks to be done using
any valid IPA host credentials.

https://fedorahosted.org/freeipa/ticket/5401
---
 install/updates/20-aci.update   | 25 +++++++++++++++++++++++++
 install/updates/45-roles.update |  1 +
 2 files changed, 26 insertions(+)

diff --git a/install/updates/20-aci.update b/install/updates/20-aci.update
index cba1897..ca4c0df 100644
--- a/install/updates/20-aci.update
+++ b/install/updates/20-aci.update
@@ -32,6 +32,14 @@ remove:aci:(targetfilter="(objectclass=nsContainer)")(version 3.0; acl "Deny rea
 dn: cn=masters,cn=ipa,cn=etc,$SUFFIX
 add:aci:(targetfilter="(objectclass=nsContainer)")(targetattr="objectclass || cn")(version 3.0; acl "Read access to masters"; allow(read, search, compare) userdn = "ldap:///all";;)
 
+# Allow hosts to read masters service configuration
+dn: cn=masters,cn=ipa,cn=etc,$SUFFIX
+add:aci:(targetfilter = "(objectclass=nsContainer)")(targetattr = "ipaConfigString")(version 3.0; acl "Allow hosts to read masters service configuration"; allow(read, search, compare) userdn = "ldap:///fqdn=*,cn=computers,cn=accounts,$SUFFIX";;)
+
+# Allow hosts to read replication managers
+dn: cn=sysaccounts,cn=etc,$SUFFIX
+add:aci: (target = "ldap:///cn=replication managers,cn=sysaccounts,cn=etc,$SUFFIX")(targetattr = "objectClass || cn")(version 3.0; acl "Allow hosts to read replication managers"; allow(read, search, compare) userdn = "ldap:///fqdn=*,cn=computers,cn=accounts,$SUFFIX";;)
+
 # Read access to Kerberos container (cn=kerberos) and realm containers (cn=$REALM,cn=kerberos)
 dn: cn=kerberos,$SUFFIX
 add:aci:(targetattr = "cn || objectclass")(targetfilter = "(|(objectclass=krbrealmcontainer)(objectclass=krbcontainer))")(version 3.0;acl "Anonymous read access to Kerberos containers";allow (read,compare,search) userdn = "ldap:///anyone";;)
@@ -54,6 +62,10 @@ add:aci:(targetattr="ipaUniqueId || memberOf || enrolledBy || krbExtraData || kr
 dn: cn=tasks,cn=config
 add:aci:(targetattr="*")(version 3.0; acl "Admin can read all tasks"; allow (read, compare, search) groupdn = "ldap:///cn=admins,cn=groups,cn=accounts,$SUFFIX";;)
 
+# Allow hosts to read their replication agreements
+dn: cn=mapping tree,cn=config
+add:aci: (target = "ldap:///cn=meTo($$dn),cn=*,cn=mapping tree,cn=config")(targetattr = "objectclass || cn")(version 3.0; acl "Allow hosts to read their replication agreements"; allow(read, search, compare) userdn = "ldap:///fqdn=($$dn),cn=computers,cn=accounts,$SUFFIX";)
+
 # Removal of obsolete ACIs
 dn: cn=config
 # Replaced by 'System: Read Replication Agreements'
@@ -91,3 +103,16 @@ add:aci: (target = "ldap:///krbprincipalname=*/($$dn)@$REALM,cn=services,cn=acco
 # CIFS service on the master can manage ID ranges
 dn: cn=ranges,cn=etc,$SUFFIX
 add:aci: (target = "ldap:///cn=*,cn=ranges,cn=etc,$SUFFIX";)(targetfilter = "(objectClass=ipaIDrange)")(version 3.0;acl "CIFS service can manage ID ranges for trust"; allow(all) userdn="ldap:///krbprincipalname=cifs/*@$REALM,cn=services,cn=accounts,$SUFFIX"; and groupdn="ldap:///cn=adtrust agents,cn=sysaccounts,cn=etc,$SUFFIX";)
+
+# IPA server hosts can modify replication managers members
+dn: cn=sysaccounts,cn=etc,$SUFFIX
+add:aci: (target = "ldap:///cn=replication managers,cn=sysaccounts,cn=etc,$SUFFIX")(targetattr = "member")(version 3.0; acl "IPA server hosts can modify replication managers members"; allow(read, search, compare, write) groupdn = "ldap:///cn=ipaservers,cn=hostgroups,cn=accounts,$SUFFIX";;)
+
+# IPA server hosts can change replica ID
+dn: cn=etc,$SUFFIX
+add:aci: (target = "ldap:///cn=replication,cn=etc,$SUFFIX";)(targetattr = "nsDS5ReplicaId")(version 3.0; acl "IPA server hosts can change replica ID"; allow(write) groupdn = "ldap:///cn=ipaservers,cn=hostgroups,cn=accounts,$SUFFIX";;)
+
+# IPA server hosts can create and manage own Custodia secrets
+dn: cn=custodia,cn=ipa,cn=etc,$SUFFIX
+add:aci: (target = "ldap:///cn=*/($$dn),cn=custodia,cn=ipa,cn=etc,$SUFFIX")(version 3.0; acl "IPA server hosts can create own Custodia secrets"; allow(add) groupdn = "ldap:///cn=ipaservers,cn=hostgroups,cn=accounts,$SUFFIX"; and userdn = "ldap:///fqdn=($$dn),cn=computers,cn=accounts,$SUFFIX";)
+add:aci: (target = "ldap:///cn=*/($$dn),cn=custodia,cn=ipa,cn=etc,$SUFFIX")(targetattr = "ipaPublicKey")(version 3.0; acl "IPA server hosts can manage own Custodia secrets"; allow(write) groupdn = "ldap:///cn=ipaservers,cn=hostgroups,cn=accounts,$SUFFIX"; and userdn = "ldap:///fqdn=($$dn),cn=computers,cn=accounts,$SUFFIX";)
diff --git a/install/updates/45-roles.update b/install/updates/45-roles.update
index dd4549f..fb28464 100644
--- a/install/updates/45-roles.update
+++ b/install/updates/45-roles.update
@@ -82,6 +82,7 @@ dn: cn=Delegation Administrator,cn=privileges,cn=pbac,$SUFFIX
 add:member: cn=Security Architect,cn=roles,cn=accounts,$SUFFIX
 
 dn: cn=Replication Administrators,cn=privileges,cn=pbac,$SUFFIX
+add:member: cn=ipaservers,cn=hostgroups,cn=accounts,$SUFFIX
 add:member: cn=Security Architect,cn=roles,cn=accounts,$SUFFIX
 
 dn: cn=Write IPA Configuration,cn=privileges,cn=pbac,$SUFFIX
-- 
2.4.3

From 978c68a70b50f86587631a139c221caadbeecf97 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Wed, 18 Nov 2015 08:34:35 +0100
Subject: [PATCH 2/5] ipautil: use file in a temporary dir as ccache in
 private_ccache

python-gssapi chokes on empty ccache files, so instead of creating an empty
temporary ccache file in private_ccache, create a temporary directory and
use a non-existent file in that directory as the ccache.

https://fedorahosted.org/freeipa/ticket/5401
---
 ipapython/ipautil.py | 11 +++++++++--
 1 file changed, 9 insertions(+), 2 deletions(-)

diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py
index 4551ea5..6a85f48 100644
--- a/ipapython/ipautil.py
+++ b/ipapython/ipautil.py
@@ -1357,8 +1357,10 @@ def posixify(string):
 def private_ccache(path=None):
 
     if path is None:
-        (desc, path) = tempfile.mkstemp(prefix='krbcc')
-        os.close(desc)
+        dir_path = tempfile.mkdtemp(prefix='krbcc')
+        path = os.path.join(dir_path, 'ccache')
+    else:
+        dir_path = None
 
     original_value = os.environ.get('KRB5CCNAME', None)
 
@@ -1374,6 +1376,11 @@ def private_ccache(path=None):
 
         if os.path.exists(path):
             os.remove(path)
+        if dir_path is not None:
+            try:
+                os.rmdir(dir_path)
+            except OSError:
+                pass
 
 
 if six.PY2:
-- 
2.4.3

From 3840ce73df4c69dc5cd600290fef0ba6e906016b Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Wed, 18 Nov 2015 08:51:34 +0100
Subject: [PATCH 3/5] replica promotion: use host credentials when setting up
 replication

Use the local host credentials rather than the user credentials when
setting up replication. The host must be a member of the ipaservers host
group. The user credentials are still required for connection check.

https://fedorahosted.org/freeipa/ticket/5401
---
 install/tools/ipa-replica-install          |  1 -
 ipaserver/install/server/replicainstall.py | 56 ++++++++++++++++++++++++------
 2 files changed, 45 insertions(+), 12 deletions(-)

diff --git a/install/tools/ipa-replica-install b/install/tools/ipa-replica-install
index 60a853b..10a1082 100755
--- a/install/tools/ipa-replica-install
+++ b/install/tools/ipa-replica-install
@@ -30,7 +30,6 @@ ReplicaInstall = cli.install_tool(
     usage='%prog [options] REPLICA_FILE',
     log_file_name=paths.IPAREPLICA_INSTALL_LOG,
     debug_option=True,
-    use_private_ccache=False,
 )
 
 
diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
index e6d96bb..4062261 100644
--- a/ipaserver/install/server/replicainstall.py
+++ b/ipaserver/install/server/replicainstall.py
@@ -801,7 +801,23 @@ def promote_check(installer):
 
     installutils.verify_fqdn(config.host_name, options.no_host_dns)
     installutils.verify_fqdn(config.master_host_name, options.no_host_dns)
-    installutils.check_creds(options, config.realm_name)
+
+    ccache = os.environ['KRB5CCNAME']
+    ipautil.kinit_keytab('host/{env.host}@{env.realm}'.format(env=api.env),
+                         paths.KRB5_KEYTAB,
+                         ccache)
+
+    if not options.skip_conncheck:
+        if installer._ccache is None:
+            del os.environ['KRB5CCNAME']
+        else:
+            os.environ['KRB5CCNAME'] = installer._ccache
+
+        try:
+            installutils.check_creds(options, config.realm_name)
+            installer._ccache = os.environ.get('KRB5CCNAME')
+        finally:
+            os.environ['KRB5CCNAME'] = ccache
 
     cafile = paths.IPA_CA_CRT
     if not ipautil.file_exists(cafile):
@@ -817,10 +833,19 @@ def promote_check(installer):
     replman = None
     try:
         # Try out authentication
-        conn.connect(ccache=os.environ.get('KRB5CCNAME'))
+        conn.connect(ccache=ccache)
         replman = ReplicationManager(config.realm_name,
                                      config.master_host_name, None)
 
+        # Check authorization
+        result = remote_api.Command['hostgroup_find'](
+            cn=u'ipaservers',
+            host=[unicode(api.env.host)]
+        )['result']
+
+        if not result:
+            raise errors.ACIError(info="Not authorized")
+
         # Check that we don't already have a replication agreement
         try:
             (acn, adn) = replman.agreement_dn(config.host_name)
@@ -938,7 +963,7 @@ def promote_check(installer):
                 print(str(e))
                 sys.exit(1)
     except errors.ACIError:
-        sys.exit("\nInsufficiently privileges to promote the server.")
+        sys.exit("\nInsufficient privileges to promote the server.")
     except errors.LDAPError:
         sys.exit("\nUnable to connect to LDAP server %s" %
                  config.master_host_name)
@@ -957,10 +982,18 @@ def promote_check(installer):
 
     # check connection
     if not options.skip_conncheck:
-        replica_conn_check(
-            config.master_host_name, config.host_name, config.realm_name,
-            options.setup_ca, 389,
-            options.admin_password, principal=options.principal)
+        if installer._ccache is None:
+            del os.environ['KRB5CCNAME']
+        else:
+            os.environ['KRB5CCNAME'] = installer._ccache
+
+        try:
+            replica_conn_check(
+                config.master_host_name, config.host_name, config.realm_name,
+                options.setup_ca, 389,
+                options.admin_password, principal=options.principal)
+        finally:
+            os.environ['KRB5CCNAME'] = ccache
 
     if not ipautil.file_exists(cafile):
         raise RuntimeError("CA cert file is not available.")
@@ -1186,6 +1219,8 @@ class Replica(BaseServer):
     def __init__(self, **kwargs):
         super(Replica, self).__init__(**kwargs)
 
+        self._ccache = os.environ.get('KRB5CCNAME')
+
         self._top_dir = None
         self._config = None
         self._update_hosts_file = False
@@ -1213,7 +1248,6 @@ class Replica(BaseServer):
             yield
             promote(self)
         else:
-            with ipautil.private_ccache():
-                install_check(self)
-                yield
-                install(self)
+            install_check(self)
+            yield
+            install(self)
-- 
2.4.3

From f5ca67b8a4a9885d020c7b8bfcc99a818462807c Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Wed, 18 Nov 2015 09:00:34 +0100
Subject: [PATCH 4/5] replica promotion: automatically add the local host to
 ipaservers

If the user is authorized to modify members of the ipaservers host group,
add the local host to ipaservers automatically.

https://fedorahosted.org/freeipa/ticket/5401
---
 ipaserver/install/server/replicainstall.py | 48 ++++++++++++++++++++++++++++--
 1 file changed, 46 insertions(+), 2 deletions(-)

diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
index 4062261..7ecd071 100644
--- a/ipaserver/install/server/replicainstall.py
+++ b/ipaserver/install/server/replicainstall.py
@@ -842,9 +842,35 @@ def promote_check(installer):
             cn=u'ipaservers',
             host=[unicode(api.env.host)]
         )['result']
+        add_to_ipaservers = not result
 
-        if not result:
-            raise errors.ACIError(info="Not authorized")
+        if add_to_ipaservers:
+            if installer._ccache is None:
+                del os.environ['KRB5CCNAME']
+            else:
+                os.environ['KRB5CCNAME'] = installer._ccache
+
+            try:
+                installutils.check_creds(options, config.realm_name)
+                installer._ccache = os.environ.get('KRB5CCNAME')
+            finally:
+                os.environ['KRB5CCNAME'] = ccache
+
+            conn.disconnect()
+            conn.connect(ccache=installer._ccache)
+
+            try:
+                result = remote_api.Command['hostgroup_show'](
+                    u'ipaservers',
+                    all=True,
+                    rights=True
+                )['result']
+
+                if 'w' not in result['attributelevelrights']['member']:
+                    raise errors.ACIError(info="Not authorized")
+            finally:
+                conn.disconnect()
+                conn.connect(ccache=ccache)
 
         # Check that we don't already have a replication agreement
         try:
@@ -1002,6 +1028,8 @@ def promote_check(installer):
     installer._fstore = fstore
     installer._sstore = sstore
     installer._config = config
+    installer._remote_api = remote_api
+    installer._add_to_ipaservers = add_to_ipaservers
 
 
 @common_cleanup
@@ -1011,6 +1039,22 @@ def promote(installer):
     sstore = installer._sstore
     config = installer._config
 
+    if installer._add_to_ipaservers:
+        ccache = os.environ['KRB5CCNAME']
+        remote_api = installer._remote_api
+        conn = remote_api.Backend.ldap2
+        try:
+            conn.connect(ccache=installer._ccache)
+
+            remote_api.Command['hostgroup_add_member'](
+                u'ipaservers',
+                host=[unicode(api.env.host)],
+            )
+        finally:
+            if conn.isconnected():
+                conn.disconnect()
+            os.environ['KRB5CCNAME'] = ccache
+
     # Save client file and merge in server directives
     target_fname = paths.IPA_DEFAULT_CONF
     fstore.backup_file(target_fname)
-- 
2.4.3

From 6e02576c3cf6b05a25fbc7c517b8645e350bf410 Mon Sep 17 00:00:00 2001
From: Jan Cholasta <jchol...@redhat.com>
Date: Tue, 1 Dec 2015 10:46:00 +0100
Subject: [PATCH 5/5] custodia: do not modify memberPrincipal on key update

https://fedorahosted.org/freeipa/ticket/5401
---
 ipapython/secrets/kem.py | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/ipapython/secrets/kem.py b/ipapython/secrets/kem.py
index 2a5f384..1025ed7 100644
--- a/ipapython/secrets/kem.py
+++ b/ipapython/secrets/kem.py
@@ -122,8 +122,7 @@ class KEMLdap(iSecLdap):
             conn.add_s(dn, mods)
         except Exception:  # pylint: disable=broad-except
             # This may fail if the entry already exists
-            mods = [(ldap.MOD_REPLACE, 'memberPrincipal', principal),
-                    (ldap.MOD_REPLACE, 'ipaPublicKey', public_key)]
+            mods = [(ldap.MOD_REPLACE, 'ipaPublicKey', public_key)]
             conn.modify_s(dn, mods)
 
 
-- 
2.4.3

-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to