google.golang.org/grpc@v1.74.2/internal/xds/bootstrap/tlscreds/bundle_ext_test.go (about) 1 /* 2 * 3 * Copyright 2023 gRPC authors. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 */ 18 19 package tlscreds_test 20 21 import ( 22 "context" 23 "crypto/tls" 24 "encoding/json" 25 "fmt" 26 "os" 27 "strings" 28 "testing" 29 "time" 30 31 "google.golang.org/grpc" 32 "google.golang.org/grpc/codes" 33 "google.golang.org/grpc/internal/envconfig" 34 "google.golang.org/grpc/internal/grpctest" 35 "google.golang.org/grpc/internal/stubserver" 36 "google.golang.org/grpc/internal/testutils" 37 "google.golang.org/grpc/internal/xds/bootstrap/tlscreds" 38 testgrpc "google.golang.org/grpc/interop/grpc_testing" 39 testpb "google.golang.org/grpc/interop/grpc_testing" 40 "google.golang.org/grpc/status" 41 "google.golang.org/grpc/testdata" 42 ) 43 44 const defaultTestTimeout = 5 * time.Second 45 46 type s struct { 47 grpctest.Tester 48 } 49 50 func Test(t *testing.T) { 51 grpctest.RunSubTests(t, s{}) 52 } 53 54 type Closable interface { 55 Close() 56 } 57 58 func (s) TestValidTlsBuilder(t *testing.T) { 59 caCert := testdata.Path("x509/server_ca_cert.pem") 60 clientCert := testdata.Path("x509/client1_cert.pem") 61 clientKey := testdata.Path("x509/client1_key.pem") 62 clientSpiffeBundle := testdata.Path("spiffe_end2end/client_spiffebundle.json") 63 tests := []struct { 64 name string 65 jd string 66 }{ 67 { 68 name: "Absent configuration", 69 jd: `null`, 70 }, 71 { 72 name: "Empty configuration", 73 jd: `{}`, 74 }, 75 { 76 name: "Only CA certificate chain", 77 jd: fmt.Sprintf(`{"ca_certificate_file": "%s"}`, caCert), 78 }, 79 { 80 name: "Only private key and certificate chain", 81 jd: fmt.Sprintf(`{"certificate_file":"%s","private_key_file":"%s"}`, clientCert, clientKey), 82 }, 83 { 84 name: "CA chain, private key and certificate chain", 85 jd: fmt.Sprintf(`{"ca_certificate_file":"%s","certificate_file":"%s","private_key_file":"%s"}`, caCert, clientCert, clientKey), 86 }, 87 { 88 name: "Only refresh interval", jd: `{"refresh_interval": "1s"}`, 89 }, 90 { 91 name: "Refresh interval and CA certificate chain", 92 jd: fmt.Sprintf(`{"refresh_interval": "1s","ca_certificate_file": "%s"}`, caCert), 93 }, 94 { 95 name: "Refresh interval, private key and certificate chain", 96 jd: fmt.Sprintf(`{"refresh_interval": "1s","certificate_file":"%s","private_key_file":"%s"}`, clientCert, clientKey), 97 }, 98 { 99 name: "Refresh interval, CA chain, private key and certificate chain", 100 jd: fmt.Sprintf(`{"refresh_interval": "1s","ca_certificate_file":"%s","certificate_file":"%s","private_key_file":"%s"}`, caCert, clientCert, clientKey), 101 }, 102 { 103 name: "Refresh interval, CA chain, private key, certificate chain, spiffe bundle", 104 jd: fmt.Sprintf(`{"refresh_interval": "1s","ca_certificate_file":"%s","certificate_file":"%s","private_key_file":"%s","spiffe_trust_bundle_map_file":"%s"}`, caCert, clientCert, clientKey, clientSpiffeBundle), 105 }, 106 { 107 name: "Unknown field", 108 jd: `{"unknown_field": "foo"}`, 109 }, 110 } 111 112 for _, test := range tests { 113 t.Run(test.name, func(t *testing.T) { 114 msg := json.RawMessage(test.jd) 115 _, stop, err := tlscreds.NewBundle(msg) 116 if err != nil { 117 t.Fatalf("NewBundle(%s) returned error %s when expected to succeed", test.jd, err) 118 } 119 stop() 120 }) 121 } 122 } 123 124 func (s) TestInvalidTlsBuilder(t *testing.T) { 125 tests := []struct { 126 name, jd, wantErrPrefix string 127 }{ 128 { 129 name: "Wrong type in json", 130 jd: `{"ca_certificate_file": 1}`, 131 wantErrPrefix: "failed to unmarshal config:"}, 132 { 133 name: "Missing private key", 134 jd: fmt.Sprintf(`{"certificate_file":"%s"}`, testdata.Path("x509/server_cert.pem")), 135 wantErrPrefix: "pemfile: private key file and identity cert file should be both specified or not specified", 136 }, 137 } 138 139 for _, test := range tests { 140 t.Run(test.name, func(t *testing.T) { 141 msg := json.RawMessage(test.jd) 142 _, stop, err := tlscreds.NewBundle(msg) 143 if err == nil || !strings.HasPrefix(err.Error(), test.wantErrPrefix) { 144 if stop != nil { 145 stop() 146 } 147 t.Fatalf("NewBundle(%s): got error %s, want an error with prefix %s", msg, err, test.wantErrPrefix) 148 } 149 }) 150 } 151 } 152 153 func (s) TestCaReloading(t *testing.T) { 154 serverCa, err := os.ReadFile(testdata.Path("x509/server_ca_cert.pem")) 155 if err != nil { 156 t.Fatalf("Failed to read test CA cert: %s", err) 157 } 158 159 // Write CA certs to a temporary file so that we can modify it later. 160 caPath := t.TempDir() + "/ca.pem" 161 if err = os.WriteFile(caPath, serverCa, 0644); err != nil { 162 t.Fatalf("Failed to write test CA cert: %v", err) 163 } 164 cfg := fmt.Sprintf(`{ 165 "ca_certificate_file": "%s", 166 "refresh_interval": ".01s" 167 }`, caPath) 168 tlsBundle, stop, err := tlscreds.NewBundle([]byte(cfg)) 169 if err != nil { 170 t.Fatalf("Failed to create TLS bundle: %v", err) 171 } 172 defer stop() 173 174 serverCredentials := grpc.Creds(testutils.CreateServerTLSCredentials(t, tls.NoClientCert)) 175 server := stubserver.StartTestService(t, nil, serverCredentials) 176 177 conn, err := grpc.NewClient( 178 server.Address, 179 grpc.WithCredentialsBundle(tlsBundle), 180 grpc.WithAuthority("x.test.example.com"), 181 ) 182 if err != nil { 183 t.Fatalf("Error dialing: %v", err) 184 } 185 defer conn.Close() 186 187 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 188 defer cancel() 189 190 client := testgrpc.NewTestServiceClient(conn) 191 if _, err = client.EmptyCall(ctx, &testpb.Empty{}); err != nil { 192 t.Errorf("Error calling EmptyCall: %v", err) 193 } 194 // close the server and create a new one to force client to do a new 195 // handshake. 196 server.Stop() 197 198 invalidCa, err := os.ReadFile(testdata.Path("ca.pem")) 199 if err != nil { 200 t.Fatalf("Failed to read test CA cert: %v", err) 201 } 202 // unload root cert 203 err = os.WriteFile(caPath, invalidCa, 0644) 204 if err != nil { 205 t.Fatalf("Failed to write test CA cert: %v", err) 206 } 207 for ; ctx.Err() == nil; <-time.After(10 * time.Millisecond) { 208 ss := stubserver.StubServer{ 209 Address: server.Address, 210 EmptyCallF: func(context.Context, *testpb.Empty) (*testpb.Empty, error) { return &testpb.Empty{}, nil }, 211 } 212 server = stubserver.StartTestService(t, &ss, serverCredentials) 213 214 // Client handshake should eventually fail because the client CA was 215 // reloaded, and thus the server cert is signed by an unknown CA. 216 t.Log(server) 217 _, err = client.EmptyCall(ctx, &testpb.Empty{}) 218 const wantErr = "certificate signed by unknown authority" 219 if status.Code(err) == codes.Unavailable && strings.Contains(err.Error(), wantErr) { 220 // Certs have reloaded. 221 server.Stop() 222 break 223 } 224 t.Logf("EmptyCall() got err: %s, want code: %s, want err: %s", err, codes.Unavailable, wantErr) 225 server.Stop() 226 } 227 if ctx.Err() != nil { 228 t.Errorf("Timed out waiting for CA certs reloading") 229 } 230 } 231 232 // Test_SPIFFE_Reloading sets up a client and server. The client is configured 233 // to use a SPIFFE bundle map, and the server is configured to use TLS creds 234 // compatible with this bundle. A handshake is performed and connection is 235 // expected to be successful. Then we change the client's SPIFFE Bundle Map file 236 // on disk to one that should fail with the server's credentials. This change 237 // should be picked up by the client via our file reloading. Another handshake 238 // is performed and checked for failure, ensuring that gRPC is correctly using 239 // the changed-on-disk bundle map. 240 func (s) Test_SPIFFE_Reloading(t *testing.T) { 241 testutils.SetEnvConfig(t, &envconfig.XDSSPIFFEEnabled, true) 242 clientSPIFFEBundle, err := os.ReadFile(testdata.Path("spiffe_end2end/client_spiffebundle.json")) 243 if err != nil { 244 t.Fatalf("Failed to read test SPIFFE bundle: %v", err) 245 } 246 247 // Write CA certs to a temporary file so that we can modify it later. 248 spiffePath := t.TempDir() + "/client_spiffe.json" 249 if err = os.WriteFile(spiffePath, clientSPIFFEBundle, 0644); err != nil { 250 t.Fatalf("Failed to write test SPIFFE Bundle %v: %v", clientSPIFFEBundle, err) 251 } 252 cfg := fmt.Sprintf(`{ 253 "spiffe_trust_bundle_map_file": "%s", 254 "refresh_interval": ".01s" 255 }`, spiffePath) 256 tlsBundle, stop, err := tlscreds.NewBundle([]byte(cfg)) 257 if err != nil { 258 t.Fatalf("Failed to create TLS bundle: %v", err) 259 } 260 defer stop() 261 262 l, err := testutils.LocalTCPListener() 263 if err != nil { 264 t.Fatalf("testutils.LocalTCPListener() failed: %v", err) 265 } 266 lis := testutils.NewRestartableListener(l) 267 defer lis.Close() 268 ss := stubserver.StubServer{ 269 Listener: lis, 270 EmptyCallF: func(context.Context, *testpb.Empty) (*testpb.Empty, error) { return &testpb.Empty{}, nil }, 271 } 272 273 serverCredentials := grpc.Creds(testutils.CreateServerTLSCredentialsCompatibleWithSPIFFE(t, tls.NoClientCert)) 274 server := stubserver.StartTestService(t, &ss, serverCredentials) 275 276 defer server.Stop() 277 278 conn, err := grpc.NewClient( 279 server.Address, 280 grpc.WithCredentialsBundle(tlsBundle), 281 grpc.WithAuthority("x.test.example.com"), 282 ) 283 if err != nil { 284 t.Fatalf("grpc.NewClient(%q) failed: %v", server.Address, err) 285 } 286 defer conn.Close() 287 288 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 289 defer cancel() 290 291 client := testgrpc.NewTestServiceClient(conn) 292 if _, err = client.EmptyCall(ctx, &testpb.Empty{}); err != nil { 293 t.Errorf("Error calling EmptyCall: %v", err) 294 } 295 296 // Setup the wrong bundle to be reloaded 297 wrongBundle, err := os.ReadFile(testdata.Path("spiffe_end2end/server_spiffebundle.json")) 298 if err != nil { 299 t.Fatalf("Failed to read test spiffe bundle %v: %v", "spiffe_end2end/server_spiffebundle.json", err) 300 } 301 // Write the bundle that will fail to the tmp file path to be reloaded 302 err = os.WriteFile(spiffePath, wrongBundle, 0644) 303 if err != nil { 304 t.Fatalf("Failed to write test spiffe bundle %v: %v", "spiffe_end2end/server_spiffebundle.json", err) 305 } 306 307 for ; ctx.Err() == nil; <-time.After(10 * time.Millisecond) { 308 // Stop and restart the listener to force new handshakes 309 lis.Stop() 310 lis.Restart() 311 // Client handshake should eventually fail because the client CA was 312 // reloaded, and thus the server cert is signed by an unknown CA. 313 t.Log(server) 314 _, err = client.EmptyCall(ctx, &testpb.Empty{}) 315 const wantErr = "no bundle found for peer certificates trust domain" 316 if status.Code(err) == codes.Unavailable && strings.Contains(err.Error(), wantErr) { 317 // Certs have reloaded. 318 server.Stop() 319 break 320 } 321 t.Logf("EmptyCall() got err: %s, want code: %s, want err: %s", err, codes.Unavailable, wantErr) 322 } 323 if ctx.Err() != nil { 324 t.Errorf("Timed out waiting for CA certs reloading") 325 } 326 } 327 328 func (s) TestMTLS(t *testing.T) { 329 s := stubserver.StartTestService(t, nil, grpc.Creds(testutils.CreateServerTLSCredentials(t, tls.RequireAndVerifyClientCert))) 330 defer s.Stop() 331 332 cfg := fmt.Sprintf(`{ 333 "ca_certificate_file": "%s", 334 "certificate_file": "%s", 335 "private_key_file": "%s" 336 }`, 337 testdata.Path("x509/server_ca_cert.pem"), 338 testdata.Path("x509/client1_cert.pem"), 339 testdata.Path("x509/client1_key.pem")) 340 tlsBundle, stop, err := tlscreds.NewBundle([]byte(cfg)) 341 if err != nil { 342 t.Fatalf("Failed to create TLS bundle: %v", err) 343 } 344 defer stop() 345 conn, err := grpc.NewClient(s.Address, grpc.WithCredentialsBundle(tlsBundle), grpc.WithAuthority("x.test.example.com")) 346 if err != nil { 347 t.Fatalf("Error dialing: %v", err) 348 } 349 defer conn.Close() 350 client := testgrpc.NewTestServiceClient(conn) 351 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 352 defer cancel() 353 if _, err = client.EmptyCall(ctx, &testpb.Empty{}); err != nil { 354 t.Errorf("EmptyCall(): got error %v when expected to succeed", err) 355 } 356 } 357 358 // Test_MTLS_SPIFFE configures a client and server. The server has a certificate 359 // chain that is compatible with the client's configured SPIFFE bundle map. An 360 // MTLS connection is attempted between the two and checked for success. 361 func (s) Test_MTLS_SPIFFE(t *testing.T) { 362 testutils.SetEnvConfig(t, &envconfig.XDSSPIFFEEnabled, true) 363 tests := []struct { 364 name string 365 serverOption grpc.ServerOption 366 }{ 367 { 368 name: "MTLS SPIFFE", 369 serverOption: grpc.Creds(testutils.CreateServerTLSCredentialsCompatibleWithSPIFFE(t, tls.RequireAndVerifyClientCert)), 370 }, 371 { 372 name: "MTLS SPIFFE Chain", 373 serverOption: grpc.Creds(testutils.CreateServerTLSCredentialsCompatibleWithSPIFFEChain(t, tls.RequireAndVerifyClientCert)), 374 }, 375 } 376 for _, tc := range tests { 377 t.Run(tc.name, func(t *testing.T) { 378 s := stubserver.StartTestService(t, nil, tc.serverOption) 379 defer s.Stop() 380 381 cfg := fmt.Sprintf(`{ 382 "certificate_file": "%s", 383 "private_key_file": "%s", 384 "spiffe_trust_bundle_map_file": "%s" 385 }`, 386 testdata.Path("spiffe_end2end/client_spiffe.pem"), 387 testdata.Path("spiffe_end2end/client.key"), 388 testdata.Path("spiffe_end2end/client_spiffebundle.json")) 389 tlsBundle, stop, err := tlscreds.NewBundle([]byte(cfg)) 390 if err != nil { 391 t.Fatalf("Failed to create TLS bundle: %v", err) 392 } 393 defer stop() 394 conn, err := grpc.NewClient(s.Address, grpc.WithCredentialsBundle(tlsBundle), grpc.WithAuthority("x.test.example.com")) 395 if err != nil { 396 t.Fatalf("Error dialing: %v", err) 397 } 398 defer conn.Close() 399 client := testgrpc.NewTestServiceClient(conn) 400 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 401 defer cancel() 402 if _, err = client.EmptyCall(ctx, &testpb.Empty{}); err != nil { 403 t.Errorf("EmptyCall(): got error %v when expected to succeed", err) 404 } 405 }) 406 } 407 } 408 409 // Test_MTLS_SPIFFE_FlagDisabled configures a client and server. The server has 410 // a certificate chain that is compatible with the client's configured SPIFFE 411 // bundle map. However, the XDS flag that enabled SPIFFE usage is disabled. An 412 // MTLS connection is attempted between the two and checked for failure. 413 func (s) Test_MTLS_SPIFFE_FlagDisabled(t *testing.T) { 414 testutils.SetEnvConfig(t, &envconfig.XDSSPIFFEEnabled, false) 415 serverOption := grpc.Creds(testutils.CreateServerTLSCredentialsCompatibleWithSPIFFE(t, tls.RequireAndVerifyClientCert)) 416 s := stubserver.StartTestService(t, nil, serverOption) 417 defer s.Stop() 418 419 cfg := fmt.Sprintf(`{ 420 "certificate_file": "%s", 421 "private_key_file": "%s", 422 "spiffe_trust_bundle_map_file": "%s" 423 }`, 424 testdata.Path("spiffe_end2end/client_spiffe.pem"), 425 testdata.Path("spiffe_end2end/client.key"), 426 testdata.Path("spiffe_end2end/client_spiffebundle.json")) 427 tlsBundle, stop, err := tlscreds.NewBundle([]byte(cfg)) 428 if err != nil { 429 t.Fatalf("Failed to create TLS bundle: %v", err) 430 } 431 defer stop() 432 conn, err := grpc.NewClient(s.Address, grpc.WithCredentialsBundle(tlsBundle), grpc.WithAuthority("x.test.example.com")) 433 if err != nil { 434 t.Fatalf("Error dialing: %v", err) 435 } 436 defer conn.Close() 437 client := testgrpc.NewTestServiceClient(conn) 438 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 439 defer cancel() 440 if _, err = client.EmptyCall(ctx, &testpb.Empty{}); err == nil { 441 t.Errorf("EmptyCall(): got success want failure") 442 } 443 } 444 445 func (s) Test_MTLS_SPIFFE_Failure(t *testing.T) { 446 testutils.SetEnvConfig(t, &envconfig.XDSSPIFFEEnabled, true) 447 tests := []struct { 448 name string 449 certFile string 450 keyFile string 451 spiffeBundleFile string 452 serverOption grpc.ServerOption 453 wantErrContains string 454 wantErrCode codes.Code 455 }{ 456 { 457 name: "No matching trust domain in bundle", 458 certFile: "spiffe_end2end/client_spiffe.pem", 459 keyFile: "spiffe_end2end/client.key", 460 spiffeBundleFile: "spiffe_end2end/server_spiffebundle.json", 461 serverOption: grpc.Creds(testutils.CreateServerTLSCredentialsCompatibleWithSPIFFE(t, tls.RequireAndVerifyClientCert)), 462 wantErrContains: "spiffe: no bundle found for peer certificates", 463 wantErrCode: codes.Unavailable, 464 }, 465 { 466 name: "Server cert has no valid SPIFFE URIs", 467 certFile: "spiffe_end2end/client_spiffe.pem", 468 keyFile: "spiffe_end2end/client.key", 469 spiffeBundleFile: "spiffe_end2end/client_spiffebundle.json", 470 serverOption: grpc.Creds(testutils.CreateServerTLSCredentials(t, tls.RequireAndVerifyClientCert)), 471 wantErrContains: "spiffe: could not get spiffe ID from peer leaf cert", 472 wantErrCode: codes.Unavailable, 473 }, 474 { 475 name: "Server cert has valid spiffe ID but doesn't chain to the root CA", 476 certFile: "spiffe_end2end/client_spiffe.pem", 477 keyFile: "spiffe_end2end/client.key", 478 spiffeBundleFile: "spiffe_end2end/client_spiffebundle.json", 479 serverOption: grpc.Creds(testutils.CreateServerTLSCredentialsValidSPIFFEButWrongCA(t, tls.RequireAndVerifyClientCert)), 480 wantErrContains: "spiffe: x509 certificate Verify failed: x509: certificate signed by unknown authority", 481 wantErrCode: codes.Unavailable, 482 }, 483 } 484 for _, tc := range tests { 485 t.Run(tc.name, func(t *testing.T) { 486 s := stubserver.StartTestService(t, nil, tc.serverOption) 487 defer s.Stop() 488 cfg := fmt.Sprintf(`{ 489 "certificate_file": "%s", 490 "private_key_file": "%s", 491 "spiffe_trust_bundle_map_file": "%s" 492 }`, 493 testdata.Path(tc.certFile), 494 testdata.Path(tc.keyFile), 495 testdata.Path(tc.spiffeBundleFile)) 496 tlsBundle, stop, err := tlscreds.NewBundle([]byte(cfg)) 497 if err != nil { 498 t.Fatalf("Failed to create TLS bundle: %v", err) 499 } 500 defer stop() 501 conn, err := grpc.NewClient(s.Address, grpc.WithCredentialsBundle(tlsBundle), grpc.WithAuthority("x.test.example.com")) 502 if err != nil { 503 t.Fatalf("grpc.NewClient(%q) failed: %v", s.Address, err) 504 } 505 defer conn.Close() 506 client := testgrpc.NewTestServiceClient(conn) 507 ctx, cancel := context.WithTimeout(context.Background(), defaultTestTimeout) 508 defer cancel() 509 if _, err = client.EmptyCall(ctx, &testpb.Empty{}); err == nil { 510 t.Errorf("EmptyCall(): got success. want failure") 511 } 512 if status.Code(err) != tc.wantErrCode { 513 t.Errorf("EmptyCall(): failed with wrong error. got code %v. want code: %v", status.Code(err), tc.wantErrCode) 514 } 515 if !strings.Contains(err.Error(), tc.wantErrContains) { 516 t.Errorf("EmptyCall(): failed with wrong error. got %v. want contains: %v", err, tc.wantErrContains) 517 } 518 }) 519 } 520 }