URL: https://github.com/freeipa/freeipa/pull/3509 Author: frasertweedale Title: #3509: [Backport][ipa-4-7] Profile-based system cert renewal Action: opened
PR body: """ Manual backport of #3316 to ipa-4-7. We may need to backport this change all the way to ipa-4-6 to allow us to change the IPA RA certificate profile on older releases. See also https://github.com/freeipa/freeipa/pull/3508 which is the ipa-4-7 backport PR. There were some trivial conflicts. There were substantive conflicts for two patches, but these were due to the switch from mod_nss to mod_ssl, and from NSSDB-based IPA RA cert to PEM files. Those patches were not relevant, and were dropped. https://pagure.io/freeipa/issue/7991 Do not rely on CI only; I will have to test this change myself so I'll add WIP label, and remove it when I'm satisfied. """ To pull the PR as Git branch: git remote add ghfreeipa https://github.com/freeipa/freeipa git fetch ghfreeipa pull/3509/head:pr3509 git checkout pr3509
From 2296e8051634fe98bcf74a2ee5d572d8ef0c2734 Mon Sep 17 00:00:00 2001 From: Christian Heimes <chei...@redhat.com> Date: Tue, 23 Apr 2019 13:47:38 +0200 Subject: [PATCH 01/13] Pass token_name to certmonger For HSM support, IPA has to pass the token name for CA and subsystem certificates to certmonger. For now, only the default 'internal' token is supported. Related: https://pagure.io/freeipa/issue/5608 Signed-off-by: Christian Heimes <chei...@redhat.com> Reviewed-By: Alexander Bokovoy <aboko...@redhat.com> Reviewed-By: Rob Crittenden <rcrit...@redhat.com> --- ipalib/install/certmonger.py | 11 +++++++++-- ipaserver/install/dogtaginstance.py | 29 ++++++++++++++++++++++------- 2 files changed, 31 insertions(+), 9 deletions(-) diff --git a/ipalib/install/certmonger.py b/ipalib/install/certmonger.py index 3e1862192e..11a250116a 100644 --- a/ipalib/install/certmonger.py +++ b/ipalib/install/certmonger.py @@ -427,7 +427,8 @@ def request_cert( def start_tracking( certpath, ca='IPA', nickname=None, pin=None, pinfile=None, - pre_command=None, post_command=None, profile=None, storage="NSSDB"): + pre_command=None, post_command=None, profile=None, storage="NSSDB", + token_name=None): """ Tell certmonger to track the given certificate in either a file or an NSS database. The certificate access can be protected by a password_file. @@ -460,6 +461,8 @@ def start_tracking( NSS or OpenSSL backend to track the certificate in ``certpath`` :param profile: Which certificate profile should be used. + :param token_name: + Hardware token name for HSM support :returns: certificate tracking nickname. """ if storage == 'FILE': @@ -500,6 +503,10 @@ def start_tracking( params['cert-postsave-command'] = post_command if profile: params['ca-profile'] = profile + if token_name not in {None, "internal"}: + # only pass token names for external tokens (e.g. HSM) + params['key-token'] = token_name + params['cert-token'] = token_name result = cm.obj_if.add_request(params) try: @@ -663,7 +670,7 @@ def modify_ca_helper(ca_name, helper): return old_helper -def get_pin(token): +def get_pin(token="internal"): """ Dogtag stores its NSS pin in a file formatted as token:PIN. diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py index e733aa423f..3cd49b65f3 100644 --- a/ipaserver/install/dogtaginstance.py +++ b/ipaserver/install/dogtaginstance.py @@ -92,6 +92,10 @@ class DogtagInstance(service.Service): tracking_reqs = None server_cert_name = None + # token for CA and subsystem certificates. For now, only internal token + # is supported. + token_name = "internal" + ipaca_groups = DN(('ou', 'groups'), ('o', 'ipaca')) ipaca_people = DN(('ou', 'people'), ('o', 'ipaca')) groups_aci = ( @@ -193,6 +197,12 @@ def enable_client_auth_to_db(self): """ Enable client auth connection to the internal db. """ + sub_system_nickname = "subsystemCert cert-pki-ca" + if self.token_name != "internal": + # TODO: Dogtag 10.6.9 does not like "internal" prefix. + sub_system_nickname = '{}:{}'.format( + self.token_name, sub_system_nickname + ) with stopped_service('pki-tomcatd', 'pki-tomcat'): installutils.set_directive( @@ -202,7 +212,7 @@ def enable_client_auth_to_db(self): installutils.set_directive( self.config, 'authz.instance.DirAclAuthz.ldap.ldapauth.clientCertNickname', - 'subsystemCert cert-pki-ca', quotes=False, separator='=') + sub_system_nickname, quotes=False, separator='=') installutils.set_directive( self.config, 'authz.instance.DirAclAuthz.ldap.ldapconn.port', '636', @@ -220,7 +230,7 @@ def enable_client_auth_to_db(self): installutils.set_directive( self.config, 'internaldb.ldapauth.clientCertNickname', - 'subsystemCert cert-pki-ca', quotes=False, separator='=') + sub_system_nickname, quotes=False, separator='=') installutils.set_directive( self.config, 'internaldb.ldapconn.port', '636', quotes=False, separator='=') @@ -284,9 +294,9 @@ def configure_certmonger_renewal(self): # Give dogtag extra time to generate cert timeout=CA_DBUS_TIMEOUT) - def __get_pin(self): + def __get_pin(self, token_name="internal"): try: - return certmonger.get_pin('internal') + return certmonger.get_pin(token_name) except IOError as e: logger.debug( 'Unable to determine PIN for the Dogtag instance: %s', e) @@ -294,7 +304,7 @@ def __get_pin(self): def configure_renewal(self): """ Configure certmonger to renew system certs """ - pin = self.__get_pin() + pin = self.__get_pin(self.token_name) for nickname in self.tracking_reqs: try: @@ -302,6 +312,7 @@ def configure_renewal(self): certpath=self.nss_db, ca='dogtag-ipa-ca-renew-agent', nickname=nickname, + token_name=self.token_name, pin=pin, pre_command='stop_pkicad', post_command='renew_ca_cert "%s"' % nickname, @@ -316,15 +327,19 @@ def track_servercert(self): done by the renewal script, renew_ca_cert once all the subsystem certificates are renewed. """ - pin = self.__get_pin() + # server cert is always stored in internal token + token_name = "internal" + pin = self.__get_pin(token_name) try: certmonger.start_tracking( certpath=self.nss_db, ca='dogtag-ipa-ca-renew-agent', nickname=self.server_cert_name, + token_name=token_name, pin=pin, pre_command='stop_pkicad', - post_command='renew_ca_cert "%s"' % self.server_cert_name) + post_command='renew_ca_cert "%s"' % self.server_cert_name + ) except RuntimeError as e: logger.error( "certmonger failed to start tracking certificate: %s", e) From 5c01c5e4113fd66c81d247d6a362918b0de97f14 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale <ftwee...@redhat.com> Date: Fri, 17 May 2019 16:04:14 +1000 Subject: [PATCH 02/13] dogtaginstance: add profile to tracking requests Enabling "fresh" renewals (c.f. "renewal"-based renewals that reference the expired certificate and its associated request object) will improve renewal robustness. To use fresh renewals the tracking request must record the profile to be used. Make dogtaginstance record the profile when creating tracking requests for both CA and KRA. Note that 'Server-Cert cert-pki-ca' and the 'IPA RA' both use profile 'caServerCert', which is the default (according to dogtag-ipa-renew-agent which is part of Certmonger). So we do not need any special handling for those certificates. This commit does not handle upgrade. It will be handled in a subsequent commit. Part of: https://pagure.io/freeipa/issue/7991 Reviewed-By: Rob Crittenden <rcrit...@redhat.com> --- ipaserver/install/cainstance.py | 10 ++++++---- ipaserver/install/dogtaginstance.py | 8 +++++++- ipaserver/install/krainstance.py | 8 +++++--- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index 1f22d12047..fb33a33e30 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -296,10 +296,12 @@ class CAInstance(DogtagInstance): 2 = have signed cert, continue installation """ - tracking_reqs = ('auditSigningCert cert-pki-ca', - 'ocspSigningCert cert-pki-ca', - 'subsystemCert cert-pki-ca', - 'caSigningCert cert-pki-ca') + tracking_reqs = { + 'auditSigningCert cert-pki-ca': 'caSignedLogCert', + 'ocspSigningCert cert-pki-ca': 'caOCSPCert', + 'subsystemCert cert-pki-ca': 'caSubsystemCert', + 'caSigningCert cert-pki-ca': 'caCACert', + } server_cert_name = 'Server-Cert cert-pki-ca' # The following must be aligned with the RewriteRule defined in # install/share/ipa-pki-proxy.conf.template diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py index 3cd49b65f3..3a2142a23d 100644 --- a/ipaserver/install/dogtaginstance.py +++ b/ipaserver/install/dogtaginstance.py @@ -89,7 +89,12 @@ class DogtagInstance(service.Service): CA, KRA, and eventually TKS and TPS. """ - tracking_reqs = None + # Mapping of nicknames for tracking requests, and the profile to use for + # that certificate. 'configure_renewal()' reads this dict and adds the + # profile if configured. Certificates that use the default profile + # ("caServerCert", as defined by dogtag-ipa-renew-agent which is part of + # Certmonger) are omitted. + tracking_reqs = dict() server_cert_name = None # token for CA and subsystem certificates. For now, only internal token @@ -316,6 +321,7 @@ def configure_renewal(self): pin=pin, pre_command='stop_pkicad', post_command='renew_ca_cert "%s"' % nickname, + profile=self.tracking_reqs[nickname], ) except RuntimeError as e: logger.error( diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py index c1daa2869b..aeffaab961 100644 --- a/ipaserver/install/krainstance.py +++ b/ipaserver/install/krainstance.py @@ -64,9 +64,11 @@ class KRAInstance(DogtagInstance): be the same for both the CA and KRA. """ - tracking_reqs = ('auditSigningCert cert-pki-kra', - 'transportCert cert-pki-kra', - 'storageCert cert-pki-kra') + tracking_reqs = { + 'auditSigningCert cert-pki-kra': 'caInternalAuthAuditSigningCert', + 'transportCert cert-pki-kra': 'caInternalAuthTransportCert', + 'storageCert cert-pki-kra': 'caInternalAuthDRMstorageCert', + } def __init__(self, realm): super(KRAInstance, self).__init__( From 104c347ebba22f0ba08271149ce4704360cd0f4f Mon Sep 17 00:00:00 2001 From: Fraser Tweedale <ftwee...@redhat.com> Date: Fri, 17 May 2019 16:30:47 +1000 Subject: [PATCH 03/13] upgrade: add profile to Dogtag tracking requests To use profile-based renewal (rather than "renewal existing cert" renewal which is brittle against database corruption or deleted certificate / request objects), Certmonger tracking requests for Dogtag system certs must record the profile to be used. Update the upgrade method that checks tracking requests to look for the profile. Tracking requests will be recreated if the expected data are not found. The code that actually adds the tracking requests was updated in a previous commit. Part of: https://pagure.io/freeipa/issue/7991 Reviewed-By: Rob Crittenden <rcrit...@redhat.com> --- ipaserver/install/cainstance.py | 3 ++ ipaserver/install/krainstance.py | 3 ++ ipaserver/install/server/upgrade.py | 57 +++++++++-------------------- 3 files changed, 24 insertions(+), 39 deletions(-) diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index fb33a33e30..c8f215eaaa 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -296,6 +296,9 @@ class CAInstance(DogtagInstance): 2 = have signed cert, continue installation """ + # Mapping of nicknames for tracking requests, and the profile to + # use for that certificate. 'configure_renewal()' reads this + # dict. The profile MUST be specified. tracking_reqs = { 'auditSigningCert cert-pki-ca': 'caSignedLogCert', 'ocspSigningCert cert-pki-ca': 'caOCSPCert', diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py index aeffaab961..a8b145c2b6 100644 --- a/ipaserver/install/krainstance.py +++ b/ipaserver/install/krainstance.py @@ -64,6 +64,9 @@ class KRAInstance(DogtagInstance): be the same for both the CA and KRA. """ + # Mapping of nicknames for tracking requests, and the profile to + # use for that certificate. 'configure_renewal()' reads this + # dict. The profile MUST be specified. tracking_reqs = { 'auditSigningCert cert-pki-kra': 'caInternalAuthAuditSigningCert', 'transportCert cert-pki-kra': 'caInternalAuthTransportCert', diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py index 42da269133..83b9e41681 100644 --- a/ipaserver/install/server/upgrade.py +++ b/ipaserver/install/server/upgrade.py @@ -954,48 +954,27 @@ def certificate_renewal_update(ca, ds, http): template = paths.CERTMONGER_COMMAND_TEMPLATE serverid = installutils.realm_to_serverid(api.env.realm) - requests = [ - { - 'cert-database': paths.PKI_TOMCAT_ALIAS_DIR, - 'cert-nickname': 'auditSigningCert cert-pki-ca', - 'ca-name': 'dogtag-ipa-ca-renew-agent', - 'cert-presave-command': template % 'stop_pkicad', - 'cert-postsave-command': - (template % 'renew_ca_cert "auditSigningCert cert-pki-ca"'), - }, - { - 'cert-database': paths.PKI_TOMCAT_ALIAS_DIR, - 'cert-nickname': 'ocspSigningCert cert-pki-ca', - 'ca-name': 'dogtag-ipa-ca-renew-agent', - 'cert-presave-command': template % 'stop_pkicad', - 'cert-postsave-command': - (template % 'renew_ca_cert "ocspSigningCert cert-pki-ca"'), - }, - { - 'cert-database': paths.PKI_TOMCAT_ALIAS_DIR, - 'cert-nickname': 'subsystemCert cert-pki-ca', - 'ca-name': 'dogtag-ipa-ca-renew-agent', - 'cert-presave-command': template % 'stop_pkicad', - 'cert-postsave-command': - (template % 'renew_ca_cert "subsystemCert cert-pki-ca"'), - }, - { - 'cert-database': paths.PKI_TOMCAT_ALIAS_DIR, - 'cert-nickname': 'caSigningCert cert-pki-ca', - 'ca-name': 'dogtag-ipa-ca-renew-agent', - 'cert-presave-command': template % 'stop_pkicad', - 'cert-postsave-command': - (template % 'renew_ca_cert "caSigningCert cert-pki-ca"'), - 'template-profile': None, - }, - { + requests = [] + + dogtag_system_nicks = ( + list(cainstance.CAInstance.tracking_reqs) + + [cainstance.CAInstance.server_cert_name] + ) + for nick in dogtag_system_nicks: + req = { 'cert-database': paths.PKI_TOMCAT_ALIAS_DIR, - 'cert-nickname': 'Server-Cert cert-pki-ca', + 'cert-nickname': nick, 'ca-name': 'dogtag-ipa-ca-renew-agent', 'cert-presave-command': template % 'stop_pkicad', 'cert-postsave-command': - (template % 'renew_ca_cert "Server-Cert cert-pki-ca"'), - }, + (template % 'renew_ca_cert "{}"'.format(nick)), + } + profile = cainstance.CAInstance.tracking_reqs.get(nick) + if profile: + req['template-profile'] = profile + requests.append(req) + + requests.append( { 'cert-file': paths.RA_AGENT_PEM, 'key-file': paths.RA_AGENT_KEY, @@ -1003,7 +982,7 @@ def certificate_renewal_update(ca, ds, http): 'cert-presave-command': template % 'renew_ra_cert_pre', 'cert-postsave-command': template % 'renew_ra_cert', }, - ] + ) logger.info("[Update certmonger certificate renewal configuration]") if not ca.is_configured(): From a74fe97cae13e574bc7343f215fe8b660b961705 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale <ftwee...@redhat.com> Date: Fri, 17 May 2019 11:43:18 +1000 Subject: [PATCH 04/13] certmonger: use long options when invoking dogtag-ipa-renew-agent To aid reader comprehension, use long options instead of short options when invoking dogtag-ipa-renew-agent. -N -> --force-new -O -> --approval-option Part of: https://pagure.io/freeipa/issue/7991 Reviewed-By: Rob Crittenden <rcrit...@redhat.com> --- install/certmonger/dogtag-ipa-ca-renew-agent-submit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit index 31b4a1b7fc..f036e25450 100755 --- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit +++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit @@ -205,7 +205,7 @@ def request_cert(reuse_existing, **kwargs): sys.argv[1:] + ['--submit-option', "requestor_name=IPA"]) if os.environ.get('CERTMONGER_CA_PROFILE') == 'caCACert': - args += ['-N', '-O', 'bypassCAnotafter=true'] + args += ['--force-new', '--approval-option', 'bypassCAnotafter=true'] result = ipautil.run(args, raiseonerr=False, env=os.environ, capture_output=True) if six.PY2: From a699118841da9f8ff84503b760fc4fe72154fce7 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale <ftwee...@redhat.com> Date: Fri, 17 May 2019 16:38:01 +1000 Subject: [PATCH 05/13] dogtag-ipa-ca-renew-agent: always use profile-based renewal Update the renewal helper to always request a new certificate ("enrollment request") instead of using "renewal request". The latter is brittle in the face of: - missing certificate record in database - missing original request record in database (pointed to by certificate record) - "mismatched" certificate or request records (there have been many cases of this; it is suspected that request/serial range conflicts, or something similar, may be the cause) The Dogtag tracking request must know what profile to use, except where the certificate uses the default profile ("caServerCert" per 'dogtag-ipa-renew-agent' implementation in Certmonger itself). This part of the puzzle was dealt with in previous commits. Part of: https://pagure.io/freeipa/issue/7991 Reviewed-By: Rob Crittenden <rcrit...@redhat.com> --- install/certmonger/dogtag-ipa-ca-renew-agent-submit | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit index f036e25450..df737843a3 100755 --- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit +++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit @@ -203,9 +203,9 @@ def request_cert(reuse_existing, **kwargs): "--certfile", paths.RA_AGENT_PEM, "--keyfile", paths.RA_AGENT_KEY] + sys.argv[1:] + - ['--submit-option', "requestor_name=IPA"]) - if os.environ.get('CERTMONGER_CA_PROFILE') == 'caCACert': - args += ['--force-new', '--approval-option', 'bypassCAnotafter=true'] + ['--submit-option', "requestor_name=IPA"] + + ['--force-new', '--approval-option', 'bypassCAnotafter=true'] + ) result = ipautil.run(args, raiseonerr=False, env=os.environ, capture_output=True) if six.PY2: From fac9d8e2dff0cb8adc3debee939d65a1bf7dc362 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale <ftwee...@redhat.com> Date: Wed, 26 Jun 2019 11:54:59 +1000 Subject: [PATCH 06/13] dogtaginstance: avoid special cases for Server-Cert The Dogtag "Server-Cert cert-pki-ca" certificate is treated specially, with its own track_servercert() method and other special casing. But there is no real need for this - the only (potential) difference is the token name. Account for the token name difference with a lookup method and treat all Dogtag system certs equally w.r.t. tracking request creation and removal. Part of: https://pagure.io/freeipa/issue/7991 Reviewed-By: Rob Crittenden <rcrit...@redhat.com> --- ipaserver/install/cainstance.py | 9 ++++-- ipaserver/install/dogtaginstance.py | 50 ++++++++--------------------- ipaserver/install/server/upgrade.py | 7 +--- 3 files changed, 22 insertions(+), 44 deletions(-) diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index c8f215eaaa..484189aeed 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -296,6 +296,8 @@ class CAInstance(DogtagInstance): 2 = have signed cert, continue installation """ + server_cert_name = 'Server-Cert cert-pki-ca' + # Mapping of nicknames for tracking requests, and the profile to # use for that certificate. 'configure_renewal()' reads this # dict. The profile MUST be specified. @@ -304,8 +306,12 @@ class CAInstance(DogtagInstance): 'ocspSigningCert cert-pki-ca': 'caOCSPCert', 'subsystemCert cert-pki-ca': 'caSubsystemCert', 'caSigningCert cert-pki-ca': 'caCACert', + server_cert_name: 'caServerCert', } - server_cert_name = 'Server-Cert cert-pki-ca' + token_names = { + server_cert_name: 'internal', # Server-Cert always on internal token + } + # The following must be aligned with the RewriteRule defined in # install/share/ipa-pki-proxy.conf.template crl_rewrite_pattern = r"^\s*(RewriteRule\s+\^/ipa/crl/MasterCRL.bin\s.*)$" @@ -464,7 +470,6 @@ def configure_instance(self, host_name, dm_password, admin_password, "Ensuring backward compatibility", self.__dogtag10_migration) self.step("configure certificate renewals", self.configure_renewal) - self.step("configure Server-Cert certificate renewal", self.track_servercert) self.step("Configure HTTP to proxy connections", self.http_proxy) self.step("restarting certificate server", self.restart_instance) diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py index 3a2142a23d..3d5f061bc5 100644 --- a/ipaserver/install/dogtaginstance.py +++ b/ipaserver/install/dogtaginstance.py @@ -89,18 +89,22 @@ class DogtagInstance(service.Service): CA, KRA, and eventually TKS and TPS. """ - # Mapping of nicknames for tracking requests, and the profile to use for - # that certificate. 'configure_renewal()' reads this dict and adds the - # profile if configured. Certificates that use the default profile - # ("caServerCert", as defined by dogtag-ipa-renew-agent which is part of - # Certmonger) are omitted. + # Mapping of nicknames for tracking requests, and the profile to + # use for that certificate. 'configure_renewal()' reads this + # dict and adds the profile if configured. tracking_reqs = dict() - server_cert_name = None # token for CA and subsystem certificates. For now, only internal token # is supported. token_name = "internal" + # override token for specific nicknames + token_names = dict() + + def get_token_name(self, nickname): + """Look up token name for nickname.""" + return self.token_names.get(nickname, self.token_name) + ipaca_groups = DN(('ou', 'groups'), ('o', 'ipaca')) ipaca_people = DN(('ou', 'people'), ('o', 'ipaca')) groups_aci = ( @@ -309,15 +313,16 @@ def __get_pin(self, token_name="internal"): def configure_renewal(self): """ Configure certmonger to renew system certs """ - pin = self.__get_pin(self.token_name) for nickname in self.tracking_reqs: + token_name = self.get_token_name(nickname) + pin = self.__get_pin(token_name) try: certmonger.start_tracking( certpath=self.nss_db, ca='dogtag-ipa-ca-renew-agent', nickname=nickname, - token_name=self.token_name, + token_name=token_name, pin=pin, pre_command='stop_pkicad', post_command='renew_ca_cert "%s"' % nickname, @@ -327,29 +332,6 @@ def configure_renewal(self): logger.error( "certmonger failed to start tracking certificate: %s", e) - def track_servercert(self): - """ - Specifically do not tell certmonger to restart the CA. This will be - done by the renewal script, renew_ca_cert once all the subsystem - certificates are renewed. - """ - # server cert is always stored in internal token - token_name = "internal" - pin = self.__get_pin(token_name) - try: - certmonger.start_tracking( - certpath=self.nss_db, - ca='dogtag-ipa-ca-renew-agent', - nickname=self.server_cert_name, - token_name=token_name, - pin=pin, - pre_command='stop_pkicad', - post_command='renew_ca_cert "%s"' % self.server_cert_name - ) - except RuntimeError as e: - logger.error( - "certmonger failed to start tracking certificate: %s", e) - def stop_tracking_certificates(self, stop_certmonger=True): """Stop tracking our certificates. Called on uninstall. """ @@ -361,11 +343,7 @@ def stop_tracking_certificates(self, stop_certmonger=True): services.knownservices.messagebus.start() cmonger.start() - nicknames = list(self.tracking_reqs) - if self.server_cert_name is not None: - nicknames.append(self.server_cert_name) - - for nickname in nicknames: + for nickname in self.tracking_reqs: try: certmonger.stop_tracking( self.nss_db, nickname=nickname) diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py index 83b9e41681..973036cf6a 100644 --- a/ipaserver/install/server/upgrade.py +++ b/ipaserver/install/server/upgrade.py @@ -956,11 +956,7 @@ def certificate_renewal_update(ca, ds, http): requests = [] - dogtag_system_nicks = ( - list(cainstance.CAInstance.tracking_reqs) + - [cainstance.CAInstance.server_cert_name] - ) - for nick in dogtag_system_nicks: + for nick in cainstance.CAInstance.tracking_reqs: req = { 'cert-database': paths.PKI_TOMCAT_ALIAS_DIR, 'cert-nickname': nick, @@ -1057,7 +1053,6 @@ def certificate_renewal_update(ca, ds, http): ca.configure_certmonger_renewal() ca.configure_renewal() ca.configure_agent_renewal() - ca.track_servercert() ca.add_lightweight_ca_tracking_requests() ds.start_tracking_certificates(serverid) http.start_tracking_certificates() From fa72db956576d67a9152f82d17287b5f5c5f78e0 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale <ftwee...@redhat.com> Date: Wed, 26 Jun 2019 13:52:20 +1000 Subject: [PATCH 07/13] upgrade: always add profile to tracking requests The profile for every Dogtag system cert tracking request is now explicitly specified. So remove the code that handled unspecified profiles. Part of: https://pagure.io/freeipa/issue/7991 Reviewed-By: Rob Crittenden <rcrit...@redhat.com> --- ipaserver/install/dogtaginstance.py | 2 +- ipaserver/install/server/upgrade.py | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py index 3d5f061bc5..d72091f49a 100644 --- a/ipaserver/install/dogtaginstance.py +++ b/ipaserver/install/dogtaginstance.py @@ -91,7 +91,7 @@ class DogtagInstance(service.Service): # Mapping of nicknames for tracking requests, and the profile to # use for that certificate. 'configure_renewal()' reads this - # dict and adds the profile if configured. + # dict. The profile MUST be specified. tracking_reqs = dict() # token for CA and subsystem certificates. For now, only internal token diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py index 973036cf6a..ebe9f8ae28 100644 --- a/ipaserver/install/server/upgrade.py +++ b/ipaserver/install/server/upgrade.py @@ -956,7 +956,7 @@ def certificate_renewal_update(ca, ds, http): requests = [] - for nick in cainstance.CAInstance.tracking_reqs: + for nick, profile in cainstance.CAInstance.tracking_reqs.items(): req = { 'cert-database': paths.PKI_TOMCAT_ALIAS_DIR, 'cert-nickname': nick, @@ -964,10 +964,8 @@ def certificate_renewal_update(ca, ds, http): 'cert-presave-command': template % 'stop_pkicad', 'cert-postsave-command': (template % 'renew_ca_cert "{}"'.format(nick)), + 'template-profile': profile, } - profile = cainstance.CAInstance.tracking_reqs.get(nick) - if profile: - req['template-profile'] = profile requests.append(req) requests.append( From ce48a101526e42381565bfa8b50a49be322cda94 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale <ftwee...@redhat.com> Date: Wed, 26 Jun 2019 14:08:48 +1000 Subject: [PATCH 08/13] upgrade: update KRA tracking requests The upgrade routine checks tracking requests for CA system certificates, IPA RA and HTTP/LDAP/KDC service certificates. If a tracking request matching our expectations is not found, we stop tracking all certificates, then create new tracking requests with the correct configuration. But the KRA was left out. Add checks for KRA certificates, and remove/recreate KRA tracking requests when appropriate. Part of: https://pagure.io/freeipa/issue/7991 Reviewed-By: Rob Crittenden <rcrit...@redhat.com> --- ipaserver/install/server/upgrade.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py index ebe9f8ae28..b25f990f3e 100644 --- a/ipaserver/install/server/upgrade.py +++ b/ipaserver/install/server/upgrade.py @@ -5,6 +5,7 @@ from __future__ import print_function, absolute_import import errno +import itertools import logging import re import os @@ -946,7 +947,7 @@ def named_add_server_id(): return True -def certificate_renewal_update(ca, ds, http): +def certificate_renewal_update(ca, kra, ds, http): """ Update certmonger certificate renewal configuration. """ @@ -956,7 +957,11 @@ def certificate_renewal_update(ca, ds, http): requests = [] - for nick, profile in cainstance.CAInstance.tracking_reqs.items(): + dogtag_reqs = ca.tracking_reqs.items() + if kra.is_installed(): + dogtag_reqs = itertools.chain(dogtag_reqs, kra.tracking_reqs.items()) + + for nick, profile in dogtag_reqs: req = { 'cert-database': paths.PKI_TOMCAT_ALIAS_DIR, 'cert-nickname': nick, @@ -1039,6 +1044,8 @@ def certificate_renewal_update(ca, ds, http): # Ok, now we need to stop tracking, then we can start tracking them # again with new configuration: ca.stop_tracking_certificates() + if kra.is_installed(): + kra.stop_tracking_certificates() ds.stop_tracking_certificates(serverid) http.stop_tracking_certificates() @@ -1052,6 +1059,8 @@ def certificate_renewal_update(ca, ds, http): ca.configure_renewal() ca.configure_agent_renewal() ca.add_lightweight_ca_tracking_requests() + if kra.is_installed(): + kra.configure_renewal() ds.start_tracking_certificates(serverid) http.start_tracking_certificates() @@ -1991,7 +2000,7 @@ def upgrade_configuration(): ca_restart, ca_upgrade_schema(ca), upgrade_ca_audit_cert_validity(ca), - certificate_renewal_update(ca, ds, http), + certificate_renewal_update(ca, kra, ds, http), ca_enable_pkix(ca), ca_configure_profiles_acl(ca), ca_configure_lightweight_ca_acls(ca), From 58f2bb6b78934171524eb00570f5cce9e58b6b2b Mon Sep 17 00:00:00 2001 From: Fraser Tweedale <ftwee...@redhat.com> Date: Thu, 27 Jun 2019 09:46:59 +1000 Subject: [PATCH 09/13] upgrade: log missing/misconfigured tracking requests For better diagnostics during upgrade, log the Certmonger tracking requests that were not found (either because they do not exist, or do not have the expected configuration). Part of: https://pagure.io/freeipa/issue/7991 Reviewed-By: Rob Crittenden <rcrit...@redhat.com> --- ipaserver/install/server/upgrade.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py index b25f990f3e..e0a9fdb087 100644 --- a/ipaserver/install/server/upgrade.py +++ b/ipaserver/install/server/upgrade.py @@ -1032,15 +1032,28 @@ def certificate_renewal_update(ca, kra, ds, http): ) # State not set, lets see if we are already configured + missing_or_misconfigured_requests = [] for request in requests: request_id = certmonger.get_request_id(request) if request_id is None: - break - else: + missing_or_misconfigured_requests.append(request) + + if len(missing_or_misconfigured_requests) == 0: logger.info("Certmonger certificate renewal configuration already " "up-to-date") return False + # Print info about missing requests + logger.info("Missing or incorrect tracking request for certificates:") + for request in missing_or_misconfigured_requests: + cert = None + if 'cert-file' in request: + cert = request['cert-file'] + elif 'cert-database' in request and 'cert-nickname' in request: + cert = '{cert-database}:{cert-nickname}'.format(**request) + if cert is not None: + logger.info(" %s", cert) + # Ok, now we need to stop tracking, then we can start tracking them # again with new configuration: ca.stop_tracking_certificates() From 28f06f7e64a6c1866fd80c5a6b39ce8c0c7facbf Mon Sep 17 00:00:00 2001 From: Fraser Tweedale <ftwee...@redhat.com> Date: Thu, 27 Jun 2019 11:27:02 +1000 Subject: [PATCH 10/13] cainstance: add profile to IPA RA tracking request Profile-based renewal means we should always explicitly specify the profile in tracking requests that use the dogtag-ipa-ca-renew-agent renewal helper. This includes the IPA RA agent certificate. Update CAInstance.configure_agent_renewal() to add the profile to the tracking request. This also covers the upgrade scenario (because the same method gets invoked). Part of: https://pagure.io/freeipa/issue/7991 Reviewed-By: Rob Crittenden <rcrit...@redhat.com> --- ipalib/constants.py | 1 + ipaserver/install/cainstance.py | 1 + ipaserver/install/server/upgrade.py | 2 ++ 3 files changed, 4 insertions(+) diff --git a/ipalib/constants.py b/ipalib/constants.py index 64736b45ef..d336d408e0 100644 --- a/ipalib/constants.py +++ b/ipalib/constants.py @@ -307,6 +307,7 @@ IPA_CA_NICKNAME = 'caSigningCert cert-pki-ca' RENEWAL_CA_NAME = 'dogtag-ipa-ca-renew-agent' RENEWAL_REUSE_CA_NAME = 'dogtag-ipa-ca-renew-agent-reuse' +RA_AGENT_PROFILE = 'caServerCert' # How long dbus clients should wait for CA certificate RPCs [seconds] CA_DBUS_TIMEOUT = 120 diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index 484189aeed..3429a6dc5b 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -1118,6 +1118,7 @@ def configure_agent_renewal(self): certmonger.start_tracking( certpath=(paths.RA_AGENT_PEM, paths.RA_AGENT_KEY), ca='dogtag-ipa-ca-renew-agent', + profile=ipalib.constants.RA_AGENT_PROFILE, pre_command='renew_ra_cert_pre', post_command='renew_ra_cert', storage='FILE') diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py index e0a9fdb087..1b1e39a35f 100644 --- a/ipaserver/install/server/upgrade.py +++ b/ipaserver/install/server/upgrade.py @@ -20,6 +20,7 @@ from augeas import Augeas import dns.exception from ipalib import api +from ipalib.constants import RA_AGENT_PROFILE from ipalib.install import certmonger, sysrestore import SSSDConfig import ipalib.util @@ -978,6 +979,7 @@ def certificate_renewal_update(ca, kra, ds, http): 'cert-file': paths.RA_AGENT_PEM, 'key-file': paths.RA_AGENT_KEY, 'ca-name': 'dogtag-ipa-ca-renew-agent', + 'template-profile': RA_AGENT_PROFILE, 'cert-presave-command': template % 'renew_ra_cert_pre', 'cert-postsave-command': template % 'renew_ra_cert', }, From c54acdddd7ba2729575525d701547311ed1522bc Mon Sep 17 00:00:00 2001 From: Fraser Tweedale <ftwee...@redhat.com> Date: Thu, 27 Jun 2019 11:48:53 +1000 Subject: [PATCH 11/13] Use RENEWAL_CA_NAME and RA_AGENT_PROFILE constants Replace renewal CA and profile name literals with corresponding symbols from ipalib.constants. Part of: https://pagure.io/freeipa/issue/7991 Reviewed-By: Rob Crittenden <rcrit...@redhat.com> --- ipaserver/install/cainstance.py | 6 +++--- ipaserver/install/dogtaginstance.py | 6 +++--- ipaserver/install/server/upgrade.py | 9 ++++----- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py index 3429a6dc5b..fe0b82d3e2 100644 --- a/ipaserver/install/cainstance.py +++ b/ipaserver/install/cainstance.py @@ -944,7 +944,7 @@ def __request_ra_certificate(self): principal='host/%s' % self.fqdn, subject=str(DN(('CN', 'IPA RA'), self.subject_base)), ca=ipalib.constants.RENEWAL_CA_NAME, - profile='caServerCert', + profile=ipalib.constants.RA_AGENT_PROFILE, pre_command='renew_ra_cert_pre', post_command='renew_ra_cert', storage="FILE", @@ -1070,7 +1070,7 @@ def uninstall(self): '/org/fedorahosted/certmonger') iface = dbus.Interface(obj, 'org.fedorahosted.certmonger') for suffix in ['', '-reuse']: - name = 'dogtag-ipa-ca-renew-agent' + suffix + name = ipalib.constants.RENEWAL_CA_NAME + suffix path = iface.find_ca_by_nickname(name) if path: iface.remove_known_ca(path) @@ -1117,7 +1117,7 @@ def configure_agent_renewal(self): try: certmonger.start_tracking( certpath=(paths.RA_AGENT_PEM, paths.RA_AGENT_KEY), - ca='dogtag-ipa-ca-renew-agent', + ca=ipalib.constants.RENEWAL_CA_NAME, profile=ipalib.constants.RA_AGENT_PROFILE, pre_command='renew_ra_cert_pre', post_command='renew_ra_cert', diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py index d72091f49a..b86a791db3 100644 --- a/ipaserver/install/dogtaginstance.py +++ b/ipaserver/install/dogtaginstance.py @@ -34,7 +34,7 @@ from ipalib import api, errors, x509 from ipalib.install import certmonger -from ipalib.constants import CA_DBUS_TIMEOUT +from ipalib.constants import CA_DBUS_TIMEOUT, RENEWAL_CA_NAME from ipaplatform import services from ipaplatform.constants import constants from ipaplatform.paths import paths @@ -292,7 +292,7 @@ def configure_certmonger_renewal(self): '/org/fedorahosted/certmonger') iface = dbus.Interface(obj, 'org.fedorahosted.certmonger') for suffix, args in [('', ''), ('-reuse', ' --reuse-existing')]: - name = 'dogtag-ipa-ca-renew-agent' + suffix + name = RENEWAL_CA_NAME + suffix path = iface.find_ca_by_nickname(name) if not path: command = paths.DOGTAG_IPA_CA_RENEW_AGENT_SUBMIT + args @@ -320,7 +320,7 @@ def configure_renewal(self): try: certmonger.start_tracking( certpath=self.nss_db, - ca='dogtag-ipa-ca-renew-agent', + ca=RENEWAL_CA_NAME, nickname=nickname, token_name=token_name, pin=pin, diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py index 1b1e39a35f..afa267348b 100644 --- a/ipaserver/install/server/upgrade.py +++ b/ipaserver/install/server/upgrade.py @@ -19,8 +19,7 @@ from contextlib import contextmanager from augeas import Augeas import dns.exception -from ipalib import api -from ipalib.constants import RA_AGENT_PROFILE +from ipalib.constants import RENEWAL_CA_NAME, RA_AGENT_PROFILE from ipalib.install import certmonger, sysrestore import SSSDConfig import ipalib.util @@ -966,7 +965,7 @@ def certificate_renewal_update(ca, kra, ds, http): req = { 'cert-database': paths.PKI_TOMCAT_ALIAS_DIR, 'cert-nickname': nick, - 'ca-name': 'dogtag-ipa-ca-renew-agent', + 'ca-name': RENEWAL_CA_NAME, 'cert-presave-command': template % 'stop_pkicad', 'cert-postsave-command': (template % 'renew_ca_cert "{}"'.format(nick)), @@ -978,7 +977,7 @@ def certificate_renewal_update(ca, kra, ds, http): { 'cert-file': paths.RA_AGENT_PEM, 'key-file': paths.RA_AGENT_KEY, - 'ca-name': 'dogtag-ipa-ca-renew-agent', + 'ca-name': RENEWAL_CA_NAME, 'template-profile': RA_AGENT_PROFILE, 'cert-presave-command': template % 'renew_ra_cert_pre', 'cert-postsave-command': template % 'renew_ra_cert', @@ -1025,7 +1024,7 @@ def certificate_renewal_update(ca, kra, ds, http): { 'cert-database': paths.PKI_TOMCAT_ALIAS_DIR, 'cert-nickname': nickname, - 'ca-name': 'dogtag-ipa-ca-renew-agent', + 'ca-name': RENEWAL_CA_NAME, 'cert-presave-command': template % 'stop_pkicad', 'cert-postsave-command': (template % ('renew_ca_cert "%s"' % nickname)), From 03d1ea02d7540a342178cf8f1f3f9cc5dd707e83 Mon Sep 17 00:00:00 2001 From: Fraser Tweedale <ftwee...@redhat.com> Date: Thu, 27 Jun 2019 18:57:02 +1000 Subject: [PATCH 12/13] ipatests: test ipa-server-upgrade in CA-less deployment Part of: https://pagure.io/freeipa/issue/7991 Reviewed-By: Rob Crittenden <rcrit...@redhat.com> --- ipatests/test_integration/test_caless.py | 4 ++++ ipatests/test_integration/test_upgrade.py | 7 +++++++ 2 files changed, 11 insertions(+) diff --git a/ipatests/test_integration/test_caless.py b/ipatests/test_integration/test_caless.py index 1ebe8fa368..f8464409f2 100644 --- a/ipatests/test_integration/test_caless.py +++ b/ipatests/test_integration/test_caless.py @@ -1299,6 +1299,10 @@ def test_host_del_doesnt_revoke(self): with self.host(): self.master.run_command(['ipa', 'host-del', self.test_hostname]) + def test_invoke_upgrader(self): + """Test that ipa-server-upgrade runs without error.""" + self.master.run_command(['ipa-server-upgrade'], raiseonerr=True) + class TestCertInstall(CALessBase): @classmethod diff --git a/ipatests/test_integration/test_upgrade.py b/ipatests/test_integration/test_upgrade.py index 5cc890e2e9..84f2dca0db 100644 --- a/ipatests/test_integration/test_upgrade.py +++ b/ipatests/test_integration/test_upgrade.py @@ -14,6 +14,13 @@ class TestUpgrade(IntegrationTest): + """ + Test ipa-server-upgrade. + + Note that ipa-server-upgrade on a CA-less installation is tested + in ``test_caless.TestIPACommands.test_invoke_upgrader``. + + """ @classmethod def install(cls, mh): tasks.install_master(cls.master, setup_dns=False) From eebd7bfa714726b9bfe72c86772463b55d08094f Mon Sep 17 00:00:00 2001 From: Fraser Tweedale <ftwee...@redhat.com> Date: Fri, 19 Jul 2019 14:38:58 +1000 Subject: [PATCH 13/13] dsinstance: add profile when tracking certificate When the DS certificate gets untracked then tracked again (via dsinstance.start_tracking_certificate()), it loses its profile configuration. Although it is the default profile, we want to retain the explicit reference. Ensure we add the profile when re-tracking the DS certificate. Part of: https://pagure.io/freeipa/issue/7991 Reviewed-By: Rob Crittenden <rcrit...@redhat.com> --- ipaserver/install/certs.py | 6 ++++-- ipaserver/install/dsinstance.py | 9 ++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/ipaserver/install/certs.py b/ipaserver/install/certs.py index 30b2aa0d3e..c6d7f6cfe1 100644 --- a/ipaserver/install/certs.py +++ b/ipaserver/install/certs.py @@ -327,14 +327,16 @@ def get_cert_from_db(self, nickname): except ipautil.CalledProcessError: return None - def track_server_cert(self, nickname, principal, password_file=None, command=None): + def track_server_cert( + self, nickname, principal, + password_file=None, command=None, profile=None): """ Tell certmonger to track the given certificate nickname. """ try: request_id = certmonger.start_tracking( self.secdir, nickname=nickname, pinfile=password_file, - post_command=command) + post_command=command, profile=profile) except RuntimeError as e: logger.error("certmonger failed starting to track certificate: %s", str(e)) diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py index dc10cf0398..1aa86cc67c 100644 --- a/ipaserver/install/dsinstance.py +++ b/ipaserver/install/dsinstance.py @@ -1142,9 +1142,12 @@ def start_tracking_certificates(self, serverid): dirname = config_dirname(serverid)[:-1] dsdb = certs.CertDB(self.realm, nssdir=dirname) if dsdb.is_ipa_issued_cert(api, nickname): - dsdb.track_server_cert(nickname, self.principal, - dsdb.passwd_fname, - 'restart_dirsrv %s' % serverid) + dsdb.track_server_cert( + nickname, + self.principal, + password_file=dsdb.passwd_fname, + command='restart_dirsrv %s' % serverid, + profile=dogtag.DEFAULT_PROFILE) else: logger.debug("Will not track DS server certificate %s as it is " "not issued by IPA", nickname)
_______________________________________________ FreeIPA-devel mailing list -- freeipa-devel@lists.fedorahosted.org To unsubscribe send an email to freeipa-devel-le...@lists.fedorahosted.org Fedora Code of Conduct: https://docs.fedoraproject.org/en-US/project/code-of-conduct/ List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines List Archives: https://lists.fedorahosted.org/archives/list/freeipa-devel@lists.fedorahosted.org