Updated patches 0053-6 and 0054-6 attached.  Comments inline.

Thanks,
Fraser

On Wed, Jun 08, 2016 at 10:31:07AM +0200, Jan Cholasta wrote:
> Patch 0052:
> 
> The target of the "Dogtag service principals can search Custodia keys" ACI
> matches keys in the top-level Custodia container, but not in the Dogtag
> container. Is this intentional?
> 
ACI applies to all entries under the 'cn=custodia' tree, not only
the entries directly under it.

> 
> Patch 0053:
> 
> It seems the `servicename`+`host` and `principal` arguments of set_key() are
> carrying the same information, could you remove one of them?
> 
OK, just `principal` now.

> 
> Patch 0054:
> 
> 1) Please use ipalib.config to read IPA configuration in
> ipa-pki-retrieve-key:
> 
>     from ipalib.config import Env
> 
>     env = Env(in_server=True)
>     hostname = env.host
>     realm = env.realm
> 
I have implemented this; it was necessary to also call
`env._finalize()`.

> 2) I'm curious why you changed the key name from "ca/$NAME" to
> "ca_wrapped/$NAME". Aren't *all* keys in Custodia wrapped?
> 
The payload of the `ca_wrapped` Custodia store is a
PKIArchiveOptions object wrapped by the main CA's private key.
(This payload is then encrypted by Custodia, as all Custodia
payloads are).

See my patch 0056 [1] for implementation details.

[1] https://www.redhat.com/archives/freeipa-devel/2016-May/msg00079.html

> 3) Given that Dogtag ExternalProcessKeyRetriever handles JSON *now*, I would
> expect a minimum required version bump in the spec file.
> 
Indeed; I added this in latest patches.
From 5116bc9cd4a2f3f1dde4fbc5f941b091036fa78c 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 | 27 ++++++++++++++++++++++-----
 1 file changed, 22 insertions(+), 5 deletions(-)

diff --git a/ipapython/secrets/kem.py b/ipapython/secrets/kem.py
index 
0abf28ae4403a7b6225404df361d12cb07ccc70b..d45efe8cc4fb63ae9d8c0b2c920fd1f9e5331a9d
 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,24 @@ class KEMLdap(iSecLdap):
             encoding=serialization.Encoding.DER,
             format=serialization.PublicFormat.SubjectPublicKeyInfo)
 
-    def set_key(self, usage, host, principal, key):
+    def set_key(self, usage, 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()
+        servicename, host = principal.split('@')[0].split('/')
         name = '%s/%s' % (KEY_USAGE_MAP[usage], host)
-        dn = 'cn=%s,%s' % (name, self.keysbase)
+        service_rdn = ('cn', servicename) if servicename != 'host' else DN()
+        dn = str(DN(('cn', name), service_rdn, self.keysbase))
         try:
             mods = [('objectClass', ['nsContainer',
                                      'ipaKeyPolicy',
@@ -170,15 +184,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('host')
+
+    def generate_keys(self, servicename):
+        principal = '%s/%s@%s' % (servicename, 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, principal, pubkeys[0])
+        ldapconn.set_key(KEY_USAGE_ENC, principal, pubkeys[1])
 
     @property
     def server_keys(self):
-- 
2.5.5

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

Also bump the minimum version of Dogtag to 10.3.2.

Part of: https://fedorahosted.org/freeipa/ticket/4559
---
 freeipa.spec.in                       |  7 +++--
 install/share/bootstrap-template.ldif |  6 ++++
 install/tools/Makefile.am             |  1 +
 install/tools/ipa-pki-retrieve-key    | 32 ++++++++++++++++++++
 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, 119 insertions(+), 8 deletions(-)
 create mode 100755 install/tools/ipa-pki-retrieve-key

diff --git a/freeipa.spec.in b/freeipa.spec.in
index 
d5d78f806607bfd61daab43f50dba8caa6d0661c..8eb3bd5fc385f9268909e8df0d570649a7de0c39
 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -94,7 +94,7 @@ BuildRequires:  libunistring-devel
 BuildRequires:  python-lesscpy
 BuildRequires:  python-yubico >= 1.2.3
 BuildRequires:  openssl-devel
-BuildRequires:  pki-base >= 10.2.6
+BuildRequires:  pki-base >= 10.3.2
 BuildRequires:  python-pytest-multihost >= 0.5
 BuildRequires:  python-pytest-sourceorder
 BuildRequires:  python-kdcproxy >= 0.3
@@ -155,8 +155,8 @@ Requires(post): systemd-units
 Requires: selinux-policy >= %{selinux_policy_version}
 Requires(post): selinux-policy-base >= %{selinux_policy_version}
 Requires: slapi-nis >= 0.55-1
-Requires: pki-ca >= 10.2.6-19
-Requires: pki-kra >= 10.2.6-19
+Requires: pki-ca >= 10.3.2
+Requires: pki-kra >= 10.3.2
 Requires(preun): python systemd-units
 Requires(postun): python systemd-units
 Requires: zip
@@ -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..740e799d2d8047570abecfb58f7dec07266ef429
--- /dev/null
+++ b/install/tools/ipa-pki-retrieve-key
@@ -0,0 +1,32 @@
+#!/usr/bin/python2
+
+from __future__ import print_function
+
+import os
+import sys
+
+from ipalib import constants
+from ipalib.config import Env
+from ipaplatform.paths import paths
+from ipapython.secrets.client import CustodiaClient
+
+env = Env()
+env._finalize()
+
+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')
+
+# pylint: disable=no-member
+client = CustodiaClient(
+    client_service='%s@%s' % (service, env.host), server=servername,
+    realm=env.realm, ldap_uri="ldaps://" + env.host,
+    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