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

[Security] Add support for SPIFFE Bundle Maps in XDS bundles #8180

Open
wants to merge 9 commits into
base: master
Choose a base branch
from

Conversation

gtcooke94
Copy link
Contributor

This adds support for configuring SPIFFE Bundle Maps inside of credentials via xds bundles.

See the gRFC for more detail grpc/proposal#462

RELEASE NOTES: N/A

@gtcooke94 gtcooke94 added Type: Security A bug or other problem affecting security Area: xDS Includes everything xDS related, including LB policies used with xDS. Area: Auth Includes regular credentials API and implementation. Also includes advancedtls, authz, rbac etc. labels Mar 18, 2025
@gtcooke94 gtcooke94 added this to the 1.72 Release milestone Mar 18, 2025
Copy link

codecov bot commented Mar 18, 2025

Codecov Report

Attention: Patch coverage is 79.86577% with 30 lines in your changes missing coverage. Please review.

Project coverage is 82.12%. Comparing base (1f6b0cf) to head (327a534).
Report is 8 commits behind head on master.

Files with missing lines Patch % Lines
internal/testutils/tls_creds.go 55.00% 18 Missing and 9 partials ⚠️
internal/xds/bootstrap/tlscreds/bundle.go 93.87% 2 Missing and 1 partial ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##           master    #8180      +/-   ##
==========================================
- Coverage   82.42%   82.12%   -0.31%     
==========================================
  Files         393      410      +17     
  Lines       39162    40371    +1209     
==========================================
+ Hits        32280    33154     +874     
- Misses       5571     5855     +284     
- Partials     1311     1362      +51     
Files with missing lines Coverage Δ
credentials/tls/certprovider/pemfile/builder.go 100.00% <100.00%> (ø)
credentials/tls/certprovider/pemfile/watcher.go 86.56% <100.00%> (-2.99%) ⬇️
internal/credentials/spiffe/spiffe.go 100.00% <100.00%> (ø)
internal/xds/bootstrap/tlscreds/bundle.go 86.86% <93.87%> (+4.41%) ⬆️
internal/testutils/tls_creds.go 55.00% <55.00%> (ø)

... and 45 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

Copy link
Contributor

Choose a reason for hiding this comment

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

We need an entry for this in testdata/spiffe/README.md?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@@ -0,0 +1,32 @@
All of the following files in this directory except `server_spiffebundle.json` and `client_spiffebundle.json` are generated with the `generate.sh` and `generate_intermediate.sh` script in this directory.
Copy link
Contributor

Choose a reason for hiding this comment

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

Nit: Wrap .md files at 80-cols?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

The SPIFFE trust bundle map files (*_spiffebundle.json) are manually created for
end to end testing. The server_spiffebundle.json contains the "foo.bar.com"
trust domain (only this entry is used in e2e tests) matching URI SAN of
client_spiffe.pem, and the CA certificate is ca.pem. The client
Copy link
Contributor

Choose a reason for hiding this comment

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

Some file names have backticks, while some don't. Can we make them consistent please.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed

Comment on lines 26 to 32
can be done in golang with the following codeblock: ``` func
GetBase64ModulusFromPublicKey(key *rsa.PublicKey) string { return
base64.RawURLEncoding.EncodeToString(key.N.Bytes()) }

block, _ := pem.Decode(rawPemCert) cert, _ := x509.ParseCertificate(block.Bytes)
publicKey := cert.PublicKey.(*rsa.PublicKey)
fmt.Println(GetBase64ModulusFromPublicKey(publicKey)) ```
Copy link
Contributor

Choose a reason for hiding this comment

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

This code block doesn't render properly. Please ensure that the markdown file renders as you expect it to.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Looks like some auto formatting messed it up, fixed


// IDFromCert parses the SPIFFE ID from the x509.Certificate. If the certificate
// does not have a valid SPIFFE ID, returns an error.
func IDFromCert(cert *x509.Certificate) (*spiffeid.ID, error) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Does this function have to be exported? I see it only being called from here and from tests.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good call, changed

Comment on lines 377 to 384
cfg := fmt.Sprintf(`{
"certificate_file": "%s",
"private_key_file": "%s",
"spiffe_trust_bundle_map_file": "%s"
}`,
testdata.Path("spiffe_end2end/client_spiffe.pem"),
testdata.Path("spiffe_end2end/client.key"),
testdata.Path("spiffe_end2end/client_spiffebundle.json"))
Copy link
Contributor

Choose a reason for hiding this comment

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

Same here too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Discussion in other comment

if _, err = client.EmptyCall(ctx, &testpb.Empty{}); err == nil {
t.Errorf("EmptyCall(): got success. want failure")
}
wantErr := "spiffe: no bundle found for peer certificates"
Copy link
Contributor

Choose a reason for hiding this comment

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

const.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

t.Errorf("EmptyCall(): got success. want failure")
}
wantErr := "spiffe: no bundle found for peer certificates"
if !strings.Contains(err.Error(), wantErr) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Also check the status code?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@@ -96,3 +102,79 @@ func (s) TestFailingProvider(t *testing.T) {
t.Errorf("EmptyCall() got err: %s, want err to contain: %s", err, wantErr)
}
}

func (s) TestSPIFFEVerifyFuncMismatchedCert(t *testing.T) {
wantErrContains := "spiffe: x509 certificate Verify failed"
Copy link
Contributor

Choose a reason for hiding this comment

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

const

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Done

@@ -96,3 +102,79 @@ func (s) TestFailingProvider(t *testing.T) {
t.Errorf("EmptyCall() got err: %s, want err to contain: %s", err, wantErr)
}
}

func (s) TestSPIFFEVerifyFuncMismatchedCert(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.

Can/Should these tests be e2e style as well? That way, we can actually verify that the error returned to the user (as part of the RPC) has enough details in it for the user to take meaningful action.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You mean should we replicate these tests in bundle_ext_test.go and actually do the client/server call rather than just this inner call? We have one failure case test where there is no bundle for the peer certificates trust domain, I can expand and add these cases?

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 on and expanded the end2end tests, PTAL

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Area: Auth Includes regular credentials API and implementation. Also includes advancedtls, authz, rbac etc. Area: xDS Includes everything xDS related, including LB policies used with xDS. Type: Security A bug or other problem affecting security
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants