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