github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/ca/config_test.go (about) 1 package ca_test 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/tls" 7 "crypto/x509" 8 "fmt" 9 "io/ioutil" 10 "net" 11 "os" 12 "path/filepath" 13 "strings" 14 "testing" 15 "time" 16 17 "google.golang.org/grpc" 18 "google.golang.org/grpc/credentials" 19 20 cfconfig "github.com/cloudflare/cfssl/config" 21 "github.com/cloudflare/cfssl/helpers" 22 "github.com/docker/swarmkit/api" 23 "github.com/docker/swarmkit/ca" 24 cautils "github.com/docker/swarmkit/ca/testutils" 25 "github.com/docker/swarmkit/log" 26 "github.com/docker/swarmkit/manager/state" 27 "github.com/docker/swarmkit/manager/state/store" 28 "github.com/docker/swarmkit/testutils" 29 "github.com/pkg/errors" 30 "github.com/sirupsen/logrus" 31 "github.com/stretchr/testify/assert" 32 "github.com/stretchr/testify/require" 33 ) 34 35 func TestDownloadRootCASuccess(t *testing.T) { 36 for _, fips := range []bool{true, false} { 37 testDownloadRootCASuccess(t, fips) 38 } 39 } 40 func testDownloadRootCASuccess(t *testing.T, fips bool) { 41 var tc *cautils.TestCA 42 if fips { 43 tc = cautils.NewFIPSTestCA(t) 44 } else { 45 tc = cautils.NewTestCA(t) 46 } 47 defer tc.Stop() 48 49 token := ca.GenerateJoinToken(&tc.RootCA, fips) 50 51 // if we require mandatory FIPS, the join token uses a new format. otherwise 52 // the join token should use the old format. 53 prefix := "SWMTKN-1-" 54 if fips { 55 prefix = "SWMTKN-2-1-" 56 } 57 require.True(t, strings.HasPrefix(token, prefix)) 58 59 // Remove the CA cert 60 os.RemoveAll(tc.Paths.RootCA.Cert) 61 62 rootCA, err := ca.DownloadRootCA(tc.Context, tc.Paths.RootCA, token, tc.ConnBroker) 63 require.NoError(t, err) 64 require.NotNil(t, rootCA.Pool) 65 require.NotNil(t, rootCA.Certs) 66 _, err = rootCA.Signer() 67 require.Equal(t, err, ca.ErrNoValidSigner) 68 require.Equal(t, tc.RootCA.Certs, rootCA.Certs) 69 70 // Remove the CA cert 71 os.RemoveAll(tc.Paths.RootCA.Cert) 72 73 // downloading without a join token also succeeds 74 rootCA, err = ca.DownloadRootCA(tc.Context, tc.Paths.RootCA, "", tc.ConnBroker) 75 require.NoError(t, err) 76 require.NotNil(t, rootCA.Pool) 77 require.NotNil(t, rootCA.Certs) 78 _, err = rootCA.Signer() 79 require.Equal(t, err, ca.ErrNoValidSigner) 80 require.Equal(t, tc.RootCA.Certs, rootCA.Certs) 81 } 82 83 func TestDownloadRootCAWrongCAHash(t *testing.T) { 84 tc := cautils.NewTestCA(t) 85 defer tc.Stop() 86 87 // Remove the CA cert 88 os.RemoveAll(tc.Paths.RootCA.Cert) 89 90 // invalid token 91 for _, invalid := range []string{ 92 "invalidtoken", // completely invalid 93 "SWMTKN-1-3wkodtpeoipd1u1hi0ykdcdwhw16dk73ulqqtn14b3indz68rf-4myj5xihyto11dg1cn55w8p6", // mistyped 94 "SWMTKN-2-1fhvpatk6ms36i3uc64tsv1ybyuxkb899zbjpq4ib64qwbibz4-1g3as27iwmko5yqh1byv868hx", // version 2 should have 5 tokens 95 "SWMTKN-0-1fhvpatk6ms36i3uc64tsv1ybyuxkb899zbjpq4ib64qwbibz4-1g3as27iwmko5yqh1byv868hx", // invalid version 96 } { 97 _, err := ca.DownloadRootCA(tc.Context, tc.Paths.RootCA, invalid, tc.ConnBroker) 98 require.Error(t, err) 99 require.Contains(t, err.Error(), "invalid join token") 100 } 101 102 // invalid hash token - can get the wrong hash from both version 1 and version 2 103 for _, wrongToken := range []string{ 104 "SWMTKN-1-1kxftv4ofnc6mt30lmgipg6ngf9luhwqopfk1tz6bdmnkubg0e-4myj5xihyto11dg1cn55w8p61", 105 "SWMTKN-2-0-1kxftv4ofnc6mt30lmgipg6ngf9luhwqopfk1tz6bdmnkubg0e-4myj5xihyto11dg1cn55w8p61", 106 } { 107 _, err := ca.DownloadRootCA(tc.Context, tc.Paths.RootCA, wrongToken, tc.ConnBroker) 108 require.Error(t, err) 109 require.Contains(t, err.Error(), "remote CA does not match fingerprint.") 110 } 111 } 112 113 func TestCreateSecurityConfigEmptyDir(t *testing.T) { 114 if cautils.External { 115 return // this doesn't require any servers at all 116 } 117 tc := cautils.NewTestCA(t) 118 defer tc.Stop() 119 assert.NoError(t, tc.CAServer.Stop()) 120 121 // Remove all the contents from the temp dir and try again with a new node 122 for _, org := range []string{ 123 "", 124 "my_org", 125 } { 126 os.RemoveAll(tc.TempDir) 127 krw := ca.NewKeyReadWriter(tc.Paths.Node, nil, nil) 128 nodeConfig, cancel, err := tc.RootCA.CreateSecurityConfig(tc.Context, krw, 129 ca.CertificateRequestConfig{ 130 Token: tc.WorkerToken, 131 ConnBroker: tc.ConnBroker, 132 Organization: org, 133 }) 134 assert.NoError(t, err) 135 cancel() 136 assert.NotNil(t, nodeConfig) 137 assert.NotNil(t, nodeConfig.ClientTLSCreds) 138 assert.NotNil(t, nodeConfig.ServerTLSCreds) 139 assert.Equal(t, tc.RootCA, *nodeConfig.RootCA()) 140 if org != "" { 141 assert.Equal(t, org, nodeConfig.ClientTLSCreds.Organization()) 142 } 143 144 root, err := helpers.ParseCertificatePEM(tc.RootCA.Certs) 145 assert.NoError(t, err) 146 147 issuerInfo := nodeConfig.IssuerInfo() 148 assert.NotNil(t, issuerInfo) 149 assert.Equal(t, root.RawSubjectPublicKeyInfo, issuerInfo.PublicKey) 150 assert.Equal(t, root.RawSubject, issuerInfo.Subject) 151 } 152 } 153 154 func TestCreateSecurityConfigNoCerts(t *testing.T) { 155 tc := cautils.NewTestCA(t) 156 defer tc.Stop() 157 158 krw := ca.NewKeyReadWriter(tc.Paths.Node, nil, nil) 159 root, err := helpers.ParseCertificatePEM(tc.RootCA.Certs) 160 assert.NoError(t, err) 161 162 validateNodeConfig := func(rootCA *ca.RootCA) { 163 nodeConfig, cancel, err := rootCA.CreateSecurityConfig(tc.Context, krw, 164 ca.CertificateRequestConfig{ 165 Token: tc.WorkerToken, 166 ConnBroker: tc.ConnBroker, 167 }) 168 assert.NoError(t, err) 169 cancel() 170 assert.NotNil(t, nodeConfig) 171 assert.NotNil(t, nodeConfig.ClientTLSCreds) 172 assert.NotNil(t, nodeConfig.ServerTLSCreds) 173 // tc.RootCA can maybe sign, and the node root CA can also maybe sign, so we want to just compare the root 174 // certs and intermediates 175 assert.Equal(t, tc.RootCA.Certs, nodeConfig.RootCA().Certs) 176 assert.Equal(t, tc.RootCA.Intermediates, nodeConfig.RootCA().Intermediates) 177 178 issuerInfo := nodeConfig.IssuerInfo() 179 assert.NotNil(t, issuerInfo) 180 assert.Equal(t, root.RawSubjectPublicKeyInfo, issuerInfo.PublicKey) 181 assert.Equal(t, root.RawSubject, issuerInfo.Subject) 182 } 183 184 // Remove only the node certificates form the directory, and attest that we get 185 // new certificates that are locally signed 186 os.RemoveAll(tc.Paths.Node.Cert) 187 validateNodeConfig(&tc.RootCA) 188 189 // Remove only the node certificates form the directory, get a new rootCA, and attest that we get 190 // new certificates that are issued by the remote CA 191 os.RemoveAll(tc.Paths.Node.Cert) 192 rootCA, err := ca.GetLocalRootCA(tc.Paths.RootCA) 193 assert.NoError(t, err) 194 validateNodeConfig(&rootCA) 195 } 196 197 func testGRPCConnection(t *testing.T, secConfig *ca.SecurityConfig) { 198 // set up a GRPC server using these credentials 199 secConfig.ServerTLSCreds.Config().ClientAuth = tls.RequireAndVerifyClientCert 200 l, err := net.Listen("tcp", "127.0.0.1:0") 201 require.NoError(t, err) 202 serverOpts := []grpc.ServerOption{grpc.Creds(secConfig.ServerTLSCreds)} 203 grpcServer := grpc.NewServer(serverOpts...) 204 go grpcServer.Serve(l) 205 defer grpcServer.Stop() 206 207 // we should be able to connect to the server using the client credentials 208 dialOpts := []grpc.DialOption{ 209 grpc.WithBlock(), 210 grpc.WithTimeout(10 * time.Second), 211 grpc.WithTransportCredentials(secConfig.ClientTLSCreds), 212 } 213 conn, err := grpc.Dial(l.Addr().String(), dialOpts...) 214 require.NoError(t, err) 215 conn.Close() 216 } 217 218 func TestLoadSecurityConfigExpiredCert(t *testing.T) { 219 if cautils.External { 220 return // this doesn't require any servers at all 221 } 222 tc := cautils.NewTestCA(t) 223 defer tc.Stop() 224 s, err := tc.RootCA.Signer() 225 require.NoError(t, err) 226 227 krw := ca.NewKeyReadWriter(tc.Paths.Node, nil, nil) 228 now := time.Now() 229 230 _, _, err = tc.RootCA.IssueAndSaveNewCertificates(krw, "cn", "ou", "org") 231 require.NoError(t, err) 232 certBytes, _, err := krw.Read() 233 require.NoError(t, err) 234 235 // A cert that is not yet valid is not valid even if expiry is allowed 236 invalidCert := cautils.ReDateCert(t, certBytes, tc.RootCA.Certs, s.Key, now.Add(time.Hour), now.Add(time.Hour*2)) 237 require.NoError(t, ioutil.WriteFile(tc.Paths.Node.Cert, invalidCert, 0700)) 238 239 _, _, err = ca.LoadSecurityConfig(tc.Context, tc.RootCA, krw, false) 240 require.Error(t, err) 241 require.IsType(t, x509.CertificateInvalidError{}, errors.Cause(err)) 242 243 _, _, err = ca.LoadSecurityConfig(tc.Context, tc.RootCA, krw, true) 244 require.Error(t, err) 245 require.IsType(t, x509.CertificateInvalidError{}, errors.Cause(err)) 246 247 // a cert that is expired is not valid if expiry is not allowed 248 invalidCert = cautils.ReDateCert(t, certBytes, tc.RootCA.Certs, s.Key, now.Add(-2*time.Minute), now.Add(-1*time.Minute)) 249 require.NoError(t, ioutil.WriteFile(tc.Paths.Node.Cert, invalidCert, 0700)) 250 251 _, _, err = ca.LoadSecurityConfig(tc.Context, tc.RootCA, krw, false) 252 require.Error(t, err) 253 require.IsType(t, x509.CertificateInvalidError{}, errors.Cause(err)) 254 255 // but it is valid if expiry is allowed 256 _, cancel, err := ca.LoadSecurityConfig(tc.Context, tc.RootCA, krw, true) 257 require.NoError(t, err) 258 cancel() 259 } 260 261 func TestLoadSecurityConfigInvalidCert(t *testing.T) { 262 if cautils.External { 263 return // this doesn't require any servers at all 264 } 265 tc := cautils.NewTestCA(t) 266 defer tc.Stop() 267 268 // Write some garbage to the cert 269 ioutil.WriteFile(tc.Paths.Node.Cert, []byte(`-----BEGIN CERTIFICATE-----\n 270 some random garbage\n 271 -----END CERTIFICATE-----`), 0644) 272 273 krw := ca.NewKeyReadWriter(tc.Paths.Node, nil, nil) 274 275 _, _, err := ca.LoadSecurityConfig(tc.Context, tc.RootCA, krw, false) 276 assert.Error(t, err) 277 } 278 279 func TestLoadSecurityConfigInvalidKey(t *testing.T) { 280 if cautils.External { 281 return // this doesn't require any servers at all 282 } 283 tc := cautils.NewTestCA(t) 284 defer tc.Stop() 285 286 // Write some garbage to the Key 287 ioutil.WriteFile(tc.Paths.Node.Key, []byte(`-----BEGIN PRIVATE KEY-----\n 288 some random garbage\n 289 -----END PRIVATE KEY-----`), 0644) 290 291 krw := ca.NewKeyReadWriter(tc.Paths.Node, nil, nil) 292 293 _, _, err := ca.LoadSecurityConfig(tc.Context, tc.RootCA, krw, false) 294 assert.Error(t, err) 295 } 296 297 func TestLoadSecurityConfigIncorrectPassphrase(t *testing.T) { 298 if cautils.External { 299 return // this doesn't require any servers at all 300 } 301 tc := cautils.NewTestCA(t) 302 defer tc.Stop() 303 304 paths := ca.NewConfigPaths(tc.TempDir) 305 _, _, err := tc.RootCA.IssueAndSaveNewCertificates(ca.NewKeyReadWriter(paths.Node, []byte("kek"), nil), 306 "nodeID", ca.WorkerRole, tc.Organization) 307 require.NoError(t, err) 308 309 _, _, err = ca.LoadSecurityConfig(tc.Context, tc.RootCA, ca.NewKeyReadWriter(paths.Node, nil, nil), false) 310 require.IsType(t, ca.ErrInvalidKEK{}, err) 311 } 312 313 func TestLoadSecurityConfigIntermediates(t *testing.T) { 314 if cautils.External { 315 return // this doesn't require any servers at all 316 } 317 tempdir, err := ioutil.TempDir("", "test-load-config-with-intermediates") 318 require.NoError(t, err) 319 defer os.RemoveAll(tempdir) 320 paths := ca.NewConfigPaths(tempdir) 321 krw := ca.NewKeyReadWriter(paths.Node, nil, nil) 322 323 rootCA, err := ca.NewRootCA(cautils.ECDSACertChain[2], nil, nil, ca.DefaultNodeCertExpiration, nil) 324 require.NoError(t, err) 325 326 ctx := log.WithLogger(context.Background(), log.L.WithFields(logrus.Fields{ 327 "testname": t.Name(), 328 "testHasExternalCA": false, 329 })) 330 331 // loading the incomplete chain fails 332 require.NoError(t, krw.Write(cautils.ECDSACertChain[0], cautils.ECDSACertChainKeys[0], nil)) 333 _, _, err = ca.LoadSecurityConfig(ctx, rootCA, krw, false) 334 require.Error(t, err) 335 336 intermediate, err := helpers.ParseCertificatePEM(cautils.ECDSACertChain[1]) 337 require.NoError(t, err) 338 339 // loading the complete chain succeeds 340 require.NoError(t, krw.Write(append(cautils.ECDSACertChain[0], cautils.ECDSACertChain[1]...), cautils.ECDSACertChainKeys[0], nil)) 341 secConfig, cancel, err := ca.LoadSecurityConfig(ctx, rootCA, krw, false) 342 require.NoError(t, err) 343 defer cancel() 344 require.NotNil(t, secConfig) 345 issuerInfo := secConfig.IssuerInfo() 346 require.NotNil(t, issuerInfo) 347 require.Equal(t, intermediate.RawSubjectPublicKeyInfo, issuerInfo.PublicKey) 348 require.Equal(t, intermediate.RawSubject, issuerInfo.Subject) 349 350 testGRPCConnection(t, secConfig) 351 } 352 353 func TestLoadSecurityConfigKeyFormat(t *testing.T) { 354 if cautils.External { 355 return // this doesn't require any servers at all 356 } 357 tempdir, err := ioutil.TempDir("", "test-load-config") 358 require.NoError(t, err) 359 defer os.RemoveAll(tempdir) 360 paths := ca.NewConfigPaths(tempdir) 361 krw := ca.NewKeyReadWriter(paths.Node, nil, nil) 362 363 rootCA, err := ca.NewRootCA(cautils.ECDSACertChain[1], nil, nil, ca.DefaultNodeCertExpiration, nil) 364 require.NoError(t, err) 365 366 ctx := log.WithLogger(context.Background(), log.L.WithFields(logrus.Fields{ 367 "testname": t.Name(), 368 "testHasExternalCA": false, 369 })) 370 371 // load leaf cert with its PKCS#1 format key 372 require.NoError(t, krw.Write(cautils.ECDSACertChain[0], cautils.ECDSACertChainKeys[0], nil)) 373 secConfig, cancel, err := ca.LoadSecurityConfig(ctx, rootCA, krw, false) 374 require.NoError(t, err) 375 defer cancel() 376 require.NotNil(t, secConfig) 377 378 testGRPCConnection(t, secConfig) 379 380 // load leaf cert with its PKCS#8 format key 381 require.NoError(t, krw.Write(cautils.ECDSACertChain[0], cautils.ECDSACertChainPKCS8Keys[0], nil)) 382 secConfig, cancel, err = ca.LoadSecurityConfig(ctx, rootCA, krw, false) 383 require.NoError(t, err) 384 defer cancel() 385 require.NotNil(t, secConfig) 386 387 testGRPCConnection(t, secConfig) 388 } 389 390 // Custom GRPC dialer that does the TLS handshake itself, so that we can grab whatever 391 // TLS error comes out. Otherwise, GRPC >=1.10.x attempts to load balance connections and dial 392 // asynchronously, thus eating whatever connection errors there are and returning nothing 393 // but a timeout error. In theory, we can dial without the `WithBlock` option, and check 394 // the error from an RPC call instead, but that's racy: https://github.com/grpc/grpc-go/issues/1917 395 // Hopefully an API will be provided to check connection errors on the underlying connection: 396 // https://github.com/grpc/grpc-go/issues/2031. 397 func tlsGRPCDial(ctx context.Context, address string, creds credentials.TransportCredentials) (*grpc.ClientConn, chan error, error) { 398 dialerErrChan := make(chan error, 1) 399 conn, err := grpc.Dial( 400 address, 401 grpc.WithBlock(), 402 grpc.WithTimeout(10*time.Second), 403 grpc.WithInsecure(), 404 grpc.WithDialer(func(address string, timeout time.Duration) (net.Conn, error) { 405 ctx, cancel := context.WithTimeout(ctx, timeout) 406 defer cancel() 407 408 conn, err := (&net.Dialer{Cancel: ctx.Done()}).Dial("tcp", address) 409 if err != nil { 410 dialerErrChan <- err 411 return nil, err 412 } 413 conn, _, err = creds.ClientHandshake(ctx, address, conn) 414 if err != nil { 415 dialerErrChan <- err 416 return nil, err 417 } 418 return conn, nil 419 }), 420 ) 421 return conn, dialerErrChan, err 422 } 423 424 // When the root CA is updated on the security config, the root pools are updated 425 func TestSecurityConfigUpdateRootCA(t *testing.T) { 426 t.Parallel() 427 if cautils.External { // don't need an external CA server 428 return 429 } 430 431 tc := cautils.NewTestCA(t) 432 defer tc.Stop() 433 tcConfig, err := tc.NewNodeConfig("worker") 434 require.NoError(t, err) 435 436 // create the "original" security config, and we'll update it to trust the test server's 437 cert, key, err := cautils.CreateRootCertAndKey("root1") 438 require.NoError(t, err) 439 440 rootCA, err := ca.NewRootCA(cert, cert, key, ca.DefaultNodeCertExpiration, nil) 441 require.NoError(t, err) 442 443 tempdir, err := ioutil.TempDir("", "test-security-config-update") 444 require.NoError(t, err) 445 defer os.RemoveAll(tempdir) 446 configPaths := ca.NewConfigPaths(tempdir) 447 448 secConfig, cancel, err := rootCA.CreateSecurityConfig(tc.Context, 449 ca.NewKeyReadWriter(configPaths.Node, nil, nil), ca.CertificateRequestConfig{}) 450 require.NoError(t, err) 451 cancel() 452 // update the server TLS to require certificates, otherwise this will all pass 453 // even if the root pools aren't updated 454 secConfig.ServerTLSCreds.Config().ClientAuth = tls.RequireAndVerifyClientCert 455 456 // set up a GRPC server using these credentials 457 l, err := net.Listen("tcp", "127.0.0.1:0") 458 require.NoError(t, err) 459 serverOpts := []grpc.ServerOption{grpc.Creds(secConfig.ServerTLSCreds)} 460 grpcServer := grpc.NewServer(serverOpts...) 461 go grpcServer.Serve(l) 462 defer grpcServer.Stop() 463 464 // We should not be able to connect to the test CA server using the original security config, and should not 465 // be able to connect to new server using the test CA's client credentials. We need to use our own 466 // dialer, so that grpc does not attempt to load balance/retry the connection - this way the x509 errors can be 467 // surfaced. 468 _, actualErrChan, err := tlsGRPCDial(tc.Context, tc.Addr, secConfig.ClientTLSCreds) 469 defer close(actualErrChan) 470 require.Error(t, err) 471 err = <-actualErrChan 472 require.Error(t, err) 473 require.IsType(t, x509.UnknownAuthorityError{}, err) 474 475 _, actualErrChan, err = tlsGRPCDial(tc.Context, l.Addr().String(), tcConfig.ClientTLSCreds) 476 defer close(actualErrChan) 477 require.Error(t, err) 478 err = <-actualErrChan 479 require.Error(t, err) 480 require.IsType(t, x509.UnknownAuthorityError{}, err) 481 482 // update the root CA on the "original security config to support both the old root 483 // and the "new root" (the testing CA root). Also make sure this root CA has an 484 // intermediate; we won't use it for anything, just make sure that newly generated TLS 485 // certs have the intermediate appended. 486 someOtherRootCA, err := ca.CreateRootCA("someOtherRootCA") 487 require.NoError(t, err) 488 intermediate, err := someOtherRootCA.CrossSignCACertificate(cert) 489 require.NoError(t, err) 490 rSigner, err := rootCA.Signer() 491 require.NoError(t, err) 492 updatedRootCA, err := ca.NewRootCA(concat(rootCA.Certs, tc.RootCA.Certs, someOtherRootCA.Certs), rSigner.Cert, rSigner.Key, ca.DefaultNodeCertExpiration, intermediate) 493 require.NoError(t, err) 494 err = secConfig.UpdateRootCA(&updatedRootCA) 495 require.NoError(t, err) 496 497 // can now connect to the test CA using our modified security config, and can cannect to our server using 498 // the test CA config 499 conn, err := grpc.Dial( 500 tc.Addr, 501 grpc.WithBlock(), 502 grpc.WithTimeout(10*time.Second), 503 grpc.WithTransportCredentials(tcConfig.ClientTLSCreds), 504 ) 505 require.NoError(t, err) 506 conn.Close() 507 508 conn, err = grpc.Dial( 509 tc.Addr, 510 grpc.WithBlock(), 511 grpc.WithTimeout(10*time.Second), 512 grpc.WithTransportCredentials(secConfig.ClientTLSCreds), 513 ) 514 require.NoError(t, err) 515 conn.Close() 516 517 // make sure any generated certs after updating contain the intermediate 518 krw := ca.NewKeyReadWriter(configPaths.Node, nil, nil) 519 _, _, err = secConfig.RootCA().IssueAndSaveNewCertificates(krw, "cn", "ou", "org") 520 require.NoError(t, err) 521 generatedCert, _, err := krw.Read() 522 require.NoError(t, err) 523 524 parsedCerts, err := helpers.ParseCertificatesPEM(generatedCert) 525 require.NoError(t, err) 526 require.Len(t, parsedCerts, 2) 527 parsedIntermediate, err := helpers.ParseCertificatePEM(intermediate) 528 require.NoError(t, err) 529 require.Equal(t, parsedIntermediate, parsedCerts[1]) 530 } 531 532 // You can't update the root CA to one that doesn't match the TLS certificates 533 func TestSecurityConfigUpdateRootCAUpdateConsistentWithTLSCertificates(t *testing.T) { 534 t.Parallel() 535 if cautils.External { 536 return // we don't care about external CAs at all 537 } 538 tempdir, err := ioutil.TempDir("", "") 539 require.NoError(t, err) 540 krw := ca.NewKeyReadWriter(ca.NewConfigPaths(tempdir).Node, nil, nil) 541 542 rootCA, err := ca.CreateRootCA("rootcn") 543 require.NoError(t, err) 544 tlsKeyPair, issuerInfo, err := rootCA.IssueAndSaveNewCertificates(krw, "cn", "ou", "org") 545 require.NoError(t, err) 546 547 otherRootCA, err := ca.CreateRootCA("otherCN") 548 require.NoError(t, err) 549 _, otherIssuerInfo, err := otherRootCA.IssueAndSaveNewCertificates(krw, "cn", "ou", "org") 550 require.NoError(t, err) 551 intermediate, err := rootCA.CrossSignCACertificate(otherRootCA.Certs) 552 require.NoError(t, err) 553 otherTLSCert, otherTLSKey, err := krw.Read() 554 require.NoError(t, err) 555 otherTLSKeyPair, err := tls.X509KeyPair(append(otherTLSCert, intermediate...), otherTLSKey) 556 require.NoError(t, err) 557 558 // Note - the validation only happens on UpdateRootCA for now, because the assumption is 559 // that something else does the validation when loading the security config for the first 560 // time and when getting new TLS credentials 561 562 secConfig, cancel, err := ca.NewSecurityConfig(&rootCA, krw, tlsKeyPair, issuerInfo) 563 require.NoError(t, err) 564 cancel() 565 566 // can't update the root CA to one that doesn't match the tls certs 567 require.Error(t, secConfig.UpdateRootCA(&otherRootCA)) 568 569 // can update the secConfig's root CA to one that does match the certs 570 combinedRootCA, err := ca.NewRootCA(append(otherRootCA.Certs, rootCA.Certs...), nil, nil, 571 ca.DefaultNodeCertExpiration, nil) 572 require.NoError(t, err) 573 require.NoError(t, secConfig.UpdateRootCA(&combinedRootCA)) 574 575 // if there are intermediates, we can update to a root CA that signed the intermediate 576 require.NoError(t, secConfig.UpdateTLSCredentials(&otherTLSKeyPair, otherIssuerInfo)) 577 require.NoError(t, secConfig.UpdateRootCA(&rootCA)) 578 579 } 580 581 func TestSecurityConfigWatch(t *testing.T) { 582 tc := cautils.NewTestCA(t) 583 defer tc.Stop() 584 585 secConfig, err := tc.NewNodeConfig(ca.ManagerRole) 586 require.NoError(t, err) 587 issuer := secConfig.IssuerInfo() 588 589 configWatch, configCancel := secConfig.Watch() 590 defer configCancel() 591 592 require.NoError(t, ca.RenewTLSConfigNow(tc.Context, secConfig, tc.ConnBroker, tc.Paths.RootCA)) 593 select { 594 case ev := <-configWatch: 595 nodeTLSInfo, ok := ev.(*api.NodeTLSInfo) 596 require.True(t, ok) 597 require.Equal(t, &api.NodeTLSInfo{ 598 TrustRoot: tc.RootCA.Certs, 599 CertIssuerPublicKey: issuer.PublicKey, 600 CertIssuerSubject: issuer.Subject, 601 }, nodeTLSInfo) 602 case <-time.After(time.Second): 603 require.FailNow(t, "on TLS certificate update, we should have gotten a security config update") 604 } 605 606 require.NoError(t, secConfig.UpdateRootCA(&tc.RootCA)) 607 select { 608 case ev := <-configWatch: 609 nodeTLSInfo, ok := ev.(*api.NodeTLSInfo) 610 require.True(t, ok) 611 require.Equal(t, &api.NodeTLSInfo{ 612 TrustRoot: tc.RootCA.Certs, 613 CertIssuerPublicKey: issuer.PublicKey, 614 CertIssuerSubject: issuer.Subject, 615 }, nodeTLSInfo) 616 case <-time.After(time.Second): 617 require.FailNow(t, "on TLS certificate update, we should have gotten a security config update") 618 } 619 620 configCancel() 621 622 // ensure that we can still update tls certs and roots without error even though the watch is closed 623 require.NoError(t, secConfig.UpdateRootCA(&tc.RootCA)) 624 require.NoError(t, ca.RenewTLSConfigNow(tc.Context, secConfig, tc.ConnBroker, tc.Paths.RootCA)) 625 } 626 627 // If we get an unknown authority error when trying to renew the TLS certificate, attempt to download the 628 // root certificate. If it validates against the current TLS credentials, it will be used to download 629 // new ones, (only if the new certificate indicates that it's a worker, though). 630 func TestRenewTLSConfigUpdatesRootOnUnknownAuthError(t *testing.T) { 631 tempdir, err := ioutil.TempDir("", "test-renew-tls-config-now-downloads-root") 632 require.NoError(t, err) 633 defer os.RemoveAll(tempdir) 634 635 // make 3 CAs 636 var ( 637 certs = make([][]byte, 3) 638 keys = make([][]byte, 3) 639 crossSigneds = make([][]byte, 3) 640 cas = make([]ca.RootCA, 3) 641 ) 642 for i := 0; i < 3; i++ { 643 certs[i], keys[i], err = cautils.CreateRootCertAndKey(fmt.Sprintf("CA%d", i)) 644 require.NoError(t, err) 645 switch i { 646 case 0: 647 crossSigneds[i] = nil 648 cas[i], err = ca.NewRootCA(certs[i], certs[i], keys[i], ca.DefaultNodeCertExpiration, nil) 649 require.NoError(t, err) 650 default: 651 crossSigneds[i], err = cas[i-1].CrossSignCACertificate(certs[i]) 652 require.NoError(t, err) 653 cas[i], err = ca.NewRootCA(certs[i-1], certs[i], keys[i], ca.DefaultNodeCertExpiration, crossSigneds[i]) 654 require.NoError(t, err) 655 } 656 } 657 658 // the CA server is going to start off with a cert issued by the second CA, cross-signed by the first CA, and then 659 // rotate to one issued by the third CA, cross-signed by the second. 660 tc := cautils.NewTestCAFromAPIRootCA(t, tempdir, api.RootCA{ 661 CACert: certs[0], 662 CAKey: keys[0], 663 RootRotation: &api.RootRotation{ 664 CACert: certs[1], 665 CAKey: keys[1], 666 CrossSignedCACert: crossSigneds[1], 667 }, 668 }, nil) 669 defer tc.Stop() 670 require.NoError(t, tc.MemoryStore.Update(func(tx store.Tx) error { 671 cluster := store.GetCluster(tx, tc.Organization) 672 cluster.RootCA.CACert = certs[1] 673 cluster.RootCA.CAKey = keys[1] 674 cluster.RootCA.RootRotation = &api.RootRotation{ 675 CACert: certs[2], 676 CAKey: keys[2], 677 CrossSignedCACert: crossSigneds[2], 678 } 679 return store.UpdateCluster(tx, cluster) 680 })) 681 // wait until the CA is returning certs signed by the latest root 682 rootCA, err := ca.NewRootCA(certs[1], nil, nil, ca.DefaultNodeCertExpiration, nil) 683 require.NoError(t, err) 684 expectedIssuer, err := helpers.ParseCertificatePEM(certs[2]) 685 require.NoError(t, err) 686 require.NoError(t, testutils.PollFuncWithTimeout(nil, func() error { 687 _, issuerInfo, err := rootCA.RequestAndSaveNewCertificates(tc.Context, tc.KeyReadWriter, ca.CertificateRequestConfig{ 688 Token: tc.WorkerToken, 689 ConnBroker: tc.ConnBroker, 690 }) 691 if err != nil { 692 return err 693 } 694 if !bytes.Equal(issuerInfo.PublicKey, expectedIssuer.RawSubjectPublicKeyInfo) { 695 return errors.New("CA server hasn't finished updating yet") 696 } 697 return nil 698 }, 2*time.Second)) 699 700 paths := ca.NewConfigPaths(tempdir) 701 krw := ca.NewKeyReadWriter(paths.Node, nil, nil) 702 for i, testCase := range []struct { 703 role api.NodeRole 704 initialRootCA *ca.RootCA 705 issuingRootCA *ca.RootCA 706 expectedRoot []byte 707 }{ 708 { 709 role: api.NodeRoleWorker, 710 initialRootCA: &cas[0], 711 issuingRootCA: &cas[1], 712 expectedRoot: certs[1], 713 }, 714 { 715 role: api.NodeRoleManager, 716 initialRootCA: &cas[0], 717 issuingRootCA: &cas[1], 718 }, 719 // TODO(cyli): once signing root CA and serving root CA for the CA server are split up, so that the server can accept 720 // requests from certs different than the cluster root CA, add another test case to make sure that the downloaded 721 // root has to validate against both the old TLS creds and new TLS creds 722 } { 723 nodeID := fmt.Sprintf("node%d", i) 724 tlsKeyPair, issuerInfo, err := testCase.issuingRootCA.IssueAndSaveNewCertificates(krw, nodeID, ca.ManagerRole, tc.Organization) 725 require.NoError(t, err) 726 // make sure the node is added to the memory store as a worker, so when we renew the cert the test CA will answer 727 require.NoError(t, tc.MemoryStore.Update(func(tx store.Tx) error { 728 return store.CreateNode(tx, &api.Node{ 729 Role: testCase.role, 730 ID: nodeID, 731 Spec: api.NodeSpec{ 732 DesiredRole: testCase.role, 733 Membership: api.NodeMembershipAccepted, 734 Availability: api.NodeAvailabilityActive, 735 }, 736 }) 737 })) 738 secConfig, qClose, err := ca.NewSecurityConfig(testCase.initialRootCA, krw, tlsKeyPair, issuerInfo) 739 require.NoError(t, err) 740 defer qClose() 741 742 paths := ca.NewConfigPaths(filepath.Join(tempdir, nodeID)) 743 err = ca.RenewTLSConfigNow(tc.Context, secConfig, tc.ConnBroker, paths.RootCA) 744 745 // TODO(cyli): remove this role check once the codepaths for worker and manager are the same 746 if testCase.expectedRoot != nil { 747 // only rotate if we are a worker, and if the new cert validates against the old TLS creds 748 require.NoError(t, err) 749 downloadedRoot, err := ioutil.ReadFile(paths.RootCA.Cert) 750 require.NoError(t, err) 751 require.Equal(t, testCase.expectedRoot, downloadedRoot) 752 } else { 753 require.Error(t, err) 754 require.IsType(t, x509.UnknownAuthorityError{}, err) 755 _, err = ioutil.ReadFile(paths.RootCA.Cert) // we didn't download a file 756 require.Error(t, err) 757 } 758 } 759 } 760 761 // If we get a not unknown authority error when trying to renew the TLS certificate, just return the 762 // error and do not attempt to download the root certificate. 763 func TestRenewTLSConfigUpdatesRootNonUnknownAuthError(t *testing.T) { 764 tempdir, err := ioutil.TempDir("", "test-renew-tls-config-now-downloads-root") 765 require.NoError(t, err) 766 defer os.RemoveAll(tempdir) 767 768 cert, key, err := cautils.CreateRootCertAndKey("rootCA") 769 require.NoError(t, err) 770 rootCA, err := ca.NewRootCA(cert, cert, key, ca.DefaultNodeCertExpiration, nil) 771 require.NoError(t, err) 772 773 tc := cautils.NewTestCAFromAPIRootCA(t, tempdir, api.RootCA{ 774 CACert: cert, 775 CAKey: key, 776 }, nil) 777 defer tc.Stop() 778 779 fakeCAServer := newNonSigningCAServer(t, tc) 780 defer fakeCAServer.stop(t) 781 782 secConfig, err := tc.NewNodeConfig(ca.WorkerRole) 783 require.NoError(t, err) 784 tc.CAServer.Stop() 785 786 signErr := make(chan error) 787 go func() { 788 updates, cancel := state.Watch(tc.MemoryStore.WatchQueue(), api.EventCreateNode{}) 789 defer cancel() 790 event := <-updates // we want to skip the first node, which is the test CA 791 n := event.(api.EventCreateNode).Node 792 if n.Certificate.Status.State == api.IssuanceStatePending { 793 signErr <- tc.MemoryStore.Update(func(tx store.Tx) error { 794 node := store.GetNode(tx, n.ID) 795 certChain, err := rootCA.ParseValidateAndSignCSR(node.Certificate.CSR, node.Certificate.CN, ca.WorkerRole, tc.Organization) 796 if err != nil { 797 return err 798 } 799 node.Certificate.Certificate = cautils.ReDateCert(t, certChain, cert, key, time.Now().Add(-5*time.Hour), time.Now().Add(-4*time.Hour)) 800 node.Certificate.Status = api.IssuanceStatus{ 801 State: api.IssuanceStateIssued, 802 } 803 return store.UpdateNode(tx, node) 804 }) 805 return 806 } 807 }() 808 809 err = ca.RenewTLSConfigNow(tc.Context, secConfig, fakeCAServer.getConnBroker(), tc.Paths.RootCA) 810 require.Error(t, err) 811 require.IsType(t, x509.CertificateInvalidError{}, errors.Cause(err)) 812 require.NoError(t, <-signErr) 813 } 814 815 // enforce that no matter what order updating the root CA and updating TLS credential happens, we 816 // end up with a security config that has updated certs, and an updated root pool 817 func TestRenewTLSConfigUpdateRootCARace(t *testing.T) { 818 tc := cautils.NewTestCA(t) 819 defer tc.Stop() 820 paths := ca.NewConfigPaths(tc.TempDir) 821 822 secConfig, err := tc.WriteNewNodeConfig(ca.ManagerRole) 823 require.NoError(t, err) 824 825 leafCert, err := ioutil.ReadFile(paths.Node.Cert) 826 require.NoError(t, err) 827 828 for i := 0; i < 5; i++ { 829 cert, _, err := cautils.CreateRootCertAndKey(fmt.Sprintf("root %d", i+2)) 830 require.NoError(t, err) 831 832 ctx, cancel := context.WithCancel(tc.Context) 833 defer cancel() 834 835 done1, done2 := make(chan struct{}), make(chan struct{}) 836 rootCA := secConfig.RootCA() 837 go func() { 838 defer close(done1) 839 s := ca.LocalSigner{} 840 if signer, err := rootCA.Signer(); err == nil { 841 s = *signer 842 } 843 updatedRootCA, err := ca.NewRootCA(append(rootCA.Certs, cert...), s.Cert, s.Key, ca.DefaultNodeCertExpiration, nil) 844 require.NoError(t, err) 845 require.NoError(t, secConfig.UpdateRootCA(&updatedRootCA)) 846 }() 847 848 go func() { 849 defer close(done2) 850 require.NoError(t, ca.RenewTLSConfigNow(ctx, secConfig, tc.ConnBroker, tc.Paths.RootCA)) 851 }() 852 853 <-done1 854 <-done2 855 856 newCert, err := ioutil.ReadFile(paths.Node.Cert) 857 require.NoError(t, err) 858 859 require.NotEqual(t, newCert, leafCert) 860 leafCert = newCert 861 862 // at the start of this loop had i+1 certs, afterward should have added one more 863 require.Len(t, secConfig.ClientTLSCreds.Config().RootCAs.Subjects(), i+2) 864 require.Len(t, secConfig.ServerTLSCreds.Config().RootCAs.Subjects(), i+2) 865 } 866 } 867 868 func writeAlmostExpiringCertToDisk(t *testing.T, tc *cautils.TestCA, cn, ou, org string) { 869 s, err := tc.RootCA.Signer() 870 require.NoError(t, err) 871 872 // Create a new RootCA, and change the policy to issue 6 minute certificates 873 // Because of the default backdate of 5 minutes, this issues certificates 874 // valid for 1 minute. 875 newRootCA, err := ca.NewRootCA(tc.RootCA.Certs, s.Cert, s.Key, ca.DefaultNodeCertExpiration, nil) 876 assert.NoError(t, err) 877 newSigner, err := newRootCA.Signer() 878 require.NoError(t, err) 879 newSigner.SetPolicy(&cfconfig.Signing{ 880 Default: &cfconfig.SigningProfile{ 881 Usage: []string{"signing", "key encipherment", "server auth", "client auth"}, 882 Expiry: 6 * time.Minute, 883 }, 884 }) 885 886 // Issue a new certificate with the same details as the current config, but with 1 min expiration time, and 887 // overwrite the existing cert on disk 888 _, _, err = newRootCA.IssueAndSaveNewCertificates(ca.NewKeyReadWriter(tc.Paths.Node, nil, nil), cn, ou, org) 889 assert.NoError(t, err) 890 } 891 892 func TestRenewTLSConfigWorker(t *testing.T) { 893 t.Parallel() 894 895 tc := cautils.NewTestCA(t) 896 defer tc.Stop() 897 898 ctx, cancel := context.WithCancel(tc.Context) 899 defer cancel() 900 901 // Get a new nodeConfig with a TLS cert that has the default Cert duration, but overwrite 902 // the cert on disk with one that expires in 1 minute 903 nodeConfig, err := tc.WriteNewNodeConfig(ca.WorkerRole) 904 assert.NoError(t, err) 905 c := nodeConfig.ClientTLSCreds 906 writeAlmostExpiringCertToDisk(t, tc, c.NodeID(), c.Role(), c.Organization()) 907 908 renewer := ca.NewTLSRenewer(nodeConfig, tc.ConnBroker, tc.Paths.RootCA) 909 updates := renewer.Start(ctx) 910 select { 911 case <-time.After(10 * time.Second): 912 assert.Fail(t, "TestRenewTLSConfig timed-out") 913 case certUpdate := <-updates: 914 assert.NoError(t, certUpdate.Err) 915 assert.NotNil(t, certUpdate) 916 assert.Equal(t, ca.WorkerRole, certUpdate.Role) 917 } 918 919 root, err := helpers.ParseCertificatePEM(tc.RootCA.Certs) 920 assert.NoError(t, err) 921 922 issuerInfo := nodeConfig.IssuerInfo() 923 assert.NotNil(t, issuerInfo) 924 assert.Equal(t, root.RawSubjectPublicKeyInfo, issuerInfo.PublicKey) 925 assert.Equal(t, root.RawSubject, issuerInfo.Subject) 926 } 927 928 func TestRenewTLSConfigManager(t *testing.T) { 929 t.Parallel() 930 931 tc := cautils.NewTestCA(t) 932 defer tc.Stop() 933 934 ctx, cancel := context.WithCancel(tc.Context) 935 defer cancel() 936 937 // Get a new nodeConfig with a TLS cert that has the default Cert duration, but overwrite 938 // the cert on disk with one that expires in 1 minute 939 nodeConfig, err := tc.WriteNewNodeConfig(ca.WorkerRole) 940 assert.NoError(t, err) 941 c := nodeConfig.ClientTLSCreds 942 writeAlmostExpiringCertToDisk(t, tc, c.NodeID(), c.Role(), c.Organization()) 943 944 renewer := ca.NewTLSRenewer(nodeConfig, tc.ConnBroker, tc.Paths.RootCA) 945 updates := renewer.Start(ctx) 946 select { 947 case <-time.After(10 * time.Second): 948 assert.Fail(t, "TestRenewTLSConfig timed-out") 949 case certUpdate := <-updates: 950 assert.NoError(t, certUpdate.Err) 951 assert.NotNil(t, certUpdate) 952 assert.Equal(t, ca.WorkerRole, certUpdate.Role) 953 } 954 955 root, err := helpers.ParseCertificatePEM(tc.RootCA.Certs) 956 assert.NoError(t, err) 957 958 issuerInfo := nodeConfig.IssuerInfo() 959 assert.NotNil(t, issuerInfo) 960 assert.Equal(t, root.RawSubjectPublicKeyInfo, issuerInfo.PublicKey) 961 assert.Equal(t, root.RawSubject, issuerInfo.Subject) 962 } 963 964 func TestRenewTLSConfigWithNoNode(t *testing.T) { 965 t.Parallel() 966 967 tc := cautils.NewTestCA(t) 968 defer tc.Stop() 969 970 ctx, cancel := context.WithCancel(tc.Context) 971 defer cancel() 972 973 // Get a new nodeConfig with a TLS cert that has the default Cert duration, but overwrite 974 // the cert on disk with one that expires in 1 minute 975 nodeConfig, err := tc.WriteNewNodeConfig(ca.WorkerRole) 976 assert.NoError(t, err) 977 c := nodeConfig.ClientTLSCreds 978 writeAlmostExpiringCertToDisk(t, tc, c.NodeID(), c.Role(), c.Organization()) 979 980 // Delete the node from the backend store 981 err = tc.MemoryStore.Update(func(tx store.Tx) error { 982 node := store.GetNode(tx, nodeConfig.ClientTLSCreds.NodeID()) 983 assert.NotNil(t, node) 984 return store.DeleteNode(tx, nodeConfig.ClientTLSCreds.NodeID()) 985 }) 986 assert.NoError(t, err) 987 988 renewer := ca.NewTLSRenewer(nodeConfig, tc.ConnBroker, tc.Paths.RootCA) 989 updates := renewer.Start(ctx) 990 select { 991 case <-time.After(10 * time.Second): 992 assert.Fail(t, "TestRenewTLSConfig timed-out") 993 case certUpdate := <-updates: 994 assert.Error(t, certUpdate.Err) 995 assert.Contains(t, certUpdate.Err.Error(), "not found when attempting to renew certificate") 996 } 997 }