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.

Cheers,
Fraser
From 7395f98bcec1e54b076cf9e2ceac1e4368cfe7c9 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 78bdf20af5dfaaa67ba0ba8b160f18c8cf31d54c 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 38f0d556f355eba118be0f838e637279f7aae55b 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    | 44 +++++++++++++++++++++++++++
 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, 128 insertions(+), 5 deletions(-)
 create mode 100755 install/tools/ipa-pki-retrieve-key

diff --git a/freeipa.spec.in b/freeipa.spec.in
index 
c6499a5c6073ac4c2814d7ad14964927b88b1981..0946b30bcf6d2624d4e2baf3a6fed3436a61a58c
 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 
628a8e2e0f5483b9f6f565b0c7d11eb000a5912d..456633012510acbce3c73fb9bf9b20f7d80944fe
 100644
--- a/install/share/bootstrap-template.ldif
+++ b/install/share/bootstrap-template.ldif
@@ -173,6 +173,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..6f6ea44fba7fbcddbd55ba8be373efb5e1301d06
--- /dev/null
+++ b/install/tools/ipa-pki-retrieve-key
@@ -0,0 +1,44 @@
+#!/usr/bin/python2
+
+from __future__ import print_function
+
+import ConfigParser
+import base64
+import os
+import sys
+
+from jwcrypto.common import json_decode
+
+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,
+    )
+
+result_json = client.fetch_key(keyname, store=False)
+result = json_decode(result_json)
+certificate = result["certificate"]
+wrapped_key = base64.b64decode(result["wrapped_key"])
+
+# Custodia returns a PEM-encoded certificate and a base64-encoded
+# DER PKIArchiveOptions object.  Output these values, separated by a
+# null byte (certificate first), to be read by the Java
+# IPACustodiaKeyRetriever that invoked this program.
+
+print(certificate, wrapped_key, sep='\0', end='')
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 
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 
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 
e4d4ab1635598eb083b2aec0c5e51a1eec63c8ce..36e8f330bcc3f124116a13f03c890436651a2654
 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