github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/swarmkit/ca/config.go (about)

     1  package ca
     2  
     3  import (
     4  	"context"
     5  	cryptorand "crypto/rand"
     6  	"crypto/tls"
     7  	"crypto/x509"
     8  	"fmt"
     9  	"math/big"
    10  	"math/rand"
    11  	"path/filepath"
    12  	"strings"
    13  	"sync"
    14  	"time"
    15  
    16  	cfconfig "github.com/cloudflare/cfssl/config"
    17  	events "github.com/docker/go-events"
    18  	"github.com/docker/swarmkit/api"
    19  	"github.com/docker/swarmkit/connectionbroker"
    20  	"github.com/docker/swarmkit/identity"
    21  	"github.com/docker/swarmkit/log"
    22  	"github.com/docker/swarmkit/watch"
    23  	"github.com/opencontainers/go-digest"
    24  	"github.com/pkg/errors"
    25  	"github.com/sirupsen/logrus"
    26  	"google.golang.org/grpc/credentials"
    27  )
    28  
    29  const (
    30  	rootCACertFilename  = "swarm-root-ca.crt"
    31  	rootCAKeyFilename   = "swarm-root-ca.key"
    32  	nodeTLSCertFilename = "swarm-node.crt"
    33  	nodeTLSKeyFilename  = "swarm-node.key"
    34  
    35  	// DefaultRootCN represents the root CN that we should create roots CAs with by default
    36  	DefaultRootCN = "swarm-ca"
    37  	// ManagerRole represents the Manager node type, and is used for authorization to endpoints
    38  	ManagerRole = "swarm-manager"
    39  	// WorkerRole represents the Worker node type, and is used for authorization to endpoints
    40  	WorkerRole = "swarm-worker"
    41  	// CARole represents the CA node type, and is used for clients attempting to get new certificates issued
    42  	CARole = "swarm-ca"
    43  
    44  	generatedSecretEntropyBytes = 16
    45  	joinTokenBase               = 36
    46  	// ceil(log(2^128-1, 36))
    47  	maxGeneratedSecretLength = 25
    48  	// ceil(log(2^256-1, 36))
    49  	base36DigestLen = 50
    50  )
    51  
    52  var (
    53  	// GetCertRetryInterval is how long to wait before retrying a node
    54  	// certificate or root certificate request.
    55  	GetCertRetryInterval = 2 * time.Second
    56  
    57  	// errInvalidJoinToken is returned when attempting to parse an invalid join
    58  	// token (e.g. when attempting to get the version, fipsness, or the root ca
    59  	// digest)
    60  	errInvalidJoinToken = errors.New("invalid join token")
    61  )
    62  
    63  // SecurityConfig is used to represent a node's security configuration. It includes information about
    64  // the RootCA and ServerTLSCreds/ClientTLSCreds transport authenticators to be used for MTLS
    65  type SecurityConfig struct {
    66  	// mu protects against concurrent access to fields inside the structure.
    67  	mu sync.Mutex
    68  
    69  	// renewalMu makes sure only one certificate renewal attempt happens at
    70  	// a time. It should never be locked after mu is already locked.
    71  	renewalMu sync.Mutex
    72  
    73  	rootCA        *RootCA
    74  	keyReadWriter *KeyReadWriter
    75  
    76  	certificate *tls.Certificate
    77  	issuerInfo  *IssuerInfo
    78  
    79  	ServerTLSCreds *MutableTLSCreds
    80  	ClientTLSCreds *MutableTLSCreds
    81  
    82  	// An optional queue for anyone interested in subscribing to SecurityConfig updates
    83  	queue *watch.Queue
    84  }
    85  
    86  // CertificateUpdate represents a change in the underlying TLS configuration being returned by
    87  // a certificate renewal event.
    88  type CertificateUpdate struct {
    89  	Role string
    90  	Err  error
    91  }
    92  
    93  // ParsedJoinToken is the data from a join token, once parsed
    94  type ParsedJoinToken struct {
    95  	// Version is the version of the join token that is being parsed
    96  	Version int
    97  
    98  	// RootDigest is the digest of the root CA certificate of the cluster, which
    99  	// is always part of the join token so that the root CA can be verified
   100  	// upon initial node join
   101  	RootDigest digest.Digest
   102  
   103  	// Secret is the randomly-generated secret part of the join token - when
   104  	// rotating a join token, this is the value that is changed unless some other
   105  	// property of the cluster (like the root CA) is changed.
   106  	Secret string
   107  
   108  	// FIPS indicates whether the join token specifies that the cluster mandates
   109  	// that all nodes must have FIPS mode enabled.
   110  	FIPS bool
   111  }
   112  
   113  // ParseJoinToken parses a join token.  Current format is v2, but this is currently used only if the cluster requires
   114  // mandatory FIPS, in order to facilitate mixed version clusters.
   115  // v1: SWMTKN-1-<SHA256 digest of root CA cert in base 36, 0-left-padded to 50 chars>-<16-byte secret in base 36 0-left-padded to 25 chars>
   116  // v2: SWMTKN-2-<0/1 whether its FIPS or not>-<same rest of data as v1>
   117  func ParseJoinToken(token string) (*ParsedJoinToken, error) {
   118  	split := strings.Split(token, "-")
   119  	numParts := len(split)
   120  
   121  	// v1 has 4, v2 has 5
   122  	if numParts < 4 || split[0] != "SWMTKN" {
   123  		return nil, errInvalidJoinToken
   124  	}
   125  
   126  	var (
   127  		version int
   128  		fips    bool
   129  	)
   130  
   131  	switch split[1] {
   132  	case "1":
   133  		if numParts != 4 {
   134  			return nil, errInvalidJoinToken
   135  		}
   136  		version = 1
   137  	case "2":
   138  		if numParts != 5 || (split[2] != "0" && split[2] != "1") {
   139  			return nil, errInvalidJoinToken
   140  		}
   141  		version = 2
   142  		fips = split[2] == "1"
   143  	default:
   144  		return nil, errInvalidJoinToken
   145  	}
   146  
   147  	secret := split[numParts-1]
   148  	rootDigest := split[numParts-2]
   149  	if len(rootDigest) != base36DigestLen || len(secret) != maxGeneratedSecretLength {
   150  		return nil, errInvalidJoinToken
   151  	}
   152  
   153  	var digestInt big.Int
   154  	digestInt.SetString(rootDigest, joinTokenBase)
   155  
   156  	d, err := digest.Parse(fmt.Sprintf("sha256:%0[1]*s", 64, digestInt.Text(16)))
   157  	if err != nil {
   158  		return nil, err
   159  	}
   160  	return &ParsedJoinToken{
   161  		Version:    version,
   162  		RootDigest: d,
   163  		Secret:     secret,
   164  		FIPS:       fips,
   165  	}, nil
   166  }
   167  
   168  func validateRootCAAndTLSCert(rootCA *RootCA, tlsKeyPair *tls.Certificate) error {
   169  	var (
   170  		leafCert         *x509.Certificate
   171  		intermediatePool *x509.CertPool
   172  	)
   173  	for i, derBytes := range tlsKeyPair.Certificate {
   174  		parsed, err := x509.ParseCertificate(derBytes)
   175  		if err != nil {
   176  			return errors.Wrap(err, "could not validate new root certificates due to parse error")
   177  		}
   178  		if i == 0 {
   179  			leafCert = parsed
   180  		} else {
   181  			if intermediatePool == nil {
   182  				intermediatePool = x509.NewCertPool()
   183  			}
   184  			intermediatePool.AddCert(parsed)
   185  		}
   186  	}
   187  	opts := x509.VerifyOptions{
   188  		Roots:         rootCA.Pool,
   189  		Intermediates: intermediatePool,
   190  	}
   191  	if _, err := leafCert.Verify(opts); err != nil {
   192  		return errors.Wrap(err, "new root CA does not match existing TLS credentials")
   193  	}
   194  	return nil
   195  }
   196  
   197  // NewSecurityConfig initializes and returns a new SecurityConfig.
   198  func NewSecurityConfig(rootCA *RootCA, krw *KeyReadWriter, tlsKeyPair *tls.Certificate, issuerInfo *IssuerInfo) (*SecurityConfig, func() error, error) {
   199  	// Create the Server TLS Credentials for this node. These will not be used by workers.
   200  	serverTLSCreds, err := rootCA.NewServerTLSCredentials(tlsKeyPair)
   201  	if err != nil {
   202  		return nil, nil, err
   203  	}
   204  
   205  	// Create a TLSConfig to be used when this node connects as a client to another remote node.
   206  	// We're using ManagerRole as remote serverName for TLS host verification because both workers
   207  	// and managers always connect to remote managers.
   208  	clientTLSCreds, err := rootCA.NewClientTLSCredentials(tlsKeyPair, ManagerRole)
   209  	if err != nil {
   210  		return nil, nil, err
   211  	}
   212  
   213  	q := watch.NewQueue()
   214  	return &SecurityConfig{
   215  		rootCA:        rootCA,
   216  		keyReadWriter: krw,
   217  
   218  		certificate: tlsKeyPair,
   219  		issuerInfo:  issuerInfo,
   220  		queue:       q,
   221  
   222  		ClientTLSCreds: clientTLSCreds,
   223  		ServerTLSCreds: serverTLSCreds,
   224  	}, q.Close, nil
   225  }
   226  
   227  // RootCA returns the root CA.
   228  func (s *SecurityConfig) RootCA() *RootCA {
   229  	s.mu.Lock()
   230  	defer s.mu.Unlock()
   231  
   232  	return s.rootCA
   233  }
   234  
   235  // KeyWriter returns the object that can write keys to disk
   236  func (s *SecurityConfig) KeyWriter() KeyWriter {
   237  	return s.keyReadWriter
   238  }
   239  
   240  // KeyReader returns the object that can read keys from disk
   241  func (s *SecurityConfig) KeyReader() KeyReader {
   242  	return s.keyReadWriter
   243  }
   244  
   245  // UpdateRootCA replaces the root CA with a new root CA
   246  func (s *SecurityConfig) UpdateRootCA(rootCA *RootCA) error {
   247  	s.mu.Lock()
   248  	defer s.mu.Unlock()
   249  
   250  	// refuse to update the root CA if the current TLS credentials do not validate against it
   251  	if err := validateRootCAAndTLSCert(rootCA, s.certificate); err != nil {
   252  		return err
   253  	}
   254  
   255  	s.rootCA = rootCA
   256  	return s.updateTLSCredentials(s.certificate, s.issuerInfo)
   257  }
   258  
   259  // Watch allows you to set a watch on the security config, in order to be notified of any changes
   260  func (s *SecurityConfig) Watch() (chan events.Event, func()) {
   261  	return s.queue.Watch()
   262  }
   263  
   264  // IssuerInfo returns the issuer subject and issuer public key
   265  func (s *SecurityConfig) IssuerInfo() *IssuerInfo {
   266  	s.mu.Lock()
   267  	defer s.mu.Unlock()
   268  	return s.issuerInfo
   269  }
   270  
   271  // This function expects something else to have taken out a lock on the SecurityConfig.
   272  func (s *SecurityConfig) updateTLSCredentials(certificate *tls.Certificate, issuerInfo *IssuerInfo) error {
   273  	certs := []tls.Certificate{*certificate}
   274  	clientConfig, err := NewClientTLSConfig(certs, s.rootCA.Pool, ManagerRole)
   275  	if err != nil {
   276  		return errors.Wrap(err, "failed to create a new client config using the new root CA")
   277  	}
   278  
   279  	serverConfig, err := NewServerTLSConfig(certs, s.rootCA.Pool)
   280  	if err != nil {
   281  		return errors.Wrap(err, "failed to create a new server config using the new root CA")
   282  	}
   283  
   284  	if err := s.ClientTLSCreds.loadNewTLSConfig(clientConfig); err != nil {
   285  		return errors.Wrap(err, "failed to update the client credentials")
   286  	}
   287  
   288  	if err := s.ServerTLSCreds.loadNewTLSConfig(serverConfig); err != nil {
   289  		return errors.Wrap(err, "failed to update the server TLS credentials")
   290  	}
   291  
   292  	s.certificate = certificate
   293  	s.issuerInfo = issuerInfo
   294  	if s.queue != nil {
   295  		s.queue.Publish(&api.NodeTLSInfo{
   296  			TrustRoot:           s.rootCA.Certs,
   297  			CertIssuerPublicKey: s.issuerInfo.PublicKey,
   298  			CertIssuerSubject:   s.issuerInfo.Subject,
   299  		})
   300  	}
   301  	return nil
   302  }
   303  
   304  // UpdateTLSCredentials updates the security config with an updated TLS certificate and issuer info
   305  func (s *SecurityConfig) UpdateTLSCredentials(certificate *tls.Certificate, issuerInfo *IssuerInfo) error {
   306  	s.mu.Lock()
   307  	defer s.mu.Unlock()
   308  	return s.updateTLSCredentials(certificate, issuerInfo)
   309  }
   310  
   311  // SigningPolicy creates a policy used by the signer to ensure that the only fields
   312  // from the remote CSRs we trust are: PublicKey, PublicKeyAlgorithm and SignatureAlgorithm.
   313  // It receives the duration a certificate will be valid for
   314  func SigningPolicy(certExpiry time.Duration) *cfconfig.Signing {
   315  	// Force the minimum Certificate expiration to be fifteen minutes
   316  	if certExpiry < MinNodeCertExpiration {
   317  		certExpiry = DefaultNodeCertExpiration
   318  	}
   319  
   320  	// Add the backdate
   321  	certExpiry = certExpiry + CertBackdate
   322  
   323  	return &cfconfig.Signing{
   324  		Default: &cfconfig.SigningProfile{
   325  			Usage:    []string{"signing", "key encipherment", "server auth", "client auth"},
   326  			Expiry:   certExpiry,
   327  			Backdate: CertBackdate,
   328  			// Only trust the key components from the CSR. Everything else should
   329  			// come directly from API call params.
   330  			CSRWhitelist: &cfconfig.CSRWhitelist{
   331  				PublicKey:          true,
   332  				PublicKeyAlgorithm: true,
   333  				SignatureAlgorithm: true,
   334  			},
   335  		},
   336  	}
   337  }
   338  
   339  // SecurityConfigPaths is used as a helper to hold all the paths of security relevant files
   340  type SecurityConfigPaths struct {
   341  	Node, RootCA CertPaths
   342  }
   343  
   344  // NewConfigPaths returns the absolute paths to all of the different types of files
   345  func NewConfigPaths(baseCertDir string) *SecurityConfigPaths {
   346  	return &SecurityConfigPaths{
   347  		Node: CertPaths{
   348  			Cert: filepath.Join(baseCertDir, nodeTLSCertFilename),
   349  			Key:  filepath.Join(baseCertDir, nodeTLSKeyFilename)},
   350  		RootCA: CertPaths{
   351  			Cert: filepath.Join(baseCertDir, rootCACertFilename),
   352  			Key:  filepath.Join(baseCertDir, rootCAKeyFilename)},
   353  	}
   354  }
   355  
   356  // GenerateJoinToken creates a new join token. Current format is v2, but this is
   357  // currently used only if the cluster requires mandatory FIPS, in order to
   358  // facilitate mixed version clusters (the `fips` parameter is set to true).
   359  // Otherwise, v1 is used so as to maintain compatibility in mixed version
   360  // non-FIPS clusters.
   361  // v1: SWMTKN-1-<SHA256 digest of root CA cert in base 36, 0-left-padded to 50 chars>-<16-byte secret in base 36 0-left-padded to 25 chars>
   362  // v2: SWMTKN-2-<0/1 whether its FIPS or not>-<same rest of data as v1>
   363  func GenerateJoinToken(rootCA *RootCA, fips bool) string {
   364  	var secretBytes [generatedSecretEntropyBytes]byte
   365  
   366  	if _, err := cryptorand.Read(secretBytes[:]); err != nil {
   367  		panic(fmt.Errorf("failed to read random bytes: %v", err))
   368  	}
   369  
   370  	var nn, digest big.Int
   371  	nn.SetBytes(secretBytes[:])
   372  	digest.SetString(rootCA.Digest.Hex(), 16)
   373  
   374  	fmtString := "SWMTKN-1-%0[1]*s-%0[3]*s"
   375  	if fips {
   376  		fmtString = "SWMTKN-2-1-%0[1]*s-%0[3]*s"
   377  	}
   378  	return fmt.Sprintf(fmtString, base36DigestLen,
   379  		digest.Text(joinTokenBase), maxGeneratedSecretLength, nn.Text(joinTokenBase))
   380  }
   381  
   382  // DownloadRootCA tries to retrieve a remote root CA and matches the digest against the provided token.
   383  func DownloadRootCA(ctx context.Context, paths CertPaths, token string, connBroker *connectionbroker.Broker) (RootCA, error) {
   384  	var rootCA RootCA
   385  	// Get a digest for the optional CA hash string that we've been provided
   386  	// If we were provided a non-empty string, and it is an invalid hash, return
   387  	// otherwise, allow the invalid digest through.
   388  	var (
   389  		d   digest.Digest
   390  		err error
   391  	)
   392  	if token != "" {
   393  		parsed, err := ParseJoinToken(token)
   394  		if err != nil {
   395  			return RootCA{}, err
   396  		}
   397  		d = parsed.RootDigest
   398  	}
   399  	// Get the remote CA certificate, verify integrity with the
   400  	// hash provided. Retry up to 5 times, in case the manager we
   401  	// first try to contact is not responding properly (it may have
   402  	// just been demoted, for example).
   403  
   404  	for i := 0; i != 5; i++ {
   405  		rootCA, err = GetRemoteCA(ctx, d, connBroker)
   406  		if err == nil {
   407  			break
   408  		}
   409  		log.G(ctx).WithError(err).Errorf("failed to retrieve remote root CA certificate")
   410  
   411  		select {
   412  		case <-time.After(GetCertRetryInterval):
   413  		case <-ctx.Done():
   414  			return RootCA{}, ctx.Err()
   415  		}
   416  	}
   417  	if err != nil {
   418  		return RootCA{}, err
   419  	}
   420  
   421  	// Save root CA certificate to disk
   422  	if err = SaveRootCA(rootCA, paths); err != nil {
   423  		return RootCA{}, err
   424  	}
   425  
   426  	log.G(ctx).Debugf("retrieved remote CA certificate: %s", paths.Cert)
   427  	return rootCA, nil
   428  }
   429  
   430  // LoadSecurityConfig loads TLS credentials from disk, or returns an error if
   431  // these credentials do not exist or are unusable.
   432  func LoadSecurityConfig(ctx context.Context, rootCA RootCA, krw *KeyReadWriter, allowExpired bool) (*SecurityConfig, func() error, error) {
   433  	ctx = log.WithModule(ctx, "tls")
   434  
   435  	// At this point we've successfully loaded the CA details from disk, or
   436  	// successfully downloaded them remotely. The next step is to try to
   437  	// load our certificates.
   438  
   439  	// Read both the Cert and Key from disk
   440  	cert, key, err := krw.Read()
   441  	if err != nil {
   442  		return nil, nil, err
   443  	}
   444  
   445  	// Check to see if this certificate was signed by our CA, and isn't expired
   446  	_, chains, err := ValidateCertChain(rootCA.Pool, cert, allowExpired)
   447  	if err != nil {
   448  		return nil, nil, err
   449  	}
   450  	// ValidateChain, if successful, will always return at least 1 chain containing
   451  	// at least 2 certificates:  the leaf and the root.
   452  	issuer := chains[0][1]
   453  
   454  	// Now that we know this certificate is valid, create a TLS Certificate for our
   455  	// credentials
   456  	keyPair, err := tls.X509KeyPair(cert, key)
   457  	if err != nil {
   458  		return nil, nil, err
   459  	}
   460  
   461  	secConfig, cleanup, err := NewSecurityConfig(&rootCA, krw, &keyPair, &IssuerInfo{
   462  		Subject:   issuer.RawSubject,
   463  		PublicKey: issuer.RawSubjectPublicKeyInfo,
   464  	})
   465  	if err == nil {
   466  		log.G(ctx).WithFields(logrus.Fields{
   467  			"node.id":   secConfig.ClientTLSCreds.NodeID(),
   468  			"node.role": secConfig.ClientTLSCreds.Role(),
   469  		}).Debug("loaded node credentials")
   470  	}
   471  	return secConfig, cleanup, err
   472  }
   473  
   474  // CertificateRequestConfig contains the information needed to request a
   475  // certificate from a remote CA.
   476  type CertificateRequestConfig struct {
   477  	// Token is the join token that authenticates us with the CA.
   478  	Token string
   479  	// Availability allows a user to control the current scheduling status of a node
   480  	Availability api.NodeSpec_Availability
   481  	// ConnBroker provides connections to CAs.
   482  	ConnBroker *connectionbroker.Broker
   483  	// Credentials provides transport credentials for communicating with the
   484  	// remote server.
   485  	Credentials credentials.TransportCredentials
   486  	// ForceRemote specifies that only a remote (TCP) connection should
   487  	// be used to request the certificate. This may be necessary in cases
   488  	// where the local node is running a manager, but is in the process of
   489  	// being demoted.
   490  	ForceRemote bool
   491  	// NodeCertificateStatusRequestTimeout determines how long to wait for a node
   492  	// status RPC result.  If not provided (zero value), will default to 5 seconds.
   493  	NodeCertificateStatusRequestTimeout time.Duration
   494  	// RetryInterval specifies how long to delay between retries, if non-zero.
   495  	RetryInterval time.Duration
   496  	// Organization is the organization to use for a TLS certificate when creating
   497  	// a security config from scratch.  If not provided, a random ID is generated.
   498  	// For swarm certificates, the organization is the cluster ID.
   499  	Organization string
   500  }
   501  
   502  // CreateSecurityConfig creates a new key and cert for this node, either locally
   503  // or via a remote CA.
   504  func (rootCA RootCA) CreateSecurityConfig(ctx context.Context, krw *KeyReadWriter, config CertificateRequestConfig) (*SecurityConfig, func() error, error) {
   505  	ctx = log.WithModule(ctx, "tls")
   506  
   507  	// Create a new random ID for this certificate
   508  	cn := identity.NewID()
   509  	org := config.Organization
   510  	if config.Organization == "" {
   511  		org = identity.NewID()
   512  	}
   513  
   514  	proposedRole := ManagerRole
   515  	tlsKeyPair, issuerInfo, err := rootCA.IssueAndSaveNewCertificates(krw, cn, proposedRole, org)
   516  	switch errors.Cause(err) {
   517  	case ErrNoValidSigner:
   518  		config.RetryInterval = GetCertRetryInterval
   519  		// Request certificate issuance from a remote CA.
   520  		// Last argument is nil because at this point we don't have any valid TLS creds
   521  		tlsKeyPair, issuerInfo, err = rootCA.RequestAndSaveNewCertificates(ctx, krw, config)
   522  		if err != nil {
   523  			log.G(ctx).WithError(err).Error("failed to request and save new certificate")
   524  			return nil, nil, err
   525  		}
   526  	case nil:
   527  		log.G(ctx).WithFields(logrus.Fields{
   528  			"node.id":   cn,
   529  			"node.role": proposedRole,
   530  		}).Debug("issued new TLS certificate")
   531  	default:
   532  		log.G(ctx).WithFields(logrus.Fields{
   533  			"node.id":   cn,
   534  			"node.role": proposedRole,
   535  		}).WithError(err).Errorf("failed to issue and save new certificate")
   536  		return nil, nil, err
   537  	}
   538  
   539  	secConfig, cleanup, err := NewSecurityConfig(&rootCA, krw, tlsKeyPair, issuerInfo)
   540  	if err == nil {
   541  		log.G(ctx).WithFields(logrus.Fields{
   542  			"node.id":   secConfig.ClientTLSCreds.NodeID(),
   543  			"node.role": secConfig.ClientTLSCreds.Role(),
   544  		}).Debugf("new node credentials generated: %s", krw.Target())
   545  	}
   546  	return secConfig, cleanup, err
   547  }
   548  
   549  // TODO(cyli): currently we have to only update if it's a worker role - if we have a single root CA update path for
   550  // both managers and workers, we won't need to check any more.
   551  func updateRootThenUpdateCert(ctx context.Context, s *SecurityConfig, connBroker *connectionbroker.Broker, rootPaths CertPaths, failedCert *x509.Certificate) (*tls.Certificate, *IssuerInfo, error) {
   552  	if len(failedCert.Subject.OrganizationalUnit) == 0 || failedCert.Subject.OrganizationalUnit[0] != WorkerRole {
   553  		return nil, nil, errors.New("cannot update root CA since this is not a worker")
   554  	}
   555  	// try downloading a new root CA if it's an unknown authority issue, in case there was a root rotation completion
   556  	// and we just didn't get the new root
   557  	rootCA, err := GetRemoteCA(ctx, "", connBroker)
   558  	if err != nil {
   559  		return nil, nil, err
   560  	}
   561  	// validate against the existing security config creds
   562  	if err := s.UpdateRootCA(&rootCA); err != nil {
   563  		return nil, nil, err
   564  	}
   565  	if err := SaveRootCA(rootCA, rootPaths); err != nil {
   566  		return nil, nil, err
   567  	}
   568  	return rootCA.RequestAndSaveNewCertificates(ctx, s.KeyWriter(),
   569  		CertificateRequestConfig{
   570  			ConnBroker:  connBroker,
   571  			Credentials: s.ClientTLSCreds,
   572  		})
   573  }
   574  
   575  // RenewTLSConfigNow gets a new TLS cert and key, and updates the security config if provided.  This is similar to
   576  // RenewTLSConfig, except while that monitors for expiry, and periodically renews, this renews once and is blocking
   577  func RenewTLSConfigNow(ctx context.Context, s *SecurityConfig, connBroker *connectionbroker.Broker, rootPaths CertPaths) error {
   578  	s.renewalMu.Lock()
   579  	defer s.renewalMu.Unlock()
   580  
   581  	ctx = log.WithModule(ctx, "tls")
   582  	log := log.G(ctx).WithFields(logrus.Fields{
   583  		"node.id":   s.ClientTLSCreds.NodeID(),
   584  		"node.role": s.ClientTLSCreds.Role(),
   585  	})
   586  
   587  	// Let's request new certs. Renewals don't require a token.
   588  	rootCA := s.RootCA()
   589  	tlsKeyPair, issuerInfo, err := rootCA.RequestAndSaveNewCertificates(ctx,
   590  		s.KeyWriter(),
   591  		CertificateRequestConfig{
   592  			ConnBroker:  connBroker,
   593  			Credentials: s.ClientTLSCreds,
   594  		})
   595  	if wrappedError, ok := err.(x509UnknownAuthError); ok {
   596  		var newErr error
   597  		tlsKeyPair, issuerInfo, newErr = updateRootThenUpdateCert(ctx, s, connBroker, rootPaths, wrappedError.failedLeafCert)
   598  		if newErr != nil {
   599  			err = wrappedError.error
   600  		} else {
   601  			err = nil
   602  		}
   603  	}
   604  	if err != nil {
   605  		log.WithError(err).Errorf("failed to renew the certificate")
   606  		return err
   607  	}
   608  
   609  	return s.UpdateTLSCredentials(tlsKeyPair, issuerInfo)
   610  }
   611  
   612  // calculateRandomExpiry returns a random duration between 50% and 80% of the
   613  // original validity period
   614  func calculateRandomExpiry(validFrom, validUntil time.Time) time.Duration {
   615  	duration := validUntil.Sub(validFrom)
   616  
   617  	var randomExpiry int
   618  	// Our lower bound of renewal will be half of the total expiration time
   619  	minValidity := int(duration.Minutes() * CertLowerRotationRange)
   620  	// Our upper bound of renewal will be 80% of the total expiration time
   621  	maxValidity := int(duration.Minutes() * CertUpperRotationRange)
   622  	// Let's select a random number of minutes between min and max, and set our retry for that
   623  	// Using randomly selected rotation allows us to avoid certificate thundering herds.
   624  	if maxValidity-minValidity < 1 {
   625  		randomExpiry = minValidity
   626  	} else {
   627  		randomExpiry = rand.Intn(maxValidity-minValidity) + minValidity
   628  	}
   629  
   630  	expiry := time.Until(validFrom.Add(time.Duration(randomExpiry) * time.Minute))
   631  	if expiry < 0 {
   632  		return 0
   633  	}
   634  	return expiry
   635  }
   636  
   637  // NewServerTLSConfig returns a tls.Config configured for a TLS Server, given a tls.Certificate
   638  // and the PEM-encoded root CA Certificate
   639  func NewServerTLSConfig(certs []tls.Certificate, rootCAPool *x509.CertPool) (*tls.Config, error) {
   640  	if rootCAPool == nil {
   641  		return nil, errors.New("valid root CA pool required")
   642  	}
   643  
   644  	return &tls.Config{
   645  		Certificates: certs,
   646  		// Since we're using the same CA server to issue Certificates to new nodes, we can't
   647  		// use tls.RequireAndVerifyClientCert
   648  		ClientAuth:               tls.VerifyClientCertIfGiven,
   649  		RootCAs:                  rootCAPool,
   650  		ClientCAs:                rootCAPool,
   651  		PreferServerCipherSuites: true,
   652  		MinVersion:               tls.VersionTLS12,
   653  	}, nil
   654  }
   655  
   656  // NewClientTLSConfig returns a tls.Config configured for a TLS Client, given a tls.Certificate
   657  // the PEM-encoded root CA Certificate, and the name of the remote server the client wants to connect to.
   658  func NewClientTLSConfig(certs []tls.Certificate, rootCAPool *x509.CertPool, serverName string) (*tls.Config, error) {
   659  	if rootCAPool == nil {
   660  		return nil, errors.New("valid root CA pool required")
   661  	}
   662  
   663  	return &tls.Config{
   664  		ServerName:   serverName,
   665  		Certificates: certs,
   666  		RootCAs:      rootCAPool,
   667  		MinVersion:   tls.VersionTLS12,
   668  	}, nil
   669  }
   670  
   671  // NewClientTLSCredentials returns GRPC credentials for a TLS GRPC client, given a tls.Certificate
   672  // a PEM-Encoded root CA Certificate, and the name of the remote server the client wants to connect to.
   673  func (rootCA *RootCA) NewClientTLSCredentials(cert *tls.Certificate, serverName string) (*MutableTLSCreds, error) {
   674  	tlsConfig, err := NewClientTLSConfig([]tls.Certificate{*cert}, rootCA.Pool, serverName)
   675  	if err != nil {
   676  		return nil, err
   677  	}
   678  
   679  	mtls, err := NewMutableTLS(tlsConfig)
   680  
   681  	return mtls, err
   682  }
   683  
   684  // NewServerTLSCredentials returns GRPC credentials for a TLS GRPC client, given a tls.Certificate
   685  // a PEM-Encoded root CA Certificate, and the name of the remote server the client wants to connect to.
   686  func (rootCA *RootCA) NewServerTLSCredentials(cert *tls.Certificate) (*MutableTLSCreds, error) {
   687  	tlsConfig, err := NewServerTLSConfig([]tls.Certificate{*cert}, rootCA.Pool)
   688  	if err != nil {
   689  		return nil, err
   690  	}
   691  
   692  	mtls, err := NewMutableTLS(tlsConfig)
   693  
   694  	return mtls, err
   695  }
   696  
   697  // ParseRole parses an apiRole into an internal role string
   698  func ParseRole(apiRole api.NodeRole) (string, error) {
   699  	switch apiRole {
   700  	case api.NodeRoleManager:
   701  		return ManagerRole, nil
   702  	case api.NodeRoleWorker:
   703  		return WorkerRole, nil
   704  	default:
   705  		return "", errors.Errorf("failed to parse api role: %v", apiRole)
   706  	}
   707  }
   708  
   709  // FormatRole parses an internal role string into an apiRole
   710  func FormatRole(role string) (api.NodeRole, error) {
   711  	switch strings.ToLower(role) {
   712  	case strings.ToLower(ManagerRole):
   713  		return api.NodeRoleManager, nil
   714  	case strings.ToLower(WorkerRole):
   715  		return api.NodeRoleWorker, nil
   716  	default:
   717  		return 0, errors.Errorf("failed to parse role: %s", role)
   718  	}
   719  }