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 wildcard CNs in root leaf certificates #1088

Merged
merged 1 commit into from
Jan 27, 2017

Conversation

ecordell
Copy link
Contributor

@ecordell ecordell commented Jan 26, 2017

This adds support for explicit wildcard suffixes in root (leaf) certificates of the form prefix*.

I considered doing something more flexible using filepath.Match but I think this more conservative form is safer and (probably) just as useful. It would be easy to change in the future if more flexibility (e.g. docker.io/*/test) is needed.

Note the wildcard * indicator is required to avoid accidentally using wildcard certs (e.g. docker.io/test and docker.io/test2).

Fixes #883
Possibly addresses #914 as well?

@ecordell ecordell force-pushed the wildcard-certs branch 3 times, most recently from b481fb3 to fb428da Compare January 26, 2017 18:42
// MatchCNToGun checks that the common name in a cert is valid for the given gun.
// This allows wildcards as suffixes, e.g. `namespace/*`
func MatchCNToGun(commonName, gun string) bool {
wildcard := "*"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should this be a const?

@endophage
Copy link
Contributor

Glad you avoided filepath.Match, it has weird behaviour. It doesn't support ** and a single * matches exactly one and only one path segment. (it also probably wouldn't be consistent on windows).

One thought, should we require the * comes after a /? I don't have any instinct either way, just throwing it out there.

// MatchCNToGun checks that the common name in a cert is valid for the given gun.
// This allows wildcards as suffixes, e.g. `namespace/*`
func MatchCNToGun(commonName, gun string) bool {
const wildcard = "*"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I figured a const at probably package scope :-P This works too as far as giving us immutability though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

figured the go compiler would do what I wanted here, didn't really verify though

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It probably will as far as optimization, I was thinking more along code organization.

@ecordell
Copy link
Contributor Author

ecordell commented Jan 26, 2017

One thought, should we require the * comes after a /? I don't have any instinct either way, just throwing it out there.

Personally I think that's a little too restrictive; I was considering an organization using a wildcard cert for a subset of their packages, e.g. docker/notary* to cover docker/notary, docker/notary-server, and docker/notary-signer

{"test/*/wild", "test/test/wild", false},
{"*/all", "test/all", false},
{"docker.com/*/*", "docker.com/notary/test", false},
{"*", "docker.com/any", true},
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add the following test cases:

CN: "", gun: *, out: false
CN: *, gun: "", out: true
CN: *abc*, gun: "abc", out: false (since we check against *abc, no longer wildcarded)
CN: test/*/wild*, gun: "test/test/wild", out: false (since we check against test/*/wild, no longer wildcarded)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of curiosity, if the common name is **, should we treat that as *? If not, can we also add one more test?

CN: **, gun: "docker.com/any", out: false

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Non-blocking: Since we're not actually checking any trust pinning here, do we need a CA+intermediate+leaf for this test? Might make the test a little shorter to just include the leaf.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@cyli: good idea - I like treating ** as * for now to keep things simple so that test would be great 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I went ahead and switched TrimSuffix to TrimRight so that any number of trailing *s will collapse down to 1.

func MatchCNToGun(commonName, gun string) bool {
const wildcard = "*"
if strings.HasSuffix(commonName, wildcard) {
prefix := commonName[:len(commonName)-len(wildcard)]
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit style: I'd prefer using strings.TrimSuffix instead of indexing directly

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, could we add a log debug to display which prefix we're comparing against?

My concern is if folks use something like prefix** and expect matching against prefix instead of prefix*

@@ -1299,3 +1299,193 @@ func TestValidateRootWithExpiredIntermediate(t *testing.T) {
)
require.Error(t, err, "failed to invalidate expired intermediate certificate")
}

func TestCheckingWilcardCert(t *testing.T) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: "TestCheckingWildcartCert"

@ecordell ecordell force-pushed the wildcard-certs branch 2 times, most recently from d474056 to 764e056 Compare January 27, 2017 12:10
Signed-off-by: Evan Cordell <cordell.evan@gmail.com>
Copy link
Contributor

@riyazdf riyazdf left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you for picking this up @ecordell!

Copy link
Contributor

@cyli cyli left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Thanks for adding this support, @ecordell!

@endophage endophage merged commit 3f8c34e into notaryproject:master Jan 27, 2017
@ecordell ecordell deleted the wildcard-certs branch February 23, 2017 12:39
@lewiada-zz
Copy link

How do I enable this feature? Today the root public key in the root.json file is a self-signed cert with CN == GUN. How do I use this such that the CN is a wildcard such as www.example.com/* ?

@endophage
Copy link
Contributor

The functionality to leverage it hasn't been added yet :-( #821 was part of the work on that but I guess @dnwake has been otherwise engaged. I pinged him a few weeks ago checking if he'd get back around to it and he said he would.

@lewiada-zz
Copy link

@endophage - we are making the changes (actually already done, just testing now) as well as some others that you and I discussed. You should see a pull request in the next few days. Once this is in place, we are planning to work on CA pinning, and then moving trust pinning into DCT. Once that's all finished, we can work on getting generalizing the pkcs11 interface to support HSMs other than Yubikey :-)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support wildcard root certs
5 participants