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