On Wed, Jun 01, 2016 at 02:49:29PM +1000, Fraser Tweedale wrote:
> Updated patches attached; comments inline.
> 
> On Thu, May 05, 2016 at 04:52:29PM +1000, Fraser Tweedale wrote:
> > > I would rather add a new ACI than have one super-ACI for everything. That
> > > way you don't have to invent any complicated naming schemes *and* it will 
> > > be
> > > more apparent what the ACI does.
> > > 
> > OK, I'll simplify the scheme and create corresponding ACIs.
> > 
> I added new ACIs for hosts to manage Dogtag keys; they keys live in
> a container with RDN cn=dogtag, nested under the main custodia keys
> container.
> 
> > > >>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()'?
> > > 
> > > Works for me.
> > > 
> > Cool, thanks.
> > 
> This is implemented in the latest patch.
>
Rebased and updated patches attached.  The only substantive change
is a simplification of the ipa-pki-retrieve-key script (patch 0054)
following a change in Dogtag's ExternalProcessKeyRetriever (it now
handles JSON).

Thanks,
Fraser
From 1a88facefdbfd1bccc3177a8777f417cdaa4ced7 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftwee...@redhat.com>
Date: Tue, 3 May 2016 13:22:39 +1000
Subject: [PATCH] Add ACIs for Dogtag custodia client

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.

Also add ACIs to allow host principals to manage Dogtag custodia
keys for the same host.

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

diff --git a/install/updates/20-aci.update b/install/updates/20-aci.update
index 
4802ae0458e8b870bf3127764ebabac1a48f7cf2..0d617d849be8a1fdc76bdeeda7560b032a45e629
 100644
--- a/install/updates/20-aci.update
+++ b/install/updates/20-aci.update
@@ -136,3 +136,11 @@ 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";)
+
+# IPA server hosts can create and manage Dogtag Custodia secrets for same host
+dn: cn=ipa,cn=etc,$SUFFIX
+add:aci: (target = 
"ldap:///cn=*/($$dn),cn=dogtag,cn=custodia,cn=ipa,cn=etc,$SUFFIX")(version 3.0; 
acl "IPA server hosts can create Dogtag Custodia secrets for same host"; 
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=dogtag,cn=custodia,cn=ipa,cn=etc,$SUFFIX")(targetattr = 
"ipaPublicKey")(version 3.0; acl "IPA server hosts can manage Dogtag Custodia 
secrets for same host"; 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

From b8df17c8a42a144907c76d949ec25d5a61526ed4 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftwee...@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, add the key
entries in a nested container with RDN 'cn=<service name>'.  (The
container is assumed to exist).

This change does not affect searching because subtree search is
used, filtering on the ipaKeyUsage and memberPrincipal attributes.

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

diff --git a/ipapython/secrets/kem.py b/ipapython/secrets/kem.py
index 
0abf28ae4403a7b6225404df361d12cb07ccc70b..0b810b090a0e7dff09d64a5ef8752eba2676babc
 100644
--- a/ipapython/secrets/kem.py
+++ b/ipapython/secrets/kem.py
@@ -3,6 +3,7 @@
 from __future__ import print_function
 from ipaplatform.paths import paths
 from six.moves.configparser import ConfigParser
+from ipapython.dn import DN
 from cryptography.hazmat.backends import default_backend
 from cryptography.hazmat.primitives import serialization
 from cryptography.hazmat.primitives.asymmetric import rsa, ec
@@ -105,11 +106,23 @@ 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):
+        """
+        Write key for the host or service.
+
+        Service keys are nested one level beneath the 'cn=custodia'
+        container, in the 'cn=<servicename>' container; this allows
+        fine-grained control over key management permissions for
+        specific services.
+
+        The container is assumed to exist.
+
+        """
         public_key = self._format_public_key(key)
         conn = self.connect()
         name = '%s/%s' % (KEY_USAGE_MAP[usage], host)
