-
-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
caddytls: Support custom GetCertificate modules (like Tailscale) #4541
Conversation
Because of the dep to the |
FYI, linter failure:
|
Also fix AP provisioning in case of empty subject list (persist loaded module on struct, much like Issuers, to surive reprovisioning). And implement start of HTTP cert getter, still WIP.
Ah, all the tests for Go 1.16 are failing; I wonder if Go 1.17 is required. Added Caddyfile support and most of the HTTP certificate getter. This is pretty much done now, except for a few things:
|
Co-authored-by: Francis Lavoie <lavofr@gmail.com>
… access So you can run Caddy etc as a non-root user and let it have access to get certs. Updates caddyserver/caddy#4541 Change-Id: Iecc5922274530e2b00ba107d4b536580f374109b Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
… access So you can run Caddy etc as a non-root user and let it have access to get certs. Updates caddyserver/caddy#4541 Change-Id: Iecc5922274530e2b00ba107d4b536580f374109b Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
Tailscale only supports the most recent version of Go. So, yes, ...
We assume we can do that conversion now. If I made a separate Go module/package for fetching certs, I can make sure it works with older Go versions. |
FWIW, we generally go with "current - 1" for Go versions for Caddy, mainly because our RPM packaging maintainers need to rely on the Go version they have available to them. Thanks for splitting it out @bradfitz 😀 |
… access So you can run Caddy etc as a non-root user and let it have access to get certs. Updates caddyserver/caddy#4541 Change-Id: Iecc5922274530e2b00ba107d4b536580f374109b Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
… access So you can run Caddy etc as a non-root user and let it have access to get certs. Updates caddyserver/caddy#4541 Change-Id: Iecc5922274530e2b00ba107d4b536580f374109b Signed-off-by: Brad Fitzpatrick <bradfitz@tailscale.com>
And I've pushed tailscale/tailscale#3809 to add support for a |
Tailscale client code is now split out into https://github.com/tailscale/tscert Tested back to Go 1.15, but it probably supports earlier. |
BTW, the tscert.GetStatus func can also tell you which (0, 1, or more) subjects that the local tailscaled can handle. |
Brilliant! Thanks Brad. I pushed a commit which adds context to the GetCertificate call and checks the Status to see if the domain in the handshake is in the list. If it's not a cert provisioned by Tailscale, false+nil is returned to CertMagic so that CertMagic knows to continue with its own management logic. I also changed to using the We'll see how the tests do now. I'll probably figure out some simple response body for the HTTP getter just as a cherry on top, though I don't think we'll need it at this time. |
You know, it just occurred to me, that we could probably make this a default, implicit behavior. If Tailscale is running AND happens to have a certificate for the hostname, just use it. If not, then continue with our own cert. Returning If we can guarantee the latency of checking the Tailscale status is very minimal (maybe we cache the results for a certain amount of time? or set a context with a timeout of ~5ms?), maybe this is safe to do. We could even restrict any of this implicit behavior to activate only on names ending in Of course, if I'm just thinking, maybe it doesn't hurt to try Tailscale implicitly even if not configured, then it really will "just work". Caddy +Tailscale, ladies and gentlemen. 🤵♂️ |
That's a safe heuristic. SGTM. We have no plans to offer other domain name suffixes. But we may permit people to BYODomain in the future, so if you make it automatic on |
And use reuse CertMagic's PEM functions for private keys.
The HTTP cert getter should be working now. It expects PEM blocks and it will decode them into a TLS certificate and private key pair. I think we're ready to merge this, and I'll try to implement implicit Tailscale cert-getting as a cherry on top in another PR, if I can make it work. (It wasn't a requirement, I just think it'd be cool.) |
Tailscale does its own caching and we don't need the added complexity... for now, at least.
What happened to the CI checks... (Edit: they're working now; were stuck for like 3 days) |
- Option to disable cert automation in auto HTTPS - Support multiple cert managers - Remove cache feature from cert manager modules - Minor improvements to auto HTTPS logging
Only for domains ending in .ts.net. I think this is really cool!
It seems like HTTP cert gatter is ignored on caddy v2.6.4. I set the get_certificate section in caddyfile, caddy verified all parameters and directly goes to HTTP-01 challenge. Log of cert.mysite shows no http request from web.mysite.
|
@BioEvo Could you open a new issue please? Be sure to enable debug logging and include all the logs. (And please don't redact the domain names, we'll need the actual values to troubleshoot.) Also the last Caddy version it does work on. The more specific you are, the more specific we can be. Thank you! |
Thank you! I try to reproduce this in docker: #5415 (comment) . I'm not sure how will caddy openration with HTTP CertManager, but I think if the config is like:
then caddy might try to request http://cert.mysite/cert?server_name=web.mysite&signature_schemes=...&cipher_suites=...
|
Sometimes there are external tools or services that are managing certificates where we just need to call
GetCertificate()
during a handshake. The implementer of that function takes care of caching, renewal logic, etc. This adds support forCertificateGetter
modules, in thetls.get_certificate
namespace.These differ from Issuer modules in that
Issue()
takes a CSR, whereasGetCertificate()
takes a*tls.ClientHelloInfo
. Also, using an Issuer implies that Caddy/CertMagic also generate and manage the private key, AND store the certificate and key in storage, since we're the ones maintaining the cert. In the case of GetCertificate, we don't want to be bothered about private keys or persisting in storage; we just want a cert. So basically, we use Issuers when we claim that we're managing the certificate. And we useCustomGetCertificate
only to get a certificate from something we know is managing it for us.The return value of our
GetCertificate()
is optional. If it returns nil+nil, it is passed by as if it was not specified at all; thus could fall back to traditional Issuer management for a name.Specifying a certificate getter module implicitly enables On-Demand TLS for that policy (as if
on_demand: true
was set) because certificates given byGetCertificate()
have to be called during a handshake.Our
GetCertificate()
signature is slightly different fromtls.Config.GetCertificate
in that it adds abool
middle return value, which if true, tells CertMagic to add the certificate to its cache. Once cached, the module'sGetCertificate()
will only be called again once the cert nears expiration. Modules should returnfalse
for this value if they want to be called for every single TLS handshake.This initial implementation integrates natively with Tailscale, although it requires being run as root or as a regular user that has access to the Tailscale socket. I'm still learning about this.
Here's an example automation policy for using Tailscale certs:
Or as a Caddyfile:
A follow-up commit may also add support for an HTTP getter, to download the cert over HTTP.
This feature will remain experimental (and subject to change) even after being released, as we learn about it from experience.
Will be adding Caddyfile support soon, too.
Huge thanks to Tailscale for making this feature possible! Excited to release this and share more about it. /cc @bradfitz
TODO:
Builds on caddyserver/certmagic#163