Add flag guarding SPIFFE Bundle provider (#8343)

* Add flag guarding SPIFFE Bundle provider

* remove the log

* vet

* address PR comments

* add comment

* fix typo

* rename flag

* add test

* vet

* add other flag check

* remove check from watcher

* add tests for new section where the spiffe bundle map file is set to empty string

* vet
This commit is contained in:
Gregory Cooke 2025-06-04 13:31:41 -04:00 committed by GitHub
parent 6dfe07c8c3
commit f6bf86cc7e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 90 additions and 5 deletions

View File

@ -24,6 +24,7 @@ import (
"time"
"google.golang.org/grpc/credentials/tls/certprovider"
"google.golang.org/grpc/internal/envconfig"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/types/known/durationpb"
)
@ -72,6 +73,9 @@ func pluginConfigFromJSON(jd json.RawMessage) (Options, error) {
if err := json.Unmarshal(jd, cfg); err != nil {
return Options{}, fmt.Errorf("pemfile: json.Unmarshal(%s) failed: %v", string(jd), err)
}
if !envconfig.XDSSPIFFEEnabled {
cfg.SPIFFETrustBundleMapFile = ""
}
opts := Options{
CertFile: cfg.CertificateFile,

View File

@ -21,14 +21,18 @@ package pemfile
import (
"encoding/json"
"testing"
"google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/testutils"
)
func TestParseConfig(t *testing.T) {
tests := []struct {
desc string
input any
wantOutput string
wantErr bool
desc string
input any
wantOutput string
wantErr bool
enabledSpiffe bool
}{
{
desc: "non JSON input",
@ -107,10 +111,38 @@ func TestParseConfig(t *testing.T) {
}`),
wantOutput: "file_watcher:/a/b/cert.pem:/a/b/key.pem:/a/b/ca.pem::3m20s",
},
{
desc: "good config with spiffe disabled",
input: json.RawMessage(`
{
"certificate_file": "/a/b/cert.pem",
"private_key_file": "/a/b/key.pem",
"ca_certificate_file": "/a/b/ca.pem",
"spiffe_trust_bundle_map_file": "/a/b/spiffe_bundle.json",
"refresh_interval": "200s"
}`),
wantOutput: "file_watcher:/a/b/cert.pem:/a/b/key.pem:/a/b/ca.pem::3m20s",
},
{
desc: "good config with spiffe enabled",
input: json.RawMessage(`
{
"certificate_file": "/a/b/cert.pem",
"private_key_file": "/a/b/key.pem",
"ca_certificate_file": "/a/b/ca.pem",
"spiffe_trust_bundle_map_file": "/a/b/spiffe_bundle.json",
"refresh_interval": "200s"
}`),
wantOutput: "file_watcher:/a/b/cert.pem:/a/b/key.pem:/a/b/ca.pem:/a/b/spiffe_bundle.json:3m20s",
enabledSpiffe: true,
},
}
for _, test := range tests {
t.Run(test.desc, func(t *testing.T) {
if test.enabledSpiffe {
testutils.SetEnvConfig(t, &envconfig.XDSSPIFFEEnabled, true)
}
builder := &pluginBuilder{}
bc, err := builder.ParseConfig(test.input)

View File

@ -63,4 +63,9 @@ var (
// For more details, see:
// https://github.com/grpc/proposal/blob/master/A82-xds-system-root-certs.md.
XDSSystemRootCertsEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_SYSTEM_ROOT_CERTS", false)
// XDSSPIFFEEnabled controls if SPIFFE Bundle Maps can be used as roots of
// trust. For more details, see:
// https://github.com/grpc/proposal/blob/master/A87-mtls-spiffe-support.md
XDSSPIFFEEnabled = boolFromEnv("GRPC_EXPERIMENTAL_XDS_MTLS_SPIFFE", false)
)

View File

@ -36,6 +36,7 @@ import (
"google.golang.org/grpc/credentials/tls/certprovider"
"google.golang.org/grpc/credentials/tls/certprovider/pemfile"
"google.golang.org/grpc/internal/credentials/spiffe"
"google.golang.org/grpc/internal/envconfig"
)
// bundle is an implementation of credentials.Bundle which implements mTLS
@ -64,6 +65,9 @@ func NewBundle(jd json.RawMessage) (credentials.Bundle, func(), error) {
}
} // Else the config field is absent. Treat it as an empty config.
if !envconfig.XDSSPIFFEEnabled {
cfg.SPIFFETrustBundleMapFile = ""
}
if cfg.CACertificateFile == "" && cfg.CertificateFile == "" && cfg.PrivateKeyFile == "" && cfg.SPIFFETrustBundleMapFile == "" {
// We cannot use (and do not need) a file_watcher provider in this case,
// and can simply directly use the TLS transport credentials.

View File

@ -30,6 +30,7 @@ import (
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/internal/envconfig"
"google.golang.org/grpc/internal/grpctest"
"google.golang.org/grpc/internal/stubserver"
"google.golang.org/grpc/internal/testutils"
@ -237,6 +238,7 @@ func (s) TestCaReloading(t *testing.T) {
// is performed and checked for failure, ensuring that gRPC is correctly using
// the changed-on-disk bundle map.
func (s) Test_SPIFFE_Reloading(t *testing.T) {
testutils.SetEnvConfig(t, &envconfig.XDSSPIFFEEnabled, true)
clientSPIFFEBundle, err := os.ReadFile(testdata.Path("spiffe_end2end/client_spiffebundle.json"))
if err != nil {
t.Fatalf("Failed to read test SPIFFE bundle: %v", err)
@ -357,6 +359,7 @@ func (s) TestMTLS(t *testing.T) {
// chain that is compatible with the client's configured SPIFFE bundle map. An
// MTLS connection is attempted between the two and checked for success.
func (s) Test_MTLS_SPIFFE(t *testing.T) {
testutils.SetEnvConfig(t, &envconfig.XDSSPIFFEEnabled, true)
tests := []struct {
name string
serverOption grpc.ServerOption
@ -372,7 +375,7 @@ func (s) Test_MTLS_SPIFFE(t *testing.T) {
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
s := stubserver.StartTestService(t, nil, grpc.Creds(testutils.CreateServerTLSCredentialsCompatibleWithSPIFFE(t, tls.RequireAndVerifyClientCert)))
s := stubserver.StartTestService(t, nil, tc.serverOption)
defer s.Stop()
cfg := fmt.Sprintf(`{
@ -403,7 +406,44 @@ func (s) Test_MTLS_SPIFFE(t *testing.T) {
}
}
// Test_MTLS_SPIFFE_FlagDisabled configures a client and server. The server has
// a certificate chain that is compatible with the client's configured SPIFFE
// bundle map. However, the XDS flag that enabled SPIFFE usage is disabled. An
// MTLS connection is attempted between the two and checked for failure.
func (s) Test_MTLS_SPIFFE_FlagDisabled(t *testing.T) {
testutils.SetEnvConfig(t, &envconfig.XDSSPIFFEEnabled, false)
serverOption := grpc.Creds(testutils.CreateServerTLSCredentialsCompatibleWithSPIFFE(t, tls.RequireAndVerifyClientCert))
s := stubserver.StartTestService(t, nil, serverOption)
defer s.Stop()
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"))
tlsBundle, stop, err := tlscreds.NewBundle([]byte(cfg))
if err != nil {
t.Fatalf("Failed to create TLS bundle: %v", err)
}
defer stop()
conn, err := grpc.NewClient(s.Address, grpc.WithCredentialsBundle(tlsBundle), grpc.WithAuthority("x.test.example.com"))
if err != nil {
t.Fatalf("Error dialing: %v", err)
}
defer conn.Close()
client := testgrpc.NewTestServiceClient(conn)
ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout)
defer cancel()
if _, err = client.EmptyCall(ctx, &testpb.Empty{}); err == nil {
t.Errorf("EmptyCall(): got success want failure")
}
}
func (s) Test_MTLS_SPIFFE_Failure(t *testing.T) {
testutils.SetEnvConfig(t, &envconfig.XDSSPIFFEEnabled, true)
tests := []struct {
name string
certFile string