-
-
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
v2: Implement 'pki' app powered by Smallstep for localhost certificates #3125
Conversation
// TODO: eliminate placeholders / needless values | ||
cfg := &authority.Config{ | ||
Address: "placeholder_Address:1", | ||
Root: []string{"placeholder_Root"}, | ||
IntermediateCert: "placeholder_IntermediateCert", | ||
IntermediateKey: "placeholder_IntermediateKey", | ||
DNSNames: []string{"placeholder_DNSNames"}, | ||
AuthorityConfig: &authority.AuthConfig{ | ||
Provisioners: provisioner.List{}, | ||
}, | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// TODO: borrowing from https://github.com/smallstep/certificates/blob/806abb6232a5691198b891d76b9898ea7f269da0/authority/provisioner/sign_options.go#L191-L211 | ||
// as per https://github.com/smallstep/certificates/issues/198. | ||
// profileDefaultDuration is a wrapper against x509util.WithOption to conform | ||
// the SignOption interface. | ||
type profileDefaultDuration time.Duration | ||
|
||
// TODO: is there a better way to set cert lifetimes than copying from the smallstep libs? | ||
func (d profileDefaultDuration) Option(so provisioner.Options) x509util.WithOption { | ||
var backdate time.Duration | ||
notBefore := so.NotBefore.Time() | ||
if notBefore.IsZero() { | ||
notBefore = time.Now().Truncate(time.Second) | ||
backdate = -1 * so.Backdate | ||
} | ||
notAfter := so.NotAfter.RelativeTime(notBefore) | ||
return func(p x509util.Profile) error { | ||
fn := x509util.WithNotBeforeAfterDuration(notBefore, notAfter, time.Duration(d)) | ||
if err := fn(p); err != nil { | ||
return err | ||
} | ||
crt := p.Subject() | ||
crt.NotBefore = crt.NotBefore.Add(backdate) | ||
return nil | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"strings" | ||
) | ||
|
||
func pemDecodeSingleCert(pemDER []byte) (*x509.Certificate, error) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is pretty close to being done. Some remaining questions pertain mostly to automatic HTTPS. As the PR currently stands, I intend for all sites -- including localhost/dev ones -- to use HTTPS by default. This is just a bit tricky in practice.
These are the two main pain points I haven't resolved yet. More visually, what behavior results from these Caddyfiles:
We'll have the ability to serve all these over HTTPS, but in the first case, binding to port 443 might be problematic. In the second case, using a non-publicly trusted cert means that any client accessing it must trust Caddy's root -- not a big deal, but just not something that is automatic yet. Oh, and what about this:
? |
I'd suggest use tls by default only on hostnames without a port, i. e. Don't use tls by default on Only exception would be for the port Of course a |
@xdevs23 Thanks for your input! I don't think that's going to fly though, because most dev environments can't (freely) bind to low ports, so defaulting to low ports for internal/localhost names will usually result in an error. I think this is what I'll end up doing for now:
|
Hasn't this always been the case? Using automatic tls only changes the bound ports from just 80 to 80 and 443. Also, since there is a Docker image of Caddy v2, I think that might be the best solution overall and would also solve this issue. |
Yes, and no, because automatic HTTPS never triggered in dev environments by default. If anyone wanted to use a public DNS name in a local dev environment, they'd turn off automatic HTTPS using
That is so untrue. Automatic HTTPS does a lot more to work properly: https://caddyserver.com/docs/automatic-https#effects - and the default port is 2015, not 80 (because of the low port permissions problem). This has always been the case, since Caddy v0.5.0 back in early 2015.
I don't think Docker has anything to do with it. |
I got something mixed up then, my bad.
Docker has the permission to bind low ports which means Caddy can do that, too, as long as Caddy itself inside the Docker container has that permission, too. I think you're right — binding 80 and 443 by default for non-public domains is not a good idea. Perhaps a global directive explicitly allowing this could be the solution – or |
In the case of Docker or any other environment where you are able to bind to low ports, you can certainly opt-in to the automatic HTTPS for an internal name by omitting the port and using
or
... I think, anyway. That's what I'm working on today. |
I think the |
Well, my point is that both methods are valid; |
Yeah, I think that's a good approach. |
Wow our CI tests disappeared |
Woohoo! CI is back 🎉 |
@mohammed90 I got our CI back, but it's still broken... I had to delete the old pipelines and re-make it... any chance you could help? 😅 |
Gah, I pushed a small commit and the checks disappeared again, now the only check is CLA Assistant again... 😓 |
Let's try adding PR trigger to the pr:
- v2 |
@mohammed90 Hm, but we didn't have to do that before, right? Anyway, we can try it if you want, but in a separate PR / let's chat on Slack -- I'm gonna merge this up for now. Thanks! |
There is one question as to the auto-redirects on port 80. If a user can't bind to low ports, they can just specify higher ones in their config, but auto-redirects still happen from port 80 (unless the |
Hello, finally you have found a way to launch https://localhost on the current V2. If yes please help me |
Hello @mholt , finally you have found a way to launch https://localhost on the current V2. If yes please help me |
Thanks for your question, and we're thrilled that you're using Caddy! This looks more like a question about how to use Caddy rather than a bug report or feature request. Since this issue tracker is reserved for actionable development items, I'm going to close this, but we have a community forum where more people will be exposed to your question, including people who may be more expert or experienced with the specific question you're facing. I hope you'll ask your question there, and thanks for understanding! |
This pull request adds support for localhost certificates. It creates a new App module called
pki
and a new Issuer module calledinternal
.In Caddy v1, users would write
tls self_signed
in the Caddyfile (#2502) to have Caddy generate a quick, self-signed certificate for use in local/dev environments. But the cert was only stored in memory, so its key had to continually be re-trusted every time you restarted Caddy. It was a quick and bad implementation.In Caddy 2, we can do better than that. These days, internal/local certificates can be very useful when done properly, and there is now tooling in Go to make this possible.
This design implements a proper, fully-managed, self-contained PKI (Public Key Infrastructure) system. Caddy generates a root and intermediate certificate, stores the root key offline, and uses the intermediate to sign leaf certificates. The intermediate is short-lived by default (7 days) and the leaf certificates are even shorter (12 hours -- maybe shorter before long).
Features:
Generated roots can be installed into the system trust store automatically (with authorization). We might even do this by default if the Caddyfile is used, since we expect most developers will want this.
Intermediates get renewed automatically (and soon roots will too, but their default lifetime is currently 10 years, so we'll get around to this later)
Command to uninstall root certificates (
caddy untrust
)mkcert is a tool by @FiloSottile which generates certificates and installs them into the root stores for you. It's really popular because it's so useful in local dev environments. This PR is basically mkcert, but with short cert lifetimes, proper renewals, and totally automatic and integrated into your web server! Think of this like a "mkcert server" and powered by Smallstep.
Configuration
The minimal required configuration to add to your Caddy JSON is an automation policy that uses an "internal" issuer:
That will assume a CA named "local". You can define and customize CAs, including the default "local" CA with the
pki
app:That config will install the root into your system trust store. Then:
Of course, this can be used for any hostname or IP address, not just
localhost
. We expect most users will want to use*.localhost
as well.WORK IN PROGRESS
This is not a finished PR, but please try it out and submit your feedback! Lots of TODOs to implement, and Caddyfile support is yet to come.
A later PR will add support for running an ACME server.
cf. #3021
/cc @mmalone @maraino -- I will have specific questions for your review sooner or later. Thanks!