certificates icon indicating copy to clipboard operation
certificates copied to clipboard

Option(s) to denylist certain common names / subjects / alternative names

Open kbabioch opened this issue 6 years ago • 7 comments

What would you like to be added

I might have missed something very obvious, but there seems to be no way to limit and constrain the issuance of certificate in general and/or for specific provisioners.

Why this is needed

There is probably a lot of possible options imaginable here, but the particular use-case I have in mind is to make sure that certificates for some domains cannot be issued using the ACME provisioner, even though someone might have control over the domain and/or the webserver on this domain.

kbabioch avatar Oct 07 '19 22:10 kbabioch

You didn't miss anything. This is a problem with ACME. The challenge here is that the sorts of policies people want to capture vary quite a bit. We have some tech to address this problem generically but it's not released yet :/.

In the meantime, I've been thinking about basic filtering based on domain names... perhaps borrowing from the name constraint mechanism from Web PKI. It'd probably be provisioner-level and you'd be able to add a parameter like "domains": ["foo.example.com", ".dev.example.com"] which, in this case, would limit the provisioner to issuing certificates for "foo.example.com" and any subdomain under ".dev.example.com". Would that be enough for you?

mmalone avatar Oct 08 '19 03:10 mmalone

name constraints is something on top of it

lets say I have an intermediate CA file with a nameConstraint=dns:example.com

but I also want to prevent people from getting certs for some shared production servers under example.com. only a subset of people should be allowed to get certs for those.

darix avatar Oct 09 '19 11:10 darix

but I would be +1000 for having builtin nameconstraints support :)

darix avatar Oct 09 '19 11:10 darix

Sorry, yea, what I meant was we could borrow the syntax and semantics of name constraints and re-use them to restrict certificate issuance on a per-provisioner basis. The question of whether CA root/intermediate certificates contain name constraints is separate.

What I'm proposing is that you could have a provisioner configuration like this:

{
    "type": "ACME",
    "name": "acme-issuer",
    "domains": {
        "allowed": ["foo.example.com", ".dev.example.com"],
        "denied": ["ca.dev.example.com"]
    }
}

The "foo.example.com" and ".dev.example.com" syntax is borrowed from name constraints. The former is an exact match, and the latter is a wildcard. So, in this case, we're saying that the ACME provisioner should only issue certificates for "foo.example.com" and for any subdomain under "dev.example.com", but not "ca.dev.example.com". We wouldn't put name constraints in the intermediate certificate or anywhere else. We're just re-using the name constraint syntax to define which domains a provisioner can/can't issue certificates for.

You could have other provisioners that are configured with different constraints. So you could have a JWK-based provisioner or an instance identity provisioner that does allow you to get certificates for these other domains.

There is a catch though: ACME is sort of all-or-nothing. So if you have any ACME provisioner setup and configured to issue certificates for your shared production servers then anyone could use it. You'd have to use a different provisioner type for this.

There is an External Account Binding mechanism defined as part of RFC8555 (the ACME RFC). This lets you connect ACME accounts to existing accounts (e.g., in Okta or AD or GSuite) instead of creating new quasi-anonymous accounts for ACME. We don't currently support it. I actually don't know of any existing implementations. But, if we did, it's possible we could tie authorizations to individual users instead of tying them to provisioners. This will require a lot more research to spec out and a lot more work to implement though. I think it might be easier to just use a different provisioner type, at least for now.

Is provisioner-level authorization sufficient for you, or do you need it to somehow map to individual users? Would you be open to using a different provisioner type or do you need to use ACME?

mmalone avatar Oct 09 '19 16:10 mmalone

The question of whether CA root/intermediate certificates contain name constraints is separate.

Exactly, those are two different things.

We're just re-using the name constraint syntax to define which domains a provisioner can/can't issue certificates for.

That should work for me and I don't have a better proposal for now.

One more powerful approach could be to have some kind of "policy engine", i.e. some interfaces that you can hook into with scripts / daemons, which will get all necessary parameters (account, domain, IPs, etc. pp.) and can then return "go" or "no-go". But this is way more sophisticated and probably takes more research / engineering to get right.

There is a catch though: ACME is sort of all-or-nothing. So if you have any ACME provisioner setup and configured to issue certificates for your shared production servers then anyone could use it. You'd have to use a different provisioner type for this.

That's the reason for this feature request. With the name constraints (on a provisioner level as discussed above), you can limit / exclude those kind of production servers.

kbabioch avatar Oct 09 '19 18:10 kbabioch

One more powerful approach could be to have some kind of "policy engine"

You read our minds. We have a pretty awesome policy engine that we're going to release at some point. In anticipation of that happening, we're trying not to add a whole bunch of ad-hoc authorization stuff. Making this a hook, as described, is an interesting idea. Then you could integrate any policy engine. Either way, you're also right that this requires a lot more work to do right... and we do want to do it right! :/

That's the reason for this feature request...

After re-reading my comment, I don't think I articulated the problem I was raising very well.

If you have a single ACME provisioner, like this, then you're good:

"provisioners": [
    {
        "type": "ACME",
        "name": "acme-issuer",
        "domains": {
            "allowed": [".dev.example.com"],
            "denied": ["ca.dev.example.com"]
        }
    }
]

ACME clients would only be allowed to get certificates for *.dev.example.com (and not for prod).

But, suppose you have two ACME provisioners. One that issues for dev and the other that issues for prod:

"provisioners": [
    {
        "type": "ACME",
        "name": "dev-acme-issuer",
        "domains": {
            "allowed": [".dev.example.com"],
            "denied": ["ca.dev.example.com"]
        }
    },
    {
        "type": "ACME",
        "name": "prod-acme-issuer",
        "domains": {
            "allowed": [".prod.example.com"],
            "denied": ["ca.dev.example.com"]
        }
    }
]

In this case we'd prevent prod certificates from being issued by the dev issuer (and vice versa). But there's nothing to prevent an ACME client from using the prod issuer. Anyone with control over the domain (i.e., anyone that can respond to an HTTP or DNS challenge) can use either issuer.

mmalone avatar Oct 09 '19 19:10 mmalone

In anticipation of that happening, we're trying not to add a whole bunch of ad-hoc authorization stuff.

This makes sense. Don't feel pressured into implementing something (in the context of this issue), if you have something better in the pipeline. I'm not (yet) using this in production, currently just evaluating it, so I'm happy to wait ;-).

But, suppose you have two ACME provisioners. One that issues for dev and the other that issues for prod:

Yes, this makes more sense now. Thanks for clarifying this. Of course, you're right. However, this will be true for any solution - as long as you specify it on a per provisioner level. In order to tackle this, you'll need some "global" settings/policy engine.

Thanks for all of the interaction and the cool product. Keep it up ;-).

kbabioch avatar Oct 09 '19 21:10 kbabioch

This functionality was largely implemented as certificate issuance policies: https://smallstep.com/docs/step-ca/policies/.

It's limited to the SANs and the Subject Common Name, and its configuration has been kept simple intentionally. There are cases for which the policy doesn't cut it, and for those cases webhooks can be used.

hslatman avatar Feb 20 '24 09:02 hslatman