github.com/sl1pm4t/consul@v1.4.5-0.20190325224627-74c31c540f9c/tlsutil/config.go (about)

     1  package tlsutil
     2  
     3  import (
     4  	"crypto/tls"
     5  	"crypto/x509"
     6  	"fmt"
     7  	"log"
     8  	"net"
     9  	"strings"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/hashicorp/go-rootcerts"
    14  )
    15  
    16  // DCWrapper is a function that is used to wrap a non-TLS connection
    17  // and returns an appropriate TLS connection or error. This takes
    18  // a datacenter as an argument.
    19  type DCWrapper func(dc string, conn net.Conn) (net.Conn, error)
    20  
    21  // Wrapper is a variant of DCWrapper, where the DC is provided as
    22  // a constant value. This is usually done by currying DCWrapper.
    23  type Wrapper func(conn net.Conn) (net.Conn, error)
    24  
    25  // TLSLookup maps the tls_min_version configuration to the internal value
    26  var TLSLookup = map[string]uint16{
    27  	"":      tls.VersionTLS10, // default in golang
    28  	"tls10": tls.VersionTLS10,
    29  	"tls11": tls.VersionTLS11,
    30  	"tls12": tls.VersionTLS12,
    31  }
    32  
    33  // Config used to create tls.Config
    34  type Config struct {
    35  	// VerifyIncoming is used to verify the authenticity of incoming
    36  	// connections.  This means that TCP requests are forbidden, only
    37  	// allowing for TLS. TLS connections must match a provided certificate
    38  	// authority. This can be used to force client auth.
    39  	VerifyIncoming bool
    40  
    41  	// VerifyIncomingRPC is used to verify the authenticity of incoming RPC
    42  	// connections.  This means that TCP requests are forbidden, only
    43  	// allowing for TLS. TLS connections must match a provided certificate
    44  	// authority. This can be used to force client auth.
    45  	VerifyIncomingRPC bool
    46  
    47  	// VerifyIncomingHTTPS is used to verify the authenticity of incoming
    48  	// HTTPS connections.  This means that TCP requests are forbidden, only
    49  	// allowing for TLS. TLS connections must match a provided certificate
    50  	// authority. This can be used to force client auth.
    51  	VerifyIncomingHTTPS bool
    52  
    53  	// VerifyOutgoing is used to verify the authenticity of outgoing
    54  	// connections.  This means that TLS requests are used, and TCP
    55  	// requests are not made. TLS connections must match a provided
    56  	// certificate authority. This is used to verify authenticity of server
    57  	// nodes.
    58  	VerifyOutgoing bool
    59  
    60  	// VerifyServerHostname is used to enable hostname verification of
    61  	// servers. This ensures that the certificate presented is valid for
    62  	// server.<datacenter>.<domain>.  This prevents a compromised client
    63  	// from being restarted as a server, and then intercepting request
    64  	// traffic as well as being added as a raft peer. This should be
    65  	// enabled by default with VerifyOutgoing, but for legacy reasons we
    66  	// cannot break existing clients.
    67  	VerifyServerHostname bool
    68  
    69  	// UseTLS is used to enable outgoing TLS connections to Consul servers.
    70  	UseTLS bool
    71  
    72  	// CAFile is a path to a certificate authority file. This is used with
    73  	// VerifyIncoming or VerifyOutgoing to verify the TLS connection.
    74  	CAFile string
    75  
    76  	// CAPath is a path to a directory containing certificate authority
    77  	// files. This is used with VerifyIncoming or VerifyOutgoing to verify
    78  	// the TLS connection.
    79  	CAPath string
    80  
    81  	// CertFile is used to provide a TLS certificate that is used for
    82  	// serving TLS connections.  Must be provided to serve TLS connections.
    83  	CertFile string
    84  
    85  	// KeyFile is used to provide a TLS key that is used for serving TLS
    86  	// connections.  Must be provided to serve TLS connections.
    87  	KeyFile string
    88  
    89  	// Node name is the name we use to advertise. Defaults to hostname.
    90  	NodeName string
    91  
    92  	// ServerName is used with the TLS certificate to ensure the name we
    93  	// provide matches the certificate
    94  	ServerName string
    95  
    96  	// Domain is the Consul TLD being used. Defaults to "consul."
    97  	Domain string
    98  
    99  	// TLSMinVersion is the minimum accepted TLS version that can be used.
   100  	TLSMinVersion string
   101  
   102  	// CipherSuites is the list of TLS cipher suites to use.
   103  	CipherSuites []uint16
   104  
   105  	// PreferServerCipherSuites specifies whether to prefer the server's
   106  	// ciphersuite over the client ciphersuites.
   107  	PreferServerCipherSuites bool
   108  
   109  	// EnableAgentTLSForChecks is used to apply the agent's TLS settings in
   110  	// order to configure the HTTP client used for health checks. Enabling
   111  	// this allows HTTP checks to present a client certificate and verify
   112  	// the server using the same TLS configuration as the agent (CA, cert,
   113  	// and key).
   114  	EnableAgentTLSForChecks bool
   115  }
   116  
   117  // KeyPair is used to open and parse a certificate and key file
   118  func (c *Config) KeyPair() (*tls.Certificate, error) {
   119  	return loadKeyPair(c.CertFile, c.KeyFile)
   120  }
   121  
   122  // SpecificDC is used to invoke a static datacenter
   123  // and turns a DCWrapper into a Wrapper type.
   124  func SpecificDC(dc string, tlsWrap DCWrapper) Wrapper {
   125  	if tlsWrap == nil {
   126  		return nil
   127  	}
   128  	return func(conn net.Conn) (net.Conn, error) {
   129  		return tlsWrap(dc, conn)
   130  	}
   131  }
   132  
   133  // Configurator holds a Config and is responsible for generating all the
   134  // *tls.Config necessary for Consul. Except the one in the api package.
   135  type Configurator struct {
   136  	sync.RWMutex
   137  	base    *Config
   138  	cert    *tls.Certificate
   139  	cas     *x509.CertPool
   140  	logger  *log.Logger
   141  	version int
   142  }
   143  
   144  // NewConfigurator creates a new Configurator and sets the provided
   145  // configuration.
   146  func NewConfigurator(config Config, logger *log.Logger) (*Configurator, error) {
   147  	c := &Configurator{logger: logger}
   148  	err := c.Update(config)
   149  	if err != nil {
   150  		return nil, err
   151  	}
   152  	return c, nil
   153  }
   154  
   155  // Update updates the internal configuration which is used to generate
   156  // *tls.Config.
   157  // This function acquires a write lock because it writes the new config.
   158  func (c *Configurator) Update(config Config) error {
   159  	cert, err := loadKeyPair(config.CertFile, config.KeyFile)
   160  	if err != nil {
   161  		return err
   162  	}
   163  	cas, err := loadCAs(config.CAFile, config.CAPath)
   164  	if err != nil {
   165  		return err
   166  	}
   167  
   168  	if err = c.check(config, cas, cert); err != nil {
   169  		return err
   170  	}
   171  	c.Lock()
   172  	c.base = &config
   173  	c.cert = cert
   174  	c.cas = cas
   175  	c.version++
   176  	c.Unlock()
   177  	c.log("Update")
   178  	return nil
   179  }
   180  
   181  func (c *Configurator) check(config Config, cas *x509.CertPool, cert *tls.Certificate) error {
   182  	// Check if a minimum TLS version was set
   183  	if config.TLSMinVersion != "" {
   184  		if _, ok := TLSLookup[config.TLSMinVersion]; !ok {
   185  			return fmt.Errorf("TLSMinVersion: value %s not supported, please specify one of [tls10,tls11,tls12]", config.TLSMinVersion)
   186  		}
   187  	}
   188  
   189  	// Ensure we have a CA if VerifyOutgoing is set
   190  	if config.VerifyOutgoing && cas == nil {
   191  		return fmt.Errorf("VerifyOutgoing set, and no CA certificate provided!")
   192  	}
   193  
   194  	// Ensure we have a CA and cert if VerifyIncoming is set
   195  	if config.VerifyIncoming || config.VerifyIncomingRPC || config.VerifyIncomingHTTPS {
   196  		if cas == nil {
   197  			return fmt.Errorf("VerifyIncoming set, and no CA certificate provided!")
   198  		}
   199  		if cert == nil {
   200  			return fmt.Errorf("VerifyIncoming set, and no Cert/Key pair provided!")
   201  		}
   202  	}
   203  	return nil
   204  }
   205  
   206  func loadKeyPair(certFile, keyFile string) (*tls.Certificate, error) {
   207  	if certFile == "" || keyFile == "" {
   208  		return nil, nil
   209  	}
   210  	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
   211  	if err != nil {
   212  		return nil, fmt.Errorf("Failed to load cert/key pair: %v", err)
   213  	}
   214  	return &cert, nil
   215  }
   216  
   217  func loadCAs(caFile, caPath string) (*x509.CertPool, error) {
   218  	if caFile != "" {
   219  		return rootcerts.LoadCAFile(caFile)
   220  	} else if caPath != "" {
   221  		pool, err := rootcerts.LoadCAPath(caPath)
   222  		if err != nil {
   223  			return nil, err
   224  		}
   225  		// make sure to not return an empty pool because this is not
   226  		// the users intention when providing a path for CAs.
   227  		if len(pool.Subjects()) == 0 {
   228  			return nil, fmt.Errorf("Error loading CA: path %q has no CAs", caPath)
   229  		}
   230  		return pool, nil
   231  	}
   232  	return nil, nil
   233  }
   234  
   235  // commonTLSConfig generates a *tls.Config from the base configuration the
   236  // Configurator has. It accepts an additional flag in case a config is needed
   237  // for incoming TLS connections.
   238  // This function acquires a read lock because it reads from the config.
   239  func (c *Configurator) commonTLSConfig(additionalVerifyIncomingFlag bool) *tls.Config {
   240  	c.RLock()
   241  	defer c.RUnlock()
   242  	tlsConfig := &tls.Config{
   243  		InsecureSkipVerify: !c.base.VerifyServerHostname,
   244  	}
   245  
   246  	// Set the cipher suites
   247  	if len(c.base.CipherSuites) != 0 {
   248  		tlsConfig.CipherSuites = c.base.CipherSuites
   249  	}
   250  
   251  	tlsConfig.PreferServerCipherSuites = c.base.PreferServerCipherSuites
   252  
   253  	tlsConfig.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {
   254  		return c.cert, nil
   255  	}
   256  	tlsConfig.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {
   257  		return c.cert, nil
   258  	}
   259  
   260  	tlsConfig.ClientCAs = c.cas
   261  	tlsConfig.RootCAs = c.cas
   262  
   263  	// This is possible because TLSLookup also contains "" with golang's
   264  	// default (tls10). And because the initial check makes sure the
   265  	// version correctly matches.
   266  	tlsConfig.MinVersion = TLSLookup[c.base.TLSMinVersion]
   267  
   268  	// Set ClientAuth if necessary
   269  	if c.base.VerifyIncoming || additionalVerifyIncomingFlag {
   270  		tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
   271  	}
   272  
   273  	return tlsConfig
   274  }
   275  
   276  // This function acquires a read lock because it reads from the config.
   277  func (c *Configurator) outgoingRPCTLSDisabled() bool {
   278  	c.RLock()
   279  	defer c.RUnlock()
   280  	return c.cas == nil && !c.base.VerifyOutgoing
   281  }
   282  
   283  // This function acquires a read lock because it reads from the config.
   284  func (c *Configurator) someValuesFromConfig() (bool, bool, string) {
   285  	c.RLock()
   286  	defer c.RUnlock()
   287  	return c.base.VerifyServerHostname, c.base.VerifyOutgoing, c.base.Domain
   288  }
   289  
   290  // This function acquires a read lock because it reads from the config.
   291  func (c *Configurator) verifyIncomingRPC() bool {
   292  	c.RLock()
   293  	defer c.RUnlock()
   294  	return c.base.VerifyIncomingRPC
   295  }
   296  
   297  // This function acquires a read lock because it reads from the config.
   298  func (c *Configurator) verifyIncomingHTTPS() bool {
   299  	c.RLock()
   300  	defer c.RUnlock()
   301  	return c.base.VerifyIncomingHTTPS
   302  }
   303  
   304  // This function acquires a read lock because it reads from the config.
   305  func (c *Configurator) enableAgentTLSForChecks() bool {
   306  	c.RLock()
   307  	defer c.RUnlock()
   308  	return c.base.EnableAgentTLSForChecks
   309  }
   310  
   311  // This function acquires a read lock because it reads from the config.
   312  func (c *Configurator) serverNameOrNodeName() string {
   313  	c.RLock()
   314  	defer c.RUnlock()
   315  	if c.base.ServerName != "" {
   316  		return c.base.ServerName
   317  	}
   318  	return c.base.NodeName
   319  }
   320  
   321  // IncomingRPCConfig generates a *tls.Config for incoming RPC connections.
   322  func (c *Configurator) IncomingRPCConfig() *tls.Config {
   323  	c.log("IncomingRPCConfig")
   324  	config := c.commonTLSConfig(c.verifyIncomingRPC())
   325  	config.GetConfigForClient = func(*tls.ClientHelloInfo) (*tls.Config, error) {
   326  		return c.IncomingRPCConfig(), nil
   327  	}
   328  	return config
   329  }
   330  
   331  // IncomingHTTPSConfig generates a *tls.Config for incoming HTTPS connections.
   332  func (c *Configurator) IncomingHTTPSConfig() *tls.Config {
   333  	c.log("IncomingHTTPSConfig")
   334  	config := c.commonTLSConfig(c.verifyIncomingHTTPS())
   335  	config.NextProtos = []string{"h2", "http/1.1"}
   336  	config.GetConfigForClient = func(*tls.ClientHelloInfo) (*tls.Config, error) {
   337  		return c.IncomingHTTPSConfig(), nil
   338  	}
   339  	return config
   340  }
   341  
   342  // IncomingTLSConfig generates a *tls.Config for outgoing TLS connections for
   343  // checks. This function is separated because there is an extra flag to
   344  // consider for checks. EnableAgentTLSForChecks and InsecureSkipVerify has to
   345  // be checked for checks.
   346  func (c *Configurator) OutgoingTLSConfigForCheck(skipVerify bool) *tls.Config {
   347  	c.log("OutgoingTLSConfigForCheck")
   348  	if !c.enableAgentTLSForChecks() {
   349  		return &tls.Config{
   350  			InsecureSkipVerify: skipVerify,
   351  		}
   352  	}
   353  
   354  	config := c.commonTLSConfig(false)
   355  	config.InsecureSkipVerify = skipVerify
   356  	config.ServerName = c.serverNameOrNodeName()
   357  
   358  	return config
   359  }
   360  
   361  // OutgoingRPCConfig generates a *tls.Config for outgoing RPC connections. If
   362  // there is a CA or VerifyOutgoing is set, a *tls.Config will be provided,
   363  // otherwise we assume that no TLS should be used.
   364  func (c *Configurator) OutgoingRPCConfig() *tls.Config {
   365  	c.log("OutgoingRPCConfig")
   366  	if c.outgoingRPCTLSDisabled() {
   367  		return nil
   368  	}
   369  	return c.commonTLSConfig(false)
   370  }
   371  
   372  // OutgoingRPCWrapper wraps the result of OutgoingRPCConfig in a DCWrapper. It
   373  // decides if verify server hostname should be used.
   374  func (c *Configurator) OutgoingRPCWrapper() DCWrapper {
   375  	c.log("OutgoingRPCWrapper")
   376  	if c.outgoingRPCTLSDisabled() {
   377  		return nil
   378  	}
   379  
   380  	// Generate the wrapper based on dc
   381  	return func(dc string, conn net.Conn) (net.Conn, error) {
   382  		return c.wrapTLSClient(dc, conn)
   383  	}
   384  }
   385  
   386  // This function acquires a read lock because it reads from the config.
   387  func (c *Configurator) log(name string) {
   388  	if c.logger != nil {
   389  		c.RLock()
   390  		defer c.RUnlock()
   391  		c.logger.Printf("[DEBUG] tlsutil: %s with version %d", name, c.version)
   392  	}
   393  }
   394  
   395  // Wrap a net.Conn into a client tls connection, performing any
   396  // additional verification as needed.
   397  //
   398  // As of go 1.3, crypto/tls only supports either doing no certificate
   399  // verification, or doing full verification including of the peer's
   400  // DNS name. For consul, we want to validate that the certificate is
   401  // signed by a known CA, but because consul doesn't use DNS names for
   402  // node names, we don't verify the certificate DNS names. Since go 1.3
   403  // no longer supports this mode of operation, we have to do it
   404  // manually.
   405  func (c *Configurator) wrapTLSClient(dc string, conn net.Conn) (net.Conn, error) {
   406  	var err error
   407  	var tlsConn *tls.Conn
   408  
   409  	config := c.OutgoingRPCConfig()
   410  	verifyServerHostname, verifyOutgoing, domain := c.someValuesFromConfig()
   411  
   412  	if verifyServerHostname {
   413  		// Strip the trailing '.' from the domain if any
   414  		domain = strings.TrimSuffix(domain, ".")
   415  		config.ServerName = "server." + dc + "." + domain
   416  	}
   417  	tlsConn = tls.Client(conn, config)
   418  
   419  	// If crypto/tls is doing verification, there's no need to do
   420  	// our own.
   421  	if !config.InsecureSkipVerify {
   422  		return tlsConn, nil
   423  	}
   424  
   425  	// If verification is not turned on, don't do it.
   426  	if !verifyOutgoing {
   427  		return tlsConn, nil
   428  	}
   429  
   430  	if err = tlsConn.Handshake(); err != nil {
   431  		tlsConn.Close()
   432  		return nil, err
   433  	}
   434  
   435  	// The following is lightly-modified from the doFullHandshake
   436  	// method in crypto/tls's handshake_client.go.
   437  	opts := x509.VerifyOptions{
   438  		Roots:         config.RootCAs,
   439  		CurrentTime:   time.Now(),
   440  		DNSName:       "",
   441  		Intermediates: x509.NewCertPool(),
   442  	}
   443  
   444  	certs := tlsConn.ConnectionState().PeerCertificates
   445  	for i, cert := range certs {
   446  		if i == 0 {
   447  			continue
   448  		}
   449  		opts.Intermediates.AddCert(cert)
   450  	}
   451  
   452  	_, err = certs[0].Verify(opts)
   453  	if err != nil {
   454  		tlsConn.Close()
   455  		return nil, err
   456  	}
   457  
   458  	return tlsConn, err
   459  }
   460  
   461  // ParseCiphers parse ciphersuites from the comma-separated string into
   462  // recognized slice
   463  func ParseCiphers(cipherStr string) ([]uint16, error) {
   464  	suites := []uint16{}
   465  
   466  	cipherStr = strings.TrimSpace(cipherStr)
   467  	if cipherStr == "" {
   468  		return []uint16{}, nil
   469  	}
   470  	ciphers := strings.Split(cipherStr, ",")
   471  
   472  	cipherMap := map[string]uint16{
   473  		"TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305":    tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
   474  		"TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305":  tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
   475  		"TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256":   tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
   476  		"TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
   477  		"TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384":   tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
   478  		"TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
   479  		"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256":   tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
   480  		"TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA":      tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
   481  		"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256,
   482  		"TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA":    tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
   483  		"TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA":      tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
   484  		"TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA":    tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
   485  		"TLS_RSA_WITH_AES_128_GCM_SHA256":         tls.TLS_RSA_WITH_AES_128_GCM_SHA256,
   486  		"TLS_RSA_WITH_AES_256_GCM_SHA384":         tls.TLS_RSA_WITH_AES_256_GCM_SHA384,
   487  		"TLS_RSA_WITH_AES_128_CBC_SHA256":         tls.TLS_RSA_WITH_AES_128_CBC_SHA256,
   488  		"TLS_RSA_WITH_AES_128_CBC_SHA":            tls.TLS_RSA_WITH_AES_128_CBC_SHA,
   489  		"TLS_RSA_WITH_AES_256_CBC_SHA":            tls.TLS_RSA_WITH_AES_256_CBC_SHA,
   490  		"TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA":     tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
   491  		"TLS_RSA_WITH_3DES_EDE_CBC_SHA":           tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA,
   492  		"TLS_RSA_WITH_RC4_128_SHA":                tls.TLS_RSA_WITH_RC4_128_SHA,
   493  		"TLS_ECDHE_RSA_WITH_RC4_128_SHA":          tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
   494  		"TLS_ECDHE_ECDSA_WITH_RC4_128_SHA":        tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
   495  	}
   496  	for _, cipher := range ciphers {
   497  		if v, ok := cipherMap[cipher]; ok {
   498  			suites = append(suites, v)
   499  		} else {
   500  			return suites, fmt.Errorf("unsupported cipher %q", cipher)
   501  		}
   502  	}
   503  
   504  	return suites, nil
   505  }