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 }