smallstep / certificates

🛡️ A private certificate authority (X.509 & SSH) & ACME server for secure automated certificate management, so you can use TLS everywhere & SSO for SSH.
https://smallstep.com/certificates
Apache License 2.0
6.38k stars 417 forks source link

Allow targeted certificate renewal after expiration #177

Closed mmalone closed 2 years ago

mmalone commented 4 years ago

What would you like to be added

The ability to renew a certificate that's already expired on an ad-hoc basis.

Why this is needed

Many use cases require supporting PKI subscribers with intermittent connectivity (e.g., connected devices & endpoints) that need to maintain valid certificates. In general, there's no "safe" lifetime that is sufficient to support all of these use cases -- a train or scooter might go offline for maintenance for months or longer, or an employee might shut their laptop and go on sabbatical for a quarter. When these subscribers come back online, they need to be able to obtain a new certificate.

One option is to simply re-enroll using whatever mechanism was used to obtain a certificate in the first place. The problem with this approach is that enrollment may use single-use credentials (e.g., AWS IIDs) or be part of a larger pipeline that does more than just certificate management. Redoing just the certificate management portion of these processes can be challenging. Moreover, a re-enrollment procedure would have to detect which enrollment mechanism is in use and support multiple forms of enrollment. In short, it's complicated.

Supporting a single "exception workflow" for "admins" to mark either an identity (e.g., foo.internal or mike@example.com) or a certificate (e.g., serial number XYZ) as renewable after expiration on a temporary (e.g., time-constrained & single-use) basis would simplify the re-enrollment process. Functionally, this would disable the certificate expiry check for the step-ca renew endpoint for a period of time / single request.

This mechanism could also be useful for SSH, where a host certificate may expire and an admin could click an "allow re-enrollment" button in the UI, for instance.

We need to put some thought into the API & CLI interface for this. For now I wanted to capture the requirements and justification.

hostops commented 4 years ago

This (or better solution to this problem) is something we need in our company. We have devices that can simply be offline for months or even years. In discussion on gitter I have seen this approach mentioned as approach that makes certificate never expire. Can someone explain how would that be security issue if this certificate can be used only for renewal if this is last certificate generated for that client and is not passively or actively revoked

mmalone commented 4 years ago

@jakobhostnik typically, expired certificates can be completely ignored. They're invalid for all purposes. This change would break that invariant. You need to consider expired certificates in audits, protect private keys for expired certificates, etc. It's a small change that seems reasonable given the tradeoffs involved, but the assumption that "expired certificates are worthless" is so fundamental and baked-into certificate-based PKI that it's worth careful consideration, probably on a per-deployment basis.

That's why I've been thinking this should be an "exception flow" where you explicitly mark an expired certificate as renewable vs. allowing renewal-after-expiry by default. But then there'd need to be some intervention to "reactivate" a device. It could be a simple "reactivate device" workflow that an admin, or even an end-user, could complete. But it wouldn't be completely automatic. Would that be sufficient for your use case, or do you want devices to be able to come back online automatically, with no user intervention, after an arbitrary amount of offline time?

Edit: just a heads up that we were actually discussing this at standup yesterday because it's becoming relevant to our upcoming SSH product. It's looking very likely that we'll implement this feature in the next couple months, if someone doesn't beat us to it, so this is a good time to submit feedback if you (or anyone else who reads this) has thoughts!

hostops commented 4 years ago

That is great news! Well at the end of the day we have devices where in normal scenario they are offline for few months and then moved back to internet where we would expect to automatically reconect and send the data that were collected in the mean time.

So best scenario would be if this is completely automated. Well if we could mark all of our certificates to be eligable for expired renewal. But all of them should be marked only once - when first certificate for that device is created.

Is there any better solution to this problem - that does not include expired certificates? What if you would connect without certificate but only use some kind of hash to renew it? Just brainstorming. ;)

mmalone commented 4 years ago

So best scenario would be if this is completely automated.

