Skip to content
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

Support name constraint extension for self-generated CA authority #5759

Open
sandstrom opened this issue Aug 17, 2023 · 13 comments
Open

Support name constraint extension for self-generated CA authority #5759

sandstrom opened this issue Aug 17, 2023 · 13 comments
Labels
feature ⚙️ New feature or request

Comments

@sandstrom
Copy link

sandstrom commented Aug 17, 2023

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

@francislavoie
Copy link
Member

francislavoie commented Aug 17, 2023

@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.

@francislavoie francislavoie added the feature ⚙️ New feature or request label Aug 17, 2023
@sandstrom
Copy link
Author

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).

@maraino
Copy link
Contributor

maraino commented Aug 23, 2023

@francislavoie: Yes, but you want the name constraints in the intermediate.

@maraino
Copy link
Contributor

maraino commented Aug 23, 2023

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

@francislavoie
Copy link
Member

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.

@maraino
Copy link
Contributor

maraino commented Aug 23, 2023

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.

@maraino
Copy link
Contributor

maraino commented Aug 24, 2023

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.

@mholt
Copy link
Member

mholt commented Sep 7, 2023

Thanks for your wisdom and guidance as usual, @maraino 😊

@apollo13
Copy link
Contributor

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 :)

@hslatman
Copy link
Contributor

hslatman commented May 28, 2024

@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.

@apollo13
Copy link
Contributor

@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)

@hslatman
Copy link
Contributor

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.

@apollo13
Copy link
Contributor

Haha, TLS is everything but easy. Guess I'll need to do some testing more :) Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature ⚙️ New feature or request
Projects
None yet
Development

No branches or pull requests

6 participants