config.go 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639
  1. package ca
  2. import (
  3. cryptorand "crypto/rand"
  4. "crypto/tls"
  5. "crypto/x509"
  6. "encoding/pem"
  7. "fmt"
  8. "math/big"
  9. "math/rand"
  10. "path/filepath"
  11. "strings"
  12. "sync"
  13. "time"
  14. "github.com/Sirupsen/logrus"
  15. cfconfig "github.com/cloudflare/cfssl/config"
  16. events "github.com/docker/go-events"
  17. "github.com/docker/swarmkit/api"
  18. "github.com/docker/swarmkit/connectionbroker"
  19. "github.com/docker/swarmkit/identity"
  20. "github.com/docker/swarmkit/log"
  21. "github.com/opencontainers/go-digest"
  22. "github.com/pkg/errors"
  23. "google.golang.org/grpc/credentials"
  24. "golang.org/x/net/context"
  25. )
  26. const (
  27. rootCACertFilename = "swarm-root-ca.crt"
  28. rootCAKeyFilename = "swarm-root-ca.key"
  29. nodeTLSCertFilename = "swarm-node.crt"
  30. nodeTLSKeyFilename = "swarm-node.key"
  31. nodeCSRFilename = "swarm-node.csr"
  32. // DefaultRootCN represents the root CN that we should create roots CAs with by default
  33. DefaultRootCN = "swarm-ca"
  34. // ManagerRole represents the Manager node type, and is used for authorization to endpoints
  35. ManagerRole = "swarm-manager"
  36. // WorkerRole represents the Worker node type, and is used for authorization to endpoints
  37. WorkerRole = "swarm-worker"
  38. // CARole represents the CA node type, and is used for clients attempting to get new certificates issued
  39. CARole = "swarm-ca"
  40. generatedSecretEntropyBytes = 16
  41. joinTokenBase = 36
  42. // ceil(log(2^128-1, 36))
  43. maxGeneratedSecretLength = 25
  44. // ceil(log(2^256-1, 36))
  45. base36DigestLen = 50
  46. )
  47. // RenewTLSExponentialBackoff sets the exponential backoff when trying to renew TLS certificates that have expired
  48. var RenewTLSExponentialBackoff = events.ExponentialBackoffConfig{
  49. Base: time.Second * 5,
  50. Factor: time.Minute,
  51. Max: 1 * time.Hour,
  52. }
  53. // SecurityConfig is used to represent a node's security configuration. It includes information about
  54. // the RootCA and ServerTLSCreds/ClientTLSCreds transport authenticators to be used for MTLS
  55. type SecurityConfig struct {
  56. // mu protects against concurrent access to fields inside the structure.
  57. mu sync.Mutex
  58. // renewalMu makes sure only one certificate renewal attempt happens at
  59. // a time. It should never be locked after mu is already locked.
  60. renewalMu sync.Mutex
  61. rootCA *RootCA
  62. externalCA *ExternalCA
  63. keyReadWriter *KeyReadWriter
  64. ServerTLSCreds *MutableTLSCreds
  65. ClientTLSCreds *MutableTLSCreds
  66. }
  67. // CertificateUpdate represents a change in the underlying TLS configuration being returned by
  68. // a certificate renewal event.
  69. type CertificateUpdate struct {
  70. Role string
  71. Err error
  72. }
  73. // NewSecurityConfig initializes and returns a new SecurityConfig.
  74. func NewSecurityConfig(rootCA *RootCA, krw *KeyReadWriter, clientTLSCreds, serverTLSCreds *MutableTLSCreds) *SecurityConfig {
  75. // Make a new TLS config for the external CA client without a
  76. // ServerName value set.
  77. clientTLSConfig := clientTLSCreds.Config()
  78. externalCATLSConfig := &tls.Config{
  79. Certificates: clientTLSConfig.Certificates,
  80. RootCAs: clientTLSConfig.RootCAs,
  81. MinVersion: tls.VersionTLS12,
  82. }
  83. return &SecurityConfig{
  84. rootCA: rootCA,
  85. keyReadWriter: krw,
  86. externalCA: NewExternalCA(rootCA, externalCATLSConfig),
  87. ClientTLSCreds: clientTLSCreds,
  88. ServerTLSCreds: serverTLSCreds,
  89. }
  90. }
  91. // RootCA returns the root CA.
  92. func (s *SecurityConfig) RootCA() *RootCA {
  93. s.mu.Lock()
  94. defer s.mu.Unlock()
  95. return s.rootCA
  96. }
  97. // KeyWriter returns the object that can write keys to disk
  98. func (s *SecurityConfig) KeyWriter() KeyWriter {
  99. return s.keyReadWriter
  100. }
  101. // KeyReader returns the object that can read keys from disk
  102. func (s *SecurityConfig) KeyReader() KeyReader {
  103. return s.keyReadWriter
  104. }
  105. // UpdateRootCA replaces the root CA with a new root CA based on the specified
  106. // certificate, key, and the number of hours the certificates issue should last.
  107. func (s *SecurityConfig) UpdateRootCA(cert, key []byte, certExpiry time.Duration) error {
  108. s.mu.Lock()
  109. defer s.mu.Unlock()
  110. rootCA, err := NewRootCA(cert, key, certExpiry)
  111. if err == nil {
  112. s.rootCA = &rootCA
  113. }
  114. return err
  115. }
  116. // SigningPolicy creates a policy used by the signer to ensure that the only fields
  117. // from the remote CSRs we trust are: PublicKey, PublicKeyAlgorithm and SignatureAlgorithm.
  118. // It receives the duration a certificate will be valid for
  119. func SigningPolicy(certExpiry time.Duration) *cfconfig.Signing {
  120. // Force the minimum Certificate expiration to be fifteen minutes
  121. if certExpiry < MinNodeCertExpiration {
  122. certExpiry = DefaultNodeCertExpiration
  123. }
  124. // Add the backdate
  125. certExpiry = certExpiry + CertBackdate
  126. return &cfconfig.Signing{
  127. Default: &cfconfig.SigningProfile{
  128. Usage: []string{"signing", "key encipherment", "server auth", "client auth"},
  129. Expiry: certExpiry,
  130. Backdate: CertBackdate,
  131. // Only trust the key components from the CSR. Everything else should
  132. // come directly from API call params.
  133. CSRWhitelist: &cfconfig.CSRWhitelist{
  134. PublicKey: true,
  135. PublicKeyAlgorithm: true,
  136. SignatureAlgorithm: true,
  137. },
  138. },
  139. }
  140. }
  141. // SecurityConfigPaths is used as a helper to hold all the paths of security relevant files
  142. type SecurityConfigPaths struct {
  143. Node, RootCA CertPaths
  144. }
  145. // NewConfigPaths returns the absolute paths to all of the different types of files
  146. func NewConfigPaths(baseCertDir string) *SecurityConfigPaths {
  147. return &SecurityConfigPaths{
  148. Node: CertPaths{
  149. Cert: filepath.Join(baseCertDir, nodeTLSCertFilename),
  150. Key: filepath.Join(baseCertDir, nodeTLSKeyFilename)},
  151. RootCA: CertPaths{
  152. Cert: filepath.Join(baseCertDir, rootCACertFilename),
  153. Key: filepath.Join(baseCertDir, rootCAKeyFilename)},
  154. }
  155. }
  156. // GenerateJoinToken creates a new join token.
  157. func GenerateJoinToken(rootCA *RootCA) string {
  158. var secretBytes [generatedSecretEntropyBytes]byte
  159. if _, err := cryptorand.Read(secretBytes[:]); err != nil {
  160. panic(fmt.Errorf("failed to read random bytes: %v", err))
  161. }
  162. var nn, digest big.Int
  163. nn.SetBytes(secretBytes[:])
  164. digest.SetString(rootCA.Digest.Hex(), 16)
  165. return fmt.Sprintf("SWMTKN-1-%0[1]*s-%0[3]*s", base36DigestLen, digest.Text(joinTokenBase), maxGeneratedSecretLength, nn.Text(joinTokenBase))
  166. }
  167. func getCAHashFromToken(token string) (digest.Digest, error) {
  168. split := strings.Split(token, "-")
  169. if len(split) != 4 || split[0] != "SWMTKN" || split[1] != "1" || len(split[2]) != base36DigestLen || len(split[3]) != maxGeneratedSecretLength {
  170. return "", errors.New("invalid join token")
  171. }
  172. var digestInt big.Int
  173. digestInt.SetString(split[2], joinTokenBase)
  174. return digest.Parse(fmt.Sprintf("sha256:%0[1]*s", 64, digestInt.Text(16)))
  175. }
  176. // DownloadRootCA tries to retrieve a remote root CA and matches the digest against the provided token.
  177. func DownloadRootCA(ctx context.Context, paths CertPaths, token string, connBroker *connectionbroker.Broker) (RootCA, error) {
  178. var rootCA RootCA
  179. // Get a digest for the optional CA hash string that we've been provided
  180. // If we were provided a non-empty string, and it is an invalid hash, return
  181. // otherwise, allow the invalid digest through.
  182. var (
  183. d digest.Digest
  184. err error
  185. )
  186. if token != "" {
  187. d, err = getCAHashFromToken(token)
  188. if err != nil {
  189. return RootCA{}, err
  190. }
  191. }
  192. // Get the remote CA certificate, verify integrity with the
  193. // hash provided. Retry up to 5 times, in case the manager we
  194. // first try to contact is not responding properly (it may have
  195. // just been demoted, for example).
  196. for i := 0; i != 5; i++ {
  197. rootCA, err = GetRemoteCA(ctx, d, connBroker)
  198. if err == nil {
  199. break
  200. }
  201. log.G(ctx).WithError(err).Errorf("failed to retrieve remote root CA certificate")
  202. }
  203. if err != nil {
  204. return RootCA{}, err
  205. }
  206. // Save root CA certificate to disk
  207. if err = saveRootCA(rootCA, paths); err != nil {
  208. return RootCA{}, err
  209. }
  210. log.G(ctx).Debugf("retrieved remote CA certificate: %s", paths.Cert)
  211. return rootCA, nil
  212. }
  213. // LoadSecurityConfig loads TLS credentials from disk, or returns an error if
  214. // these credentials do not exist or are unusable.
  215. func LoadSecurityConfig(ctx context.Context, rootCA RootCA, krw *KeyReadWriter, allowExpired bool) (*SecurityConfig, error) {
  216. ctx = log.WithModule(ctx, "tls")
  217. // At this point we've successfully loaded the CA details from disk, or
  218. // successfully downloaded them remotely. The next step is to try to
  219. // load our certificates.
  220. // Read both the Cert and Key from disk
  221. cert, key, err := krw.Read()
  222. if err != nil {
  223. return nil, err
  224. }
  225. // Create an x509 certificate out of the contents on disk
  226. certBlock, _ := pem.Decode([]byte(cert))
  227. if certBlock == nil {
  228. return nil, errors.New("failed to parse certificate PEM")
  229. }
  230. // Create an X509Cert so we can .Verify()
  231. X509Cert, err := x509.ParseCertificate(certBlock.Bytes)
  232. if err != nil {
  233. return nil, err
  234. }
  235. // Include our root pool
  236. opts := x509.VerifyOptions{
  237. Roots: rootCA.Pool,
  238. }
  239. // Check to see if this certificate was signed by our CA, and isn't expired
  240. if err := verifyCertificate(X509Cert, opts, allowExpired); err != nil {
  241. return nil, err
  242. }
  243. // Now that we know this certificate is valid, create a TLS Certificate for our
  244. // credentials
  245. keyPair, err := tls.X509KeyPair(cert, key)
  246. if err != nil {
  247. return nil, err
  248. }
  249. // Load the Certificates as server credentials
  250. serverTLSCreds, err := rootCA.NewServerTLSCredentials(&keyPair)
  251. if err != nil {
  252. return nil, err
  253. }
  254. // Load the Certificates also as client credentials.
  255. // Both workers and managers always connect to remote managers,
  256. // so ServerName is always set to ManagerRole here.
  257. clientTLSCreds, err := rootCA.NewClientTLSCredentials(&keyPair, ManagerRole)
  258. if err != nil {
  259. return nil, err
  260. }
  261. log.G(ctx).WithFields(logrus.Fields{
  262. "node.id": clientTLSCreds.NodeID(),
  263. "node.role": clientTLSCreds.Role(),
  264. }).Debug("loaded node credentials")
  265. return NewSecurityConfig(&rootCA, krw, clientTLSCreds, serverTLSCreds), nil
  266. }
  267. // CertificateRequestConfig contains the information needed to request a
  268. // certificate from a remote CA.
  269. type CertificateRequestConfig struct {
  270. // Token is the join token that authenticates us with the CA.
  271. Token string
  272. // Availability allows a user to control the current scheduling status of a node
  273. Availability api.NodeSpec_Availability
  274. // ConnBroker provides connections to CAs.
  275. ConnBroker *connectionbroker.Broker
  276. // Credentials provides transport credentials for communicating with the
  277. // remote server.
  278. Credentials credentials.TransportCredentials
  279. // ForceRemote specifies that only a remote (TCP) connection should
  280. // be used to request the certificate. This may be necessary in cases
  281. // where the local node is running a manager, but is in the process of
  282. // being demoted.
  283. ForceRemote bool
  284. }
  285. // CreateSecurityConfig creates a new key and cert for this node, either locally
  286. // or via a remote CA.
  287. func (rootCA RootCA) CreateSecurityConfig(ctx context.Context, krw *KeyReadWriter, config CertificateRequestConfig) (*SecurityConfig, error) {
  288. ctx = log.WithModule(ctx, "tls")
  289. var (
  290. tlsKeyPair *tls.Certificate
  291. err error
  292. )
  293. if rootCA.CanSign() {
  294. // Create a new random ID for this certificate
  295. cn := identity.NewID()
  296. org := identity.NewID()
  297. proposedRole := ManagerRole
  298. tlsKeyPair, err = rootCA.IssueAndSaveNewCertificates(krw, cn, proposedRole, org)
  299. if err != nil {
  300. log.G(ctx).WithFields(logrus.Fields{
  301. "node.id": cn,
  302. "node.role": proposedRole,
  303. }).WithError(err).Errorf("failed to issue and save new certificate")
  304. return nil, err
  305. }
  306. log.G(ctx).WithFields(logrus.Fields{
  307. "node.id": cn,
  308. "node.role": proposedRole,
  309. }).Debug("issued new TLS certificate")
  310. } else {
  311. // Request certificate issuance from a remote CA.
  312. // Last argument is nil because at this point we don't have any valid TLS creds
  313. tlsKeyPair, err = rootCA.RequestAndSaveNewCertificates(ctx, krw, config)
  314. if err != nil {
  315. log.G(ctx).WithError(err).Error("failed to request save new certificate")
  316. return nil, err
  317. }
  318. }
  319. // Create the Server TLS Credentials for this node. These will not be used by workers.
  320. serverTLSCreds, err := rootCA.NewServerTLSCredentials(tlsKeyPair)
  321. if err != nil {
  322. return nil, err
  323. }
  324. // Create a TLSConfig to be used when this node connects as a client to another remote node.
  325. // We're using ManagerRole as remote serverName for TLS host verification
  326. clientTLSCreds, err := rootCA.NewClientTLSCredentials(tlsKeyPair, ManagerRole)
  327. if err != nil {
  328. return nil, err
  329. }
  330. log.G(ctx).WithFields(logrus.Fields{
  331. "node.id": clientTLSCreds.NodeID(),
  332. "node.role": clientTLSCreds.Role(),
  333. }).Debugf("new node credentials generated: %s", krw.Target())
  334. return NewSecurityConfig(&rootCA, krw, clientTLSCreds, serverTLSCreds), nil
  335. }
  336. // RenewTLSConfigNow gets a new TLS cert and key, and updates the security config if provided. This is similar to
  337. // RenewTLSConfig, except while that monitors for expiry, and periodically renews, this renews once and is blocking
  338. func RenewTLSConfigNow(ctx context.Context, s *SecurityConfig, connBroker *connectionbroker.Broker) error {
  339. s.renewalMu.Lock()
  340. defer s.renewalMu.Unlock()
  341. ctx = log.WithModule(ctx, "tls")
  342. log := log.G(ctx).WithFields(logrus.Fields{
  343. "node.id": s.ClientTLSCreds.NodeID(),
  344. "node.role": s.ClientTLSCreds.Role(),
  345. })
  346. // Let's request new certs. Renewals don't require a token.
  347. rootCA := s.RootCA()
  348. tlsKeyPair, err := rootCA.RequestAndSaveNewCertificates(ctx,
  349. s.KeyWriter(),
  350. CertificateRequestConfig{
  351. ConnBroker: connBroker,
  352. Credentials: s.ClientTLSCreds,
  353. })
  354. if err != nil {
  355. log.WithError(err).Errorf("failed to renew the certificate")
  356. return err
  357. }
  358. clientTLSConfig, err := NewClientTLSConfig(tlsKeyPair, rootCA.Pool, CARole)
  359. if err != nil {
  360. log.WithError(err).Errorf("failed to create a new client config")
  361. return err
  362. }
  363. serverTLSConfig, err := NewServerTLSConfig(tlsKeyPair, rootCA.Pool)
  364. if err != nil {
  365. log.WithError(err).Errorf("failed to create a new server config")
  366. return err
  367. }
  368. if err = s.ClientTLSCreds.LoadNewTLSConfig(clientTLSConfig); err != nil {
  369. log.WithError(err).Errorf("failed to update the client credentials")
  370. return err
  371. }
  372. // Update the external CA to use the new client TLS
  373. // config using a copy without a serverName specified.
  374. s.externalCA.UpdateTLSConfig(&tls.Config{
  375. Certificates: clientTLSConfig.Certificates,
  376. RootCAs: clientTLSConfig.RootCAs,
  377. MinVersion: tls.VersionTLS12,
  378. })
  379. if err = s.ServerTLSCreds.LoadNewTLSConfig(serverTLSConfig); err != nil {
  380. log.WithError(err).Errorf("failed to update the server TLS credentials")
  381. return err
  382. }
  383. return nil
  384. }
  385. // RenewTLSConfig will continuously monitor for the necessity of renewing the local certificates, either by
  386. // issuing them locally if key-material is available, or requesting them from a remote CA.
  387. func RenewTLSConfig(ctx context.Context, s *SecurityConfig, connBroker *connectionbroker.Broker, renew <-chan struct{}) <-chan CertificateUpdate {
  388. updates := make(chan CertificateUpdate)
  389. go func() {
  390. var retry time.Duration
  391. expBackoff := events.NewExponentialBackoff(RenewTLSExponentialBackoff)
  392. defer close(updates)
  393. for {
  394. ctx = log.WithModule(ctx, "tls")
  395. log := log.G(ctx).WithFields(logrus.Fields{
  396. "node.id": s.ClientTLSCreds.NodeID(),
  397. "node.role": s.ClientTLSCreds.Role(),
  398. })
  399. // Our starting default will be 5 minutes
  400. retry = 5 * time.Minute
  401. // Since the expiration of the certificate is managed remotely we should update our
  402. // retry timer on every iteration of this loop.
  403. // Retrieve the current certificate expiration information.
  404. validFrom, validUntil, err := readCertValidity(s.KeyReader())
  405. if err != nil {
  406. // We failed to read the expiration, let's stick with the starting default
  407. log.Errorf("failed to read the expiration of the TLS certificate in: %s", s.KeyReader().Target())
  408. select {
  409. case updates <- CertificateUpdate{Err: errors.New("failed to read certificate expiration")}:
  410. case <-ctx.Done():
  411. log.Info("shutting down certificate renewal routine")
  412. return
  413. }
  414. } else {
  415. // If we have an expired certificate, try to renew immediately: the hope that this is a temporary clock skew, or
  416. // we can issue our own TLS certs.
  417. if validUntil.Before(time.Now()) {
  418. log.Warn("the current TLS certificate is expired, so an attempt to renew it will be made immediately")
  419. // retry immediately(ish) with exponential backoff
  420. retry = expBackoff.Proceed(nil)
  421. } else {
  422. // Random retry time between 50% and 80% of the total time to expiration
  423. retry = calculateRandomExpiry(validFrom, validUntil)
  424. }
  425. }
  426. log.WithFields(logrus.Fields{
  427. "time": time.Now().Add(retry),
  428. }).Debugf("next certificate renewal scheduled for %v from now", retry)
  429. select {
  430. case <-time.After(retry):
  431. log.Info("renewing certificate")
  432. case <-renew:
  433. log.Info("forced certificate renewal")
  434. case <-ctx.Done():
  435. log.Info("shutting down certificate renewal routine")
  436. return
  437. }
  438. // ignore errors - it will just try again later
  439. var certUpdate CertificateUpdate
  440. if err := RenewTLSConfigNow(ctx, s, connBroker); err != nil {
  441. certUpdate.Err = err
  442. expBackoff.Failure(nil, nil)
  443. } else {
  444. certUpdate.Role = s.ClientTLSCreds.Role()
  445. expBackoff = events.NewExponentialBackoff(RenewTLSExponentialBackoff)
  446. }
  447. select {
  448. case updates <- certUpdate:
  449. case <-ctx.Done():
  450. log.Info("shutting down certificate renewal routine")
  451. return
  452. }
  453. }
  454. }()
  455. return updates
  456. }
  457. // calculateRandomExpiry returns a random duration between 50% and 80% of the
  458. // original validity period
  459. func calculateRandomExpiry(validFrom, validUntil time.Time) time.Duration {
  460. duration := validUntil.Sub(validFrom)
  461. var randomExpiry int
  462. // Our lower bound of renewal will be half of the total expiration time
  463. minValidity := int(duration.Minutes() * CertLowerRotationRange)
  464. // Our upper bound of renewal will be 80% of the total expiration time
  465. maxValidity := int(duration.Minutes() * CertUpperRotationRange)
  466. // Let's select a random number of minutes between min and max, and set our retry for that
  467. // Using randomly selected rotation allows us to avoid certificate thundering herds.
  468. if maxValidity-minValidity < 1 {
  469. randomExpiry = minValidity
  470. } else {
  471. randomExpiry = rand.Intn(maxValidity-minValidity) + int(minValidity)
  472. }
  473. expiry := validFrom.Add(time.Duration(randomExpiry) * time.Minute).Sub(time.Now())
  474. if expiry < 0 {
  475. return 0
  476. }
  477. return expiry
  478. }
  479. // NewServerTLSConfig returns a tls.Config configured for a TLS Server, given a tls.Certificate
  480. // and the PEM-encoded root CA Certificate
  481. func NewServerTLSConfig(cert *tls.Certificate, rootCAPool *x509.CertPool) (*tls.Config, error) {
  482. if rootCAPool == nil {
  483. return nil, errors.New("valid root CA pool required")
  484. }
  485. return &tls.Config{
  486. Certificates: []tls.Certificate{*cert},
  487. // Since we're using the same CA server to issue Certificates to new nodes, we can't
  488. // use tls.RequireAndVerifyClientCert
  489. ClientAuth: tls.VerifyClientCertIfGiven,
  490. RootCAs: rootCAPool,
  491. ClientCAs: rootCAPool,
  492. PreferServerCipherSuites: true,
  493. MinVersion: tls.VersionTLS12,
  494. }, nil
  495. }
  496. // NewClientTLSConfig returns a tls.Config configured for a TLS Client, given a tls.Certificate
  497. // the PEM-encoded root CA Certificate, and the name of the remote server the client wants to connect to.
  498. func NewClientTLSConfig(cert *tls.Certificate, rootCAPool *x509.CertPool, serverName string) (*tls.Config, error) {
  499. if rootCAPool == nil {
  500. return nil, errors.New("valid root CA pool required")
  501. }
  502. return &tls.Config{
  503. ServerName: serverName,
  504. Certificates: []tls.Certificate{*cert},
  505. RootCAs: rootCAPool,
  506. MinVersion: tls.VersionTLS12,
  507. }, nil
  508. }
  509. // NewClientTLSCredentials returns GRPC credentials for a TLS GRPC client, given a tls.Certificate
  510. // a PEM-Encoded root CA Certificate, and the name of the remote server the client wants to connect to.
  511. func (rootCA *RootCA) NewClientTLSCredentials(cert *tls.Certificate, serverName string) (*MutableTLSCreds, error) {
  512. tlsConfig, err := NewClientTLSConfig(cert, rootCA.Pool, serverName)
  513. if err != nil {
  514. return nil, err
  515. }
  516. mtls, err := NewMutableTLS(tlsConfig)
  517. return mtls, err
  518. }
  519. // NewServerTLSCredentials returns GRPC credentials for a TLS GRPC client, given a tls.Certificate
  520. // a PEM-Encoded root CA Certificate, and the name of the remote server the client wants to connect to.
  521. func (rootCA *RootCA) NewServerTLSCredentials(cert *tls.Certificate) (*MutableTLSCreds, error) {
  522. tlsConfig, err := NewServerTLSConfig(cert, rootCA.Pool)
  523. if err != nil {
  524. return nil, err
  525. }
  526. mtls, err := NewMutableTLS(tlsConfig)
  527. return mtls, err
  528. }
  529. // ParseRole parses an apiRole into an internal role string
  530. func ParseRole(apiRole api.NodeRole) (string, error) {
  531. switch apiRole {
  532. case api.NodeRoleManager:
  533. return ManagerRole, nil
  534. case api.NodeRoleWorker:
  535. return WorkerRole, nil
  536. default:
  537. return "", errors.Errorf("failed to parse api role: %v", apiRole)
  538. }
  539. }
  540. // FormatRole parses an internal role string into an apiRole
  541. func FormatRole(role string) (api.NodeRole, error) {
  542. switch strings.ToLower(role) {
  543. case strings.ToLower(ManagerRole):
  544. return api.NodeRoleManager, nil
  545. case strings.ToLower(WorkerRole):
  546. return api.NodeRoleWorker, nil
  547. default:
  548. return 0, errors.Errorf("failed to parse role: %s", role)
  549. }
  550. }