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

Add support for verifying server certs against SPIFFE IDs #252

Merged
merged 3 commits into from
Jan 10, 2022

Conversation

klyubin
Copy link
Collaborator

@klyubin klyubin commented Jan 8, 2022

This adds support to "certigo connect" to verify that the
server presented a certificate for a specific hostname or
SPIFFE ID. This name can now be unrelated to the hostname
to connect to and the Server Name Indication (SNI) to send
to the server during the TLS handshake.

Prior to this commit, the name expected in the certificate
could only be a hostname, specified either via --name (SNI)
or the hostname to connect to. SPIFFE IDs -- URLs with
"spiffe" as the scheme -- are not useful as hostnames or
SNIs. As a result, this commit adds a new --expected-name
switch to certigo connect. This override provides full
control over what name to expect in the server certificate
presented during the TLS handshake. For example, this name
can now be different from the hostname connected to and the
name specified in Server Name Indication (SNI). Moreover,
if --expected-name is a URL with "spiffe" scheme, it is
only matched against Subject Alternative Names of type URI,
as per the SPIFFE spec.

This adds support to "certigo connect" to verify that the
server presented a certificate for a specific hostname or
SPIFFE ID. This name can now be unrelated to the hostname
to connect to and the Server Name Indication (SNI) to send
to the server during the TLS handshake.

Prior to this commit, the name expected in the certificate
could only be a hostname, specified either via --name (SNI)
or the hostname to connect to. SPIFFE IDs -- URLs with
"spiffe" as the scheme -- are not useful as hostnames or
SNIs. As a result, this commit adds a new --expected-name
switch to certigo connect. This override provides full
control over what name to expect in the server certificate
presented during the TLS handshake. For example, this name
can now be different from the hostname connected to and the
name specified in Server Name Indication (SNI). Moreover,
if --expected-name is a URL with "spiffe" scheme, it is
only matched against Subject Alternative Names of type URI,
as per the SPIFFE spec.
@CLAassistant
Copy link

CLAassistant commented Jan 8, 2022

CLA assistant check
All committers have signed the CLA.

lib/verify.go Outdated
spiffeIDExpected := strings.HasPrefix(expectedName, "spiffe://")
var expectedDnsName string
if spiffeIDExpected {
expectedDnsName = ""
Copy link
Contributor

Choose a reason for hiding this comment

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

This case can be omitted, since it's a no-op right now?

var expectedDNSName string
if !spiffeIDExpected {
  expectedDnsName = expectedName
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

agreed

lib/verify.go Outdated
// SPIFFE ID. We thus perform this check explicitly here.
expectedSpiffeID := expectedName
matched := false
for _, uri := range certs[0].URIs {
Copy link
Contributor

Choose a reason for hiding this comment

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

It's invalid for a SPIFFE cert to have more than one URI SAN. Should we warn in this case?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I'm going with failing the match when there's not exactly one URI SAN, as per X.509 SPIFFE SVID spec.

cli/cli.go Outdated
hostname = *connectName
// Determine what name the server's certificate should match
var expectedNameInCertificate string
if *connectVerifyExpectedName != "" {
Copy link
Contributor

Choose a reason for hiding this comment

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

Optional. I prefer a switch in these situations:

switch {
  case *connectVerifyExpectedName != "":
    expectedNameInCertificate = *connectVerifyExpectedName
  case *connectName != "":
    expectedNameInCertificate = *connectName
  default:
    expectedNameInCertificate = strings.Split(*connectTo, ":")[0]
}

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

oooh. That's nice! done.

lib/verify.go Outdated
// expectedName could be a hostname or could be a SPIFFE ID (spiffe://...)
// x509 package doesn't support verifying SPIFFE IDs. We thus have to do a bit more work here.
spiffeIDExpected := strings.HasPrefix(expectedName, "spiffe://")
var expectedDnsName string
Copy link
Contributor

Choose a reason for hiding this comment

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

nit: this should be expectedDNSName, since Go likes keeping acronym casing the same (DNS instead of Dns)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done!

@jdtw jdtw self-assigned this Jan 10, 2022
As per SPIFFE spec:
* Reject certificates with multiple URI SANs when expected name is a SPIFFE ID
* Case-insensitive protocol/scheme check of URI SANs when expected name is a SPIFFE ID
@klyubin klyubin merged commit ba10cd1 into master Jan 10, 2022
@klyubin klyubin deleted the override-expected-name-in-cert branch January 10, 2022 23:57
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.

None yet

3 participants