diff --git a/auth/internal/transport/cba.go b/auth/internal/transport/cba.go index d94e0af08a35..26e037c1a374 100644 --- a/auth/internal/transport/cba.go +++ b/auth/internal/transport/cba.go @@ -17,7 +17,9 @@ package transport import ( "context" "crypto/tls" + "crypto/x509" "errors" + "log" "net" "net/http" "net/url" @@ -44,10 +46,12 @@ const ( googleAPIUseMTLSOld = "GOOGLE_API_USE_MTLS" universeDomainPlaceholder = "UNIVERSE_DOMAIN" + + mtlsMDSRoot = "/run/google-mds-mtls/root.crt" + mtlsMDSKey = "/run/google-mds-mtls/client.key" ) var ( - mdsMTLSAutoConfigSource mtlsConfigSource errUniverseNotSupportedMTLS = errors.New("mTLS is not supported in any universe other than googleapis.com") ) @@ -120,7 +124,20 @@ func GetGRPCTransportCredsAndEndpoint(opts *Options) (credentials.TransportCrede defaultTransportCreds := credentials.NewTLS(&tls.Config{ GetClientCertificate: config.clientCertSource, }) - if config.s2aAddress == "" { + + var s2aAddr string + var transportCredsForS2A credentials.TransportCredentials + + if config.mtlsS2AAddress != "" { + s2aAddr = config.mtlsS2AAddress + transportCredsForS2A, err = loadMTLSMDSTransportCreds(mtlsMDSRoot, mtlsMDSKey) + if err != nil { + log.Printf("Loading MTLS MDS credentials failed: %v", err) + return defaultTransportCreds, config.endpoint, nil + } + } else if config.s2aAddress != "" { + s2aAddr = config.s2aAddress + } else { return defaultTransportCreds, config.endpoint, nil } @@ -133,8 +150,9 @@ func GetGRPCTransportCredsAndEndpoint(opts *Options) (credentials.TransportCrede } s2aTransportCreds, err := s2a.NewClientCreds(&s2a.ClientOptions{ - S2AAddress: config.s2aAddress, - FallbackOpts: fallbackOpts, + S2AAddress: s2aAddr, + TransportCreds: transportCredsForS2A, + FallbackOpts: fallbackOpts, }) if err != nil { // Use default if we cannot initialize S2A client transport credentials. @@ -151,7 +169,19 @@ func GetHTTPTransportConfig(opts *Options) (cert.Provider, func(context.Context, return nil, nil, err } - if config.s2aAddress == "" { + var s2aAddr string + var transportCredsForS2A credentials.TransportCredentials + + if config.mtlsS2AAddress != "" { + s2aAddr = config.mtlsS2AAddress + transportCredsForS2A, err = loadMTLSMDSTransportCreds(mtlsMDSRoot, mtlsMDSKey) + if err != nil { + log.Printf("Loading MTLS MDS credentials failed: %v", err) + return config.clientCertSource, nil, nil + } + } else if config.s2aAddress != "" { + s2aAddr = config.s2aAddress + } else { return config.clientCertSource, nil, nil } @@ -169,12 +199,38 @@ func GetHTTPTransportConfig(opts *Options) (cert.Provider, func(context.Context, } dialTLSContextFunc := s2a.NewS2ADialTLSContextFunc(&s2a.ClientOptions{ - S2AAddress: config.s2aAddress, - FallbackOpts: fallbackOpts, + S2AAddress: s2aAddr, + TransportCreds: transportCredsForS2A, + FallbackOpts: fallbackOpts, }) return nil, dialTLSContextFunc, nil } +func loadMTLSMDSTransportCreds(mtlsMDSRootFile, mtlsMDSKeyFile string) (credentials.TransportCredentials, error) { + rootPEM, err := os.ReadFile(mtlsMDSRootFile) + if err != nil { + return nil, err + } + caCertPool := x509.NewCertPool() + ok := caCertPool.AppendCertsFromPEM(rootPEM) + if !ok { + return nil, errors.New("failed to load MTLS MDS root certificate") + } + // The mTLS MDS credentials are formatted as the concatenation of a PEM-encoded certificate chain + // followed by a PEM-encoded private key. For this reason, the concatenation is passed in to the + // tls.X509KeyPair function as both the certificate chain and private key arguments. + cert, err := tls.LoadX509KeyPair(mtlsMDSKeyFile, mtlsMDSKeyFile) + if err != nil { + return nil, err + } + tlsConfig := tls.Config{ + RootCAs: caCertPool, + Certificates: []tls.Certificate{cert}, + MinVersion: tls.VersionTLS13, + } + return credentials.NewTLS(&tlsConfig), nil +} + func getTransportConfig(opts *Options) (*transportConfig, error) { clientCertSource, err := GetClientCertificateProvider(opts) if err != nil { @@ -196,17 +252,17 @@ func getTransportConfig(opts *Options) (*transportConfig, error) { return nil, errUniverseNotSupportedMTLS } - s2aMTLSEndpoint := opts.DefaultMTLSEndpoint - s2aAddress := GetS2AAddress() - if s2aAddress == "" { + mtlsS2AAddress := GetMTLSS2AAddress() + if s2aAddress == "" && mtlsS2AAddress == "" { return &defaultTransportConfig, nil } return &transportConfig{ clientCertSource: clientCertSource, endpoint: endpoint, s2aAddress: s2aAddress, - s2aMTLSEndpoint: s2aMTLSEndpoint, + mtlsS2AAddress: mtlsS2AAddress, + s2aMTLSEndpoint: opts.DefaultMTLSEndpoint, }, nil } @@ -241,8 +297,10 @@ type transportConfig struct { clientCertSource cert.Provider // The corresponding endpoint to use based on client certificate source. endpoint string - // The S2A address if it can be used, otherwise an empty string. + // The plaintext S2A address if it can be used, otherwise an empty string. s2aAddress string + // The MTLS S2A address if it can be used, otherwise an empty string. + mtlsS2AAddress string // The MTLS endpoint to use with S2A. s2aMTLSEndpoint string } diff --git a/auth/internal/transport/cba_test.go b/auth/internal/transport/cba_test.go index 6be9c69e49bd..656caeb94418 100644 --- a/auth/internal/transport/cba_test.go +++ b/auth/internal/transport/cba_test.go @@ -20,7 +20,6 @@ import ( "fmt" "net/http" "testing" - "time" "cloud.google.com/go/auth/internal" "cloud.google.com/go/auth/internal/transport/cert" @@ -50,6 +49,20 @@ var ( return string(configStr), nil } + validConfigRespMTLSS2A = func() (string, error) { + validConfig := mtlsConfig{ + S2A: &s2aAddresses{ + PlaintextAddress: "", + MTLSAddress: testMTLSS2AAddr, + }, + } + configStr, err := json.Marshal(validConfig) + if err != nil { + return "", err + } + return string(configStr), nil + } + errorConfigResp = func() (string, error) { return "", fmt.Errorf("error getting config") } @@ -250,7 +263,7 @@ func TestGetEndpointWithClientCertSource(t *testing.T) { } } -func TestGetGRPCTransportConfigAndEndpoint(t *testing.T) { +func TestGetGRPCTransportConfigAndEndpoint_S2A(t *testing.T) { testCases := []struct { name string opts *Options @@ -324,11 +337,21 @@ func TestGetGRPCTransportConfigAndEndpoint(t *testing.T) { validConfigResp, testRegularEndpoint, }, + { + "no client cert, MTLS S2A address not empty, no MTLS MDS cert", + &Options{ + DefaultMTLSEndpoint: testMTLSEndpoint, + DefaultEndpointTemplate: testEndpointTemplate, + }, + validConfigRespMTLSS2A, + testRegularEndpoint, + }, } defer setupTest(t)() for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { httpGetMetadataMTLSConfig = tc.s2ARespFn + mtlsConfiguration, _ = queryConfig() if tc.opts.ClientCertProvider != nil { t.Setenv(googleAPIUseCertSource, "true") } else { @@ -338,17 +361,15 @@ func TestGetGRPCTransportConfigAndEndpoint(t *testing.T) { if tc.want != endpoint { t.Fatalf("want endpoint: %s, got %s", tc.want, endpoint) } - // Let the cached MTLS config expire at the end of each test case. - time.Sleep(2 * time.Millisecond) }) } } -func TestGetHTTPTransportConfig_S2a(t *testing.T) { +func TestGetHTTPTransportConfig_S2A(t *testing.T) { testCases := []struct { name string opts *Options - s2aFn func() (string, error) + s2ARespFn func() (string, error) want string isDialFnNil bool }{ @@ -359,7 +380,7 @@ func TestGetHTTPTransportConfig_S2a(t *testing.T) { DefaultEndpointTemplate: testEndpointTemplate, ClientCertProvider: fakeClientCertSource, }, - s2aFn: validConfigResp, + s2ARespFn: validConfigResp, want: testMTLSEndpoint, isDialFnNil: true, }, @@ -369,8 +390,8 @@ func TestGetHTTPTransportConfig_S2a(t *testing.T) { DefaultMTLSEndpoint: testMTLSEndpoint, DefaultEndpointTemplate: testEndpointTemplate, }, - s2aFn: validConfigResp, - want: testMTLSEndpoint, + s2ARespFn: validConfigResp, + want: testMTLSEndpoint, }, { name: "no client cert, S2A address empty", @@ -378,7 +399,7 @@ func TestGetHTTPTransportConfig_S2a(t *testing.T) { DefaultMTLSEndpoint: testMTLSEndpoint, DefaultEndpointTemplate: testEndpointTemplate, }, - s2aFn: invalidConfigResp, + s2ARespFn: invalidConfigResp, want: testRegularEndpoint, isDialFnNil: true, }, @@ -389,7 +410,7 @@ func TestGetHTTPTransportConfig_S2a(t *testing.T) { DefaultEndpointTemplate: testEndpointTemplate, Endpoint: testOverrideEndpoint, }, - s2aFn: validConfigResp, + s2ARespFn: validConfigResp, want: testOverrideEndpoint, isDialFnNil: true, }, @@ -399,7 +420,7 @@ func TestGetHTTPTransportConfig_S2a(t *testing.T) { DefaultMTLSEndpoint: "", DefaultEndpointTemplate: testEndpointTemplate, }, - s2aFn: validConfigResp, + s2ARespFn: validConfigResp, want: testRegularEndpoint, isDialFnNil: true, }, @@ -410,7 +431,17 @@ func TestGetHTTPTransportConfig_S2a(t *testing.T) { DefaultEndpointTemplate: testEndpointTemplate, Client: http.DefaultClient, }, - s2aFn: validConfigResp, + s2ARespFn: validConfigResp, + want: testRegularEndpoint, + isDialFnNil: true, + }, + { + name: "no client cert, MTLS S2A address not empty, no MTLS MDS cert", + opts: &Options{ + DefaultMTLSEndpoint: testMTLSEndpoint, + DefaultEndpointTemplate: testEndpointTemplate, + }, + s2ARespFn: validConfigRespMTLSS2A, want: testRegularEndpoint, isDialFnNil: true, }, @@ -418,7 +449,8 @@ func TestGetHTTPTransportConfig_S2a(t *testing.T) { defer setupTest(t)() for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { - httpGetMetadataMTLSConfig = tc.s2aFn + httpGetMetadataMTLSConfig = tc.s2ARespFn + mtlsConfiguration, _ = queryConfig() if tc.opts.ClientCertProvider != nil { t.Setenv(googleAPIUseCertSource, "true") } else { @@ -431,22 +463,58 @@ func TestGetHTTPTransportConfig_S2a(t *testing.T) { if want, got := tc.isDialFnNil, dialFunc == nil; want != got { t.Errorf("expecting returned dialFunc is nil: [%v], got [%v]", tc.isDialFnNil, got) } - // Let MTLS config expire at end of each test case. - time.Sleep(2 * time.Millisecond) + }) + } +} + +func TestLoadMTLSMDSTransportCreds(t *testing.T) { + testCases := []struct { + name string + rootFile string + keyFile string + wantErr bool + }{ + { + name: "missing root file", + rootFile: "", + keyFile: "./testdata/mtls_mds_key.pem", + wantErr: true, + }, + { + name: "missing key file", + rootFile: "./testdata/mtls_mds_root.pem", + keyFile: "", + wantErr: true, + }, + { + name: "missing both root and key files", + rootFile: "", + keyFile: "", + wantErr: true, + }, + { + name: "load credentials success", + rootFile: "./testdata/mtls_mds_root.pem", + keyFile: "./testdata/mtls_mds_key.pem", + wantErr: false, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + _, err := loadMTLSMDSTransportCreds(tc.rootFile, tc.keyFile) + if gotErr := err != nil; gotErr != tc.wantErr { + t.Errorf("loadMTLSMDSTransportCreds(%q, %q) got error: %v, want error: %v", tc.rootFile, tc.keyFile, gotErr, tc.wantErr) + } }) } } func setupTest(t *testing.T) func() { oldHTTPGet := httpGetMetadataMTLSConfig - oldExpiry := configExpiry - - configExpiry = time.Millisecond t.Setenv(googleAPIUseS2AEnv, "true") return func() { httpGetMetadataMTLSConfig = oldHTTPGet - configExpiry = oldExpiry } } diff --git a/auth/internal/transport/s2a.go b/auth/internal/transport/s2a.go index 2ed532deb7a0..4df73edce986 100644 --- a/auth/internal/transport/s2a.go +++ b/auth/internal/transport/s2a.go @@ -16,11 +16,11 @@ package transport import ( "encoding/json" + "fmt" "log" "os" "strconv" "sync" - "time" "cloud.google.com/go/auth/internal/transport/cert" "cloud.google.com/go/compute/metadata" @@ -31,41 +31,38 @@ const ( ) var ( - // The period an MTLS config can be reused before needing refresh. - configExpiry = time.Hour + mtlsConfiguration *mtlsConfig - // mdsMTLSAutoConfigSource is an instance of reuseMTLSConfigSource, with metadataMTLSAutoConfig as its config source. mtlsOnce sync.Once ) // GetS2AAddress returns the S2A address to be reached via plaintext connection. // Returns empty string if not set or invalid. func GetS2AAddress() string { - c, err := getMetadataMTLSAutoConfig().Config() - if err != nil { - return "" - } - if !c.Valid() { + getMetadataMTLSAutoConfig() + if !mtlsConfiguration.valid() { return "" } - return c.S2A.PlaintextAddress + return mtlsConfiguration.S2A.PlaintextAddress } -type mtlsConfigSource interface { - Config() (*mtlsConfig, error) +// GetMTLSS2AAddress returns the S2A address to be reached via MTLS connection. +// Returns empty string if not set or invalid. +func GetMTLSS2AAddress() string { + getMetadataMTLSAutoConfig() + if !mtlsConfiguration.valid() { + return "" + } + return mtlsConfiguration.S2A.MTLSAddress } // mtlsConfig contains the configuration for establishing MTLS connections with Google APIs. type mtlsConfig struct { - S2A *s2aAddresses `json:"s2a"` - Expiry time.Time + S2A *s2aAddresses `json:"s2a"` } -func (c *mtlsConfig) Valid() bool { - return c != nil && c.S2A != nil && !c.expired() -} -func (c *mtlsConfig) expired() bool { - return c.Expiry.Before(time.Now()) +func (c *mtlsConfig) valid() bool { + return c != nil && c.S2A != nil } // s2aAddresses contains the plaintext and/or MTLS S2A addresses. @@ -76,80 +73,36 @@ type s2aAddresses struct { MTLSAddress string `json:"mtls_address"` } -// getMetadataMTLSAutoConfig returns mdsMTLSAutoConfigSource, which is backed by config from MDS with auto-refresh. -func getMetadataMTLSAutoConfig() mtlsConfigSource { +func getMetadataMTLSAutoConfig() { + var err error mtlsOnce.Do(func() { - mdsMTLSAutoConfigSource = &reuseMTLSConfigSource{ - src: &metadataMTLSAutoConfig{}, + mtlsConfiguration, err = queryConfig() + if err != nil { + log.Printf("Getting MTLS config failed: %v", err) } }) - return mdsMTLSAutoConfigSource -} - -// reuseMTLSConfigSource caches a valid version of mtlsConfig, and uses `src` to refresh upon config expiry. -// It implements the mtlsConfigSource interface, so calling Config() on it returns an mtlsConfig. -type reuseMTLSConfigSource struct { - src mtlsConfigSource // src.Config() is called when config is expired - mu sync.Mutex // mutex guards config - config *mtlsConfig // cached config } -func (cs *reuseMTLSConfigSource) Config() (*mtlsConfig, error) { - cs.mu.Lock() - defer cs.mu.Unlock() - - if cs.config.Valid() { - return cs.config, nil - } - c, err := cs.src.Config() - if err != nil { - return nil, err - } - cs.config = c - return c, nil -} - -// metadataMTLSAutoConfig is an implementation of the interface mtlsConfigSource -// It has the logic to query MDS and return an mtlsConfig -type metadataMTLSAutoConfig struct{} - var httpGetMetadataMTLSConfig = func() (string, error) { return metadata.Get(configEndpointSuffix) } -func (cs *metadataMTLSAutoConfig) Config() (*mtlsConfig, error) { +func queryConfig() (*mtlsConfig, error) { resp, err := httpGetMetadataMTLSConfig() if err != nil { - log.Printf("querying MTLS config from MDS endpoint failed: %v", err) - return defaultMTLSConfig(), nil + return nil, fmt.Errorf("querying MTLS config from MDS endpoint failed: %w", err) } var config mtlsConfig err = json.Unmarshal([]byte(resp), &config) if err != nil { - log.Printf("unmarshalling MTLS config from MDS endpoint failed: %v", err) - return defaultMTLSConfig(), nil + return nil, fmt.Errorf("unmarshalling MTLS config from MDS endpoint failed: %w", err) } - if config.S2A == nil { - log.Printf("returned MTLS config from MDS endpoint is invalid: %v", config) - return defaultMTLSConfig(), nil + return nil, fmt.Errorf("returned MTLS config from MDS endpoint is invalid: %v", config) } - - // set new expiry - config.Expiry = time.Now().Add(configExpiry) return &config, nil } -func defaultMTLSConfig() *mtlsConfig { - return &mtlsConfig{ - S2A: &s2aAddresses{ - PlaintextAddress: "", - MTLSAddress: "", - }, - Expiry: time.Now().Add(configExpiry), - } -} - func shouldUseS2A(clientCertSource cert.Provider, opts *Options) bool { // If client cert is found, use that over S2A. if clientCertSource != nil { diff --git a/auth/internal/transport/s2a_test.go b/auth/internal/transport/s2a_test.go index 51a495fd66fd..3a44f60b4513 100644 --- a/auth/internal/transport/s2a_test.go +++ b/auth/internal/transport/s2a_test.go @@ -16,84 +16,77 @@ package transport import ( "testing" - "time" ) const ( - testS2AAddr = "testS2AAddress:port" + testS2AAddr = "testS2AAddress:port" + testMTLSS2AAddr = "testMTLSS2AAddress:port" ) func TestGetS2AAddress(t *testing.T) { testCases := []struct { - name string - respFn func() (string, error) - want string + name string + respFn func() (string, error) + wantErr bool + wantS2AAddress string + wantMTLSS2AAddress string }{ { - name: "test valid config", - respFn: validConfigResp, - want: testS2AAddr, + name: "test valid config with plaintext S2A address", + respFn: validConfigResp, + wantErr: false, + wantS2AAddress: testS2AAddr, + wantMTLSS2AAddress: "", }, { - name: "test error when getting config", - respFn: errorConfigResp, - want: "", + name: "test valid config with MTLS S2A address", + respFn: validConfigRespMTLSS2A, + wantErr: false, + wantS2AAddress: "", + wantMTLSS2AAddress: testMTLSS2AAddr, }, { - name: "test invalid config", - respFn: invalidConfigResp, - want: "", + name: "test error when getting config", + respFn: errorConfigResp, + wantErr: true, + wantS2AAddress: "", + wantMTLSS2AAddress: "", }, { - name: "test invalid JSON response", - respFn: invalidJSONResp, - want: "", + name: "test invalid config", + respFn: invalidConfigResp, + wantErr: true, + wantS2AAddress: "", + wantMTLSS2AAddress: "", + }, + { + name: "test invalid JSON response", + respFn: invalidJSONResp, + wantErr: true, + wantS2AAddress: "", + wantMTLSS2AAddress: "", }, } - oldHTTPGet := httpGetMetadataMTLSConfig - oldExpiry := configExpiry - configExpiry = time.Millisecond - defer func() { - httpGetMetadataMTLSConfig = oldHTTPGet - configExpiry = oldExpiry - }() + defer setupTest(t)() for _, tc := range testCases { t.Run(tc.name, func(t *testing.T) { + var err error httpGetMetadataMTLSConfig = tc.respFn - if want, got := tc.want, GetS2AAddress(); got != want { - t.Errorf("want address [%s], got address [%s]", want, got) + mtlsConfiguration, err = queryConfig() + if gotErr := err != nil; gotErr != tc.wantErr { + t.Errorf("queryConfig() got error: %v, want error: %v", gotErr, tc.wantErr) + } + if want, got := tc.wantS2AAddress, GetS2AAddress(); got != want { + t.Errorf("want S2A address [%s], got address [%s]", want, got) + } + if want, got := tc.wantMTLSS2AAddress, GetMTLSS2AAddress(); got != want { + t.Errorf("want MTLS S2A address [%s], got address [%s]", want, got) } - // Let the MTLS config expire at the end of each test case. - time.Sleep(2 * time.Millisecond) }) } } -func TestMTLSConfigExpiry(t *testing.T) { - oldHTTPGet := httpGetMetadataMTLSConfig - oldExpiry := configExpiry - configExpiry = 1 * time.Second - defer func() { - httpGetMetadataMTLSConfig = oldHTTPGet - configExpiry = oldExpiry - }() - httpGetMetadataMTLSConfig = validConfigResp - if got, want := GetS2AAddress(), testS2AAddr; got != want { - t.Errorf("expected address: [%s], got [%s]", want, got) - } - httpGetMetadataMTLSConfig = invalidConfigResp - if got, want := GetS2AAddress(), testS2AAddr; got != want { - t.Errorf("cached config should still be valid, expected address: [%s], got [%s]", want, got) - } - time.Sleep(1 * time.Second) - if got, want := GetS2AAddress(), ""; got != want { - t.Errorf("config should be refreshed, expected address: [%s], got [%s]", want, got) - } - // Let the MTLS config expire before running other tests. - time.Sleep(1 * time.Second) -} - func TestIsGoogleS2AEnabled(t *testing.T) { testCases := []struct { name string diff --git a/auth/internal/transport/testdata/mtls_mds_key.pem b/auth/internal/transport/testdata/mtls_mds_key.pem new file mode 100644 index 000000000000..7b51d46c464c --- /dev/null +++ b/auth/internal/transport/testdata/mtls_mds_key.pem @@ -0,0 +1,47 @@ +-----BEGIN CERTIFICATE----- +MIIDCDCCAfACFFlYsYCFit01ZpYmfjxpo7/6wMEbMA0GCSqGSIb3DQEBCwUAMEgx +CzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEPMA0GA1UECgwGR29vZ2xlMRswGQYD +VQQDDBJ0ZXN0LXMyYS1tdGxzLXJvb3QwHhcNMjMwODIyMTY0NTE4WhcNNDMwODIy +MTY0NTE4WjA5MQswCQYDVQQGEwJVUzELMAkGA1UECAwCQ0ExHTAbBgNVBAMMFHRl +c3QtczJhLW10bHMtY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKC +AQEAqrQQMyxNtmdCB+uY3szgRsfPrKC+TV9Fusnd8PfaCVuGTGcSBKM018nV2TDn +3IYFQ1HgLpGwGwOFDBb3y0o9i2/l2VJySriX1GSNX6nDmVasQlO1wuOLCP7/LRmO +7b6Kise5W0IFhYaptKyWnekn2pS0tAjimqpfn2w0U6FDGtQUqg/trQQmGtTSJHjb +A+OFd0EFC18KGP8Q+jOMaMkJRmpeEiAPyHPDoMhqQNT26RApv9j2Uzo4SuXzHH6T +cAdm1+zG+EXY/UZKX9oDkSbwIJvN+gCmNyORLalJ12gsGYOCjMd8K0mlXBqrmmbO +VHVbUm9062lhE7x59AA8DK4DoQIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQCPOvtL +dq2hxFHlIy0YUK8jp/DtwJZPwzx1id5FtWwd0CxBS1StIgmkHMxtkJGz1iyQLplI +je+Msd4sTsb5zZi/8kGKehi8Wj4lghp4oP30cpob41OvM68M9RC/wSOVk9igSww+ +l3zof6wKRIswsi5VHrL16ruIVVoDlyFbKr8yk+cp9OPOV8hNNN7ewY9xC8OgnTt8 +YtdaLe6uTplKBLW+j3GtshigRhyfkGJyPFYL4LAeDJCHlC1qmBnkyP0ijMp6vneM +E8TLavnMTMcpihWTWpyKeRkO6HDRsP4AofQAp7VAiAdSOplga+w2qgrVICV+m8MK +BTq2PBvc59T6OFLq +-----END CERTIFICATE----- +-----BEGIN PRIVATE KEY----- +MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCqtBAzLE22Z0IH +65jezOBGx8+soL5NX0W6yd3w99oJW4ZMZxIEozTXydXZMOfchgVDUeAukbAbA4UM +FvfLSj2Lb+XZUnJKuJfUZI1fqcOZVqxCU7XC44sI/v8tGY7tvoqKx7lbQgWFhqm0 +rJad6SfalLS0COKaql+fbDRToUMa1BSqD+2tBCYa1NIkeNsD44V3QQULXwoY/xD6 +M4xoyQlGal4SIA/Ic8OgyGpA1PbpECm/2PZTOjhK5fMcfpNwB2bX7Mb4Rdj9Rkpf +2gORJvAgm836AKY3I5EtqUnXaCwZg4KMx3wrSaVcGquaZs5UdVtSb3TraWETvHn0 +ADwMrgOhAgMBAAECggEAUccupZ1ZY4OHTi0PkNk8rpwFwTFGyeFVEf2ofkr24RnA +NnUAXEllxOUUNlcoFOz9s3kTeavg3qgqgpa0QmdAIb9LMXg+ec6CKkW7trMpGho8 +LxBUWNfSoU4sKEqAvyPT0lWJVo9D/up6/avbAi6TIbOw+Djzel4ZrlHTpabxc3WT +EilXzn4q54b3MzxCQeQjcnzTieW4Q5semG2kLiXFToHIY2di01P/O8awUjgrD+uW +/Cb6H49MnHm9VPkqea1iwZeMQd6Gh5FrC7RezsBjdB1JBcfsv6PFt2ySInjB8SF+ +XR5Gr3Cc5sh9s0LfprZ9Dq0rlSWmwasPMI1COK6SswKBgQDczgeWd3erQ1JX9LEI +wollawqC9y7uJhEsw1hrPqA3uqZYiLUc7Nmi4laZ12mcGoXNDS3R3XmD58qGmGaU +lxEVTb8KDVWBgw450VoBKzSMQnCP6zn4nZxTYxeqMKjDGf6TRB6TZc843qsG3eRC +k91yxrCQ/0HV6PT48C+lieDzLwKBgQDF6aNKiyrswr457undBnM1H8q/Y6xC5ZlK +UtiQdhuyBnicvz0U8WPxBY/8gha0OXWuSnBqq/z77iFVNv/zT6p9K7kM7nBGd8cB +8KO6FNbyaHWFrhCI5zNzRTH4oha0hfvUOoti09vqavCtWD4L+D/63ba1wNLKPO9o +4gWbCnUCLwKBgQC/vus372csgrnvR761LLrEJ8BpGt7WUJh5luoht7DKtHvgRleB +Vu1oVcV+s2Iy/ZVUDC3OIdZ0hcWKPK5YOxfKuEk+IXYvke+4peTTPwHTC59UW6Fs +FPK8N0FFuhvT0a8RlAY5WiAp8rPysp6WcnHMSl7qi8BQUozp4Sp/RsziYQKBgBXv +r4mzoy5a53rEYGd/L4XT4EUWZyGDEVqLlDVu4eL5lKTLDZokp08vrqXuRVX0iHap +CYzJQ2EpI8iuL/BoBB2bmwcz5n3pCMXORld5t9lmeqA2it6hwbIlGUTVsm6P6zm6 +w3hQwy9YaxTLkxUAjxbfPEEo/jQsTNzzMGve3NlBAoGAbgJExpDyMDnaD2Vi5eyr +63b54BsqeLHqxJmADifyRCj7G1SJMm3zMKkNNOS0vsXgoiId973STFf1XQiojiv8 +Slbxyv5rczcY0n3LOuQYcM5OzsjzpNFZsT2dDnMfNRUF3rx3Geu/FuJ9scF1b00r +fVMrcL3jSf/W1Xh4TgtyoU8= +-----END PRIVATE KEY----- \ No newline at end of file diff --git a/auth/internal/transport/testdata/mtls_mds_root.pem b/auth/internal/transport/testdata/mtls_mds_root.pem new file mode 100644 index 000000000000..eb0b6218ca2e --- /dev/null +++ b/auth/internal/transport/testdata/mtls_mds_root.pem @@ -0,0 +1,21 @@ +-----BEGIN CERTIFICATE----- +MIIDcTCCAlmgAwIBAgIUDUkgI+2FZtuUHyUUi0ZBH7JvN00wDQYJKoZIhvcNAQEL +BQAwSDELMAkGA1UEBhMCVVMxCzAJBgNVBAgMAkNBMQ8wDQYDVQQKDAZHb29nbGUx +GzAZBgNVBAMMEnRlc3QtczJhLW10bHMtcm9vdDAeFw0yMzA4MjEyMTI5MTVaFw00 +MzA4MjEyMTI5MTVaMEgxCzAJBgNVBAYTAlVTMQswCQYDVQQIDAJDQTEPMA0GA1UE +CgwGR29vZ2xlMRswGQYDVQQDDBJ0ZXN0LXMyYS1tdGxzLXJvb3QwggEiMA0GCSqG +SIb3DQEBAQUAA4IBDwAwggEKAoIBAQCbFEQfpvla27bATedrN4BAWsI9GSwSnJLW +QWzXcnAk6cKxQBAhnaKHRxHY8ttLhNTtxQeub894CLzJvHE/0xDhuMzjtCCCZ7i2 +r08tKZ1KcEzPJCPNlxlzAXPA45XU3LRlbGvju/PBPhm6n1hCEKTNI/KETJ5DEaYg +Cf2LcXVsl/zW20MwDZ+e2w/9a2a6n6DdpW1ekOR550hXAUOIxvmXRBeYeGLFvp1n +rQgZBhRaxP03UB+PQD2oMi/4mfsS96uGCXdzzX8qV46O8m132HUbnA/wagIwboEe +d7Bx237dERDyHw5GFnll7orgA0FOtoEufXdeQxWVvTjO0+PVPgsvAgMBAAGjUzBR +MB0GA1UdDgQWBBRyMtg/yutV8hw8vOq0i8x0eBQi7DAfBgNVHSMEGDAWgBRyMtg/ +yutV8hw8vOq0i8x0eBQi7DAPBgNVHRMBAf8EBTADAQH/MA0GCSqGSIb3DQEBCwUA +A4IBAQArN/gdqWMxd5Rvq2eJMTp6I4RepJOT7Go4sMsRsy1caJqqcoS2EvREDZMN +XNEBcyQBB5kYd6TCcZGoLnEtWYXQ4jjEiXG1g7/+rWxyqw0ZYuP7FWzuHg3Uor/x +fApbEKwptP5ywVc+33h4qreGcqXkVCCn+sAcstGgrqubdGZW2T5gazUMyammOOuN +9IWL1PbvXmgEKD+80NUIrk09zanYyrElGdU/zw/kUbZ3Jf6WUBtJGhTzRQ1qZeKa +VnpCbLoG3vObEB8mxDUAlIzwAtfvw4U32BVIZA8xrocz6OOoAnSW1bTlo3EOIo/G +MTV7jmY9TBPtfhRuO/cG650+F+cw +-----END CERTIFICATE----- \ No newline at end of file