#7482 ipa-getcert rekey no longer revokes the old key
Closed: invalid a year ago Opened a year ago by cheimes.


The command ipa-getcert rekey no longer revokes the old certificate.

Steps to Reproduce

  1. certutil -d /etc/httpd/alias/ -L -n Server-Cert | grep Serial
  2. ipa-getcert rekey -d /etc/httpd/alias -n Server-Cert
  3. ipa cert-show $SERIAL | grep Revoked

Actual behavior

# certutil -d /etc/httpd/alias/ -L -n Server-Cert | grep Serial
        Serial Number: 268369924 (0xfff0004)
# ipa-getcert rekey -d /etc/httpd/alias -n Server-Cert
Resubmitting "20180401114816" to "IPA".
# certutil -d /etc/httpd/alias/ -L -n Server-Cert | grep Serial
        Serial Number: 268369925 (0xfff0005)
# ipa cert-show 0xfff0004 | grep Revoked
  Revoked: False

Expected behavior

The previous certificate should get revoked automatically.


package ipa-server is not installed
package ipa-client is not installed

Additional info:

Just in case it makes a difference: my test system is composed of three replicas, two of them with CA and KRA.

I'm seeing one RPC request for the rekeying operation, [xmlserver] ... cert_request('MIIE...', profile_id='caIPAserviceCert', principal='HTTP/host.ipa.example@IPA.EXAMPLE', add=True, version='2.51'): SUCCESS

IPA keeps the old certificates in the multi-valued userCert attribute of the principal. For the HTTP server, it's krbprincipalname=HTTP/hostname@REALM,cn=services,cn=accounts,$SUFFIX. The old certs could be used to fix existing systems.

On a side note, the certificates are pretty large. It might be a good idea to replace the revoked certificates with a reference to Dogtag's tree, e.g. cn=268369925,ou=certificateRepository,ou=ca,o=ipaca for serial 0xff0004.

ipa service-del correctly revokes all certificates, both the current one and all old ones.

The service_mod call is wrong. The cert_request plugin calls service_mod but the service plugin doesn't recognize that it should revoke the old certs.

(Pdb) w
-> return api.Backend.wsgi_dispatch(environ, start_response)
-> return self.route(environ, start_response)
-> return app(environ, start_response)
-> environ, start_response)
-> response = self.wsgi_execute(environ)
-> result = command(*args, **options)
-> return self.__do_call(*args, **options)
-> ret = self.run(*args, **options)
-> return self.execute(*args, **options)
-> if principal_type == SERVICE:
-> return self.__do_call(*args, **options)
-> ret = self.run(*args, **options)
-> return self.execute(*args, **options)
-> *keys, **options)
> /usr/lib/python3.6/site-packages/ipaserver/plugins/service.py(698)pre_callback()
-> if 'usercertificate' in options and ca_is_enabled:
(Pdb) p 'usercertificate' in options
(Pdb) p options
{'addattr': ('usercertificate=MIIFo...',), 'rights': False, 'all': False, 'raw': False, 'version': '2.229', 'no_members': False}

host_mod is also affected. user_mod seems to ignore user certificates.

@ftweedal user_mod doesn't revoke old user certs. Is this the expected behavior?

Metadata Update from @rcritten:
- Issue assigned to rcritten
- Issue priority set to: critical
- Issue set to the milestone: FreeIPA 4.7

a year ago

Researching this has exposed another bug in cert_request.

cert_request uses <object>_mod to set the usercertificate value. This does a full attribute replace, it is the equivalent of string a = b rather that list a += b.

The revocation code is already smart enough to revoke all the certs so if you do something like:

# getcert request ... -K test/$HOSTNAME
# ipa service-add-cert --certificate=<other cert. test/$HOSTNAME

You now have 2 valid certs in a service (why you'd want to is beside the point).

When you renew the cert them BOTH are revoked

# getcert resubmit ...

There is only one cert in ipa service-show because the other two have been revoked and/or removed from the entry (the revoke_cert cmd is already smart enough to only revoke certs issued by IPA).

Thinking more on this I'm not sure if this is a bug, feature, side-effect or expected.

The ONLY way that a second IPA-issued cert could get into the entry is if it is required for a DIFFERENT service/host/object and then added to another entry (which is how I reproduced this).

I don't know that this is a case we need to care about.

I think it would be a good idea to decompose the revocation and issuance behaviours.
i.e. it is undecidable in general whether a new certificate is intended to supersede some
existing certificate, such that revocation should be performed.

There are various heuristics you could use (e.g. "for cert with same key and same characteristics, which is close to expiry, assume it should be revoked). But even then, if it is close to expiry there is little benefit to revoking it. And the logic is fuzzy. And the heuristic are non-trivial.

I would rather see the behaviours decomposed and no implicit revocation. If cert-request revokes certificates it should be explicitly requested, and ideally the certificate(s) to revoke explicitly specified. But at that point it's simpler to just run cert-request and cert-revoke as separate commands.


(Side-note, I am cool with {user,host,service}-del revoking certificates. (Although there should probably be a way to suppress that behaviour... but it's a question for a different ticket.)

Some other notes I put in the PR (https://github.com/freeipa/freeipa/pull/1915):

As I alluded in the ticket (https://pagure.io/freeipa/issue/7482) I think we should have a broader discussion about what is the correct revocation behaviour in cert-request.

There are three basic scenarios where cert-request will be revoked:

  1. requesting a certificate for some new purpose. the subject principal may already have certs for other purposes. existing certificates should not be revoked.
  2. renew an existing certificate, because the end of the validity period is near. therefore there is little benefit to revoking. furthermore, if there are multiple existing certificates, to decide which one is being replaced is non-trivial in ideal circumstances, and undecidable in general.
  3. rekey or reissue an existing certificate, for any reason, when the expiry is not near. the existing cert should be revoked. But again, it is not simple to determine which certificate is being replaced.


  • remove revocation behaviour from cert-request

    • revocation behaviour could be retained, but only activated if a speciifc argument is given e.g. cert-request CSR --principal PRINC --revoke ISSUER:SERIAL would revoke the certificate with ISSUER:SERIAL if (and only if) it appears in the subject principal's entry, and remove it from that entry.
  • enhance the IPA certmonger helper to supply the appropriate options or invoke ipa cert-revoke separately, to revoke the certificate being renewed. IIIRC the old certificate is provided in the certmonger helper's environment so this should be straightforward.

Metadata Update from @ftweedal:
- Issue close_status updated to: invalid

a year ago

Login to comment on this ticket.