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  }