Open sandstrom opened 1 year ago
@maraino do you know if this is an option Smallstep supports? Haven't checked the API yet, wondering if you know.
Also, I'd suggest enabling the "Discussions" tab on Github. Then you'd get fewer issues for ideas like this 😄
We have forums: https://caddy.community, no need for another place for discussion.
There seems to be some kind of support: https://smallstep.com/docs/step-ca/templates/#adding-name-constraints
But I don't know certificates and these extensions in enough detail to discern if this support is enough, or how much work it would be (or if it's even a good idea).
@francislavoie: Yes, but you want the name constraints in the intermediate.
Caddy supports a root and intermediate coming from the configuration. If the intermediate has name constraints, it should work automatically.
But if you want this from the intermediate that Caddy generates, you will have to add the necessary attributes here: https://github.com/caddyserver/caddy/blob/4776f62caa36c580d24be8e55ebc6a61ae129f51/modules/caddypki/certificates.go#L38C1-L42
You can do it either in the template or directly in the template. The template is probably more straightforward for you. You will need to get the configuration values from the Caddyfile, so you will need to pass them as a new argument to generateIntermediate
.
But again, as this is also an edge case, you can document how to create a new root and intermediate with name constraints, and it will work. So, for example, with step, given a root certificate and key, you can sign an intermediate with the name constraints defined in a template like this (nc.tpl
):
{
"subject": {{ toJson .Subject }},
"keyUsage": ["certSign", "crlSign"],
"basicConstraints": {
"isCA": true,
"maxPathLen": 0
},
"nameConstraints": {
"critical": true,
"permittedDNSDomains": ["smallstep.com"]
}
}
Running:
step certificate create --ca root_ca.crt --ca-key root_ca_key --template nc.tpl \
'Smallstep Intermediate' intermediate_ca.crt intermediate_ca_key
People using name constraints should know what they exactly mean, as some cases are not obvious. For example, adding just permittedDNSDomains
as above does not exclude creating domains with IP addresses or any other type of SAN. Name constraints are defined in RFC5280#4.2.1.10
Thanks @maraino! That helps!
I'm clicking around through the code, and I'm noticing that there's nothing in https://github.com/smallstep/crypto/blob/c8a8532f869761ee64c8520ec4f1951a7865d161/x509util/templates.go for name constraints (and some other template fields) currently. Is it reasonable to ask that that's added? I think that would make the API more natural/resilient for us, by using exported consts or functions instead of hard-coding template keys in Caddy.
You can do it either in the template or directly in the template. The template is probably more straightforward for you
With the second template, I meant the template variable in the code, not the x509util.DefaultIntermediateTemplate
. It will be easier to add something like:
template, signer, err := newCert(commonName, x509util.DefaultIntermediateTemplate, lifetime)
if err != nil {
return nil, nil, err
}
if len(config.PermittedDNSDomains) > 0 {
template.PermittedDNSDomainsCritical = true
template.PermittedDNSDomains = config.PermittedDNSDomains
}
That assumes that config are configuration options in the Caddyfile. But you can also configure that template and do if conditions.
I'm clicking around through the code, and I'm noticing that there's nothing in https://github.com/smallstep/crypto/blob/c8a8532f869761ee64c8520ec4f1951a7865d161/x509util/templates.go for name constraints (and some other template fields) currently. Is it reasonable to ask that that's added? I think that would make the API more natural/resilient for us, by using exported consts or functions instead of hard-coding template keys in Caddy.
We support name constraints in the template, nameConstraints
object can have these attributes.
The docs from above show that https://smallstep.com/docs/step-ca/templates/#adding-name-constraints
But for your use case, it is probably better to do it like my previous comment, you can even allow users to define the x509util.DefaultIntermediateTemplate
, to make it 100% configurable. But if somebody wants 100% configurability, they should create the root and intermediate themselves, and that should be already working.
Thanks for your wisdom and guidance as usual, @maraino :blush:
Yes, but you want the name constraints in the intermediate.
@maraino Any reason why one would want it on the intermediate? Given that one usually adds the root to the trust store I'd most certainly want it there as well no? It also seems to be very hard to find out what software supports name constraints and where :)
@apollo13 you're right it would be nicer to have them on the root (too), but the RFC 5280 section 4.2.1.10 mentions this:
Name constraints are not applied to self-issued certificates (unless
the certificate is the final certificate in the path). (This could
prevent CAs that use name constraints from employing self-issued
certificates to implement key rollover.)
So, technically, systems are not required to support name constraints on root certificates. Some software might support it, but you'll need to research if it works for your environment. This SO thread seems to indicate that Chromium does support it these days. For Firefox I've seen some references stating there's support for them on root certificates, and configuration after the root certificate is issued, but haven't found a conclusive answer to that yet.
A workaround for the above situation could be to initialize your PKI without name constraints on the root, with name constraints on the intermediate, and then removing the root private key. In this case you would not be able to rotate your intermediate, so you would have to redistribute a new root when the time comes. But at least it's not possible to issue new certificates from the root that are not valid under the constraints.
In terms of general support for name constraints, BetterTLS is a great source of data on this.
Note that even with name constraints set on an intermediate, the intermediate can still be used to sign certificates that are considered invalid under the name constraints. It is up to the relying party (e.g. browsers and other client systems) to properly validate the name constraints. It is possible that issuing systems prevent this case from happening, but that's not something described in the RFC, afaik. This check is present in the core signing flow of step-ca
and is enabled by default.
@hslatman Thank you for the detailed answer.
A workaround for the above situation could be to initialize your PKI without name constraints on the root, with name constraints on the intermediate, and then removing the root private key.
Yeah I am trying to avoid that. In an ideal world I would be able to distribute a CA cert where the users can add it into the truststore without a fear of MITM on other domains and without having to trust me that I have thrown away the private key.
Would putting the intermediate into the truststore be another option? At least firefox seems to be okay with that.
In terms of general support for name constraints, BetterTLS is a great source of data on this.
Yeah, I knew of that site and support seems to be generally okay nowadays which is why I was considering using it.
Note that even with name constraints set on an intermediate, the intermediate can still be used to sign certificates that are considered invalid under the name constraints. It is up to the relying party (e.g. browsers and other client systems) to properly validate the name constraints.
That is okay. I am mostly concerned about browser here which properly validate it. For everything else I can explicitly set a custom CA easily anyways (at least most of the time)
Would putting the intermediate into the truststore be another option? At least firefox seems to be okay with that.
Generally that should work when a chain is built, but I don't know about exact browser behavior when combined with name constraints. Clients might use the same logic as they do for roots in this case, so may or may not support it. But I think it might just work, indeed. In this case, and as an extra precaution, you could also remove the root private key if you don't need it anymore.
Haha, TLS is everything but easy. Guess I'll need to do some testing more :) Thanks!
When generating a CA cert via caddy and putting that in the trust store, those private keys can also forge certificates for any other domain.
We're only using this for
company.dev
and two other domains. Would be neat if we could tell Caddy to create a CA with name constraint extension, reducing the scope of its authority to only domains (and their subdomains) that we need it for.Just an idea, feel free to close this if it isn't relevant.
Also, I'd suggest enabling the "Discussions" tab on Github. Then you'd get fewer issues for ideas like this 😄
Background: https://security.stackexchange.com/questions/31376/can-i-restrict-a-certification-authority-to-signing-certain-domains-only