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 San selectors in x509pop node attestor plugin #5775

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

snanjundaswamy
Copy link

@snanjundaswamy snanjundaswamy commented Jan 15, 2025

Pull Request check list

  • Commit conforms to CONTRIBUTING.md?
  • Proper tests/regressions included?
  • Documentation updated?

Affected functionality
x509pop Node Attestor plugin now supports SAN based additional selectors. Each san is for the URI format x509pop:///key:value. Each of these SANs is exposed as a selector with x509pop:san::. These selectors can also be used as a part of agent template with {{ .San.}}

Description of change
The change contains feature augmentation on x509pop node attestor plugin along with tests. The certificates for testing were generated with test/fixture/nodeattestor/x509pop/generate.go

Which issue this PR fixes
fixes #5746

@@ -48,6 +48,10 @@ func main() {
KeyUsage: x509.KeyUsageDigitalSignature,
NotAfter: neverExpires,
Subject: pkix.Name{CommonName: "COMMONNAME"},
URIs: []*url.URL{
{Scheme: "x509pop", Host: "example.org", Path: "/datacenter:us-east-1"},
{Scheme: "x509pop", Host: "example.org", Path: "/environment:production"},
Copy link
Contributor

Choose a reason for hiding this comment

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

just curious (naming things is hard), there a reason for /environment:production over say, /environment/production ?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Looks like : is a reserved character for URLs so it might not be the best choice. It may work now, but the standard library could add more validation in the future. /name/value seems like a reasonable choice to me.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Although by https://datatracker.ietf.org/doc/html/rfc3986#section-3.3 it seems to allowed in the path.

Copy link
Author

Choose a reason for hiding this comment

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

I'm not too opinionated on ':', I simply chose the character for consistency with separators in selectors. Based on what I hear, '/' sounds like a natural fit. I will make this change.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Since : is allowed in URL paths, I would suggest we stick to it as the SAN selector delimiter since it's what we use everywhere else.

Copy link
Collaborator

@sorindumitru sorindumitru left a comment

Choose a reason for hiding this comment

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

Thanks for the contribution. Generally looks good to me, with some small comments. It would also be good if we had some tests for using the selectors in the path component, do you think that could be done?

@@ -36,6 +36,7 @@ type agentPathTemplateData struct {
PluginName string
TrustDomain string
SVIDPathTrimmed string
San map[string]string
Copy link
Collaborator

Choose a reason for hiding this comment

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

Maybe something like this for the name, to be a bit clearer and to avoid confusion with other kinds of Sans

Suggested change
San map[string]string
URISanSelectors map[string]string

Comment on lines +377 to +382
if strings.Contains(unprefixedUriSan, ":") {
lastIndex := strings.LastIndex(unprefixedUriSan, ":")
uriSelectorKey := unprefixedUriSan[:lastIndex]
uriSelectorValue := unprefixedUriSan[lastIndex+1:]
uriSelectorMap[uriSelectorKey] = uriSelectorValue
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Could we also log something if it's not possible to extract a selector from the path? The x509pop scheme isn't likely to be used for anything else so the user might want to know that one of the URIs couldn't be used.

Comment on lines +378 to +381
lastIndex := strings.LastIndex(unprefixedUriSan, ":")
uriSelectorKey := unprefixedUriSan[:lastIndex]
uriSelectorValue := unprefixedUriSan[lastIndex+1:]
uriSelectorMap[uriSelectorKey] = uriSelectorValue
Copy link
Collaborator

Choose a reason for hiding this comment

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

Any reason for using LastIndex for splitting the string? Something like x509pop://example.org/datacenter:ny:1 would be split as datacenter:ny => 1, but to me it makes more sense as datacenter => ny:1 (which could be done through SplitN).

Definitely a bit of an uncommon edge case, but it would be good to figure out from the beginning.

Copy link
Author

Choose a reason for hiding this comment

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

I thought about this a little bit myself. In my experience, the Key is usually fully qualified as against the values.
Something like company.org.service.datacenter = us-east-1. References on k8s annotations here.
Either way, this should be a simple change, lmk what you think

Copy link
Collaborator

@rturner3 rturner3 Jan 21, 2025

Choose a reason for hiding this comment

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

We have a convention in the project around how selectors are formatted - key:value, where key cannot contain a : delimiter. See this helper method for reference:

parts := strings.SplitAfterN(str, ":", 2)

We should be consistent with that convention here as well.

Copy link
Author

Choose a reason for hiding this comment

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

@rturner3 @sorindumitru
Ok makes sense now.
But what about the values? Should we also retain the '/' in the values coming from the URI San? This will help us eliminate ambiguity around the usage of ':' in values. Here's an example for reference
datacenter:company/foo/us-east-1

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes, I think we should keep the SAN value as-is.

@@ -44,6 +44,7 @@ A sample configuration:
| Common Name | `x509pop:subject:cn:example.org` | The Subject's Common Name (see X.500 Distinguished Names) |
| SHA1 Fingerprint | `x509pop:ca:fingerprint:0beec7b5ea3f0fdbc95d0dd47f3c5bc275da8a33` | The SHA1 fingerprint as a hex string for each cert in the PoP chain, excluding the leaf. |
| SerialNumber | `x509pop:serialnumber:0a1b2c3d4e5f` | The leaf certificate serial number as a lowercase hexadecimal string |
| San | `x509pop:san.<key>:<value>` | The san selectors on the leaf selectors. The expected format of the uri san is `x509pop://<trust_domain>/<key>:<value>` string |
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
| San | `x509pop:san.<key>:<value>` | The san selectors on the leaf selectors. The expected format of the uri san is `x509pop://<trust_domain>/<key>:<value>` string |
| San | `x509pop:san:<key>:<value>` | The san selectors on the leaf selectors. The expected format of the uri san is `x509pop://<trust_domain>/<key>:<value>` string |

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.

4 participants