github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/ca/certificates_test.go (about) 1 package ca_test 2 3 import ( 4 "context" 5 "crypto/ecdsa" 6 "crypto/elliptic" 7 cryptorand "crypto/rand" 8 "crypto/sha256" 9 "crypto/tls" 10 "crypto/x509" 11 "encoding/hex" 12 "encoding/pem" 13 "fmt" 14 "io/ioutil" 15 "net" 16 "os" 17 "sync" 18 "sync/atomic" 19 "testing" 20 "time" 21 22 "google.golang.org/grpc" 23 "google.golang.org/grpc/codes" 24 25 cfcsr "github.com/cloudflare/cfssl/csr" 26 "github.com/cloudflare/cfssl/helpers" 27 "github.com/cloudflare/cfssl/initca" 28 "github.com/docker/go-events" 29 "github.com/docker/swarmkit/api" 30 "github.com/docker/swarmkit/ca" 31 cautils "github.com/docker/swarmkit/ca/testutils" 32 "github.com/docker/swarmkit/connectionbroker" 33 "github.com/docker/swarmkit/identity" 34 "github.com/docker/swarmkit/manager/state" 35 "github.com/docker/swarmkit/manager/state/store" 36 "github.com/docker/swarmkit/remotes" 37 "github.com/docker/swarmkit/testutils" 38 "github.com/opencontainers/go-digest" 39 "github.com/phayes/permbits" 40 "github.com/stretchr/testify/assert" 41 "github.com/stretchr/testify/require" 42 "google.golang.org/grpc/status" 43 ) 44 45 func init() { 46 ca.RenewTLSExponentialBackoff = events.ExponentialBackoffConfig{ 47 Base: 250 * time.Millisecond, 48 Factor: 250 * time.Millisecond, 49 Max: 1 * time.Hour, 50 } 51 ca.GetCertRetryInterval = 50 * time.Millisecond 52 } 53 54 func checkLeafCert(t *testing.T, certBytes []byte, issuerName, cn, ou, org string, additionalDNSNames ...string) []*x509.Certificate { 55 certs, err := helpers.ParseCertificatesPEM(certBytes) 56 require.NoError(t, err) 57 require.NotEmpty(t, certs) 58 require.Equal(t, issuerName, certs[0].Issuer.CommonName) 59 require.Equal(t, cn, certs[0].Subject.CommonName) 60 require.Equal(t, []string{ou}, certs[0].Subject.OrganizationalUnit) 61 require.Equal(t, []string{org}, certs[0].Subject.Organization) 62 63 require.Len(t, certs[0].DNSNames, len(additionalDNSNames)+2) 64 for _, dnsName := range append(additionalDNSNames, cn, ou) { 65 require.Contains(t, certs[0].DNSNames, dnsName) 66 } 67 return certs 68 } 69 70 // TestMain runs every test in this file twice - once with a local CA and 71 // again with an external CA server. 72 func TestMain(m *testing.M) { 73 if status := m.Run(); status != 0 { 74 os.Exit(status) 75 } 76 77 cautils.External = true 78 os.Exit(m.Run()) 79 } 80 81 func TestCreateRootCASaveRootCA(t *testing.T) { 82 tempBaseDir, err := ioutil.TempDir("", "swarm-ca-test-") 83 assert.NoError(t, err) 84 defer os.RemoveAll(tempBaseDir) 85 86 paths := ca.NewConfigPaths(tempBaseDir) 87 88 rootCA, err := ca.CreateRootCA("rootCN") 89 assert.NoError(t, err) 90 91 err = ca.SaveRootCA(rootCA, paths.RootCA) 92 assert.NoError(t, err) 93 94 perms, err := permbits.Stat(paths.RootCA.Cert) 95 assert.NoError(t, err) 96 assert.False(t, perms.GroupWrite()) 97 assert.False(t, perms.OtherWrite()) 98 99 _, err = permbits.Stat(paths.RootCA.Key) 100 assert.True(t, os.IsNotExist(err)) 101 102 // ensure that the cert that was written is already normalized 103 written, err := ioutil.ReadFile(paths.RootCA.Cert) 104 assert.NoError(t, err) 105 assert.Equal(t, written, ca.NormalizePEMs(written)) 106 } 107 108 func TestCreateRootCAExpiry(t *testing.T) { 109 rootCA, err := ca.CreateRootCA("rootCN") 110 assert.NoError(t, err) 111 112 // Convert the certificate into an object to create a RootCA 113 parsedCert, err := helpers.ParseCertificatePEM(rootCA.Certs) 114 assert.NoError(t, err) 115 duration, err := time.ParseDuration(ca.RootCAExpiration) 116 assert.NoError(t, err) 117 assert.True(t, time.Now().Add(duration).AddDate(0, -1, 0).Before(parsedCert.NotAfter)) 118 } 119 120 func TestGetLocalRootCA(t *testing.T) { 121 tempBaseDir, err := ioutil.TempDir("", "swarm-ca-test-") 122 assert.NoError(t, err) 123 defer os.RemoveAll(tempBaseDir) 124 125 paths := ca.NewConfigPaths(tempBaseDir) 126 127 // First, try to load the local Root CA with the certificate missing. 128 _, err = ca.GetLocalRootCA(paths.RootCA) 129 assert.Equal(t, ca.ErrNoLocalRootCA, err) 130 131 // Create the local Root CA to ensure that we can reload it correctly. 132 rootCA, err := ca.CreateRootCA("rootCN") 133 assert.NoError(t, err) 134 s, err := rootCA.Signer() 135 assert.NoError(t, err) 136 err = ca.SaveRootCA(rootCA, paths.RootCA) 137 assert.NoError(t, err) 138 139 // No private key here 140 rootCA2, err := ca.GetLocalRootCA(paths.RootCA) 141 assert.NoError(t, err) 142 assert.Equal(t, rootCA.Certs, rootCA2.Certs) 143 _, err = rootCA2.Signer() 144 assert.Equal(t, err, ca.ErrNoValidSigner) 145 146 // write private key and assert we can load it and sign 147 assert.NoError(t, ioutil.WriteFile(paths.RootCA.Key, s.Key, os.FileMode(0600))) 148 rootCA3, err := ca.GetLocalRootCA(paths.RootCA) 149 assert.NoError(t, err) 150 assert.Equal(t, rootCA.Certs, rootCA3.Certs) 151 _, err = rootCA3.Signer() 152 assert.NoError(t, err) 153 154 // Try with a private key that does not match the CA cert public key. 155 privKey, err := ecdsa.GenerateKey(elliptic.P256(), cryptorand.Reader) 156 assert.NoError(t, err) 157 privKeyBytes, err := x509.MarshalECPrivateKey(privKey) 158 assert.NoError(t, err) 159 privKeyPem := pem.EncodeToMemory(&pem.Block{ 160 Type: "EC PRIVATE KEY", 161 Bytes: privKeyBytes, 162 }) 163 assert.NoError(t, ioutil.WriteFile(paths.RootCA.Key, privKeyPem, os.FileMode(0600))) 164 165 _, err = ca.GetLocalRootCA(paths.RootCA) 166 assert.EqualError(t, err, "certificate key mismatch") 167 } 168 169 func TestGetLocalRootCAInvalidCert(t *testing.T) { 170 tempBaseDir, err := ioutil.TempDir("", "swarm-ca-test-") 171 assert.NoError(t, err) 172 defer os.RemoveAll(tempBaseDir) 173 174 paths := ca.NewConfigPaths(tempBaseDir) 175 176 // Write some garbage to the CA cert 177 require.NoError(t, ioutil.WriteFile(paths.RootCA.Cert, []byte(`-----BEGIN CERTIFICATE-----\n 178 some random garbage\n 179 -----END CERTIFICATE-----`), 0644)) 180 181 _, err = ca.GetLocalRootCA(paths.RootCA) 182 require.Error(t, err) 183 } 184 185 func TestGetLocalRootCAInvalidKey(t *testing.T) { 186 tempBaseDir, err := ioutil.TempDir("", "swarm-ca-test-") 187 assert.NoError(t, err) 188 defer os.RemoveAll(tempBaseDir) 189 190 paths := ca.NewConfigPaths(tempBaseDir) 191 // Create the local Root CA to ensure that we can reload it correctly. 192 rootCA, err := ca.CreateRootCA("rootCN") 193 require.NoError(t, err) 194 require.NoError(t, ca.SaveRootCA(rootCA, paths.RootCA)) 195 196 // Write some garbage to the root key - this will cause the loading to fail 197 require.NoError(t, ioutil.WriteFile(paths.RootCA.Key, []byte(`-----BEGIN PRIVATE KEY-----\n 198 some random garbage\n 199 -----END PRIVATE KEY-----`), 0600)) 200 201 _, err = ca.GetLocalRootCA(paths.RootCA) 202 require.Error(t, err) 203 } 204 205 func TestParseValidateAndSignCSR(t *testing.T) { 206 rootCA, err := ca.CreateRootCA("rootCN") 207 assert.NoError(t, err) 208 209 csr, _, err := ca.GenerateNewCSR() 210 assert.NoError(t, err) 211 212 signedCert, err := rootCA.ParseValidateAndSignCSR(csr, "CN", "OU", "ORG") 213 assert.NoError(t, err) 214 assert.NotNil(t, signedCert) 215 216 assert.Len(t, checkLeafCert(t, signedCert, "rootCN", "CN", "OU", "ORG"), 1) 217 } 218 219 func TestParseValidateAndSignMaliciousCSR(t *testing.T) { 220 rootCA, err := ca.CreateRootCA("rootCN") 221 assert.NoError(t, err) 222 223 req := &cfcsr.CertificateRequest{ 224 Names: []cfcsr.Name{ 225 { 226 O: "maliciousOrg", 227 OU: "maliciousOU", 228 L: "maliciousLocality", 229 }, 230 }, 231 CN: "maliciousCN", 232 Hosts: []string{"docker.com"}, 233 KeyRequest: &cfcsr.BasicKeyRequest{A: "ecdsa", S: 256}, 234 } 235 236 csr, _, err := cfcsr.ParseRequest(req) 237 assert.NoError(t, err) 238 239 signedCert, err := rootCA.ParseValidateAndSignCSR(csr, "CN", "OU", "ORG") 240 assert.NoError(t, err) 241 assert.NotNil(t, signedCert) 242 243 assert.Len(t, checkLeafCert(t, signedCert, "rootCN", "CN", "OU", "ORG"), 1) 244 } 245 246 func TestGetRemoteCA(t *testing.T) { 247 tc := cautils.NewTestCA(t) 248 defer tc.Stop() 249 250 shaHash := sha256.New() 251 shaHash.Write(tc.RootCA.Certs) 252 md := shaHash.Sum(nil) 253 mdStr := hex.EncodeToString(md) 254 255 d, err := digest.Parse("sha256:" + mdStr) 256 require.NoError(t, err) 257 258 downloadedRootCA, err := ca.GetRemoteCA(tc.Context, d, tc.ConnBroker) 259 require.NoError(t, err) 260 require.Equal(t, downloadedRootCA.Certs, tc.RootCA.Certs) 261 262 // update the test CA to include a multi-certificate bundle as the root - the digest 263 // we use to verify with must be the digest of the whole bundle 264 tmpDir, err := ioutil.TempDir("", "GetRemoteCA") 265 require.NoError(t, err) 266 defer os.RemoveAll(tmpDir) 267 paths := ca.NewConfigPaths(tmpDir) 268 otherRootCA, err := ca.CreateRootCA("other") 269 require.NoError(t, err) 270 271 comboCertBundle := append(tc.RootCA.Certs, otherRootCA.Certs...) 272 s, err := tc.RootCA.Signer() 273 require.NoError(t, err) 274 require.NoError(t, tc.MemoryStore.Update(func(tx store.Tx) error { 275 cluster := store.GetCluster(tx, tc.Organization) 276 cluster.RootCA.CACert = comboCertBundle 277 cluster.RootCA.CAKey = s.Key 278 return store.UpdateCluster(tx, cluster) 279 })) 280 require.NoError(t, testutils.PollFunc(nil, func() error { 281 _, err := ca.GetRemoteCA(tc.Context, d, tc.ConnBroker) 282 if err == nil { 283 return fmt.Errorf("testca's rootca hasn't updated yet") 284 } 285 require.Contains(t, err.Error(), "remote CA does not match fingerprint") 286 return nil 287 })) 288 289 // If we provide the right digest, the root CA is updated and we can validate 290 // certs signed by either one 291 d = digest.FromBytes(comboCertBundle) 292 downloadedRootCA, err = ca.GetRemoteCA(tc.Context, d, tc.ConnBroker) 293 require.NoError(t, err) 294 require.Equal(t, comboCertBundle, downloadedRootCA.Certs) 295 require.Equal(t, 2, len(downloadedRootCA.Pool.Subjects())) 296 297 for _, rootCA := range []ca.RootCA{tc.RootCA, otherRootCA} { 298 krw := ca.NewKeyReadWriter(paths.Node, nil, nil) 299 _, _, err := rootCA.IssueAndSaveNewCertificates(krw, "cn", "ou", "org") 300 require.NoError(t, err) 301 302 certPEM, _, err := krw.Read() 303 require.NoError(t, err) 304 305 cert, err := helpers.ParseCertificatesPEM(certPEM) 306 require.NoError(t, err) 307 308 chains, err := cert[0].Verify(x509.VerifyOptions{ 309 Roots: downloadedRootCA.Pool, 310 }) 311 require.NoError(t, err) 312 require.Len(t, chains, 1) 313 } 314 } 315 316 func TestGetRemoteCAInvalidHash(t *testing.T) { 317 tc := cautils.NewTestCA(t) 318 defer tc.Stop() 319 320 _, err := ca.GetRemoteCA(tc.Context, "sha256:2d2f968475269f0dde5299427cf74348ee1d6115b95c6e3f283e5a4de8da445b", tc.ConnBroker) 321 assert.Error(t, err) 322 } 323 324 // returns the issuer as well as all the parsed certs returned from the request 325 func testRequestAndSaveNewCertificates(t *testing.T, tc *cautils.TestCA) (*ca.IssuerInfo, []*x509.Certificate) { 326 // Copy the current RootCA without the signer 327 rca := ca.RootCA{Certs: tc.RootCA.Certs, Pool: tc.RootCA.Pool} 328 tlsCert, issuerInfo, err := rca.RequestAndSaveNewCertificates(tc.Context, tc.KeyReadWriter, 329 ca.CertificateRequestConfig{ 330 Token: tc.ManagerToken, 331 ConnBroker: tc.ConnBroker, 332 }) 333 require.NoError(t, err) 334 require.NotNil(t, tlsCert) 335 require.NotNil(t, issuerInfo) 336 perms, err := permbits.Stat(tc.Paths.Node.Cert) 337 require.NoError(t, err) 338 require.False(t, perms.GroupWrite()) 339 require.False(t, perms.OtherWrite()) 340 341 certs, err := ioutil.ReadFile(tc.Paths.Node.Cert) 342 require.NoError(t, err) 343 require.Equal(t, certs, ca.NormalizePEMs(certs)) 344 345 // ensure that the same number of certs was written 346 parsedCerts, err := helpers.ParseCertificatesPEM(certs) 347 require.NoError(t, err) 348 return issuerInfo, parsedCerts 349 } 350 351 func TestRequestAndSaveNewCertificatesNoIntermediate(t *testing.T) { 352 t.Parallel() 353 354 tc := cautils.NewTestCA(t) 355 defer tc.Stop() 356 issuerInfo, parsedCerts := testRequestAndSaveNewCertificates(t, tc) 357 require.Len(t, parsedCerts, 1) 358 359 root, err := helpers.ParseCertificatePEM(tc.RootCA.Certs) 360 require.NoError(t, err) 361 require.Equal(t, root.RawSubject, issuerInfo.Subject) 362 } 363 364 func TestRequestAndSaveNewCertificatesWithIntermediates(t *testing.T) { 365 t.Parallel() 366 367 // use a RootCA with an intermediate 368 apiRootCA := api.RootCA{ 369 CACert: cautils.ECDSACertChain[2], 370 CAKey: cautils.ECDSACertChainKeys[2], 371 RootRotation: &api.RootRotation{ 372 CACert: cautils.ECDSACertChain[1], 373 CAKey: cautils.ECDSACertChainKeys[1], 374 CrossSignedCACert: concat([]byte(" "), cautils.ECDSACertChain[1]), 375 }, 376 } 377 tempdir, err := ioutil.TempDir("", "test-request-and-save-new-certificates") 378 require.NoError(t, err) 379 defer os.RemoveAll(tempdir) 380 381 tc := cautils.NewTestCAFromAPIRootCA(t, tempdir, apiRootCA, nil) 382 defer tc.Stop() 383 issuerInfo, parsedCerts := testRequestAndSaveNewCertificates(t, tc) 384 require.Len(t, parsedCerts, 2) 385 386 intermediate, err := helpers.ParseCertificatePEM(tc.RootCA.Intermediates) 387 require.NoError(t, err) 388 require.Equal(t, intermediate, parsedCerts[1]) 389 require.Equal(t, intermediate.RawSubject, issuerInfo.Subject) 390 require.Equal(t, intermediate.RawSubjectPublicKeyInfo, issuerInfo.PublicKey) 391 } 392 393 func TestRequestAndSaveNewCertificatesWithKEKUpdate(t *testing.T) { 394 t.Parallel() 395 396 tc := cautils.NewTestCA(t) 397 defer tc.Stop() 398 399 // Copy the current RootCA without the signer 400 rca := ca.RootCA{Certs: tc.RootCA.Certs, Pool: tc.RootCA.Pool} 401 402 unencryptedKeyReader := ca.NewKeyReadWriter(tc.Paths.Node, nil, nil) 403 404 // key for the manager and worker are both unencrypted 405 for _, token := range []string{tc.ManagerToken, tc.WorkerToken} { 406 _, _, err := rca.RequestAndSaveNewCertificates(tc.Context, tc.KeyReadWriter, 407 ca.CertificateRequestConfig{ 408 Token: token, 409 ConnBroker: tc.ConnBroker, 410 }) 411 require.NoError(t, err) 412 413 // there was no encryption config in the remote, so the key should be unencrypted 414 _, _, err = unencryptedKeyReader.Read() 415 require.NoError(t, err) 416 } 417 418 // If there is a different kek in the remote store, when TLS certs are renewed the new key will 419 // be encrypted with that kek 420 require.NoError(t, tc.MemoryStore.Update(func(tx store.Tx) error { 421 cluster := store.GetCluster(tx, tc.Organization) 422 cluster.Spec.EncryptionConfig.AutoLockManagers = true 423 cluster.UnlockKeys = []*api.EncryptionKey{{ 424 Subsystem: ca.ManagerRole, 425 Key: []byte("kek!"), 426 }} 427 return store.UpdateCluster(tx, cluster) 428 })) 429 require.NoError(t, os.RemoveAll(tc.Paths.Node.Cert)) 430 require.NoError(t, os.RemoveAll(tc.Paths.Node.Key)) 431 432 // key for the manager will be encrypted, but certs for the worker will not be 433 for _, token := range []string{tc.ManagerToken, tc.WorkerToken} { 434 _, _, err := rca.RequestAndSaveNewCertificates(tc.Context, tc.KeyReadWriter, 435 ca.CertificateRequestConfig{ 436 Token: token, 437 ConnBroker: tc.ConnBroker, 438 }) 439 require.NoError(t, err) 440 441 // there was no encryption config in the remote, so the key should be unencrypted 442 _, _, err = unencryptedKeyReader.Read() 443 444 if token == tc.ManagerToken { 445 require.Error(t, err) 446 _, _, err = ca.NewKeyReadWriter(tc.Paths.Node, []byte("kek!"), nil).Read() 447 require.NoError(t, err) 448 } else { 449 require.NoError(t, err) 450 } 451 } 452 } 453 454 // returns the issuer of the issued certificate and the parsed certs of the issued certificate 455 func testIssueAndSaveNewCertificates(t *testing.T, rca *ca.RootCA) { 456 tempdir, err := ioutil.TempDir("", "test-issue-and-save-new-certificates") 457 require.NoError(t, err) 458 defer os.RemoveAll(tempdir) 459 paths := ca.NewConfigPaths(tempdir) 460 krw := ca.NewKeyReadWriter(paths.Node, nil, nil) 461 462 var issuer *x509.Certificate 463 if len(rca.Intermediates) > 0 { 464 issuer, err = helpers.ParseCertificatePEM(rca.Intermediates) 465 require.NoError(t, err) 466 } else { 467 issuer, err = helpers.ParseCertificatePEM(rca.Certs) 468 require.NoError(t, err) 469 } 470 471 // Test the creation of a manager and worker certificate 472 for _, role := range []string{ca.ManagerRole, ca.WorkerRole} { 473 var additionalNames []string 474 if role == ca.ManagerRole { 475 additionalNames = []string{ca.CARole} 476 } 477 478 cert, issuerInfo, err := rca.IssueAndSaveNewCertificates(krw, "CN", role, "org") 479 require.NoError(t, err) 480 require.NotNil(t, cert) 481 require.Equal(t, issuer.RawSubjectPublicKeyInfo, issuerInfo.PublicKey) 482 require.Equal(t, issuer.RawSubject, issuerInfo.Subject) 483 perms, err := permbits.Stat(paths.Node.Cert) 484 require.NoError(t, err) 485 require.False(t, perms.GroupWrite()) 486 require.False(t, perms.OtherWrite()) 487 488 certBytes, err := ioutil.ReadFile(paths.Node.Cert) 489 require.NoError(t, err) 490 parsed := checkLeafCert(t, certBytes, issuer.Subject.CommonName, "CN", role, "org", additionalNames...) 491 if len(rca.Intermediates) > 0 { 492 require.Len(t, parsed, 2) 493 require.Equal(t, parsed[1], issuer) 494 } else { 495 require.Len(t, parsed, 1) 496 } 497 } 498 } 499 500 func TestIssueAndSaveNewCertificatesNoIntermediates(t *testing.T) { 501 if cautils.External { 502 return // this does not use the test CA at all 503 } 504 rca, err := ca.CreateRootCA("rootCN") 505 require.NoError(t, err) 506 testIssueAndSaveNewCertificates(t, &rca) 507 } 508 509 func TestIssueAndSaveNewCertificatesWithIntermediates(t *testing.T) { 510 if cautils.External { 511 return // this does not use the test CA at all 512 } 513 rca, err := ca.NewRootCA(cautils.ECDSACertChain[2], cautils.ECDSACertChain[1], cautils.ECDSACertChainKeys[1], 514 ca.DefaultNodeCertExpiration, cautils.ECDSACertChain[1]) 515 require.NoError(t, err) 516 testIssueAndSaveNewCertificates(t, &rca) 517 } 518 519 func TestGetRemoteSignedCertificate(t *testing.T) { 520 tc := cautils.NewTestCA(t) 521 defer tc.Stop() 522 523 // Create a new CSR to be signed 524 csr, _, err := ca.GenerateNewCSR() 525 assert.NoError(t, err) 526 527 certs, err := ca.GetRemoteSignedCertificate(tc.Context, csr, tc.RootCA.Pool, 528 ca.CertificateRequestConfig{ 529 Token: tc.ManagerToken, 530 ConnBroker: tc.ConnBroker, 531 }) 532 assert.NoError(t, err) 533 assert.NotNil(t, certs) 534 535 // Test the expiration for a manager certificate 536 parsedCerts, err := helpers.ParseCertificatesPEM(certs) 537 assert.NoError(t, err) 538 assert.Len(t, parsedCerts, 1) 539 assert.True(t, time.Now().Add(ca.DefaultNodeCertExpiration).AddDate(0, 0, -1).Before(parsedCerts[0].NotAfter)) 540 assert.True(t, time.Now().Add(ca.DefaultNodeCertExpiration).AddDate(0, 0, 1).After(parsedCerts[0].NotAfter)) 541 assert.Equal(t, parsedCerts[0].Subject.OrganizationalUnit[0], ca.ManagerRole) 542 543 // Test the expiration for an worker certificate 544 certs, err = ca.GetRemoteSignedCertificate(tc.Context, csr, tc.RootCA.Pool, 545 ca.CertificateRequestConfig{ 546 Token: tc.WorkerToken, 547 ConnBroker: tc.ConnBroker, 548 }) 549 assert.NoError(t, err) 550 assert.NotNil(t, certs) 551 parsedCerts, err = helpers.ParseCertificatesPEM(certs) 552 assert.NoError(t, err) 553 assert.Len(t, parsedCerts, 1) 554 assert.True(t, time.Now().Add(ca.DefaultNodeCertExpiration).AddDate(0, 0, -1).Before(parsedCerts[0].NotAfter)) 555 assert.True(t, time.Now().Add(ca.DefaultNodeCertExpiration).AddDate(0, 0, 1).After(parsedCerts[0].NotAfter)) 556 assert.Equal(t, parsedCerts[0].Subject.OrganizationalUnit[0], ca.WorkerRole) 557 } 558 559 func TestGetRemoteSignedCertificateNodeInfo(t *testing.T) { 560 tc := cautils.NewTestCA(t) 561 defer tc.Stop() 562 563 // Create a new CSR to be signed 564 csr, _, err := ca.GenerateNewCSR() 565 assert.NoError(t, err) 566 567 cert, err := ca.GetRemoteSignedCertificate(tc.Context, csr, tc.RootCA.Pool, 568 ca.CertificateRequestConfig{ 569 Token: tc.WorkerToken, 570 ConnBroker: tc.ConnBroker, 571 }) 572 assert.NoError(t, err) 573 assert.NotNil(t, cert) 574 } 575 576 // A CA Server implementation that doesn't actually sign anything - something else 577 // will have to update the memory store to have a valid value for a node 578 type nonSigningCAServer struct { 579 tc *cautils.TestCA 580 server *grpc.Server 581 addr string 582 nodeStatusCalled int64 583 } 584 585 func newNonSigningCAServer(t *testing.T, tc *cautils.TestCA) *nonSigningCAServer { 586 secConfig, err := tc.NewNodeConfig(ca.ManagerRole) 587 require.NoError(t, err) 588 serverOpts := []grpc.ServerOption{grpc.Creds(secConfig.ServerTLSCreds)} 589 grpcServer := grpc.NewServer(serverOpts...) 590 l, err := net.Listen("tcp", "127.0.0.1:0") 591 require.NoError(t, err) 592 593 n := &nonSigningCAServer{ 594 tc: tc, 595 addr: l.Addr().String(), 596 server: grpcServer, 597 } 598 599 api.RegisterNodeCAServer(grpcServer, n) 600 go grpcServer.Serve(l) 601 return n 602 } 603 604 func (n *nonSigningCAServer) stop(t *testing.T) { 605 n.server.Stop() 606 } 607 608 func (n *nonSigningCAServer) getConnBroker() *connectionbroker.Broker { 609 return connectionbroker.New(remotes.NewRemotes(api.Peer{Addr: n.addr})) 610 } 611 612 // only returns the status in the store 613 func (n *nonSigningCAServer) NodeCertificateStatus(ctx context.Context, request *api.NodeCertificateStatusRequest) (*api.NodeCertificateStatusResponse, error) { 614 atomic.AddInt64(&n.nodeStatusCalled, 1) 615 for { 616 var node *api.Node 617 n.tc.MemoryStore.View(func(tx store.ReadTx) { 618 node = store.GetNode(tx, request.NodeID) 619 }) 620 if node != nil && node.Certificate.Status.State == api.IssuanceStateIssued { 621 return &api.NodeCertificateStatusResponse{ 622 Status: &node.Certificate.Status, 623 Certificate: &node.Certificate, 624 }, nil 625 } 626 select { 627 case <-ctx.Done(): 628 return nil, ctx.Err() 629 case <-time.After(500 * time.Millisecond): 630 } 631 } 632 } 633 634 func (n *nonSigningCAServer) IssueNodeCertificate(ctx context.Context, request *api.IssueNodeCertificateRequest) (*api.IssueNodeCertificateResponse, error) { 635 nodeID := identity.NewID() 636 role := api.NodeRoleWorker 637 if n.tc.ManagerToken == request.Token { 638 role = api.NodeRoleManager 639 } 640 641 // Create a new node 642 err := n.tc.MemoryStore.Update(func(tx store.Tx) error { 643 node := &api.Node{ 644 Role: role, 645 ID: nodeID, 646 Certificate: api.Certificate{ 647 CSR: request.CSR, 648 CN: nodeID, 649 Role: role, 650 Status: api.IssuanceStatus{ 651 State: api.IssuanceStatePending, 652 }, 653 }, 654 Spec: api.NodeSpec{ 655 DesiredRole: role, 656 Membership: api.NodeMembershipAccepted, 657 Availability: request.Availability, 658 }, 659 } 660 661 return store.CreateNode(tx, node) 662 }) 663 if err != nil { 664 return nil, err 665 } 666 return &api.IssueNodeCertificateResponse{ 667 NodeID: nodeID, 668 NodeMembership: api.NodeMembershipAccepted, 669 }, nil 670 } 671 672 func TestGetRemoteSignedCertificateWithPending(t *testing.T) { 673 t.Parallel() 674 if cautils.External { 675 // we don't actually need an external signing server, since we're faking a CA server which doesn't really sign 676 return 677 } 678 679 tc := cautils.NewTestCA(t) 680 defer tc.Stop() 681 require.NoError(t, tc.CAServer.Stop()) 682 683 // Create a new CSR to be signed 684 csr, _, err := ca.GenerateNewCSR() 685 require.NoError(t, err) 686 687 updates, cancel := state.Watch(tc.MemoryStore.WatchQueue(), api.EventCreateNode{}) 688 defer cancel() 689 690 fakeCAServer := newNonSigningCAServer(t, tc) 691 defer fakeCAServer.stop(t) 692 693 completed := make(chan error) 694 defer close(completed) 695 go func() { 696 _, err := ca.GetRemoteSignedCertificate(tc.Context, csr, tc.RootCA.Pool, 697 ca.CertificateRequestConfig{ 698 Token: tc.WorkerToken, 699 ConnBroker: fakeCAServer.getConnBroker(), 700 // ensure the RPC call to get state is cancelled after 500 milliseconds 701 NodeCertificateStatusRequestTimeout: 500 * time.Millisecond, 702 }) 703 completed <- err 704 }() 705 706 var node *api.Node 707 // wait for a new node to show up 708 for node == nil { 709 event := <-updates // we want to skip the first node, which is the test CA 710 n := event.(api.EventCreateNode).Node.Copy() 711 if n.Certificate.Status.State == api.IssuanceStatePending { 712 node = n 713 } 714 } 715 716 // wait for the calls to NodeCertificateStatus to begin on the first signing server before we start timing 717 require.NoError(t, testutils.PollFuncWithTimeout(nil, func() error { 718 if atomic.LoadInt64(&fakeCAServer.nodeStatusCalled) == 0 { 719 return fmt.Errorf("waiting for NodeCertificateStatus to be called") 720 } 721 return nil 722 }, time.Second*2)) 723 724 // wait for 2.5 seconds and ensure that GetRemoteSignedCertificate has not returned with an error yet - 725 // the first attempt to get the certificate status should have timed out after 500 milliseconds, but 726 // it should have tried to poll again. Add a few seconds for fudge time to make sure it's actually 727 // still polling. 728 select { 729 case <-completed: 730 require.FailNow(t, "GetRemoteSignedCertificate should wait at least 500 milliseconds") 731 case <-time.After(2500 * time.Millisecond): 732 // good, it's still polling so we can proceed with the test 733 } 734 require.True(t, atomic.LoadInt64(&fakeCAServer.nodeStatusCalled) > 1, "expected NodeCertificateStatus to have been polled more than once") 735 736 // Directly update the status of the store 737 err = tc.MemoryStore.Update(func(tx store.Tx) error { 738 node.Certificate.Status.State = api.IssuanceStateIssued 739 return store.UpdateNode(tx, node) 740 }) 741 require.NoError(t, err) 742 743 // Make sure GetRemoteSignedCertificate didn't return an error 744 require.NoError(t, <-completed) 745 746 // make sure if we time out the GetRemoteSignedCertificate call, it cancels immediately and doesn't keep 747 // polling the status 748 go func() { 749 ctx, cancel := context.WithTimeout(tc.Context, 1*time.Second) 750 defer cancel() 751 _, err := ca.GetRemoteSignedCertificate(ctx, csr, tc.RootCA.Pool, 752 ca.CertificateRequestConfig{ 753 Token: tc.WorkerToken, 754 ConnBroker: fakeCAServer.getConnBroker(), 755 }) 756 completed <- err 757 }() 758 759 // wait for 3 seconds and ensure that GetRemoteSignedCertificate has returned with a context DeadlineExceeded 760 // error - it should have returned after 1 second, but add some more for rudge time. 761 select { 762 case err = <-completed: 763 s, _ := status.FromError(err) 764 require.Equal(t, s.Code(), codes.DeadlineExceeded) 765 case <-time.After(3 * time.Second): 766 require.FailNow(t, "GetRemoteSignedCertificate should have been canceled after 1 second, and it has been 3") 767 } 768 } 769 770 // fake remotes interface that just selects the remotes in order 771 type fakeRemotes struct { 772 mu sync.Mutex 773 peers []api.Peer 774 } 775 776 func (f *fakeRemotes) Weights() map[api.Peer]int { 777 panic("this is not called") 778 } 779 780 func (f *fakeRemotes) Select(...string) (api.Peer, error) { 781 f.mu.Lock() 782 defer f.mu.Unlock() 783 if len(f.peers) > 0 { 784 return f.peers[0], nil 785 } 786 return api.Peer{}, fmt.Errorf("no more peers") 787 } 788 789 func (f *fakeRemotes) Observe(peer api.Peer, weight int) { 790 panic("this is not called") 791 } 792 793 // just removes a peer if the weight is negative 794 func (f *fakeRemotes) ObserveIfExists(peer api.Peer, weight int) { 795 f.mu.Lock() 796 defer f.mu.Unlock() 797 if weight < 0 { 798 var newPeers []api.Peer 799 for _, p := range f.peers { 800 if p != peer { 801 newPeers = append(newPeers, p) 802 } 803 } 804 f.peers = newPeers 805 } 806 } 807 808 func (f *fakeRemotes) Remove(addrs ...api.Peer) { 809 panic("this is not called") 810 } 811 812 var _ remotes.Remotes = &fakeRemotes{} 813 814 // On connection errors, so long as they happen after IssueNodeCertificate is successful, GetRemoteSignedCertificate 815 // tries to open a new connection and continue polling for NodeCertificateStatus. If there are no more connections, 816 // then fail. 817 func TestGetRemoteSignedCertificateConnectionErrors(t *testing.T) { 818 t.Parallel() 819 if cautils.External { 820 // we don't actually need an external signing server, since we're faking a CA server which doesn't really sign 821 return 822 } 823 824 tc := cautils.NewTestCA(t) 825 defer tc.Stop() 826 require.NoError(t, tc.CAServer.Stop()) 827 828 // Create a new CSR to be signed 829 csr, _, err := ca.GenerateNewCSR() 830 require.NoError(t, err) 831 832 // create 2 CA servers referencing the same memory store, so we can have multiple connections 833 fakeSigningServers := []*nonSigningCAServer{newNonSigningCAServer(t, tc), newNonSigningCAServer(t, tc)} 834 defer fakeSigningServers[0].stop(t) 835 defer fakeSigningServers[1].stop(t) 836 multiBroker := connectionbroker.New(&fakeRemotes{ 837 peers: []api.Peer{ 838 {Addr: fakeSigningServers[0].addr}, 839 {Addr: fakeSigningServers[1].addr}, 840 }, 841 }) 842 843 completed, done := make(chan error), make(chan struct{}) 844 defer close(completed) 845 defer close(done) 846 go func() { 847 _, err := ca.GetRemoteSignedCertificate(tc.Context, csr, tc.RootCA.Pool, 848 ca.CertificateRequestConfig{ 849 Token: tc.WorkerToken, 850 ConnBroker: multiBroker, 851 }) 852 select { 853 case <-done: 854 case completed <- err: 855 } 856 }() 857 858 // wait for the calls to NodeCertificateStatus to begin on the first signing server 859 require.NoError(t, testutils.PollFuncWithTimeout(nil, func() error { 860 if atomic.LoadInt64(&fakeSigningServers[0].nodeStatusCalled) == 0 { 861 return fmt.Errorf("waiting for NodeCertificateStatus to be called") 862 } 863 return nil 864 }, time.Second*2)) 865 866 // stop 1 server, because it will have been the remote GetRemoteSignedCertificate first connected to, and ensure 867 // that GetRemoteSignedCertificate is still going 868 fakeSigningServers[0].stop(t) 869 select { 870 case <-completed: 871 require.FailNow(t, "GetRemoteSignedCertificate should still be going after 2.5 seconds") 872 case <-time.After(2500 * time.Millisecond): 873 // good, it's still polling so we can proceed with the test 874 } 875 876 // wait for the calls to NodeCertificateStatus to begin on the second signing server 877 require.NoError(t, testutils.PollFuncWithTimeout(nil, func() error { 878 if atomic.LoadInt64(&fakeSigningServers[1].nodeStatusCalled) == 0 { 879 return fmt.Errorf("waiting for NodeCertificateStatus to be called") 880 } 881 return nil 882 }, time.Second*2)) 883 884 // kill the last server - this should cause GetRemoteSignedCertificate to fail because there are no more peers 885 fakeSigningServers[1].stop(t) 886 // wait for 5 seconds and ensure that GetRemoteSignedCertificate has returned with an error. 887 select { 888 case err = <-completed: 889 require.Contains(t, err.Error(), "no more peers") 890 case <-time.After(5 * time.Second): 891 require.FailNow(t, "GetRemoteSignedCertificate should errored after 5 seconds") 892 } 893 894 // calling GetRemoteSignedCertificate with a connection that doesn't work with IssueNodeCertificate will fail 895 // immediately without retrying with a new connection 896 fakeSigningServers[1] = newNonSigningCAServer(t, tc) 897 defer fakeSigningServers[1].stop(t) 898 multiBroker = connectionbroker.New(&fakeRemotes{ 899 peers: []api.Peer{ 900 {Addr: fakeSigningServers[0].addr}, 901 {Addr: fakeSigningServers[1].addr}, 902 }, 903 }) 904 _, err = ca.GetRemoteSignedCertificate(tc.Context, csr, tc.RootCA.Pool, 905 ca.CertificateRequestConfig{ 906 Token: tc.WorkerToken, 907 ConnBroker: multiBroker, 908 }) 909 require.Error(t, err) 910 } 911 912 func TestNewRootCA(t *testing.T) { 913 for _, pair := range []struct{ cert, key []byte }{ 914 {cert: cautils.ECDSA256SHA256Cert, key: cautils.ECDSA256Key}, 915 {cert: cautils.RSA2048SHA256Cert, key: cautils.RSA2048Key}, 916 } { 917 rootCA, err := ca.NewRootCA(pair.cert, pair.cert, pair.key, ca.DefaultNodeCertExpiration, nil) 918 require.NoError(t, err, string(pair.key)) 919 require.Equal(t, pair.cert, rootCA.Certs) 920 s, err := rootCA.Signer() 921 require.NoError(t, err) 922 require.Equal(t, pair.key, s.Key) 923 _, err = rootCA.Digest.Verifier().Write(pair.cert) 924 require.NoError(t, err) 925 } 926 } 927 928 func TestNewRootCABundle(t *testing.T) { 929 tempBaseDir, err := ioutil.TempDir("", "swarm-ca-test-") 930 assert.NoError(t, err) 931 defer os.RemoveAll(tempBaseDir) 932 933 paths := ca.NewConfigPaths(tempBaseDir) 934 935 // make one rootCA 936 firstRootCA, err := ca.CreateRootCA("rootCN1") 937 assert.NoError(t, err) 938 939 // make a second root CA 940 secondRootCA, err := ca.CreateRootCA("rootCN2") 941 assert.NoError(t, err) 942 s, err := firstRootCA.Signer() 943 require.NoError(t, err) 944 945 // Overwrite the bytes of the second Root CA with the bundle, creating a valid 2 cert bundle 946 bundle := append(firstRootCA.Certs, secondRootCA.Certs...) 947 err = ioutil.WriteFile(paths.RootCA.Cert, bundle, 0644) 948 assert.NoError(t, err) 949 950 newRootCA, err := ca.NewRootCA(bundle, firstRootCA.Certs, s.Key, ca.DefaultNodeCertExpiration, nil) 951 assert.NoError(t, err) 952 assert.Equal(t, bundle, newRootCA.Certs) 953 assert.Equal(t, 2, len(newRootCA.Pool.Subjects())) 954 955 // If I use newRootCA's IssueAndSaveNewCertificates to sign certs, I'll get the correct CA in the chain 956 kw := ca.NewKeyReadWriter(paths.Node, nil, nil) 957 _, _, err = newRootCA.IssueAndSaveNewCertificates(kw, "CN", "OU", "ORG") 958 assert.NoError(t, err) 959 960 certBytes, err := ioutil.ReadFile(paths.Node.Cert) 961 assert.NoError(t, err) 962 assert.Len(t, checkLeafCert(t, certBytes, "rootCN1", "CN", "OU", "ORG"), 1) 963 } 964 965 func TestNewRootCANonDefaultExpiry(t *testing.T) { 966 rootCA, err := ca.CreateRootCA("rootCN") 967 assert.NoError(t, err) 968 s, err := rootCA.Signer() 969 require.NoError(t, err) 970 971 newRootCA, err := ca.NewRootCA(rootCA.Certs, rootCA.Certs, s.Key, 1*time.Hour, nil) 972 assert.NoError(t, err) 973 974 // Create and sign a new CSR 975 csr, _, err := ca.GenerateNewCSR() 976 assert.NoError(t, err) 977 cert, err := newRootCA.ParseValidateAndSignCSR(csr, "CN", ca.ManagerRole, "ORG") 978 assert.NoError(t, err) 979 980 parsedCerts, err := helpers.ParseCertificatesPEM(cert) 981 assert.NoError(t, err) 982 assert.Len(t, parsedCerts, 1) 983 assert.True(t, time.Now().Add(time.Minute*59).Before(parsedCerts[0].NotAfter)) 984 assert.True(t, time.Now().Add(time.Hour).Add(time.Minute).After(parsedCerts[0].NotAfter)) 985 986 // Sign the same CSR again, this time with a 59 Minute expiration RootCA (under the 60 minute minimum). 987 // This should use the default of 3 months 988 newRootCA, err = ca.NewRootCA(rootCA.Certs, rootCA.Certs, s.Key, 59*time.Minute, nil) 989 assert.NoError(t, err) 990 991 cert, err = newRootCA.ParseValidateAndSignCSR(csr, "CN", ca.ManagerRole, "ORG") 992 assert.NoError(t, err) 993 994 parsedCerts, err = helpers.ParseCertificatesPEM(cert) 995 assert.NoError(t, err) 996 assert.Len(t, parsedCerts, 1) 997 assert.True(t, time.Now().Add(ca.DefaultNodeCertExpiration).AddDate(0, 0, -1).Before(parsedCerts[0].NotAfter)) 998 assert.True(t, time.Now().Add(ca.DefaultNodeCertExpiration).AddDate(0, 0, 1).After(parsedCerts[0].NotAfter)) 999 } 1000 1001 type invalidNewRootCATestCase struct { 1002 roots, cert, key, intermediates []byte 1003 errorStr string 1004 } 1005 1006 func TestNewRootCAInvalidCertAndKeys(t *testing.T) { 1007 now := time.Now() 1008 1009 expiredIntermediate := cautils.ReDateCert(t, cautils.ECDSACertChain[1], 1010 cautils.ECDSACertChain[2], cautils.ECDSACertChainKeys[2], now.Add(-10*time.Hour), now.Add(-1*time.Minute)) 1011 notYetValidIntermediate := cautils.ReDateCert(t, cautils.ECDSACertChain[1], 1012 cautils.ECDSACertChain[2], cautils.ECDSACertChainKeys[2], now.Add(time.Hour), now.Add(2*time.Hour)) 1013 1014 certChainRootCA, err := ca.NewRootCA(cautils.ECDSACertChain[2], cautils.ECDSACertChain[2], cautils.ECDSACertChainKeys[2], 1015 ca.DefaultNodeCertExpiration, nil) 1016 require.NoError(t, err) 1017 1018 cert, _, _ := cautils.CreateRootCertAndKey("alternateIntermediate") 1019 alternateIntermediate, err := certChainRootCA.CrossSignCACertificate(cert) 1020 require.NoError(t, err) 1021 1022 invalids := []invalidNewRootCATestCase{ 1023 // invalid root or signer cert 1024 { 1025 roots: []byte("malformed"), 1026 cert: cautils.ECDSA256SHA256Cert, 1027 key: cautils.ECDSA256Key, 1028 errorStr: "Failed to decode certificate", 1029 }, 1030 { 1031 roots: cautils.ECDSA256SHA256Cert, 1032 cert: []byte("malformed"), 1033 key: cautils.ECDSA256Key, 1034 errorStr: "Failed to decode certificate", 1035 }, 1036 { 1037 roots: []byte(" "), 1038 cert: cautils.ECDSA256SHA256Cert, 1039 key: cautils.ECDSA256Key, 1040 errorStr: "no valid root CA certificates found", 1041 }, 1042 { 1043 roots: cautils.ECDSA256SHA256Cert, 1044 cert: []byte(" "), 1045 key: cautils.ECDSA256Key, 1046 errorStr: "no valid signing CA certificates found", 1047 }, 1048 { 1049 roots: cautils.NotYetValidCert, 1050 cert: cautils.ECDSA256SHA256Cert, 1051 key: cautils.ECDSA256Key, 1052 errorStr: "not yet valid", 1053 }, 1054 { 1055 roots: cautils.ECDSA256SHA256Cert, 1056 cert: cautils.NotYetValidCert, 1057 key: cautils.NotYetValidKey, 1058 errorStr: "not yet valid", 1059 }, 1060 { 1061 roots: cautils.ExpiredCert, 1062 cert: cautils.ECDSA256SHA256Cert, 1063 key: cautils.ECDSA256Key, 1064 errorStr: "expired", 1065 }, 1066 { 1067 roots: cautils.ExpiredCert, 1068 cert: cautils.ECDSA256SHA256Cert, 1069 key: cautils.ECDSA256Key, 1070 errorStr: "expired", 1071 }, 1072 { 1073 roots: cautils.RSA2048SHA1Cert, 1074 cert: cautils.ECDSA256SHA256Cert, 1075 key: cautils.ECDSA256Key, 1076 errorStr: "unsupported signature algorithm", 1077 }, 1078 { 1079 roots: cautils.ECDSA256SHA256Cert, 1080 cert: cautils.RSA2048SHA1Cert, 1081 key: cautils.RSA2048Key, 1082 errorStr: "unsupported signature algorithm", 1083 }, 1084 { 1085 roots: cautils.ECDSA256SHA256Cert, 1086 cert: cautils.ECDSA256SHA1Cert, 1087 key: cautils.ECDSA256Key, 1088 errorStr: "unsupported signature algorithm", 1089 }, 1090 { 1091 roots: cautils.ECDSA256SHA1Cert, 1092 cert: cautils.ECDSA256SHA256Cert, 1093 key: cautils.ECDSA256Key, 1094 errorStr: "unsupported signature algorithm", 1095 }, 1096 { 1097 roots: cautils.ECDSA256SHA256Cert, 1098 cert: cautils.DSA2048Cert, 1099 key: cautils.DSA2048Key, 1100 errorStr: "unsupported signature algorithm", 1101 }, 1102 { 1103 roots: cautils.DSA2048Cert, 1104 cert: cautils.ECDSA256SHA256Cert, 1105 key: cautils.ECDSA256Key, 1106 errorStr: "unsupported signature algorithm", 1107 }, 1108 // invalid signer 1109 { 1110 roots: cautils.ECDSA256SHA256Cert, 1111 cert: cautils.ECDSA256SHA256Cert, 1112 key: []byte("malformed"), 1113 errorStr: "malformed private key", 1114 }, 1115 { 1116 roots: cautils.RSA1024Cert, 1117 cert: cautils.RSA1024Cert, 1118 key: cautils.RSA1024Key, 1119 errorStr: "unsupported RSA key parameters", 1120 }, 1121 { 1122 roots: cautils.ECDSA224Cert, 1123 cert: cautils.ECDSA224Cert, 1124 key: cautils.ECDSA224Key, 1125 errorStr: "unsupported ECDSA key parameters", 1126 }, 1127 { 1128 roots: cautils.ECDSA256SHA256Cert, 1129 cert: cautils.ECDSA256SHA256Cert, 1130 key: cautils.ECDSA224Key, 1131 errorStr: "certificate key mismatch", 1132 }, 1133 { 1134 roots: cautils.ECDSA256SHA256Cert, 1135 cert: cautils.ECDSACertChain[1], 1136 key: cautils.ECDSACertChainKeys[1], 1137 errorStr: "unknown authority", // signer cert doesn't chain up to the root 1138 }, 1139 // invalid intermediates 1140 { 1141 roots: cautils.ECDSACertChain[2], 1142 cert: cautils.ECDSACertChain[1], 1143 key: cautils.ECDSACertChainKeys[1], 1144 intermediates: []byte("malformed"), 1145 errorStr: "Failed to decode certificate", 1146 }, 1147 { 1148 roots: cautils.ECDSACertChain[2], 1149 cert: cautils.ECDSACertChain[1], 1150 key: cautils.ECDSACertChainKeys[1], 1151 intermediates: expiredIntermediate, 1152 errorStr: "expired", 1153 }, 1154 { 1155 roots: cautils.ECDSACertChain[2], 1156 cert: cautils.ECDSACertChain[1], 1157 key: cautils.ECDSACertChainKeys[1], 1158 intermediates: notYetValidIntermediate, 1159 errorStr: "expired", 1160 }, 1161 { 1162 roots: cautils.ECDSACertChain[2], 1163 cert: cautils.ECDSACertChain[1], 1164 key: cautils.ECDSACertChainKeys[1], 1165 intermediates: append(cautils.ECDSACertChain[1], cautils.ECDSA256SHA256Cert...), 1166 errorStr: "do not form a chain", 1167 }, 1168 { 1169 roots: cautils.ECDSACertChain[2], 1170 cert: cautils.ECDSACertChain[1], 1171 key: cautils.ECDSACertChainKeys[1], 1172 intermediates: cautils.ECDSA256SHA256Cert, 1173 errorStr: "unknown authority", // intermediates don't chain up to root 1174 }, 1175 { 1176 roots: cautils.ECDSACertChain[2], 1177 cert: cautils.ECDSACertChain[1], 1178 key: cautils.ECDSACertChainKeys[1], 1179 intermediates: alternateIntermediate, 1180 errorStr: "the first intermediate must have the same subject and public key as the signing cert", 1181 }, 1182 } 1183 1184 for i, invalid := range invalids { 1185 _, err := ca.NewRootCA(invalid.roots, invalid.cert, invalid.key, ca.DefaultNodeCertExpiration, invalid.intermediates) 1186 require.Error(t, err, fmt.Sprintf("expected error containing: \"%s\", test case (%d)", invalid.errorStr, i)) 1187 require.Contains(t, err.Error(), invalid.errorStr, fmt.Sprintf("%d", i)) 1188 } 1189 } 1190 1191 func TestRootCAWithCrossSignedIntermediates(t *testing.T) { 1192 tempdir, err := ioutil.TempDir("", "swarm-ca-test-") 1193 require.NoError(t, err) 1194 defer os.RemoveAll(tempdir) 1195 1196 // re-generate the intermediate to be a self-signed root, and use that as the second root 1197 parsedKey, err := helpers.ParsePrivateKeyPEM(cautils.ECDSACertChainKeys[1]) 1198 require.NoError(t, err) 1199 parsedIntermediate, err := helpers.ParseCertificatePEM(cautils.ECDSACertChain[1]) 1200 require.NoError(t, err) 1201 fauxRootDER, err := x509.CreateCertificate(cryptorand.Reader, parsedIntermediate, parsedIntermediate, parsedKey.Public(), parsedKey) 1202 require.NoError(t, err) 1203 fauxRootCert := pem.EncodeToMemory(&pem.Block{ 1204 Type: "CERTIFICATE", 1205 Bytes: fauxRootDER, 1206 }) 1207 1208 // It is not required, but not wrong, for the intermediate chain to terminate with a self-signed root 1209 signWithIntermediate, err := ca.NewRootCA(cautils.ECDSACertChain[2], cautils.ECDSACertChain[1], cautils.ECDSACertChainKeys[1], 1210 ca.DefaultNodeCertExpiration, append(cautils.ECDSACertChain[1], cautils.ECDSACertChain[2]...)) 1211 require.NoError(t, err) 1212 1213 // just the intermediate, without a terminating self-signed root, is also ok 1214 signWithIntermediate, err = ca.NewRootCA(cautils.ECDSACertChain[2], cautils.ECDSACertChain[1], cautils.ECDSACertChainKeys[1], 1215 ca.DefaultNodeCertExpiration, cautils.ECDSACertChain[1]) 1216 require.NoError(t, err) 1217 1218 paths := ca.NewConfigPaths(tempdir) 1219 krw := ca.NewKeyReadWriter(paths.Node, nil, nil) 1220 _, _, err = signWithIntermediate.IssueAndSaveNewCertificates(krw, "cn", "ou", "org") 1221 require.NoError(t, err) 1222 tlsCert, _, err := krw.Read() 1223 require.NoError(t, err) 1224 1225 parsedCerts, chains, err := ca.ValidateCertChain(signWithIntermediate.Pool, tlsCert, false) 1226 require.NoError(t, err) 1227 require.Len(t, parsedCerts, 2) 1228 require.Len(t, chains, 1) 1229 require.Equal(t, parsedIntermediate.Raw, parsedCerts[1].Raw) 1230 require.Equal(t, parsedCerts, chains[0][:len(chains[0])-1]) // the last one is the root 1231 1232 oldRoot, err := ca.NewRootCA(cautils.ECDSACertChain[2], cautils.ECDSACertChain[2], cautils.ECDSACertChainKeys[2], ca.DefaultNodeCertExpiration, nil) 1233 require.NoError(t, err) 1234 1235 newRoot, err := ca.NewRootCA(fauxRootCert, fauxRootCert, cautils.ECDSACertChainKeys[1], ca.DefaultNodeCertExpiration, nil) 1236 require.NoError(t, err) 1237 apiNewRoot := api.RootCA{ 1238 CACert: fauxRootCert, 1239 CAKey: cautils.ECDSACertChainKeys[1], 1240 } 1241 1242 checkValidateAgainstAllRoots := func(cert []byte) { 1243 for i, root := range []ca.RootCA{signWithIntermediate, oldRoot, newRoot} { 1244 parsedCerts, chains, err := ca.ValidateCertChain(root.Pool, cert, false) 1245 require.NoError(t, err) 1246 require.Len(t, parsedCerts, 2) 1247 require.Len(t, chains, 1) 1248 require.True(t, len(chains[0]) >= 2) // there are always at least 2 certs at minimum: the leaf and the root 1249 require.Equal(t, parsedCerts[0], chains[0][0]) 1250 require.Equal(t, parsedIntermediate.Raw, parsedCerts[1].Raw) 1251 1252 chainWithoutRoot := chains[0][:len(chains[0])-1] 1253 if i == 2 { 1254 // against the new root, the cert can chain directly up to the root without the intermediate 1255 require.Equal(t, parsedCerts[0:1], chainWithoutRoot) 1256 } else { 1257 require.Equal(t, parsedCerts, chainWithoutRoot) 1258 } 1259 } 1260 } 1261 checkValidateAgainstAllRoots(tlsCert) 1262 1263 if !cautils.External { 1264 return 1265 } 1266 1267 // create an external signing server that generates leaf certs with the new root (but does not append the intermediate) 1268 tc := cautils.NewTestCAFromAPIRootCA(t, tempdir, apiNewRoot, nil) 1269 defer tc.Stop() 1270 1271 // we need creds that trust both the old and new root in order to connect to the test CA, and we want this root CA to 1272 // append certificates 1273 connectToExternalRootCA, err := ca.NewRootCA(append(cautils.ECDSACertChain[2], fauxRootCert...), cautils.ECDSACertChain[1], 1274 cautils.ECDSACertChainKeys[1], ca.DefaultNodeCertExpiration, cautils.ECDSACertChain[1]) 1275 require.NoError(t, err) 1276 tlsKeyPair, _, err := connectToExternalRootCA.IssueAndSaveNewCertificates(krw, "cn", ca.ManagerRole, tc.Organization) 1277 require.NoError(t, err) 1278 externalCA := ca.NewExternalCA(cautils.ECDSACertChain[1], 1279 ca.NewExternalCATLSConfig([]tls.Certificate{*tlsKeyPair}, connectToExternalRootCA.Pool), tc.ExternalSigningServer.URL) 1280 1281 newCSR, _, err := ca.GenerateNewCSR() 1282 require.NoError(t, err) 1283 1284 tlsCert, err = externalCA.Sign(tc.Context, ca.PrepareCSR(newCSR, "cn", ca.ManagerRole, tc.Organization)) 1285 require.NoError(t, err) 1286 1287 checkValidateAgainstAllRoots(tlsCert) 1288 } 1289 1290 type certTestCase struct { 1291 cert []byte 1292 errorStr string 1293 root []byte 1294 allowExpiry bool 1295 } 1296 1297 func TestValidateCertificateChain(t *testing.T) { 1298 leaf, intermediate, root := cautils.ECDSACertChain[0], cautils.ECDSACertChain[1], cautils.ECDSACertChain[2] 1299 intermediateKey, rootKey := cautils.ECDSACertChainKeys[1], cautils.ECDSACertChainKeys[2] // we don't care about the leaf key 1300 1301 chain := func(certs ...[]byte) []byte { 1302 var all []byte 1303 for _, cert := range certs { 1304 all = append(all, cert...) 1305 } 1306 return all 1307 } 1308 1309 now := time.Now() 1310 expiredLeaf := cautils.ReDateCert(t, leaf, intermediate, intermediateKey, now.Add(-10*time.Hour), now.Add(-1*time.Minute)) 1311 expiredIntermediate := cautils.ReDateCert(t, intermediate, root, rootKey, now.Add(-10*time.Hour), now.Add(-1*time.Minute)) 1312 notYetValidLeaf := cautils.ReDateCert(t, leaf, intermediate, intermediateKey, now.Add(time.Hour), now.Add(2*time.Hour)) 1313 notYetValidIntermediate := cautils.ReDateCert(t, intermediate, root, rootKey, now.Add(time.Hour), now.Add(2*time.Hour)) 1314 1315 rootPool := x509.NewCertPool() 1316 rootPool.AppendCertsFromPEM(root) 1317 1318 invalids := []certTestCase{ 1319 { 1320 cert: nil, 1321 root: root, 1322 errorStr: "no certificates to validate", 1323 }, 1324 { 1325 cert: []byte("malformed"), 1326 root: root, 1327 errorStr: "Failed to decode certificate", 1328 }, 1329 { 1330 cert: chain(leaf, intermediate, leaf), 1331 root: root, 1332 errorStr: "certificates do not form a chain", 1333 }, 1334 { 1335 cert: chain(leaf, intermediate), 1336 root: cautils.ECDSA256SHA256Cert, 1337 errorStr: "unknown authority", 1338 }, 1339 { 1340 cert: chain(expiredLeaf, intermediate), 1341 root: root, 1342 errorStr: "not valid after", 1343 }, 1344 { 1345 cert: chain(leaf, expiredIntermediate), 1346 root: root, 1347 errorStr: "not valid after", 1348 }, 1349 { 1350 cert: chain(notYetValidLeaf, intermediate), 1351 root: root, 1352 errorStr: "not valid before", 1353 }, 1354 { 1355 cert: chain(leaf, notYetValidIntermediate), 1356 root: root, 1357 errorStr: "not valid before", 1358 }, 1359 1360 // if we allow expiry, we still don't allow not yet valid certs or expired certs that don't chain up to the root 1361 { 1362 cert: chain(notYetValidLeaf, intermediate), 1363 root: root, 1364 allowExpiry: true, 1365 errorStr: "not valid before", 1366 }, 1367 { 1368 cert: chain(leaf, notYetValidIntermediate), 1369 root: root, 1370 allowExpiry: true, 1371 errorStr: "not valid before", 1372 }, 1373 { 1374 cert: chain(expiredLeaf, intermediate), 1375 root: cautils.ECDSA256SHA256Cert, 1376 allowExpiry: true, 1377 errorStr: "unknown authority", 1378 }, 1379 1380 // construct a weird cases where one cert is expired, we allow expiry, but the other cert is not yet valid at the first cert's expiry 1381 // (this is not something that can happen unless we allow expiry, because if the cert periods don't overlap, one or the other will 1382 // be either not yet valid or already expired) 1383 { 1384 cert: chain( 1385 cautils.ReDateCert(t, leaf, intermediate, intermediateKey, now.Add(-3*helpers.OneDay), now.Add(-2*helpers.OneDay)), 1386 cautils.ReDateCert(t, intermediate, root, rootKey, now.Add(-1*helpers.OneDay), now.Add(helpers.OneDay))), 1387 root: root, 1388 allowExpiry: true, 1389 errorStr: "there is no time span", 1390 }, 1391 // similarly, but for root pool 1392 { 1393 cert: chain(expiredLeaf, expiredIntermediate), 1394 root: cautils.ReDateCert(t, root, root, rootKey, now.Add(-3*helpers.OneYear), now.Add(-2*helpers.OneYear)), 1395 allowExpiry: true, 1396 errorStr: "there is no time span", 1397 }, 1398 } 1399 1400 for _, invalid := range invalids { 1401 pool := x509.NewCertPool() 1402 pool.AppendCertsFromPEM(invalid.root) 1403 _, _, err := ca.ValidateCertChain(pool, invalid.cert, invalid.allowExpiry) 1404 require.Error(t, err, invalid.errorStr) 1405 require.Contains(t, err.Error(), invalid.errorStr) 1406 } 1407 1408 // these will default to using the root pool, so we don't have to specify the root pool 1409 valids := []certTestCase{ 1410 {cert: chain(leaf, intermediate, root)}, 1411 {cert: chain(leaf, intermediate)}, 1412 {cert: intermediate}, 1413 { 1414 cert: chain(expiredLeaf, intermediate), 1415 allowExpiry: true, 1416 }, 1417 { 1418 cert: chain(leaf, expiredIntermediate), 1419 allowExpiry: true, 1420 }, 1421 { 1422 cert: chain(expiredLeaf, expiredIntermediate), 1423 allowExpiry: true, 1424 }, 1425 } 1426 1427 for _, valid := range valids { 1428 parsedCerts, chains, err := ca.ValidateCertChain(rootPool, valid.cert, valid.allowExpiry) 1429 require.NoError(t, err) 1430 require.NotEmpty(t, chain) 1431 for _, chain := range chains { 1432 require.Equal(t, parsedCerts[0], chain[0]) // the leaf certs are equal 1433 require.True(t, len(chain) >= 2) 1434 } 1435 } 1436 } 1437 1438 // Tests cross-signing an RSA cert with an ECDSA cert and vice versa, and an ECDSA 1439 // cert with another ECDSA cert and a RSA cert with another RSA cert 1440 func TestRootCACrossSignCACertificate(t *testing.T) { 1441 t.Parallel() 1442 if cautils.External { 1443 return 1444 } 1445 1446 oldCAs := []struct { 1447 cert, key []byte 1448 }{ 1449 { 1450 cert: cautils.ECDSA256SHA256Cert, 1451 key: cautils.ECDSA256Key, 1452 }, 1453 { 1454 cert: cautils.RSA2048SHA256Cert, 1455 key: cautils.RSA2048Key, 1456 }, 1457 } 1458 1459 cert1, key1, err := cautils.CreateRootCertAndKey("rootCNECDSA") 1460 require.NoError(t, err) 1461 1462 rsaReq := cfcsr.CertificateRequest{ 1463 CN: "rootCNRSA", 1464 KeyRequest: &cfcsr.BasicKeyRequest{ 1465 A: "rsa", 1466 S: 2048, 1467 }, 1468 CA: &cfcsr.CAConfig{Expiry: ca.RootCAExpiration}, 1469 } 1470 1471 // Generate the CA and get the certificate and private key 1472 cert2, _, key2, err := initca.New(&rsaReq) 1473 require.NoError(t, err) 1474 1475 newCAs := []struct { 1476 cert, key []byte 1477 }{ 1478 { 1479 cert: cert1, 1480 key: key1, 1481 }, 1482 { 1483 cert: cert2, 1484 key: key2, 1485 }, 1486 } 1487 1488 tempdir, err := ioutil.TempDir("", "cross-sign-cert") 1489 require.NoError(t, err) 1490 defer os.RemoveAll(tempdir) 1491 paths := ca.NewConfigPaths(tempdir) 1492 krw := ca.NewKeyReadWriter(paths.Node, nil, nil) 1493 1494 for _, oldRoot := range oldCAs { 1495 for _, newRoot := range newCAs { 1496 rootCA1, err := ca.NewRootCA(oldRoot.cert, oldRoot.cert, oldRoot.key, ca.DefaultNodeCertExpiration, nil) 1497 require.NoError(t, err) 1498 1499 rootCA2, err := ca.NewRootCA(newRoot.cert, newRoot.cert, newRoot.key, ca.DefaultNodeCertExpiration, nil) 1500 require.NoError(t, err) 1501 1502 _, _, err = rootCA2.IssueAndSaveNewCertificates(krw, "cn", "ou", "org") 1503 require.NoError(t, err) 1504 certBytes, keyBytes, err := krw.Read() 1505 require.NoError(t, err) 1506 leafCert, err := helpers.ParseCertificatePEM(certBytes) 1507 require.NoError(t, err) 1508 1509 // cross-signing a non-CA fails 1510 _, err = rootCA1.CrossSignCACertificate(certBytes) 1511 require.Error(t, err) 1512 1513 // cross-signing some non-cert PEM bytes fail 1514 _, err = rootCA1.CrossSignCACertificate(keyBytes) 1515 require.Error(t, err) 1516 1517 intermediate, err := rootCA1.CrossSignCACertificate(newRoot.cert) 1518 require.NoError(t, err) 1519 parsedIntermediate, err := helpers.ParseCertificatePEM(intermediate) 1520 require.NoError(t, err) 1521 parsedRoot2, err := helpers.ParseCertificatePEM(newRoot.cert) 1522 require.NoError(t, err) 1523 require.Equal(t, parsedRoot2.RawSubject, parsedIntermediate.RawSubject) 1524 require.Equal(t, parsedRoot2.RawSubjectPublicKeyInfo, parsedIntermediate.RawSubjectPublicKeyInfo) 1525 require.True(t, parsedIntermediate.IsCA) 1526 1527 intermediatePool := x509.NewCertPool() 1528 intermediatePool.AddCert(parsedIntermediate) 1529 1530 // we can validate a chain from the leaf to the first root through the intermediate, 1531 // or from the leaf cert to the second root with or without the intermediate 1532 _, err = leafCert.Verify(x509.VerifyOptions{Roots: rootCA1.Pool}) 1533 require.Error(t, err) 1534 _, err = leafCert.Verify(x509.VerifyOptions{Roots: rootCA1.Pool, Intermediates: intermediatePool}) 1535 require.NoError(t, err) 1536 1537 _, err = leafCert.Verify(x509.VerifyOptions{Roots: rootCA2.Pool}) 1538 require.NoError(t, err) 1539 _, err = leafCert.Verify(x509.VerifyOptions{Roots: rootCA2.Pool, Intermediates: intermediatePool}) 1540 require.NoError(t, err) 1541 } 1542 } 1543 } 1544 1545 func concat(byteSlices ...[]byte) []byte { 1546 var results []byte 1547 for _, slice := range byteSlices { 1548 results = append(results, slice...) 1549 } 1550 return results 1551 } 1552 1553 func TestNormalizePEMs(t *testing.T) { 1554 pemBlock, _ := pem.Decode(cautils.ECDSA256SHA256Cert) 1555 pemBlock.Headers = map[string]string{ 1556 "hello": "world", 1557 } 1558 withHeaders := pem.EncodeToMemory(pemBlock) 1559 for _, testcase := range []struct{ input, expect []byte }{ 1560 { 1561 input: nil, 1562 expect: nil, 1563 }, 1564 { 1565 input: []byte("garbage"), 1566 expect: nil, 1567 }, 1568 { 1569 input: concat([]byte("garbage\n\t\n\n"), cautils.ECDSA256SHA256Cert, []byte(" \n")), 1570 expect: ca.NormalizePEMs(cautils.ECDSA256SHA256Cert), 1571 }, 1572 { 1573 input: concat([]byte("\n\t\n "), withHeaders, []byte("\t\n\n"), cautils.ECDSACertChain[0]), 1574 expect: ca.NormalizePEMs(append(cautils.ECDSA256SHA256Cert, cautils.ECDSACertChain[0]...)), 1575 }, 1576 } { 1577 require.Equal(t, testcase.expect, ca.NormalizePEMs(testcase.input)) 1578 } 1579 }