[Freeipa-devel] [PATCH] 0053..0054 Configure lightweight CA key replication

Fraser Tweedale ftweedal at redhat.com
Wed May 4 04:04:05 UTC 2016


On Tue, May 03, 2016 at 05:05:58PM +1000, Fraser Tweedale wrote:
> On Tue, Apr 26, 2016 at 10:02:45AM +0200, Jan Cholasta wrote:
> > On 21.4.2016 05:30, Fraser Tweedale wrote:
> > >On Thu, Apr 14, 2016 at 04:39:37PM +1000, Fraser Tweedale wrote:
> > >>Hi all,
> > >>
> > >>The attached patches configure lightweight CA key replication on IPA
> > >>CAs, on upgrade and installation.
> > >>
> > >>Patches 0051..0052 from my other mail are also needed for the system
> > >>to work, but this patchset does not depend on them and can be
> > >>reviewed independently.
> > >>
> > >>There is also no hard dependency on the (unreleased) Dogtag 10.3.0b1
> > >>- it just puts the necessary principals/keys/configuration in place.
> > >>
> > >>Cheers,
> > >>Fraser
> > >>
> > >New patches attached;  0054-2 changes the service name from
> > >'dogtag-ipa-custodia' to just 'dogtag', and adds an ACI to allow the
> > >principal to search server Custodia keys.
> > 
> Honza, thanks for review.  Comments inline.
> 
> > Patch 53:
> > 
> > I'm not sure about this approach - the cn of custodia keys in LDAP is a
> > free-form string, I would not tie it to service names, but rather try to
> > keep it short.
> > 
> > In the key replication section of the design page, you mention "ca/$NAME", I
> > think this is a good template for the cn and that we should stick to it.
> > 
> This scheme (or something like it, *without* '/' as the separator)
> is needed to satisfy the ACI that allows host principals to manage
> Custodia keys:
> 
>     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";)
> 
> The CN must contain the hostname, and we must also disambiguate on
> key type.  The current scheme is:
> 
>     {sig,enc}~dogtag/<HOSTNAME>
>     e.g.
>     enc~dogtag/f23-2.ipa.local
> 
> The first separator cannot be '/' because the '*' wildcard in the
> ACI is not greedy - the captured text would include the servicename
> and fail to match any userdn.
> 
> If you do not like '~' feel free to suggest a different symbol :)
> The alternative is to add more ACIs.
> 
> > 
> > Patch 54:
> > 
> > 1) This belongs to CAInstance.configure_instance():
> > 
> > +    CA = cainstance.CAInstance(
> > +            api.env.realm, certs.NSS_DIR, host_name=api.env.host)
> > +    CA.setup_lightweight_ca_key_retrieval()
> > 
> See comments for (5).
> 
> > 
> > 2) Any ACI changes should be in a separate patch. (What happened to patch
> > 52?)
> > 
> Patch 52 added an ACI that allowed any authenticated user to see the
> keys.  Simo wanted it limited it to the Dogtag principal so I
> rescinded patch 52 and added the ACI in the same patch where the
> principal was added.
> 
> Separate patch is no problem; I will resurrect number 52.
> 
> > 
> > 3) This is not a platform constant, just a constant:
> > 
> > +    PKI_GSSAPI_SERVICE_NAME = 'dogtag'
> > 
> Thanks, will put it in `ipalib.constants'.
> 
> > 
> > 4) CAInstance.setup_lightweight_ca_key_retrieval() does too much. Please
> > split it into a "setup keytab" and "setup custodia" parts.
> > 
> Will extract methods for next patchset.
> 
> > 
> > 5) This also belongs to CAInstance.configure_instance():
> > 
> > +    if setup_ca:
> > +        # CA was configured before Kerberos;
> > +        # add Custodia client princ and keys now
> > +        ca_instance.setup_lightweight_ca_key_retrieval()
> > 
> > In order for that to work, you need to move the ca.install_step_1() after
> > krb.create_instance(), but that should be OK, since KrbInstance does not
> > talk to the CA.
> > 
> `setup_lightweight_ca_key_retrieval' calls `kadmin_addprinc', which
> fails if called before `krb.create_instance' due to missing
> krb5.conf::
> 
>     2016-05-03T06:29:23Z DEBUG args=kadmin.local -q addprinc -randkey dogtag/f23-2.ipa.local at IPA.LOCAL -x ipa-setup-override-restrictions
>     2016-05-03T06:29:23Z DEBUG Process finished, return code=1
>     2016-05-03T06:29:23Z DEBUG stdout=
>     2016-05-03T06:29:23Z DEBUG stderr=kadmin.local: unable to get default realm
> 
> Moving `ca.install_step_1()' to after `krb.create_instance()' does
> not help, because `CAInstance.configure_instance' is called from
> `ca.install_step_0()'.
> 
> However, calling `CAInstance.setup_lightweight_ca_key_retrieval()'
> *directly* from `ca.install_step_1' would probably work.  Are you
> happy with putting it there, instead of `configure_instance()'?
> 
> Cheers,
> Fraser
> 
Updated patches attached, include bringing back 0052-2 for the ACI
change.

Cheers,
Fraser
-------------- next part --------------
From 3d047e3dc1e7f700751c0f52f26326764b70d94d Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Tue, 3 May 2016 13:22:39 +1000
Subject: [PATCH] Allow Dogtag service principals to read Custodia keys

The "dogtag/$HOSTNAME@$REALM" service principal uses Custodia to
retrieve lightweight CA signing keys, and therefore needs search and
read access to Custodia keys.  Add an ACI to permit this.

Part of: https://fedorahosted.org/freeipa/ticket/4559
---
 install/updates/20-aci.update | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/install/updates/20-aci.update b/install/updates/20-aci.update
index 4802ae0458e8b870bf3127764ebabac1a48f7cf2..2e9a36da442c392c9161861615b1744eeb6b799c 100644
--- a/install/updates/20-aci.update
+++ b/install/updates/20-aci.update
@@ -136,3 +136,6 @@ add:aci: (target = "ldap:///cn=replication,cn=etc,$SUFFIX")(targetattr = "nsDS5R
 dn: 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";)
+
+# Dogtag service principals can search Custodia keys
+add:aci: (target = "ldap:///cn=*,cn=custodia,cn=ipa,cn=etc,$SUFFIX")(targetattr = "ipaPublicKey || ipaKeyUsage || memberPrincipal")(version 3.0; acl "Dogtag service principals can search Custodia keys"; allow(read, search, compare) userdn = "ldap:///krbprincipalname=dogtag/*@$REALM,cn=services,cn=accounts,$SUFFIX";)
-- 
2.5.5

-------------- next part --------------
From b303284245627e4214d1ba3d03bdb06fc89c3a53 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Mon, 11 Apr 2016 12:42:35 +1000
Subject: [PATCH 53/54] Optionally add service name to Custodia key DNs

Lightweight CAs support introduces new service principals for
Dogtag, with Custodia keys.  The current Custodia key creation uses
a DN that contains only they key type and the hostname, so keys for
multiple services on the same host cannot be created.

Add the 'generate_keys' method to generate keys for a host or an
arbitrary service.  When a service name is given, include the
service name in the DN.

This change does not affect searching because all searching is done
using the ipaKeyUsage and memberPrincipal attributes.

Part of: https://fedorahosted.org/freeipa/ticket/4559
---
 ipapython/secrets/kem.py | 14 +++++++++-----
 1 file changed, 9 insertions(+), 5 deletions(-)

diff --git a/ipapython/secrets/kem.py b/ipapython/secrets/kem.py
index 1025ed7980f055c82c602634e8845fa490cf0514..533121779241d30e19fef4c050bb69c55d29ec22 100644
--- a/ipapython/secrets/kem.py
+++ b/ipapython/secrets/kem.py
@@ -105,10 +105,11 @@ class KEMLdap(iSecLdap):
             encoding=serialization.Encoding.DER,
             format=serialization.PublicFormat.SubjectPublicKeyInfo)
 
-    def set_key(self, usage, host, principal, key):
+    def set_key(self, usage, servicename, host, principal, key):
         public_key = self._format_public_key(key)
         conn = self.connect()
-        name = '%s/%s' % (KEY_USAGE_MAP[usage], host)
+        service_segment = '~' + servicename if servicename else ''
+        name = '%s%s/%s' % (KEY_USAGE_MAP[usage], service_segment, host)
         dn = 'cn=%s,%s' % (name, self.keysbase)
         try:
             mods = [('objectClass', ['nsContainer',
@@ -170,15 +171,18 @@ class IPAKEMKeys(KEMKeysStore):
         return conn.get_key(usage, kid)
 
     def generate_server_keys(self):
-        principal = 'host/%s@%s' % (self.host, self.realm)
+        self.generate_keys()
+
+    def generate_keys(self, servicename=None):
+        principal = '%s/%s@%s' % (servicename or 'host', self.host, self.realm)
         # Neutralize the key with read if any
         self._server_keys = None
         # Generate private key and store it
         pubkeys = newServerKeys(self.config['server_keys'], principal)
         # Store public key in LDAP
         ldapconn = KEMLdap(self.ldap_uri)
-        ldapconn.set_key(KEY_USAGE_SIG, self.host, principal, pubkeys[0])
-        ldapconn.set_key(KEY_USAGE_ENC, self.host, principal, pubkeys[1])
+        ldapconn.set_key(KEY_USAGE_SIG, servicename, self.host, principal, pubkeys[0])
+        ldapconn.set_key(KEY_USAGE_ENC, servicename, self.host, principal, pubkeys[1])
 
     @property
     def server_keys(self):
-- 
2.5.5

-------------- next part --------------
From d38f29d48da644d9cdb9455384ee4ae107e9acc0 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftweedal at redhat.com>
Date: Mon, 11 Apr 2016 16:47:33 +1000
Subject: [PATCH 54/54] Setup lightweight CA key retrieval on install/upgrade

To configure Dogtag lightweight CA key replication on installation
and upgrade:

- add the 'dogtag/$HOSTNAME' service principal
- create the pricipal's Custodia keys
- retrieve keytab
- configure the IPACustodiaKeyRetriever in CS.cfg

Part of: https://fedorahosted.org/freeipa/ticket/4559
---
 ipalib/constants.py                 |  1 +
 ipaserver/install/ca.py             |  9 ++++++-
 ipaserver/install/cainstance.py     | 52 +++++++++++++++++++++++++++++++++++++
 ipaserver/install/server/install.py |  6 ++---
 ipaserver/install/server/upgrade.py |  6 ++++-
 5 files changed, 69 insertions(+), 5 deletions(-)

diff --git a/ipalib/constants.py b/ipalib/constants.py
index 021f18cd366b821427bdbfcc5e354d2047ef39b1..d544595898e52d3910cff94fc3cc0276fe099a98 100644
--- a/ipalib/constants.py
+++ b/ipalib/constants.py
@@ -261,3 +261,4 @@ REPL_AGMT_STRIP_ATTRS = ('modifiersName',
 
 DOMAIN_SUFFIX_NAME = 'domain'
 CA_SUFFIX_NAME = 'ca'
+PKI_GSSAPI_SERVICE_NAME = 'dogtag'
diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py
index acc54334e1b6e64a0b2d4b097e7464c95988f3f5..4d25af5870ce7a130c996d1e7de4a05c2167d494 100644
--- a/ipaserver/install/ca.py
+++ b/ipaserver/install/ca.py
@@ -182,7 +182,7 @@ def install_step_1(standalone, replica_config, options):
 
     basedn = ipautil.realm_to_suffix(realm_name)
 
-    ca = cainstance.CAInstance(realm_name, certs.NSS_DIR)
+    ca = cainstance.CAInstance(realm_name, certs.NSS_DIR, host_name=host_name)
 
     if standalone:
         ca.stop('pki-tomcat')
@@ -193,6 +193,13 @@ def install_step_1(standalone, replica_config, options):
     # This is done within stopped_service context, which restarts CA
     ca.enable_client_auth_to_db(paths.CA_CS_CFG_PATH)
 
+    # Lightweight CA key retrieval is configured in step 1 instead
+    # of CAInstance.configure_instance (which is invoked from step
+    # 0) because kadmin_addprinc fails until krb5.conf is installed
+    # by krb.create_instance.
+    #
+    ca.setup_lightweight_ca_key_retrieval()
+
     if standalone and replica_config is None:
         serverid = installutils.realm_to_serverid(realm_name)
         dirname = dsinstance.config_dirname(serverid)
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
index a21f7d2671461dfb99797d39fc7ee5706317241f..ec31283972d84f0b682bea444a70286585fa489e 100644
--- a/ipaserver/install/cainstance.py
+++ b/ipaserver/install/cainstance.py
@@ -45,6 +45,7 @@ from six.moves.configparser import ConfigParser, RawConfigParser
 from ipalib import api
 from ipalib import pkcs10, x509
 from ipalib import errors
+import ipalib.constants
 
 from ipaplatform import services
 from ipaplatform.constants import constants
@@ -59,6 +60,7 @@ from ipapython.certdb import get_ca_nickname
 from ipapython.dn import DN
 from ipapython.ipa_log_manager import log_mgr,\
     standard_logging_setup, root_logger
+from ipapython.secrets.kem import IPAKEMKeys
 
 from ipaserver.install import certs
 from ipaserver.install import dsinstance
@@ -66,6 +68,7 @@ from ipaserver.install import installutils
 from ipaserver.install import ldapupdate
 from ipaserver.install import replication
 from ipaserver.install import service
+from ipaserver.install import sysupgrade
 from ipaserver.install.dogtaginstance import (export_kra_agent_pem,
                                               DogtagInstance)
 from ipaserver.plugins import ldap2
@@ -1356,11 +1359,60 @@ class CAInstance(DogtagInstance):
         self.step("updating IPA configuration", update_ipa_conf)
         self.step("Restart HTTP server to pick up changes",
                   self.__restart_http_instance)
+        self.step("Configure lightweight CA key retrieval",
+                  self.setup_lightweight_ca_key_retrieval)
 
         self.step("enabling CA instance", self.__enable_instance)
 
         self.start_creation(runtime=210)
 
+    def setup_lightweight_ca_key_retrieval(self):
+        if sysupgrade.get_upgrade_state('dogtag', 'setup_lwca_key_retrieval'):
+            return
+
+        root_logger.info('[Set up lightweight CA key retrieval]')
+
+        self.__setup_lightweight_ca_key_retrieval_kerberos()
+        self.__setup_lightweight_ca_key_retrieval_custodia()
+
+        root_logger.info('Configuring key retriever')
+        installutils.set_directive(
+            paths.CA_CS_CFG_PATH,
+            'features.authority.keyRetrieverClass',
+            'com.netscape.ca.IPACustodiaKeyRetriever',
+            quotes=False, separator='=')
+
+        sysupgrade.set_upgrade_state('dogtag', 'setup_lwca_key_retieval', True)
+
+    def __setup_lightweight_ca_key_retrieval_kerberos(self):
+        service = ipalib.constants.PKI_GSSAPI_SERVICE_NAME
+        principal = '{}/{}@{}'.format(service, api.env.host, self.realm)
+        pent = pwd.getpwnam(constants.PKI_USER)
+
+        root_logger.info('Creating principal')
+        installutils.kadmin_addprinc(principal)
+        self.suffix = ipautil.realm_to_suffix(self.realm)
+        if not self.admin_conn:
+            self.ldap_connect()
+        self.move_service(principal)
+
+        root_logger.info('Retrieving keytab')
+        keytab = os.path.join(paths.PKI_TOMCAT, service + '.keytab')
+        installutils.create_keytab(keytab, principal)
+        os.chmod(keytab, 0o600)
+        os.chown(keytab, pent.pw_uid, pent.pw_gid)
+
+    def __setup_lightweight_ca_key_retrieval_custodia(self):
+        service = ipalib.constants.PKI_GSSAPI_SERVICE_NAME
+        pent = pwd.getpwnam(constants.PKI_USER)
+
+        root_logger.info('Creating Custodia keys')
+        keyfile = os.path.join(paths.PKI_TOMCAT, service + '.keys')
+        keystore = IPAKEMKeys({'server_keys': keyfile})
+        keystore.generate_keys(service)
+        os.chmod(keyfile, 0o600)
+        os.chown(keyfile, pent.pw_uid, pent.pw_gid)
+
 
 def replica_ca_install_check(config):
     if not config.setup_ca:
diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py
index 2d71b3837214683566f37644c41a680953a64207..9f5219887c671f2663a791068d6383ab18443265 100644
--- a/ipaserver/install/server/install.py
+++ b/ipaserver/install/server/install.py
@@ -891,9 +891,6 @@ def install(installer):
     # we now need to enable ssl on the ds
     ds.enable_ssl()
 
-    if setup_ca:
-        ca.install_step_1(False, None, options)
-
     krb = krbinstance.KrbInstance(fstore)
     if options.pkinit_cert_files:
         krb.create_instance(realm_name, host_name, domain_name,
@@ -907,6 +904,9 @@ def install(installer):
                             setup_pkinit=not options.no_pkinit,
                             subject_base=options.subject)
 
+    if setup_ca:
+        ca.install_step_1(False, None, options)
+
     # The DS instance is created before the keytab, add the SSL cert we
     # generated
     ds.add_cert_to_service()
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
index 4f3a2cb065319a26bfa517b4d1d2cb4b41fb486d..f2e01b546d4be0177eeee1e6a264586321646015 100644
--- a/ipaserver/install/server/upgrade.py
+++ b/ipaserver/install/server/upgrade.py
@@ -1467,7 +1467,8 @@ def upgrade_configuration():
     if subject_base:
         sub_dict['SUBJECT_BASE'] = subject_base
 
-    ca = cainstance.CAInstance(api.env.realm, certs.NSS_DIR)
+    ca = cainstance.CAInstance(
+            api.env.realm, certs.NSS_DIR, host_name=api.env.host)
 
     with installutils.stopped_service('pki-tomcatd', 'pki-tomcat'):
         # Dogtag must be stopped to be able to backup CS.cfg config
@@ -1663,6 +1664,9 @@ def upgrade_configuration():
     ca_import_included_profiles(ca)
     add_default_caacl(ca)
 
+    if ca.is_configured():
+        ca.setup_lightweight_ca_key_retrieval()
+
     set_sssd_domain_option('ipa_server_mode', 'True')
 
     if ds_running and not ds.is_running():
-- 
2.5.5



More information about the Freeipa-devel mailing list