Ok so it sounds like we have a +1 for some way to turn this on globally, or at least for a provisioner, rather than per-certificate.

Design note for whoever implements: I think the best way to do this would be to allow "renewal-after-expiry" for non-revoked certificates, then automatically passively revoke the old certificate on renewals. That way you'd only be able to renew the latest certificate in a particular "lineage". I've been wanting to turn on "automatic passive revocation of old cert on renewal" for a while now. It shouldn't break anything, and this is a good opportunity to do it since it'll make this feature easier to implement.

Is there any better solution to this problem - that does not include expired certificates?

I don't think so. We've thought about using a different credential for renewal. But, no matter what you do, this use case requires some non-expiring credential that allows you to get a certificate. That's unavoidable. So the easiest (and probably safest) thing to do seems to be to just use the certificate we've already issued and ignore expiry.

Some protocols (e.g., OAuth) issue a separate credential for renewal (e.g., the "renew token"). But that's done because these are symmetric / shared-secret systems. The non-expiring renew token is more sensitive than the expiring access token, so having two credentials has a security benefit -- you get tighter control over who gets to see the non-expiring renew token. But we're talking about asymmetric keys here. The private key is never shared with anyone. So I don't think there's any benefit in using a separate credential. It just adds complexity.

hostops commented 4 years ago

That is true. This is probably the conclusion of that topic.

hostops commented 3 years ago

It has been almost a year. Any update on this?

dopey commented 3 years ago

Hey! We're definitely going to implement this feature. I can't give a date estimate, but I can say that we have enough people asking for this functionality that it's on our our short term roadmap.

michaelmohamed commented 3 years ago

Hi, is there a workaround for this use case? I would like to provision certificates to our IoT devices. These devices may be offline for some time. When they come back online, we would like the ability for the device to obtain a renewed certificate.

schieberegister commented 2 years ago

Is there any update on this? This feature would be awesome. :)

dopey commented 2 years ago

Hey, sorry for the radio silence here. Had a chance to discuss with the team this morning.

We're definitely still planning to implement this, but we see targeted certificate renewal as part of a larger certificate lifetime events webhook project. This would also enable webhooks for pre-issuance, post-issuance, pre-renewal, post-renewal, etc.

Unfortunately, I don't have an update on timeline. But please keep asking, because it actually does help us with prioritization.

hostops commented 2 years ago

Well, our team would also appreciate this feature, since right now we have to manually update any certificate that expires in such conditions.

hostops commented 2 years ago

But please keep asking, because it actually does help us with prioritization.

So I am asking. Do you have any news?

maraino commented 2 years ago

@hostops The last released version of step/step-ca supports renewal after expiration. Not there's the new claim allowRenewalAfterExpiry. https://github.com/smallstep/certificates/blob/88a1bf17cf0dc3e69938eb28f03a02ffeecdf3e7/authority/provisioner/claims.go#L28

It can be enabled per provisioner or to the full authority instance.

When step ca renew detects that the provisioner that intends to renew is expired it will generate a token with the certificate (and intermediate) embedded in the token header x5cInsecure property, in JSON form:

{
  "header": {
    "alg": "ES256",
    "kid": "WVh...",
    "typ": "JWT",
    "x5cInsecure": [
      "MII...",
      "MII..."
    ]
  },
  "payload": {
    "aud": "https://ca.example.com/renew",
    "exp": 1651168487,
    "iat": 1651168187,
    "iss": "step-ca-client/1.0",
    "nbf": 1651168187,
    "sub": "www.example.com"
  },
  "signature": "jzy..."
}

The CA will process the token and if the certificate was signed by the CA, was not revoked, and the provisioner who signed it allows renewal after expiry, the certificate will be renewed. This token can be also used to renew non-expired certificates, but at the moment step ca renew defaults to mTLS renewal for those.

Note that the open-source step-ca allows renewals, without any kind of policy, for example, it will renew a 1-year-old certificate, even if that one was renewed multiple times. The hosted solution will support a more granular way, being able to specify the certs that you want to renew after their expiration.