certmagic
certmagic copied to clipboard
Allow Certmagic to generate 'next' private key to allow safe TLSA/DANE deployment and rollover
What would you like to have changed?
DANE (DNS-based Authentication of Named Entities) is a protocol that allows X.509 certificates to be bound to DNS names using Domain Name System Security Extensions (DNSSEC). This is done by having TLSA records which publish the public key of a certificate. In this procedure a safe rollover of certificates is needed, to ensure that a the public key for a new certificate already exists in DNS before the TTL is up. This is documented by Viktor Dukhovni at ICANN61: https://imrryr.org/~viktor/ICANN61-viktor.pdf - see especially slides 20+21 for the rollover procedure ('current + next').
To enable this a 'next' private key should be created when the 'current' private is being used for the certificate renewal. Next time the certificate is renewed the 'next' certificate should be used.
The feature request is to implement an option in Certmagic to allow a 'next' private key to be generated when a certificate is requested. Currently the only (somewhat) viable ways to automate a safe DANE/TLSA rollover are provided by acme.sh and certbot. It would be helpful to have Certmagic (and by extension Caddy) to allow for this as well.
From a quick look it seems that generating a 'next' private key would be trivial - as would the checking for the existence of a 'next' private key when generating a certificate. The somewhat more complicated part seems to be saving just the private key to disk as now it seems all certificate resources are saved to disk at once; and here we would only have a private key to save.
Before starting any work on a PR or making changes I'd like to discuss (@mholt) if (1) this would be something in scope for Certmagic and (2) what would be the suggested way forward?
Why is this feature a useful, necessary, and/or important addition to this project?
This allows for the proposed (safest) procedure to offer DANE/TLSA
What alternatives are there, or what are you doing in the meantime to work around the lack of this feature?
Currently the alternatives are:
- Workaround: not follow documented procedure, but request a new certificate, sleep 2x TTL and only then rollover
- Use acme.sh or certbot, possibly in conjunction with https://github.com/nixigaj/cf-tlsa-acmesh
Please link to any relevant issues, pull requests, or other discussions.
Similar discussion in acme.sh https://github.com/acmesh-official/acme.sh/issues/3096
Thanks for the thoughtful feature request!
I have mixed feelings about the effectivness of DNNSEC (and by extension, DANE) -- are you using this to secure SMTP servers? DNSSEC might be better than STARTTLS.
I'd be open to a proposal here (or, if the implementation is not much work, you could submit a PR for review -- but no guarantees it would be merged so I'd feel bad if you put in a lot of effort at first). Curious what kind of complexity it would introduce.
The somewhat more complicated part seems to be saving just the private key to disk as now it seems all certificate resources are saved to disk at once; and here we would only have a private key to save.
A private key can be stored individually as well, just using the Store() method directly. We are also considering a new Storage interface (a "v2" as it were) but this has yet to be designed. Just FYI :)
Thanks for the feedback Matt. In some cases DANE is mandatory, e.g. for Dutch government (more information: https://en.internet.nl/faqs/starttls/). I hadn't dug deep enough yet to find Store() but if that exists, it would mean a rather 'simple' PR I would think (I though that maybe the certificate struct would have to be changed, but that wouldn't be necessary). Didn't yet check, but is there also a Load() (or similar?) to retrieve a stored key file?
From an overview I think this would need to be done:
- Add option (on a per host basis) that allows for generation of a 'next' key (only enable when
ReusePrivateKeyis disabled?) - Check for existence of a 'next' key when obtaining a certificate to be used a a private key
- Create 'next' key when obtaining a certificate
- Store 'next' key when obtaining a certificate
If the above would be the general approach I could definitely try looking into a PR
Yeah, there's a Load() as well.
Interesting, I had no idea governments would require it. But I guess it makes sense, given that DNSSEC is basically government-controlled PKI.
I think that approach is feasible though. I would want to ensure that this is all behind a config setting that's off by default though.
Thanks. Agree with offering this as an option on a per host basis (e.g. I only need this for my SMTP server record). I think the option should imply that ReusePrivateKey = false
As an aside if this is implemented, together with libdns I think we could of offer an integrated DANE/TLSA rotation tool in a single binary (which again means easy inclusion in containers or elsewhere) 🎉
On another note: I've skimmed the DNSSEC article you linked, but it's written in 2015; I'd love to get an opinion on this 10 years later (and there sure are issues, but I'm not sure if DNSSEC as part of a security-mix is particularly bad, but I haven't investigated this in-depth as others, including you, probably have)
One thing I am in favor of is not reusing private keys, so any scaffolding moving us away from key pinning is a win IMO. If what came of this is the ability to generically rotate keys with DNS records then that'd be a huge win for the Internet.
Hello,
I've just found this issue and while I'm not a user of certmagic, I've already finished a project that takes care exactly of this with hooking into certbot and rotating TLSA records gracefully:
https://git.ksol.io/karolyi/daneupdate
I know my tool might not fit your usecase (not written in go), but it could give you ideas as to how to realize such a thing. This project is already doing its thing at a couple places, my webserver being one of them. I don't just use the certs as HTTPS certificates, I use them for SMTPS/IMAPS/POP3S/XMPP and more.
If you want to use the signed certificates with external daemons like I do, this is the way of doing it.