github.com/cockroachdb/cockroach@v20.2.0-alpha.1+incompatible/pkg/security/certificate_manager.go (about)

     1  // Copyright 2017 The Cockroach Authors.
     2  //
     3  // Use of this software is governed by the Business Source License
     4  // included in the file licenses/BSL.txt.
     5  //
     6  // As of the Change Date specified in that file, in accordance with
     7  // the Business Source License, use of this software will be governed
     8  // by the Apache License, Version 2.0, included in the file
     9  // licenses/APL.txt.
    10  
    11  package security
    12  
    13  import (
    14  	"context"
    15  	"crypto/tls"
    16  	"fmt"
    17  	"os"
    18  	"path/filepath"
    19  
    20  	"github.com/cockroachdb/cockroach/pkg/util/log"
    21  	"github.com/cockroachdb/cockroach/pkg/util/metric"
    22  	"github.com/cockroachdb/cockroach/pkg/util/stop"
    23  	"github.com/cockroachdb/cockroach/pkg/util/syncutil"
    24  	"github.com/cockroachdb/cockroach/pkg/util/sysutil"
    25  	"github.com/cockroachdb/errors"
    26  )
    27  
    28  var (
    29  	metaCAExpiration = metric.Metadata{
    30  		Name:        "security.certificate.expiration.ca",
    31  		Help:        "Expiration for the CA certificate. 0 means no certificate or error.",
    32  		Measurement: "Certificate Expiration",
    33  		Unit:        metric.Unit_TIMESTAMP_SEC,
    34  	}
    35  	metaClientCAExpiration = metric.Metadata{
    36  		Name:        "security.certificate.expiration.client-ca",
    37  		Help:        "Expiration for the client CA certificate. 0 means no certificate or error.",
    38  		Measurement: "Certificate Expiration",
    39  		Unit:        metric.Unit_TIMESTAMP_SEC,
    40  	}
    41  	metaUICAExpiration = metric.Metadata{
    42  		Name:        "security.certificate.expiration.ui-ca",
    43  		Help:        "Expiration for the UI CA certificate. 0 means no certificate or error.",
    44  		Measurement: "Certificate Expiration",
    45  		Unit:        metric.Unit_TIMESTAMP_SEC,
    46  	}
    47  	metaNodeExpiration = metric.Metadata{
    48  		Name:        "security.certificate.expiration.node",
    49  		Help:        "Expiration for the node certificate. 0 means no certificate or error.",
    50  		Measurement: "Certificate Expiration",
    51  		Unit:        metric.Unit_TIMESTAMP_SEC,
    52  	}
    53  	metaNodeClientExpiration = metric.Metadata{
    54  		Name:        "security.certificate.expiration.node-client",
    55  		Help:        "Expiration for the node's client certificate. 0 means no certificate or error.",
    56  		Measurement: "Certificate Expiration",
    57  		Unit:        metric.Unit_TIMESTAMP_SEC,
    58  	}
    59  	metaUIExpiration = metric.Metadata{
    60  		Name:        "security.certificate.expiration.ui",
    61  		Help:        "Expiration for the UI certificate. 0 means no certificate or error.",
    62  		Measurement: "Certificate Expiration",
    63  		Unit:        metric.Unit_TIMESTAMP_SEC,
    64  	}
    65  )
    66  
    67  // CertificateManager lives for the duration of the process and manages certificates and keys.
    68  // It reloads all certificates when triggered and construct tls.Config objects for
    69  // servers or clients.
    70  //
    71  // Important note: Load() performs some sanity checks (file pairs match, CA certs don't disappear),
    72  // but these are by no means complete. Completeness is not required as nodes restarting have
    73  // no fallback if invalid certs/keys are present.
    74  //
    75  // The nomenclature for certificates is as follows, all within the certs-dir.
    76  // - ca.crt             main CA certificate.
    77  //                      Used to verify everything unless overridden by more specifica CAs.
    78  // - ca-client.crt      CA certificate to verify client certificates. If it does not exist,
    79  //                      fall back on 'ca.crt'.
    80  // - node.crt           node certificate.
    81  //                      Server-side certificate (always) and client-side certificate unless
    82  //                      client.node.crt is found.
    83  //                      Verified using 'ca.crt'.
    84  // - client.<user>.crt  client certificate for 'user'. Verified using 'ca.crt', or 'ca-client.crt'.
    85  // - client.node.crt    client certificate for the 'node' user. If it does not exist,
    86  //                      fall back on 'node.crt'.
    87  type CertificateManager struct {
    88  	// Certificate directory is not modified after initialization.
    89  	certsDir string
    90  	// The metrics struct is initialized at init time and metrics do their
    91  	// own locking.
    92  	certMetrics CertificateMetrics
    93  
    94  	// mu protects all remaining fields.
    95  	mu syncutil.RWMutex
    96  
    97  	// If false, this is the first load. Needed to ensure we do not drop certain certs.
    98  	initialized bool
    99  
   100  	// Set of certs. These are swapped in during Load(), and never mutated afterwards.
   101  	caCert         *CertInfo // default CA certificate
   102  	clientCACert   *CertInfo // optional: certificate to verify client certificates
   103  	uiCACert       *CertInfo // optional: certificate to verify UI certficates
   104  	nodeCert       *CertInfo // certificate for nodes (always server cert, sometimes client cert)
   105  	nodeClientCert *CertInfo // optional: client certificate for 'node' user. Also included in 'clientCerts'
   106  	uiCert         *CertInfo // optional: server certificate for the admin UI.
   107  	clientCerts    map[string]*CertInfo
   108  
   109  	// TLS configs. Initialized lazily. Wiped on every successful Load().
   110  	// Server-side config.
   111  	serverConfig *tls.Config
   112  	// Server-side config for the Admin UI.
   113  	uiServerConfig *tls.Config
   114  	// Client-side config for the cockroach node.
   115  	// All other client tls.Config objects are built as requested and not cached.
   116  	clientConfig *tls.Config
   117  }
   118  
   119  // CertificateMetrics holds metrics about the various certificates.
   120  // These are initialized when the certificate manager is created and updated
   121  // on reload.
   122  type CertificateMetrics struct {
   123  	CAExpiration         *metric.Gauge
   124  	ClientCAExpiration   *metric.Gauge
   125  	UICAExpiration       *metric.Gauge
   126  	NodeExpiration       *metric.Gauge
   127  	NodeClientExpiration *metric.Gauge
   128  	UIExpiration         *metric.Gauge
   129  }
   130  
   131  func makeCertificateManager(certsDir string) *CertificateManager {
   132  	cm := &CertificateManager{certsDir: os.ExpandEnv(certsDir)}
   133  	// Initialize metrics:
   134  	cm.certMetrics = CertificateMetrics{
   135  		CAExpiration:         metric.NewGauge(metaCAExpiration),
   136  		ClientCAExpiration:   metric.NewGauge(metaClientCAExpiration),
   137  		UICAExpiration:       metric.NewGauge(metaUICAExpiration),
   138  		NodeExpiration:       metric.NewGauge(metaNodeExpiration),
   139  		NodeClientExpiration: metric.NewGauge(metaNodeClientExpiration),
   140  		UIExpiration:         metric.NewGauge(metaUIExpiration),
   141  	}
   142  	return cm
   143  }
   144  
   145  // NewCertificateManager creates a new certificate manager.
   146  func NewCertificateManager(certsDir string) (*CertificateManager, error) {
   147  	cm := makeCertificateManager(certsDir)
   148  	return cm, cm.LoadCertificates()
   149  }
   150  
   151  // NewCertificateManagerFirstRun creates a new certificate manager.
   152  // The certsDir is created if it does not exist.
   153  // This should only be called when generating certificates, the server has
   154  // no business creating the certs directory.
   155  func NewCertificateManagerFirstRun(certsDir string) (*CertificateManager, error) {
   156  	cm := makeCertificateManager(certsDir)
   157  	if err := NewCertificateLoader(cm.certsDir).MaybeCreateCertsDir(); err != nil {
   158  		return nil, err
   159  	}
   160  
   161  	return cm, cm.LoadCertificates()
   162  }
   163  
   164  // Metrics returns the metrics struct.
   165  func (cm *CertificateManager) Metrics() CertificateMetrics {
   166  	return cm.certMetrics
   167  }
   168  
   169  // RegisterSignalHandler registers a signal handler for SIGHUP, triggering a
   170  // refresh of the certificates directory on notification.
   171  func (cm *CertificateManager) RegisterSignalHandler(stopper *stop.Stopper) {
   172  	go func() {
   173  		ch := sysutil.RefreshSignaledChan()
   174  		for {
   175  			select {
   176  			case <-stopper.ShouldStop():
   177  				return
   178  			case sig := <-ch:
   179  				log.Infof(context.Background(), "received signal %q, triggering certificate reload", sig)
   180  				if err := cm.LoadCertificates(); err != nil {
   181  					log.Warningf(context.Background(), "could not reload certificates: %v", err)
   182  				} else {
   183  					log.Info(context.Background(), "successfully reloaded certificates")
   184  				}
   185  			}
   186  		}
   187  	}()
   188  }
   189  
   190  // CACertPath returns the expected file path for the CA certificate.
   191  func (cm *CertificateManager) CACertPath() string {
   192  	return filepath.Join(cm.certsDir, CACertFilename())
   193  }
   194  
   195  // CACertFilename returns the expected file name for the CA certificate.
   196  func CACertFilename() string { return "ca" + certExtension }
   197  
   198  // ClientCACertPath returns the expected file path for the CA certificate
   199  // used to verify client certificates.
   200  func (cm *CertificateManager) ClientCACertPath() string {
   201  	return filepath.Join(cm.certsDir, "ca-client"+certExtension)
   202  }
   203  
   204  // UICACertPath returns the expected file path for the CA certificate
   205  // used to verify Admin UI certificates.
   206  func (cm *CertificateManager) UICACertPath() string {
   207  	return filepath.Join(cm.certsDir, "ca-ui"+certExtension)
   208  }
   209  
   210  // NodeCertPath returns the expected file path for the node certificate.
   211  func (cm *CertificateManager) NodeCertPath() string {
   212  	return filepath.Join(cm.certsDir, "node"+certExtension)
   213  }
   214  
   215  // NodeKeyPath returns the expected file path for the node key.
   216  func (cm *CertificateManager) NodeKeyPath() string {
   217  	return filepath.Join(cm.certsDir, "node"+keyExtension)
   218  }
   219  
   220  // UICertPath returns the expected file path for the UI certificate.
   221  func (cm *CertificateManager) UICertPath() string {
   222  	return filepath.Join(cm.certsDir, "ui"+certExtension)
   223  }
   224  
   225  // UIKeyPath returns the expected file path for the UI key.
   226  func (cm *CertificateManager) UIKeyPath() string {
   227  	return filepath.Join(cm.certsDir, "ui"+keyExtension)
   228  }
   229  
   230  // ClientCertPath returns the expected file path for the user's certificate.
   231  func (cm *CertificateManager) ClientCertPath(user string) string {
   232  	return filepath.Join(cm.certsDir, ClientCertFilename(user))
   233  }
   234  
   235  // ClientCertFilename returns the expected file name for the user's certificate.
   236  func ClientCertFilename(user string) string { return "client." + user + certExtension }
   237  
   238  // ClientKeyPath returns the expected file path for the user's key.
   239  func (cm *CertificateManager) ClientKeyPath(user string) string {
   240  	return filepath.Join(cm.certsDir, ClientKeyFilename(user))
   241  }
   242  
   243  // ClientKeyFilename returns the expected file name for the user's key.
   244  func ClientKeyFilename(user string) string { return "client." + user + keyExtension }
   245  
   246  // CACert returns the CA cert. May be nil.
   247  // Callers should check for an internal Error field.
   248  func (cm *CertificateManager) CACert() *CertInfo {
   249  	cm.mu.RLock()
   250  	defer cm.mu.RUnlock()
   251  	return cm.caCert
   252  }
   253  
   254  // ClientCACert returns the CA cert used to verify client certificates. May be nil.
   255  // Callers should check for an internal Error field.
   256  func (cm *CertificateManager) ClientCACert() *CertInfo {
   257  	cm.mu.RLock()
   258  	defer cm.mu.RUnlock()
   259  	return cm.clientCACert
   260  }
   261  
   262  // UICACert returns the CA cert used to verify the Admin UI certificate. May be nil.
   263  // Callers should check for an internal Error field.
   264  func (cm *CertificateManager) UICACert() *CertInfo {
   265  	cm.mu.RLock()
   266  	defer cm.mu.RUnlock()
   267  	return cm.uiCACert
   268  }
   269  
   270  // UICert returns the certificate used by the Admin UI. May be nil.
   271  // Callers should check for an internal Error field.
   272  func (cm *CertificateManager) UICert() *CertInfo {
   273  	cm.mu.RLock()
   274  	defer cm.mu.RUnlock()
   275  	return cm.uiCert
   276  }
   277  
   278  // checkCertIsValid returns an error if the passed cert is missing or has an error.
   279  func checkCertIsValid(cert *CertInfo) error {
   280  	if cert == nil {
   281  		return errors.New("not found")
   282  	}
   283  	return cert.Error
   284  }
   285  
   286  // NodeCert returns the Node cert. May be nil.
   287  // Callers should check for an internal Error field.
   288  func (cm *CertificateManager) NodeCert() *CertInfo {
   289  	cm.mu.RLock()
   290  	defer cm.mu.RUnlock()
   291  	return cm.nodeCert
   292  }
   293  
   294  // ClientCerts returns the Client certs.
   295  // Callers should check for internal Error fields.
   296  func (cm *CertificateManager) ClientCerts() map[string]*CertInfo {
   297  	cm.mu.RLock()
   298  	defer cm.mu.RUnlock()
   299  	return cm.clientCerts
   300  }
   301  
   302  // Error is the error type for this package.
   303  // TODO(knz): make this an error wrapper.
   304  type Error struct {
   305  	Message string
   306  	Err     error
   307  }
   308  
   309  // Error implements the error interface.
   310  func (e *Error) Error() string {
   311  	return fmt.Sprintf("%s: %v", e.Message, e.Err)
   312  }
   313  
   314  // makeErrorf constructs an Error and returns it.
   315  func makeErrorf(err error, format string, args ...interface{}) *Error {
   316  	return &Error{
   317  		Message: fmt.Sprintf(format, args...),
   318  		Err:     err,
   319  	}
   320  }
   321  
   322  // makeError constructs an Error with just a string.
   323  func makeError(err error, s string) *Error { return makeErrorf(err, "%s", s) }
   324  
   325  // LoadCertificates creates a CertificateLoader to load all certs and keys.
   326  // Upon success, it swaps the existing certificates for the new ones.
   327  func (cm *CertificateManager) LoadCertificates() error {
   328  	cl := NewCertificateLoader(cm.certsDir)
   329  	if err := cl.Load(); err != nil {
   330  		return makeErrorf(err, "problem loading certs directory %s", cm.certsDir)
   331  	}
   332  
   333  	var caCert, clientCACert, uiCACert, nodeCert, uiCert, nodeClientCert *CertInfo
   334  	clientCerts := make(map[string]*CertInfo)
   335  	for _, ci := range cl.Certificates() {
   336  		switch ci.FileUsage {
   337  		case CAPem:
   338  			caCert = ci
   339  		case ClientCAPem:
   340  			clientCACert = ci
   341  		case UICAPem:
   342  			uiCACert = ci
   343  		case NodePem:
   344  			nodeCert = ci
   345  		case UIPem:
   346  			uiCert = ci
   347  		case ClientPem:
   348  			clientCerts[ci.Name] = ci
   349  			if ci.Name == NodeUser {
   350  				nodeClientCert = ci
   351  			}
   352  		}
   353  	}
   354  
   355  	cm.mu.Lock()
   356  	defer cm.mu.Unlock()
   357  	if cm.initialized {
   358  		// If we ran before, make sure we don't reload with missing/bad certificates.
   359  		if err := checkCertIsValid(caCert); checkCertIsValid(cm.caCert) == nil && err != nil {
   360  			return makeError(err, "reload would lose valid CA cert")
   361  		}
   362  		if err := checkCertIsValid(nodeCert); checkCertIsValid(cm.nodeCert) == nil && err != nil {
   363  			return makeError(err, "reload would lose valid node cert")
   364  		}
   365  		if err := checkCertIsValid(nodeClientCert); checkCertIsValid(cm.nodeClientCert) == nil && err != nil {
   366  			return makeErrorf(err, "reload would lose valid client cert for '%s'", NodeUser)
   367  		}
   368  		if err := checkCertIsValid(clientCACert); checkCertIsValid(cm.clientCACert) == nil && err != nil {
   369  			return makeError(err, "reload would lose valid CA certificate for client verification")
   370  		}
   371  		if err := checkCertIsValid(uiCACert); checkCertIsValid(cm.uiCACert) == nil && err != nil {
   372  			return makeError(err, "reload would lose valid CA certificate for UI")
   373  		}
   374  		if err := checkCertIsValid(uiCert); checkCertIsValid(cm.uiCert) == nil && err != nil {
   375  			return makeError(err, "reload would lose valid UI certificate")
   376  		}
   377  	}
   378  
   379  	if nodeClientCert == nil && nodeCert != nil {
   380  		// No client certificate for node, but we have a node certificate. Check that
   381  		// it contains the required client fields.
   382  		if err := validateDualPurposeNodeCert(nodeCert); err != nil {
   383  			return err
   384  		}
   385  	}
   386  
   387  	// Swap everything.
   388  	cm.caCert = caCert
   389  	cm.clientCACert = clientCACert
   390  	cm.uiCACert = uiCACert
   391  
   392  	cm.nodeCert = nodeCert
   393  	cm.nodeClientCert = nodeClientCert
   394  	cm.uiCert = uiCert
   395  	cm.clientCerts = clientCerts
   396  
   397  	cm.initialized = true
   398  
   399  	cm.serverConfig = nil
   400  	cm.uiServerConfig = nil
   401  	cm.clientConfig = nil
   402  
   403  	cm.updateMetricsLocked()
   404  	return nil
   405  }
   406  
   407  // updateMetricsLocked updates the values on the certificate metrics.
   408  // The metrics may not exist (eg: in tests that build their own CertificateManager).
   409  // If the corresponding certificate is missing or invalid (Error != nil), we reset the
   410  // metric to zero.
   411  // cm.mu must be held to protect the certificates. Metrics do their own atomicity.
   412  func (cm *CertificateManager) updateMetricsLocked() {
   413  	maybeSetMetric := func(m *metric.Gauge, ci *CertInfo) {
   414  		if m == nil {
   415  			return
   416  		}
   417  		if ci != nil && ci.Error == nil {
   418  			m.Update(ci.ExpirationTime.Unix())
   419  		} else {
   420  			m.Update(0)
   421  		}
   422  	}
   423  
   424  	// CA certificate expiration.
   425  	maybeSetMetric(cm.certMetrics.CAExpiration, cm.caCert)
   426  
   427  	// Client CA certificate expiration.
   428  	maybeSetMetric(cm.certMetrics.ClientCAExpiration, cm.clientCACert)
   429  
   430  	// UI CA certificate expiration.
   431  	maybeSetMetric(cm.certMetrics.UICAExpiration, cm.uiCACert)
   432  
   433  	// Node certificate expiration.
   434  	// TODO(marc): we need to examine the entire certificate chain here, if the CA cert
   435  	// used to sign the node cert expires sooner, then that is the expiration time to report.
   436  	maybeSetMetric(cm.certMetrics.NodeExpiration, cm.nodeCert)
   437  
   438  	// Node client certificate expiration.
   439  	maybeSetMetric(cm.certMetrics.NodeClientExpiration, cm.nodeClientCert)
   440  
   441  	// UI certificate expiration.
   442  	maybeSetMetric(cm.certMetrics.UIExpiration, cm.uiCert)
   443  }
   444  
   445  // GetServerTLSConfig returns a server TLS config with a callback to fetch the
   446  // latest TLS config. We still attempt to get the config to make sure
   447  // the initial call has a valid config loaded.
   448  func (cm *CertificateManager) GetServerTLSConfig() (*tls.Config, error) {
   449  	if _, err := cm.getEmbeddedServerTLSConfig(nil); err != nil {
   450  		return nil, err
   451  	}
   452  	return &tls.Config{
   453  		GetConfigForClient: cm.getEmbeddedServerTLSConfig,
   454  	}, nil
   455  }
   456  
   457  // getEmbeddedServerTLSConfig returns the most up-to-date server tls.Config.
   458  // This is the callback set in tls.Config.GetConfigForClient. We currently
   459  // ignore the ClientHelloInfo object.
   460  func (cm *CertificateManager) getEmbeddedServerTLSConfig(
   461  	_ *tls.ClientHelloInfo,
   462  ) (*tls.Config, error) {
   463  	cm.mu.Lock()
   464  	defer cm.mu.Unlock()
   465  
   466  	if cm.serverConfig != nil {
   467  		return cm.serverConfig, nil
   468  	}
   469  
   470  	ca, err := cm.getCACertLocked()
   471  	if err != nil {
   472  		return nil, err
   473  	}
   474  
   475  	nodeCert, err := cm.getNodeCertLocked()
   476  	if err != nil {
   477  		return nil, err
   478  	}
   479  
   480  	clientCA, err := cm.getClientCACertLocked()
   481  	if err != nil {
   482  		return nil, err
   483  	}
   484  
   485  	cfg, err := newServerTLSConfig(
   486  		nodeCert.FileContents,
   487  		nodeCert.KeyFileContents,
   488  		ca.FileContents,
   489  		clientCA.FileContents)
   490  	if err != nil {
   491  		return nil, err
   492  	}
   493  
   494  	cm.serverConfig = cfg
   495  	return cfg, nil
   496  }
   497  
   498  // GetUIServerTLSConfig returns a server TLS config for the Admin UI with a
   499  // callback to fetch the latest TLS config. We still attempt to get the config to make sure
   500  // the initial call has a valid config loaded.
   501  func (cm *CertificateManager) GetUIServerTLSConfig() (*tls.Config, error) {
   502  	if _, err := cm.getEmbeddedUIServerTLSConfig(nil); err != nil {
   503  		return nil, err
   504  	}
   505  	return &tls.Config{
   506  		GetConfigForClient: cm.getEmbeddedUIServerTLSConfig,
   507  	}, nil
   508  }
   509  
   510  // getEmbeddedUIServerTLSConfig returns the most up-to-date server tls.Config for the Admin UI.
   511  // This is the callback set in tls.Config.GetConfigForClient. We currently
   512  // ignore the ClientHelloInfo object.
   513  func (cm *CertificateManager) getEmbeddedUIServerTLSConfig(
   514  	_ *tls.ClientHelloInfo,
   515  ) (*tls.Config, error) {
   516  	cm.mu.Lock()
   517  	defer cm.mu.Unlock()
   518  
   519  	if cm.uiServerConfig != nil {
   520  		return cm.uiServerConfig, nil
   521  	}
   522  
   523  	uiCert, err := cm.getUICertLocked()
   524  	if err != nil {
   525  		return nil, err
   526  	}
   527  
   528  	cfg, err := newUIServerTLSConfig(
   529  		uiCert.FileContents,
   530  		uiCert.KeyFileContents)
   531  	if err != nil {
   532  		return nil, err
   533  	}
   534  
   535  	cm.uiServerConfig = cfg
   536  	return cfg, nil
   537  }
   538  
   539  // getCACertLocked returns the general CA cert.
   540  // cm.mu must be held.
   541  func (cm *CertificateManager) getCACertLocked() (*CertInfo, error) {
   542  	if err := checkCertIsValid(cm.caCert); err != nil {
   543  		return nil, makeError(err, "problem with CA certificate")
   544  	}
   545  	return cm.caCert, nil
   546  }
   547  
   548  // getClientCACertLocked returns the CA cert used to verify client certificates.
   549  // Use the client CA if it exists, otherwise fall back on the general CA.
   550  // cm.mu must be held.
   551  func (cm *CertificateManager) getClientCACertLocked() (*CertInfo, error) {
   552  	if cm.clientCACert == nil {
   553  		// No client CA: use general CA.
   554  		return cm.getCACertLocked()
   555  	}
   556  
   557  	if err := checkCertIsValid(cm.clientCACert); err != nil {
   558  		return nil, makeError(err, "problem with client CA certificate")
   559  	}
   560  	return cm.clientCACert, nil
   561  }
   562  
   563  // getUICACertLocked returns the CA cert for the Admin UI.
   564  // Use the UI CA if it exists, otherwise fall back on the general CA.
   565  // cm.mu must be held.
   566  func (cm *CertificateManager) getUICACertLocked() (*CertInfo, error) {
   567  	if cm.uiCACert == nil {
   568  		// No UI CA: use general CA.
   569  		return cm.getCACertLocked()
   570  	}
   571  
   572  	if err := checkCertIsValid(cm.uiCACert); err != nil {
   573  		return nil, makeError(err, "problem with UI CA certificate")
   574  	}
   575  	return cm.uiCACert, nil
   576  }
   577  
   578  // getNodeCertLocked returns the node certificate.
   579  // cm.mu must be held.
   580  func (cm *CertificateManager) getNodeCertLocked() (*CertInfo, error) {
   581  	if err := checkCertIsValid(cm.nodeCert); err != nil {
   582  		return nil, makeError(err, "problem with node certificate")
   583  	}
   584  	return cm.nodeCert, nil
   585  }
   586  
   587  // getUICertLocked returns the UI certificate if present, otherwise returns
   588  // the node certificate.
   589  // cm.mu must be held.
   590  func (cm *CertificateManager) getUICertLocked() (*CertInfo, error) {
   591  	if cm.uiCert == nil {
   592  		// No UI certificate: use node certificate.
   593  		return cm.getNodeCertLocked()
   594  	}
   595  	if err := checkCertIsValid(cm.uiCert); err != nil {
   596  		return nil, makeError(err, "problem with UI certificate")
   597  	}
   598  	return cm.uiCert, nil
   599  }
   600  
   601  // getClientCertLocked returns the client cert/key for the specified user,
   602  // or an error if not found.
   603  // cm.mu must be held.
   604  func (cm *CertificateManager) getClientCertLocked(user string) (*CertInfo, error) {
   605  	ci := cm.clientCerts[user]
   606  	if err := checkCertIsValid(ci); err != nil {
   607  		return nil, makeErrorf(err, "problem with client cert for user %s", user)
   608  	}
   609  
   610  	return ci, nil
   611  }
   612  
   613  // getNodeClientCertLocked returns the client cert/key for the node user.
   614  // Use the client certificate for 'node' if it exists, otherwise use
   615  // the node certificate which should be a combined client/server certificate.
   616  // cm.mu must be held.
   617  func (cm *CertificateManager) getNodeClientCertLocked() (*CertInfo, error) {
   618  	if cm.nodeClientCert == nil {
   619  		// No specific client cert for 'node': use multi-purpose node cert.
   620  		return cm.getNodeCertLocked()
   621  	}
   622  
   623  	if err := checkCertIsValid(cm.nodeClientCert); err != nil {
   624  		return nil, makeError(err, "problem with node client certificate")
   625  	}
   626  	return cm.nodeClientCert, nil
   627  }
   628  
   629  // GetClientTLSConfig returns the most up-to-date client tls.Config.
   630  // Returns the dual-purpose node certs if user == NodeUser and there is no
   631  // separate client cert for 'node'.
   632  func (cm *CertificateManager) GetClientTLSConfig(user string) (*tls.Config, error) {
   633  	cm.mu.Lock()
   634  	defer cm.mu.Unlock()
   635  
   636  	// We always need the CA cert.
   637  	ca, err := cm.getCACertLocked()
   638  	if err != nil {
   639  		return nil, err
   640  	}
   641  
   642  	if user != NodeUser {
   643  		clientCert, err := cm.getClientCertLocked(user)
   644  		if err != nil {
   645  			return nil, err
   646  		}
   647  
   648  		cfg, err := newClientTLSConfig(
   649  			clientCert.FileContents,
   650  			clientCert.KeyFileContents,
   651  			ca.FileContents)
   652  		if err != nil {
   653  			return nil, err
   654  		}
   655  
   656  		return cfg, nil
   657  	}
   658  
   659  	// We're the node user:
   660  	// Return the cached config if we have one.
   661  	if cm.clientConfig != nil {
   662  		return cm.clientConfig, nil
   663  	}
   664  
   665  	clientCert, err := cm.getNodeClientCertLocked()
   666  	if err != nil {
   667  		return nil, err
   668  	}
   669  
   670  	cfg, err := newClientTLSConfig(
   671  		clientCert.FileContents,
   672  		clientCert.KeyFileContents,
   673  		ca.FileContents)
   674  	if err != nil {
   675  		return nil, err
   676  	}
   677  
   678  	// Cache the config.
   679  	cm.clientConfig = cfg
   680  	return cfg, nil
   681  }
   682  
   683  // GetUIClientTLSConfig returns the most up-to-date client tls.Config for Admin UI clients.
   684  // It does not include a client certificate and uses the UI CA certificate if present.
   685  func (cm *CertificateManager) GetUIClientTLSConfig() (*tls.Config, error) {
   686  	cm.mu.Lock()
   687  	defer cm.mu.Unlock()
   688  
   689  	// We always need the CA cert.
   690  	uiCA, err := cm.getUICACertLocked()
   691  	if err != nil {
   692  		return nil, err
   693  	}
   694  
   695  	cfg, err := newUIClientTLSConfig(uiCA.FileContents)
   696  	if err != nil {
   697  		return nil, err
   698  	}
   699  
   700  	return cfg, nil
   701  }
   702  
   703  // GetClientCertPaths returns the paths to the client cert and key.
   704  // Returns the node cert and key if user == NodeUser.
   705  func (cm *CertificateManager) GetClientCertPaths(user string) (string, string, error) {
   706  	var clientCert *CertInfo
   707  	var err error
   708  
   709  	cm.mu.RLock()
   710  	defer cm.mu.RUnlock()
   711  
   712  	if user == NodeUser {
   713  		clientCert, err = cm.getNodeClientCertLocked()
   714  	} else {
   715  		clientCert, err = cm.getClientCertLocked(user)
   716  	}
   717  	if err != nil {
   718  		return "", "", err
   719  	}
   720  
   721  	return filepath.Join(cm.certsDir, clientCert.Filename),
   722  		filepath.Join(cm.certsDir, clientCert.KeyFilename),
   723  		nil
   724  }
   725  
   726  // GetCACertPath returns the path to the CA certificate.
   727  func (cm *CertificateManager) GetCACertPath() (string, error) {
   728  	cm.mu.RLock()
   729  	defer cm.mu.RUnlock()
   730  
   731  	ca, err := cm.getCACertLocked()
   732  	if err != nil {
   733  		return "", err
   734  	}
   735  
   736  	return filepath.Join(cm.certsDir, ca.Filename), nil
   737  }
   738  
   739  // ListCertificates returns all loaded certificates, or an error if not yet initialized.
   740  func (cm *CertificateManager) ListCertificates() ([]*CertInfo, error) {
   741  	cm.mu.RLock()
   742  	defer cm.mu.RUnlock()
   743  
   744  	if !cm.initialized {
   745  		return nil, errors.New("certificate manager has not been initialized")
   746  	}
   747  
   748  	ret := make([]*CertInfo, 0, 2+len(cm.clientCerts))
   749  	if cm.caCert != nil {
   750  		ret = append(ret, cm.caCert)
   751  	}
   752  	if cm.clientCACert != nil {
   753  		ret = append(ret, cm.clientCACert)
   754  	}
   755  	if cm.uiCACert != nil {
   756  		ret = append(ret, cm.uiCACert)
   757  	}
   758  	if cm.nodeCert != nil {
   759  		ret = append(ret, cm.nodeCert)
   760  	}
   761  	if cm.uiCert != nil {
   762  		ret = append(ret, cm.uiCert)
   763  	}
   764  	if cm.clientCerts != nil {
   765  		for _, cert := range cm.clientCerts {
   766  			ret = append(ret, cert)
   767  		}
   768  	}
   769  
   770  	return ret, nil
   771  }