-        dn = 'cn=%s,%s' % (name, self.keysbase)
+        service_rdn = ('cn', servicename) if servicename else DN()
+        dn = str(DN(('cn', name), service_rdn, self.keysbase))
         try:
             mods = [('objectClass', ['nsContainer',
                                      'ipaKeyPolicy',
@@ -170,15 +183,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

From 15b322874ec6cd16a12ef2f06724490fcb847937 Mon Sep 17 00:00:00 2001
From: Fraser Tweedale <ftwee...@redhat.com>
Date: Wed, 1 Jun 2016 08:07:33 +1000
Subject: [PATCH 54/54] Setup lightweight CA key retrieval on install/upgrade

Add the ipa-pki-retrieve-key helper program and configure
lightweight CA key replication on installation and upgrade.  The
specific configuration steps are:

- Add the 'dogtag/$HOSTNAME' service principal
- Create the pricipal's Custodia keys
- Retrieve the principal's keytab
- Configure Dogtag's CS.cfg to use ExternalProcessKeyRetriever
  to invoke ipa-pki-retrieve-key for key retrieval

Part of: https://fedorahosted.org/freeipa/ticket/4559
---
 freeipa.spec.in                       |  1 +
 install/share/bootstrap-template.ldif |  6 ++++
 install/tools/Makefile.am             |  1 +
 install/tools/ipa-pki-retrieve-key    | 33 +++++++++++++++++++++
 install/updates/73-custodia.update    |  5 ++++
 ipalib/constants.py                   |  1 +
 ipaserver/install/ca.py               |  9 +++++-
 ipaserver/install/cainstance.py       | 56 +++++++++++++++++++++++++++++++++++
 ipaserver/install/server/install.py   |  6 ++--
 ipaserver/install/server/upgrade.py   |  4 ++-
 10 files changed, 117 insertions(+), 5 deletions(-)
 create mode 100755 install/tools/ipa-pki-retrieve-key

diff --git a/freeipa.spec.in b/freeipa.spec.in
index 
d5d78f806607bfd61daab43f50dba8caa6d0661c..b42987de2fbdd6923303d6856cc46146500b0532
 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -1074,6 +1074,7 @@ fi
 %{_libexecdir}/ipa/ipa-dnskeysync-replica
 %{_libexecdir}/ipa/ipa-ods-exporter
 %{_libexecdir}/ipa/ipa-httpd-kdcproxy
+%{_libexecdir}/ipa/ipa-pki-retrieve-key
 %dir %{_libexecdir}/ipa/oddjob
 %attr(0755,root,root) %{_libexecdir}/ipa/oddjob/org.freeipa.server.conncheck
 %config(noreplace) %{_sysconfdir}/dbus-1/system.d/org.freeipa.server.conf
diff --git a/install/share/bootstrap-template.ldif 
b/install/share/bootstrap-template.ldif
index 
83be4399508a905f8eae7e2f59140a6b4051b661..f6ab35495ad7e9377404eb7a6b0bca26906f5421
 100644
--- a/install/share/bootstrap-template.ldif
+++ b/install/share/bootstrap-template.ldif
@@ -179,6 +179,12 @@ objectClass: nsContainer
 objectClass: top
 cn: custodia
 
+dn: cn=dogtag,cn=custodia,cn=ipa,cn=etc,$SUFFIX
+changetype: add
+objectClass: nsContainer
+objectClass: top
+cn: dogtag
+
 dn: cn=s4u2proxy,cn=etc,$SUFFIX
 changetype: add
 objectClass: nsContainer
diff --git a/install/tools/Makefile.am b/install/tools/Makefile.am
index 
7212dabdbf968ee76cb2830e5054fe1a27712225..2866a30b2feacfec29a22eaf7965216fbbd69665
 100644
--- a/install/tools/Makefile.am
+++ b/install/tools/Makefile.am
@@ -39,6 +39,7 @@ EXTRA_DIST =                  \
 appdir = $(libexecdir)/ipa/
 app_SCRIPTS =                  \
        ipa-httpd-kdcproxy      \
+       ipa-pki-retrieve-key    \
        $(NULL)
 
 MAINTAINERCLEANFILES =         \
diff --git a/install/tools/ipa-pki-retrieve-key 
b/install/tools/ipa-pki-retrieve-key
new file mode 100755
index 
0000000000000000000000000000000000000000..0bfa877b049a3c136a13ac92289c8836d145adf1
--- /dev/null
+++ b/install/tools/ipa-pki-retrieve-key
@@ -0,0 +1,33 @@
+#!/usr/bin/python2
+
+from __future__ import print_function
+
+import ConfigParser
+import os
+import sys
+
+from ipalib import constants
+from ipaplatform.paths import paths
+from ipapython.secrets.client import CustodiaClient
+
+conf = ConfigParser.ConfigParser()
+conf.read(paths.IPA_DEFAULT_CONF)
+hostname = conf.get('global', 'host')
+realm = conf.get('global', 'realm')
+
+keyname = "ca_wrapped/" + sys.argv[1]
+servername = sys.argv[2]
+
+service = constants.PKI_GSSAPI_SERVICE_NAME
+client_keyfile = os.path.join(paths.PKI_TOMCAT, service + '.keys')
+client_keytab = os.path.join(paths.PKI_TOMCAT, service + '.keytab')
+
+client = CustodiaClient(
+    client_service='%s@%s' % (service, hostname), server=servername,
+    realm=realm, ldap_uri="ldaps://" + hostname,
+    keyfile=client_keyfile, keytab=client_keytab,
+    )
+
+# Print the response JSON to stdout; it is already in the format
+# that Dogtag's ExternalProcessKeyRetriever expects
+print(client.fetch_key(keyname, store=False))
diff --git a/install/updates/73-custodia.update 
b/install/updates/73-custodia.update
index 
f6520fb2e36dd1b234344a8cc4199ab72c664163..60f805ab82f141777c0d7731d4a0362e76961cce
 100644
--- a/install/updates/73-custodia.update
+++ b/install/updates/73-custodia.update
@@ -2,3 +2,8 @@ dn: cn=custodia,cn=ipa,cn=etc,$SUFFIX
 default: objectClass: top
 default: objectClass: nsContainer
 default: cn: custodia
+
+dn: cn=dogtag,cn=custodia,cn=ipa,cn=etc,$SUFFIX
+default: objectClass: top
+default: objectClass: nsContainer
+default: cn: dogtag
diff --git a/ipalib/constants.py b/ipalib/constants.py
index 
a2cbfdbcda07429478f97c62cc6896890f2f7979..97dff1d805a4f77469882103ab63cdb0fa55a024
 100644
--- a/ipalib/constants.py
+++ b/ipalib/constants.py
@@ -264,3 +264,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 
3a827aee86616d874bdc3d1a5735e46d1ab9bf9b..ac72c76883fda00e2f258a9ecfe9925722041fe9
 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')
@@ -197,6 +197,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 
475e74d7fbf796a87e1673fc997c05e41e352d2a..9f6a5037f92ad0168e80267999c7fbf5b7634ae0
 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,64 @@ 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')
+        directives = [
+            ('features.authority.keyRetrieverClass',
+                'com.netscape.ca.ExternalProcessKeyRetriever'),
+            ('features.authority.keyRetrieverConfig.executable',
+                '/usr/libexec/ipa/ipa-pki-retrieve-key'),
+        ]
+        for k, v in directives:
+            installutils.set_directive(
+                paths.CA_CS_CFG_PATH, k, v, 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 
4b8ef048618823387481fdde53609e526bf3f244..e8d4db878207e3369dc1de7baeaa333c5374fc61
 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 
2398aea90bb1def6ab1534d3640a6bfa20a6b237..1a1090f0c767238062916ec715a59983ddc59fdb
 100644
--- a/ipaserver/install/server/upgrade.py
+++ b/ipaserver/install/server/upgrade.py
@@ -1492,7 +1492,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)
     ca_running = ca.is_running()
 
     with installutils.stopped_service('pki-tomcatd', 'pki-tomcat'):
@@ -1697,6 +1698,7 @@ def upgrade_configuration():
 
     if ca.is_configured():
         cainstance.repair_profile_caIPAserviceCert()
+        ca.setup_lightweight_ca_key_retrieval()
 
     set_sssd_domain_option('ipa_server_mode', 'True')
 
-- 
2.5.5

-- 
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