github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/node/node_test.go (about) 1 package node 2 3 import ( 4 "bytes" 5 "context" 6 "crypto/x509" 7 "encoding/pem" 8 "fmt" 9 "io/ioutil" 10 "os" 11 "path/filepath" 12 "strings" 13 "testing" 14 "time" 15 16 "github.com/cloudflare/cfssl/helpers" 17 "github.com/docker/swarmkit/agent" 18 agentutils "github.com/docker/swarmkit/agent/testutils" 19 "github.com/docker/swarmkit/api" 20 "github.com/docker/swarmkit/ca" 21 "github.com/docker/swarmkit/ca/keyutils" 22 cautils "github.com/docker/swarmkit/ca/testutils" 23 "github.com/docker/swarmkit/identity" 24 "github.com/docker/swarmkit/log" 25 "github.com/docker/swarmkit/manager/state/store" 26 "github.com/docker/swarmkit/testutils" 27 "github.com/pkg/errors" 28 "github.com/stretchr/testify/require" 29 ) 30 31 func getLoggingContext(t *testing.T) context.Context { 32 return log.WithLogger(context.Background(), log.L.WithField("test", t.Name())) 33 } 34 35 // If there is nothing on disk and no join addr, we create a new CA and a new set of TLS certs. 36 // If AutoLockManagers is enabled, the TLS key is encrypted with a randomly generated lock key. 37 func TestLoadSecurityConfigNewNode(t *testing.T) { 38 for _, autoLockManagers := range []bool{true, false} { 39 tempdir, err := ioutil.TempDir("", "test-new-node") 40 require.NoError(t, err) 41 defer os.RemoveAll(tempdir) 42 43 paths := ca.NewConfigPaths(filepath.Join(tempdir, "certificates")) 44 45 node, err := New(&Config{ 46 StateDir: tempdir, 47 AutoLockManagers: autoLockManagers, 48 }) 49 require.NoError(t, err) 50 securityConfig, cancel, err := node.loadSecurityConfig(context.Background(), paths) 51 require.NoError(t, err) 52 defer cancel() 53 require.NotNil(t, securityConfig) 54 55 unencryptedReader := ca.NewKeyReadWriter(paths.Node, nil, nil) 56 _, _, err = unencryptedReader.Read() 57 if !autoLockManagers { 58 require.NoError(t, err) 59 } else { 60 require.IsType(t, ca.ErrInvalidKEK{}, err) 61 } 62 } 63 } 64 65 // If there's only a root CA on disk (no TLS certs), and no join addr, we create a new CA 66 // and a new set of TLS certs. Similarly if there's only a TLS cert and key, and no CA. 67 func TestLoadSecurityConfigPartialCertsOnDisk(t *testing.T) { 68 tempdir, err := ioutil.TempDir("", "test-new-node") 69 require.NoError(t, err) 70 defer os.RemoveAll(tempdir) 71 72 paths := ca.NewConfigPaths(filepath.Join(tempdir, "certificates")) 73 rootCA, err := ca.CreateRootCA(ca.DefaultRootCN) 74 require.NoError(t, err) 75 require.NoError(t, ca.SaveRootCA(rootCA, paths.RootCA)) 76 77 node, err := New(&Config{ 78 StateDir: tempdir, 79 }) 80 require.NoError(t, err) 81 securityConfig, cancel, err := node.loadSecurityConfig(context.Background(), paths) 82 require.NoError(t, err) 83 defer cancel() 84 require.NotNil(t, securityConfig) 85 86 cert, key, err := securityConfig.KeyReader().Read() 87 require.NoError(t, err) 88 89 // a new CA was generated because no existing TLS certs were present 90 require.NotEqual(t, rootCA.Certs, securityConfig.RootCA().Certs) 91 92 // if the TLS key and cert are on disk, but there's no CA, a new CA and TLS 93 // key+cert are generated 94 require.NoError(t, os.RemoveAll(paths.RootCA.Cert)) 95 96 node, err = New(&Config{ 97 StateDir: tempdir, 98 }) 99 require.NoError(t, err) 100 securityConfig, cancel, err = node.loadSecurityConfig(context.Background(), paths) 101 require.NoError(t, err) 102 defer cancel() 103 require.NotNil(t, securityConfig) 104 105 newCert, newKey, err := securityConfig.KeyReader().Read() 106 require.NoError(t, err) 107 require.NotEqual(t, cert, newCert) 108 require.NotEqual(t, key, newKey) 109 } 110 111 // If there are CAs and TLS certs on disk, it tries to load and fails if there 112 // are any errors, even if a join token is provided. 113 func TestLoadSecurityConfigLoadFromDisk(t *testing.T) { 114 tempdir, err := ioutil.TempDir("", "test-load-node-tls") 115 require.NoError(t, err) 116 defer os.RemoveAll(tempdir) 117 118 paths := ca.NewConfigPaths(filepath.Join(tempdir, "certificates")) 119 120 tc := cautils.NewTestCA(t) 121 defer tc.Stop() 122 peer, err := tc.ConnBroker.Remotes().Select() 123 require.NoError(t, err) 124 125 // Load successfully with valid passphrase 126 rootCA, err := ca.CreateRootCA(ca.DefaultRootCN) 127 require.NoError(t, err) 128 require.NoError(t, ca.SaveRootCA(rootCA, paths.RootCA)) 129 130 krw := ca.NewKeyReadWriter(paths.Node, []byte("passphrase"), nil) 131 require.NoError(t, err) 132 _, _, err = rootCA.IssueAndSaveNewCertificates(krw, identity.NewID(), ca.WorkerRole, identity.NewID()) 133 require.NoError(t, err) 134 135 node, err := New(&Config{ 136 StateDir: tempdir, 137 JoinAddr: peer.Addr, 138 JoinToken: tc.ManagerToken, 139 UnlockKey: []byte("passphrase"), 140 }) 141 require.NoError(t, err) 142 securityConfig, cancel, err := node.loadSecurityConfig(context.Background(), paths) 143 require.NoError(t, err) 144 defer cancel() 145 require.NotNil(t, securityConfig) 146 147 // Invalid passphrase 148 node, err = New(&Config{ 149 StateDir: tempdir, 150 JoinAddr: peer.Addr, 151 JoinToken: tc.ManagerToken, 152 }) 153 require.NoError(t, err) 154 _, _, err = node.loadSecurityConfig(context.Background(), paths) 155 require.Equal(t, ErrInvalidUnlockKey, err) 156 157 // Invalid CA 158 otherRootCA, err := ca.CreateRootCA(ca.DefaultRootCN) 159 require.NoError(t, err) 160 require.NoError(t, ca.SaveRootCA(otherRootCA, paths.RootCA)) 161 node, err = New(&Config{ 162 StateDir: tempdir, 163 JoinAddr: peer.Addr, 164 JoinToken: tc.ManagerToken, 165 UnlockKey: []byte("passphrase"), 166 }) 167 require.NoError(t, err) 168 _, _, err = node.loadSecurityConfig(context.Background(), paths) 169 require.IsType(t, x509.UnknownAuthorityError{}, errors.Cause(err)) 170 171 // Convert to PKCS1 and require FIPS 172 require.NoError(t, krw.DowngradeKey()) 173 // go back to the previous root CA 174 require.NoError(t, ca.SaveRootCA(rootCA, paths.RootCA)) 175 node, err = New(&Config{ 176 StateDir: tempdir, 177 JoinAddr: peer.Addr, 178 JoinToken: tc.ManagerToken, 179 UnlockKey: []byte("passphrase"), 180 FIPS: true, 181 }) 182 require.NoError(t, err) 183 _, _, err = node.loadSecurityConfig(context.Background(), paths) 184 require.Equal(t, keyutils.ErrFIPSUnsupportedKeyFormat, errors.Cause(err)) 185 } 186 187 // If there is no CA, and a join addr is provided, one is downloaded from the 188 // join server. If there is a CA, it is just loaded from disk. The TLS key and 189 // cert are also downloaded. 190 func TestLoadSecurityConfigDownloadAllCerts(t *testing.T) { 191 tempdir, err := ioutil.TempDir("", "test-join-node") 192 require.NoError(t, err) 193 defer os.RemoveAll(tempdir) 194 195 paths := ca.NewConfigPaths(filepath.Join(tempdir, "certificates")) 196 197 // join addr is invalid 198 node, err := New(&Config{ 199 StateDir: tempdir, 200 JoinAddr: "127.0.0.1:12", 201 }) 202 require.NoError(t, err) 203 _, _, err = node.loadSecurityConfig(context.Background(), paths) 204 require.Error(t, err) 205 206 tc := cautils.NewTestCA(t) 207 defer tc.Stop() 208 209 peer, err := tc.ConnBroker.Remotes().Select() 210 require.NoError(t, err) 211 212 node, err = New(&Config{ 213 StateDir: tempdir, 214 JoinAddr: peer.Addr, 215 JoinToken: tc.ManagerToken, 216 }) 217 require.NoError(t, err) 218 _, cancel, err := node.loadSecurityConfig(context.Background(), paths) 219 require.NoError(t, err) 220 cancel() 221 222 // the TLS key and cert were written to disk unencrypted 223 _, _, err = ca.NewKeyReadWriter(paths.Node, nil, nil).Read() 224 require.NoError(t, err) 225 226 // remove the TLS cert and key, and mark the root CA cert so that we will 227 // know if it gets replaced 228 require.NoError(t, os.Remove(paths.Node.Cert)) 229 require.NoError(t, os.Remove(paths.Node.Key)) 230 certBytes, err := ioutil.ReadFile(paths.RootCA.Cert) 231 require.NoError(t, err) 232 pemBlock, _ := pem.Decode(certBytes) 233 require.NotNil(t, pemBlock) 234 pemBlock.Headers["marked"] = "true" 235 certBytes = pem.EncodeToMemory(pemBlock) 236 require.NoError(t, ioutil.WriteFile(paths.RootCA.Cert, certBytes, 0644)) 237 238 // also make sure the new set gets downloaded and written to disk with a passphrase 239 // by updating the memory store with manager autolock on and an unlock key 240 require.NoError(t, tc.MemoryStore.Update(func(tx store.Tx) error { 241 clusters, err := store.FindClusters(tx, store.All) 242 require.NoError(t, err) 243 require.Len(t, clusters, 1) 244 245 newCluster := clusters[0].Copy() 246 newCluster.Spec.EncryptionConfig.AutoLockManagers = true 247 newCluster.UnlockKeys = []*api.EncryptionKey{{ 248 Subsystem: ca.ManagerRole, 249 Key: []byte("passphrase"), 250 }} 251 return store.UpdateCluster(tx, newCluster) 252 })) 253 254 // Join with without any passphrase - this should be fine, because the TLS 255 // key is downloaded and then loaded just fine. However, it *is* written 256 // to disk encrypted. 257 node, err = New(&Config{ 258 StateDir: tempdir, 259 JoinAddr: peer.Addr, 260 JoinToken: tc.ManagerToken, 261 }) 262 require.NoError(t, err) 263 _, cancel, err = node.loadSecurityConfig(context.Background(), paths) 264 require.NoError(t, err) 265 cancel() 266 267 // make sure the CA cert has not been replaced 268 readCertBytes, err := ioutil.ReadFile(paths.RootCA.Cert) 269 require.NoError(t, err) 270 require.Equal(t, certBytes, readCertBytes) 271 272 // the TLS node cert and key were saved to disk encrypted, though 273 _, _, err = ca.NewKeyReadWriter(paths.Node, nil, nil).Read() 274 require.Error(t, err) 275 _, _, err = ca.NewKeyReadWriter(paths.Node, []byte("passphrase"), nil).Read() 276 require.NoError(t, err) 277 } 278 279 // If there is nothing on disk and no join addr, and FIPS is enabled, we create a cluster whose 280 // ID starts with 'FIPS.' 281 func TestLoadSecurityConfigNodeFIPSCreateCluster(t *testing.T) { 282 tempdir, err := ioutil.TempDir("", "test-security-config-fips-new-cluster") 283 require.NoError(t, err) 284 defer os.RemoveAll(tempdir) 285 286 paths := ca.NewConfigPaths(filepath.Join(tempdir, "certificates")) 287 288 tc := cautils.NewTestCA(t) 289 defer tc.Stop() 290 291 config := &Config{ 292 StateDir: tempdir, 293 FIPS: true, 294 } 295 296 node, err := New(config) 297 require.NoError(t, err) 298 securityConfig, cancel, err := node.loadSecurityConfig(tc.Context, paths) 299 require.NoError(t, err) 300 defer cancel() 301 require.NotNil(t, securityConfig) 302 require.True(t, strings.HasPrefix(securityConfig.ClientTLSCreds.Organization(), "FIPS.")) 303 } 304 305 // If FIPS is enabled and there is a join address, the cluster ID is whatever the CA set 306 // the cluster ID to. 307 func TestLoadSecurityConfigNodeFIPSJoinCluster(t *testing.T) { 308 tempdir, err := ioutil.TempDir("", "test-security-config-fips-join-cluster") 309 require.NoError(t, err) 310 defer os.RemoveAll(tempdir) 311 312 certDir := filepath.Join(tempdir, "certificates") 313 paths := ca.NewConfigPaths(certDir) 314 315 for _, fips := range []bool{true, false} { 316 require.NoError(t, os.RemoveAll(certDir)) 317 318 var tc *cautils.TestCA 319 if fips { 320 tc = cautils.NewFIPSTestCA(t) 321 } else { 322 tc = cautils.NewTestCA(t) 323 } 324 defer tc.Stop() 325 326 peer, err := tc.ConnBroker.Remotes().Select() 327 require.NoError(t, err) 328 329 node, err := New(&Config{ 330 StateDir: tempdir, 331 JoinAddr: peer.Addr, 332 JoinToken: tc.ManagerToken, 333 FIPS: true, 334 }) 335 require.NoError(t, err) 336 securityConfig, cancel, err := node.loadSecurityConfig(tc.Context, paths) 337 require.NoError(t, err) 338 defer cancel() 339 require.NotNil(t, securityConfig) 340 require.Equal(t, fips, strings.HasPrefix(securityConfig.ClientTLSCreds.Organization(), "FIPS.")) 341 } 342 } 343 344 // If the certificate specifies that the cluster requires FIPS mode, loading the security 345 // config will fail if the node is not FIPS enabled. 346 func TestLoadSecurityConfigRespectsFIPSCert(t *testing.T) { 347 tempdir, err := ioutil.TempDir("", "test-security-config-fips-cert-on-disk") 348 require.NoError(t, err) 349 defer os.RemoveAll(tempdir) 350 351 tc := cautils.NewFIPSTestCA(t) 352 defer tc.Stop() 353 354 certDir := filepath.Join(tempdir, "certificates") 355 require.NoError(t, os.Mkdir(certDir, 0700)) 356 paths := ca.NewConfigPaths(certDir) 357 358 // copy certs and keys from the test CA using a hard link 359 _, err = tc.WriteNewNodeConfig(ca.ManagerRole) 360 require.NoError(t, err) 361 require.NoError(t, os.Link(tc.Paths.Node.Cert, paths.Node.Cert)) 362 require.NoError(t, os.Link(tc.Paths.Node.Key, paths.Node.Key)) 363 require.NoError(t, os.Link(tc.Paths.RootCA.Cert, paths.RootCA.Cert)) 364 365 node, err := New(&Config{StateDir: tempdir}) 366 require.NoError(t, err) 367 _, _, err = node.loadSecurityConfig(tc.Context, paths) 368 require.Equal(t, ErrMandatoryFIPS, err) 369 370 node, err = New(&Config{ 371 StateDir: tempdir, 372 FIPS: true, 373 }) 374 require.NoError(t, err) 375 securityConfig, cancel, err := node.loadSecurityConfig(tc.Context, paths) 376 require.NoError(t, err) 377 defer cancel() 378 require.NotNil(t, securityConfig) 379 require.True(t, strings.HasPrefix(securityConfig.ClientTLSCreds.Organization(), "FIPS.")) 380 } 381 382 // If FIPS is disabled and there is a join address and token, and the join token indicates 383 // the cluster requires fips, then loading the security config will fail. However, if 384 // there are already certs on disk, it will load them and ignore the join token. 385 func TestLoadSecurityConfigNonFIPSNodeJoinCluster(t *testing.T) { 386 tempdir, err := ioutil.TempDir("", "test-security-config-nonfips-join-cluster") 387 require.NoError(t, err) 388 defer os.RemoveAll(tempdir) 389 390 certDir := filepath.Join(tempdir, "certificates") 391 require.NoError(t, os.Mkdir(certDir, 0700)) 392 paths := ca.NewConfigPaths(certDir) 393 394 tc := cautils.NewTestCA(t) 395 defer tc.Stop() 396 // copy certs and keys from the test CA using a hard link 397 _, err = tc.WriteNewNodeConfig(ca.ManagerRole) 398 require.NoError(t, err) 399 require.NoError(t, os.Link(tc.Paths.Node.Cert, paths.Node.Cert)) 400 require.NoError(t, os.Link(tc.Paths.Node.Key, paths.Node.Key)) 401 require.NoError(t, os.Link(tc.Paths.RootCA.Cert, paths.RootCA.Cert)) 402 403 tcFIPS := cautils.NewFIPSTestCA(t) 404 defer tcFIPS.Stop() 405 406 peer, err := tcFIPS.ConnBroker.Remotes().Select() 407 require.NoError(t, err) 408 409 node, err := New(&Config{ 410 StateDir: tempdir, 411 JoinAddr: peer.Addr, 412 JoinToken: tcFIPS.ManagerToken, 413 }) 414 require.NoError(t, err) 415 securityConfig, cancel, err := node.loadSecurityConfig(tcFIPS.Context, paths) 416 require.NoError(t, err) 417 defer cancel() 418 require.NotNil(t, securityConfig) 419 require.False(t, strings.HasPrefix(securityConfig.ClientTLSCreds.Organization(), "FIPS.")) 420 421 // remove the node cert only - now that the node has to download the certs, it will check the 422 // join address and fail 423 require.NoError(t, os.Remove(paths.Node.Cert)) 424 425 _, _, err = node.loadSecurityConfig(tcFIPS.Context, paths) 426 require.Equal(t, ErrMandatoryFIPS, err) 427 428 // remove all the certs (CA and node) - the node will also check the join address and fail 429 require.NoError(t, os.RemoveAll(certDir)) 430 431 _, _, err = node.loadSecurityConfig(tcFIPS.Context, paths) 432 require.Equal(t, ErrMandatoryFIPS, err) 433 } 434 435 func TestManagerRespectsDispatcherRootCAUpdate(t *testing.T) { 436 tmpDir, err := ioutil.TempDir("", "manager-root-ca-update") 437 require.NoError(t, err) 438 defer os.RemoveAll(tmpDir) 439 440 // don't bother with a listening socket 441 cAddr := filepath.Join(tmpDir, "control.sock") 442 cfg := &Config{ 443 ListenControlAPI: cAddr, 444 StateDir: tmpDir, 445 Executor: &agentutils.TestExecutor{}, 446 } 447 448 node, err := New(cfg) 449 require.NoError(t, err) 450 451 require.NoError(t, node.Start(context.Background())) 452 453 select { 454 case <-node.Ready(): 455 case <-time.After(5 * time.Second): 456 require.FailNow(t, "node did not ready in time") 457 } 458 459 // ensure that we have a second dispatcher that we can connect to when we shut down ours 460 paths := ca.NewConfigPaths(filepath.Join(tmpDir, certDirectory)) 461 rootCA, err := ca.GetLocalRootCA(paths.RootCA) 462 require.NoError(t, err) 463 managerSecConfig, cancel, err := ca.LoadSecurityConfig(context.Background(), rootCA, ca.NewKeyReadWriter(paths.Node, nil, nil), false) 464 require.NoError(t, err) 465 defer cancel() 466 467 mockDispatcher, cleanup := agentutils.NewMockDispatcher(t, managerSecConfig, false) 468 defer cleanup() 469 node.remotes.Observe(api.Peer{Addr: mockDispatcher.Addr}, 1) 470 471 currentCACerts := rootCA.Certs 472 473 // shut down our current manager so that when the root CA changes, the manager doesn't "fix" it. 474 node.manager.Stop(context.Background(), false) 475 476 // fake an update from a remote dispatcher 477 node.notifyNodeChange <- &agent.NodeChanges{ 478 RootCert: append(currentCACerts, cautils.ECDSA256SHA256Cert...), 479 } 480 481 // the node root CA certificates have changed now 482 time.Sleep(250 * time.Millisecond) 483 certPath := filepath.Join(tmpDir, certDirectory, "swarm-root-ca.crt") 484 caCerts, err := ioutil.ReadFile(certPath) 485 require.NoError(t, err) 486 require.NotEqual(t, currentCACerts, caCerts) 487 488 require.NoError(t, node.Stop(context.Background())) 489 } 490 491 func TestAgentRespectsDispatcherRootCAUpdate(t *testing.T) { 492 tmpDir, err := ioutil.TempDir("", "manager-root-ca-update") 493 require.NoError(t, err) 494 defer os.RemoveAll(tmpDir) 495 496 // bootstrap worker TLS certificates 497 paths := ca.NewConfigPaths(filepath.Join(tmpDir, certDirectory)) 498 rootCA, err := ca.CreateRootCA("rootCN") 499 require.NoError(t, err) 500 require.NoError(t, ca.SaveRootCA(rootCA, paths.RootCA)) 501 managerSecConfig, cancel, err := rootCA.CreateSecurityConfig(context.Background(), 502 ca.NewKeyReadWriter(paths.Node, nil, nil), ca.CertificateRequestConfig{}) 503 require.NoError(t, err) 504 defer cancel() 505 506 _, _, err = rootCA.IssueAndSaveNewCertificates(ca.NewKeyReadWriter(paths.Node, nil, nil), "workerNode", 507 ca.WorkerRole, managerSecConfig.ServerTLSCreds.Organization()) 508 require.NoError(t, err) 509 510 mockDispatcher, cleanup := agentutils.NewMockDispatcher(t, managerSecConfig, false) 511 defer cleanup() 512 513 cfg := &Config{ 514 StateDir: tmpDir, 515 Executor: &agentutils.TestExecutor{}, 516 JoinAddr: mockDispatcher.Addr, 517 } 518 node, err := New(cfg) 519 require.NoError(t, err) 520 521 require.NoError(t, node.Start(context.Background())) 522 523 select { 524 case <-node.Ready(): 525 case <-time.After(5 * time.Second): 526 require.FailNow(t, "node did not ready in time") 527 } 528 529 currentCACerts, err := ioutil.ReadFile(paths.RootCA.Cert) 530 require.NoError(t, err) 531 parsedCerts, err := helpers.ParseCertificatesPEM(currentCACerts) 532 require.NoError(t, err) 533 require.Len(t, parsedCerts, 1) 534 535 // fake an update from the dispatcher 536 node.notifyNodeChange <- &agent.NodeChanges{ 537 RootCert: append(currentCACerts, cautils.ECDSA256SHA256Cert...), 538 } 539 540 require.NoError(t, testutils.PollFuncWithTimeout(nil, func() error { 541 caCerts, err := ioutil.ReadFile(paths.RootCA.Cert) 542 require.NoError(t, err) 543 if bytes.Equal(currentCACerts, caCerts) { 544 return errors.New("new certificates have not been replaced yet") 545 } 546 parsedCerts, err := helpers.ParseCertificatesPEM(caCerts) 547 if err != nil { 548 return err 549 } 550 if len(parsedCerts) != 2 { 551 return fmt.Errorf("expecting 2 new certificates, got %d", len(parsedCerts)) 552 } 553 return nil 554 }, time.Second)) 555 556 require.NoError(t, node.Stop(context.Background())) 557 } 558 559 func TestCertRenewals(t *testing.T) { 560 tmpDir, err := ioutil.TempDir("", "no-top-level-role") 561 require.NoError(t, err) 562 defer os.RemoveAll(tmpDir) 563 564 paths := ca.NewConfigPaths(filepath.Join(tmpDir, "certificates")) 565 566 // don't bother with a listening socket 567 cAddr := filepath.Join(tmpDir, "control.sock") 568 cfg := &Config{ 569 ListenControlAPI: cAddr, 570 StateDir: tmpDir, 571 Executor: &agentutils.TestExecutor{}, 572 } 573 node, err := New(cfg) 574 require.NoError(t, err) 575 576 require.NoError(t, node.Start(context.Background())) 577 578 select { 579 case <-node.Ready(): 580 case <-time.After(5 * time.Second): 581 require.FailNow(t, "node did not ready in time") 582 } 583 584 currentNodeCert, err := ioutil.ReadFile(paths.Node.Cert) 585 require.NoError(t, err) 586 587 // Fake an update from the dispatcher. Make sure the Role field is 588 // ignored when DesiredRole has not changed. 589 node.notifyNodeChange <- &agent.NodeChanges{ 590 Node: &api.Node{ 591 Spec: api.NodeSpec{ 592 DesiredRole: api.NodeRoleManager, 593 }, 594 Role: api.NodeRoleWorker, 595 }, 596 } 597 598 time.Sleep(500 * time.Millisecond) 599 600 nodeCert, err := ioutil.ReadFile(paths.Node.Cert) 601 require.NoError(t, err) 602 if !bytes.Equal(currentNodeCert, nodeCert) { 603 t.Fatal("Certificate should not have been renewed") 604 } 605 606 // Fake an update from the dispatcher. When DesiredRole doesn't match 607 // the current role, a cert renewal should be triggered. 608 node.notifyNodeChange <- &agent.NodeChanges{ 609 Node: &api.Node{ 610 Spec: api.NodeSpec{ 611 DesiredRole: api.NodeRoleWorker, 612 }, 613 Role: api.NodeRoleWorker, 614 }, 615 } 616 617 require.NoError(t, testutils.PollFuncWithTimeout(nil, func() error { 618 nodeCert, err := ioutil.ReadFile(paths.Node.Cert) 619 require.NoError(t, err) 620 if bytes.Equal(currentNodeCert, nodeCert) { 621 return errors.New("certificate has not been replaced yet") 622 } 623 currentNodeCert = nodeCert 624 return nil 625 }, 5*time.Second)) 626 627 require.NoError(t, node.Stop(context.Background())) 628 } 629 630 func TestManagerFailedStartup(t *testing.T) { 631 tmpDir, err := ioutil.TempDir("", "manager-root-ca-update") 632 require.NoError(t, err) 633 defer os.RemoveAll(tmpDir) 634 635 paths := ca.NewConfigPaths(filepath.Join(tmpDir, certDirectory)) 636 637 rootCA, err := ca.CreateRootCA(ca.DefaultRootCN) 638 require.NoError(t, err) 639 require.NoError(t, ca.SaveRootCA(rootCA, paths.RootCA)) 640 641 krw := ca.NewKeyReadWriter(paths.Node, nil, nil) 642 require.NoError(t, err) 643 _, _, err = rootCA.IssueAndSaveNewCertificates(krw, identity.NewID(), ca.ManagerRole, identity.NewID()) 644 require.NoError(t, err) 645 646 // don't bother with a listening socket 647 cAddr := filepath.Join(tmpDir, "control.sock") 648 cfg := &Config{ 649 ListenControlAPI: cAddr, 650 StateDir: tmpDir, 651 Executor: &agentutils.TestExecutor{}, 652 JoinAddr: "127.0.0.1", 653 } 654 655 node, err := New(cfg) 656 require.NoError(t, err) 657 658 require.NoError(t, node.Start(context.Background())) 659 660 select { 661 case <-node.Ready(): 662 require.FailNow(t, "node should not become ready") 663 case <-time.After(5 * time.Second): 664 require.FailNow(t, "node neither became ready nor encountered an error") 665 case <-node.closed: 666 require.EqualError(t, node.err, "manager stopped: can't initialize raft node: attempted to join raft cluster without knowing own address") 667 } 668 } 669 670 // TestFIPSConfiguration ensures that new keys will be stored in PKCS8 format. 671 func TestFIPSConfiguration(t *testing.T) { 672 ctx := getLoggingContext(t) 673 tmpDir, err := ioutil.TempDir("", "fips") 674 require.NoError(t, err) 675 defer os.RemoveAll(tmpDir) 676 677 paths := ca.NewConfigPaths(filepath.Join(tmpDir, "certificates")) 678 679 // don't bother with a listening socket 680 cAddr := filepath.Join(tmpDir, "control.sock") 681 cfg := &Config{ 682 ListenControlAPI: cAddr, 683 StateDir: tmpDir, 684 Executor: &agentutils.TestExecutor{}, 685 FIPS: true, 686 } 687 node, err := New(cfg) 688 require.NoError(t, err) 689 require.NoError(t, node.Start(ctx)) 690 defer func() { 691 require.NoError(t, node.Stop(ctx)) 692 }() 693 694 select { 695 case <-node.Ready(): 696 case <-time.After(5 * time.Second): 697 require.FailNow(t, "node did not ready in time") 698 } 699 700 nodeKey, err := ioutil.ReadFile(paths.Node.Key) 701 require.NoError(t, err) 702 pemBlock, _ := pem.Decode(nodeKey) 703 require.NotNil(t, pemBlock) 704 require.True(t, keyutils.IsPKCS8(pemBlock.Bytes)) 705 }