On 4.12.2015 11:15, Petr Vobornik wrote:
On 12/03/2015 03:11 PM, Martin Basti wrote:


On 01.12.2015 12:19, Jan Cholasta wrote:
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.



Functional ACK


Simo, do you want to review the ACIs or other things it the patches? Or
can the patches be pushed?

There were no changes in the ACIs since last time.

Rebased patches attached.

--
Jan Cholasta
From 0b7455460928df1376029e7b3b9d5c76308eb148 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 fa867f3..842dff0 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 4b29ff8d2142ab0ec6db583aa48b1d906e40f891 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 13a8b82..aaa841c 100644
--- a/ipaserver/install/replication.py
+++ b/ipaserver/install/replication.py
@@ -1267,117 +1267,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 4aefeea16a15605571655e13a34d9e483abe6ec7 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 005a1551398539a325604f815c230e79323c21df 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 104f9d1..89047b2 100644
--- a/ipapython/ipautil.py
+++ b/ipapython/ipautil.py
@@ -1345,8 +1345,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)
 
@@ -1362,6 +1364,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 615672dd12cb2054d6e121bed3f6be17f9b066f1 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 ec77ab2..1e97dfb 100644
--- a/ipaserver/install/server/replicainstall.py
+++ b/ipaserver/install/server/replicainstall.py
@@ -925,7 +925,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):
@@ -941,10 +957,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)
@@ -1069,7 +1094,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)
@@ -1088,10 +1113,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.")
@@ -1331,6 +1364,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
@@ -1402,7 +1437,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 91357d4247c9f4f397697ecfc6cd331eea4068bc 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 1e97dfb..efc31e0 100644
--- a/ipaserver/install/server/replicainstall.py
+++ b/ipaserver/install/server/replicainstall.py
@@ -966,9 +966,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:
@@ -1133,6 +1159,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
     installer._dirsrv_pkcs12_file = dirsrv_pkcs12_file
     installer._dirsrv_pkcs12_info = dirsrv_pkcs12_info
     installer._http_pkcs12_file = http_pkcs12_file
@@ -1154,6 +1182,22 @@ def promote(installer):
     pkinit_pkcs12_file = installer._pkinit_pkcs12_file
     pkinit_pkcs12_info = installer._pkinit_pkcs12_info
 
+    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 3b5b236b9879fc0c6ce3159a045a321bdfddfd46 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