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 <[email protected]> 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 <[email protected]> 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 <[email protected]> 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
