diff --git a/20220403-ACME-TLS.md b/20220403-ACME-TLS.md new file mode 100644 index 0000000..ca31b8a --- /dev/null +++ b/20220403-ACME-TLS.md @@ -0,0 +1,234 @@ +# Add ACME protocol support for certificate management into TLS plugin + +| Status | Proposed | +:-------------- |:---------------------------------------------------- | +| **RFC #** | [13](https://github.com/coredns/rfc/pull/13) | +| **Author(s)** | Marius Kimmina(@[mariuskimmina](https://github.com/mariuskimmina)) | +| **Sponsor** | Yong Tang (@[yongtang](https://github.com/yongtang)), Paul Greenberg (@[greenpau](https://github.com/greenpau)) | +| **Updated** | 2022-04-01 | +| **Obsoletes** | | + +## Objective + +This project aims to add ACME support to the existing TLS plugin, which is used to configure the CoreDNS server's TLS certificates, so that the management of these certificate can be automated. + +## Motivation and Scope + +There are two ways in which ACME can be intereting for the CoreDNS project: + +* CoreDNS needs a certificate management tool to solve its own DNS over TLS certificate issue. +* CoreDNS could be used to fullfill the DNS-01 Challenge for other applications, such as a webservers. + +This proposal will focus on the first case - meaning that when CoreDNS is serving DNS over TLS (DoT) or DNS over HTTPs (DoH) it needs to have a valid TLS certificate. Obtaining such a valid certificate for the CoreDNS server itself is what should be automated by this project. +To use the ACME challenge ownership of a domain, such as `coredns.io` or `example.com` is required, thus when using CoreDNS with an IP address and without any domain, obtaining a valid certificate can not be automated by ACME. +CoreDNS also needs to be the authoritative DNS Server for the domain in question - CoreDNS needs to be able to set a TXT Record on the domain that ownership needs to be proven of. + +### Manual Certificate Management + +To obtain a valid (signed by a CA) certificate historically the following steps were required + +1. Generate private key +2. Generate CSR +3. Order SSL certificate +4. Paste CSR into online form +5. Choose an email address +6. Wait for email +7. Click link in email +8. Wait for another email +9. Download certificate +10. Concat into bundle +11. Upload bundle to server +12. Configure server to use cert and key +13. Remember to renew the certificate before it expires + +Some parts here can already be automated easily, like Generating a private key and a CSR - it's really the middle part that's involves emails and clicking links that's a problem. + +### Automatic Certificate Management + +1. Generate private key +2. Generate CSR +3. Solve ACME Challenge +4. Download certificate bundle +5. Configure server to use cert and key +6. Repeat Steps 2 to 5 before the certificate expries + +And all of these steps can be automated + +## How ACME works + +ACME is a client-server protocl where the Server side is implemented by a Certificate Authority (CA) such as [Let's Encrypt][lets-encrypt] the client side is implemented by a service that wants to obtain a valid certificate, such as CoreDNS in this case. The client has to prove ownership of a domain before the CA can give out a certificate, to achieve that, one of the following challenges has to be completed: + +### HTTP01 Challenge +This requires port 80. +CA gives the ACME client a token which puts a file on the server at http:///.well-known/acme-challenge/. The file contains the token, plus a thumbprint of your account key. Once the client tells the CA that the file is ready, Let’s Encrypt tries retrieving it. If the validation checks get the right responses from the web server, the validation is considered successful and the certificate can be issued. +To implement this in CoreDNS I propose that the underlying Caddy server could be used to serve the file at `/.well-known/acme-challenge/`. + +This Challenge is out of scope for this proposal, the ACME implementation in CoreDNS will only support the DNS Challenge, CoreDNS should not act as a web-server. + +### DNS01 Challenge +After the CA gives the ACME client a token, the client will then create a TXT record derived from that token and your account key, and put that record at _acme-challenge.. The CA queries the DNS system for that record and the certificate can be issued if it matches. +For this to work, CoreDNS needs to be the authoritative DNS server for the domain and thus be able to set the TXT Record. + +## Deliverables +* The existing TLS plugin will be extended to support the ACME protocol +* It will be possible to setup CoreDNS with a verified certificate, signed by a CA such as Let's Encrypt, without manually generating and renewing it +* Documentation on how to use the TLS plugin with ACME + +## Design Proposal + +The major task of this project is to integrate the above defined specs of ACME protocol into CoreDNS so that it could automate certificate management. +In current scenarios, in order to enable DNS over TLS users have to manually provide certificates via the following Corefile configurations: + +``` +tls://dns.example.com { + tls cert.pem key.pem +} +``` + +This `cert.pem` and `key.pem` file currently have to be manually created and renewed. + +I propose to extend the existing `tls` plugin to support the ACME protocol so that we can instead create a Corefile like this: + +``` +tls://dns.example.com { + tls acme { + domain exmaple.com + } +} +``` + +To implement ACME there are a couple packages that we could use to help us such as: + +* [acmez][acmez] +* [CertMagic][CertMagic] +* [lego][lego] +* [autocert][autocert] + +As it was discussed before on the original Issue, CertMagic might have too many features that we don't need for our use case - the same could be said for lego and autocert. +So just using acmez as the most barebones implemenation of the acme-protocol seems to be the right call for this project. + +## Storage + +During the whole process there are a couple things that need to be stored: + +* Private Key +* Certificate Signing Request (CSR) +* Certificate + +Where the generation of the private key and the CSR as well as storing them might be handled by an ACME library which might limit our decisions for these two. for the Cert that's being returned from the ACME server there should be no restraints. +The following storing options are available: + +* [Cert Resource Records][CRR] +* TXT Records +* Local file storage + +I propose to use the Cert RR for the Certificate, as for the private key and the CSR we definitely don't want to share these with anyone so local file storage should be the right choice here. + +## Clustered Environments + +In clustered environment, where there are multiple CoreDNS instances running, certificate sharing is a major concern. Certificate sharing is important as it is neither feasible nor desireable for each CoreDNS instance to fetch certificate individually. + +Since high availability is not a major concern, as queries are cached at every level, a solution is to configure CoreDNS instances as "leader" and "worker". + +1. Leader: In a cluster, a single DNS Server can be configured as "leader", which would be responsible for actually performing ACME challenges and obtaining certificates from the Let's Encrypt CA. The leader would then store the certificate in the zone file as CERT RR. + +2. Worker: The worker is responsible for fetching the certificates from the master. The worker is not responsible for performing ACME challenges. Whenever a DNS query is received at the worker node, the worker first queries the master for the certificate. Workers should cache the certificate and use it for further DoH queries. + +Configurations: + +Leader +``` +tls://dns.example.com { + tls acme { + leader + domain example.com + } +} +``` + +Worker +``` +tls://dns.example.com { + tls acme { + worker + domain example.com + } +} +``` + +The worker caches the CERT RR (for X seconds) to reduce querying the master for the certificate every time a DNS query is received. + +## Further configuration options + +### Wildcard certificates + +The DNS-01 Challenge can be used to create Wildcard certificates, the plugin could either get a cert for e.g. `dns.example.com` or for `*.example.com`. +By default it seems more reasonable to not use Wildcard certificates but we could have them as an opt-in feature with a Corefile like This + + +tls://dns.example.com { + tls acme { + domain example.com + wildcard + } +} + + +## Timeline + +**April 4 - Mai 20: Pre GSoC** +* Familiarize myself more with the project +* Improve the project proposal +* Study concepts related to the project + +**Mai 20 - June 13:** +* (Final) Design decisions: + * ACME library + * Corefile layout + * Storage + * Clustered Implementation +* Start implementing + + +**June 13 - September 19:** +1. Integrate ACME library (or own implementation if necessary) into TLS plugin +2. Implement Challenges +3. Develop Tests for ACME challenges +3. Start writing documentation +4. Start working on cluster implementation +5. Develop Tests for cluster implementation +6. Finish the documentation + +I hope to be done by September 01 the latest and have until September 19 as a buffer. +I will also be in contact with the mentors and hope to gather a lot of feedback along the way. + + +**September 19 - onwards: After GSoC** +* Keep Contributing to CoreDNS and possibly other CNCF projects + + +## Questions and Discussion Topics + +1. Can a CoreDNS plugin utilize the underlying Caddy server to solve the HTTP-01 Challenge - which means serving a file on port 80? + * This question has been answered: the HTTP Challenge is out-of-scope for this project, only the DNS challenge is supported +2. For the DNS-01 Challenge, I would assume that CoreDNS will generally not be the authoritative DNS Server for the domain in question, thus, we would also use the API of a DNS Provider to solve this challenge, right? + * This question has been answered: We do assume that CoreDNS will be the authoritative DNS server for the domain and sets the TXT Record itself. +3. How should I configure the workers to look for the leader? When the certificate is renewed, how does the master know where the workers are and how to send the certificate to them? + +## References + +A lot of this proposal has been reused from a previous attempts [here](https://github.com/coredns/rfc/pull/12) and [here](https://github.com/coredns/rfc/pull/11), so a lot of credit belongs to the authors of these previous proposals. + +1. [ACME RFC][acme-rfc] +2. [DNS Challenge Types][dns-challenge-types] +3. [Explanation of ACME Protocol][acme-explanation] + +[lets-encrypt]: https://letsencrypt.org/ +[acmez]: https://pkg.go.dev/github.com/mholt/acmez +[CertMagic]: https://github.com/caddyserver/certmagic +[lego]: https://github.com/go-acme/lego +[autocert]: https://github.com/golang/crypto/tree/master/acme/autocert +[CRR]: https://datatracker.ietf.org/doc/html/rfc4398 +[acme-rfc]: https://tools.ietf.org/html/rfc8555 +[dns-challenge-types]: https://letsencrypt.org/docs/challenge-types/ +[acme-explanation]: https://www.thesslstore.com/blog/acme-protocol-what-it-is-and-how-it-works/