github.com/lmb/consul@v1.4.1/connect/tls.go (about)

     1  package connect
     2  
     3  import (
     4  	"crypto/tls"
     5  	"crypto/x509"
     6  	"crypto/x509/pkix"
     7  	"encoding/asn1"
     8  	"errors"
     9  	"fmt"
    10  	"io/ioutil"
    11  	"log"
    12  	"net"
    13  	"net/url"
    14  	"strings"
    15  	"sync"
    16  
    17  	"github.com/hashicorp/consul/agent/connect"
    18  	"github.com/hashicorp/consul/api"
    19  )
    20  
    21  // parseLeafX509Cert will parse an X509 certificate
    22  // from the TLS certificate and store the parsed
    23  // value in the TLS certificate as the Leaf field.
    24  func parseLeafX509Cert(leaf *tls.Certificate) error {
    25  	if leaf == nil {
    26  		// nothing to parse for nil cert
    27  		return nil
    28  	}
    29  
    30  	if leaf.Leaf != nil {
    31  		// leaf cert was already parsed
    32  		return nil
    33  	}
    34  
    35  	cert, err := x509.ParseCertificate(leaf.Certificate[0])
    36  
    37  	if err != nil {
    38  		return err
    39  	}
    40  
    41  	leaf.Leaf = cert
    42  	return nil
    43  }
    44  
    45  // verifierFunc is a function that can accept rawCertificate bytes from a peer
    46  // and verify them against a given tls.Config. It's called from the
    47  // tls.Config.VerifyPeerCertificate hook.
    48  //
    49  // We don't pass verifiedChains since that is always nil in our usage.
    50  // Implementations can use the roots provided in the cfg to verify the certs.
    51  //
    52  // The passed *tls.Config may have a nil VerifyPeerCertificates function but
    53  // will have correct roots, leaf and other fields.
    54  type verifierFunc func(cfg *tls.Config, rawCerts [][]byte) error
    55  
    56  // defaultTLSConfig returns the standard config with no peer verifier. It is
    57  // insecure to use it as-is.
    58  func defaultTLSConfig() *tls.Config {
    59  	cfg := &tls.Config{
    60  		MinVersion: tls.VersionTLS12,
    61  		ClientAuth: tls.RequireAndVerifyClientCert,
    62  		// We don't have access to go internals that decide if AES hardware
    63  		// acceleration is available in order to prefer CHA CHA if not. So let's
    64  		// just always prefer AES for now. We can look into doing something uglier
    65  		// later like using an external lib for AES checking if it seems important.
    66  		// https://github.com/golang/go/blob/df91b8044dbe790c69c16058330f545be069cc1f/src/crypto/tls/common.go#L919:14
    67  		CipherSuites: []uint16{
    68  			tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
    69  			tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
    70  			tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
    71  			tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
    72  			tls.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305,
    73  			tls.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,
    74  		},
    75  		// We have to set this since otherwise Go will attempt to verify DNS names
    76  		// match DNS SAN/CN which we don't want. We hook up VerifyPeerCertificate to
    77  		// do our own path validation as well as Connect AuthZ.
    78  		InsecureSkipVerify: true,
    79  		// Include h2 to allow connect http servers to automatically support http2.
    80  		// See: https://github.com/golang/go/blob/917c33fe8672116b04848cf11545296789cafd3b/src/net/http/server.go#L2724-L2731
    81  		NextProtos: []string{"h2"},
    82  	}
    83  	return cfg
    84  }
    85  
    86  // devTLSConfigFromFiles returns a default TLS Config but with certs and CAs
    87  // based on local files for dev. No verification is setup.
    88  func devTLSConfigFromFiles(caFile, certFile,
    89  	keyFile string) (*tls.Config, error) {
    90  
    91  	roots := x509.NewCertPool()
    92  
    93  	bs, err := ioutil.ReadFile(caFile)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  
    98  	roots.AppendCertsFromPEM(bs)
    99  
   100  	cert, err := tls.LoadX509KeyPair(certFile, keyFile)
   101  	if err != nil {
   102  		return nil, err
   103  	}
   104  
   105  	cfg := defaultTLSConfig()
   106  	cfg.Certificates = []tls.Certificate{cert}
   107  	cfg.RootCAs = roots
   108  	cfg.ClientCAs = roots
   109  
   110  	return cfg, nil
   111  }
   112  
   113  // PKIXNameFromRawSubject attempts to parse a DER encoded "Subject" as a PKIX
   114  // Name. It's useful for inspecting root certificates in an x509.CertPool which
   115  // only expose RawSubject via the Subjects method.
   116  func PKIXNameFromRawSubject(raw []byte) (*pkix.Name, error) {
   117  	var subject pkix.RDNSequence
   118  	if _, err := asn1.Unmarshal(raw, &subject); err != nil {
   119  		return nil, err
   120  	}
   121  	var name pkix.Name
   122  	name.FillFromRDNSequence(&subject)
   123  	return &name, nil
   124  }
   125  
   126  // CommonNamesFromCertPool returns the common names of the certificates in the
   127  // cert pool.
   128  func CommonNamesFromCertPool(p *x509.CertPool) ([]string, error) {
   129  	var names []string
   130  	for _, rawSubj := range p.Subjects() {
   131  		n, err := PKIXNameFromRawSubject(rawSubj)
   132  		if err != nil {
   133  			return nil, err
   134  		}
   135  		names = append(names, n.CommonName)
   136  	}
   137  	return names, nil
   138  }
   139  
   140  // CertURIFromConn is a helper to extract the service identifier URI from a
   141  // net.Conn. If the net.Conn is not a *tls.Conn then an error is always
   142  // returned. If the *tls.Conn didn't present a valid connect certificate, or is
   143  // not yet past the handshake, an error is returned.
   144  func CertURIFromConn(conn net.Conn) (connect.CertURI, error) {
   145  	tc, ok := conn.(*tls.Conn)
   146  	if !ok {
   147  		return nil, fmt.Errorf("invalid non-TLS connect client")
   148  	}
   149  	gotURI, err := extractCertURI(tc.ConnectionState().PeerCertificates)
   150  	if err != nil {
   151  		return nil, err
   152  	}
   153  	return connect.ParseCertURI(gotURI)
   154  }
   155  
   156  // extractCertURI returns the first URI SAN from the leaf certificate presented
   157  // in the slice. The slice is expected to be the passed from
   158  // tls.Conn.ConnectionState().PeerCertificates and requires that the leaf has at
   159  // least one URI and the first URI is the correct one to use.
   160  func extractCertURI(certs []*x509.Certificate) (*url.URL, error) {
   161  	if len(certs) < 1 {
   162  		return nil, errors.New("no peer certificate presented")
   163  	}
   164  
   165  	// Only check the first cert assuming this is the only leaf. It's not clear if
   166  	// services might ever legitimately present multiple leaf certificates or if
   167  	// the slice is just to allow presenting the whole chain of intermediates.
   168  	cert := certs[0]
   169  
   170  	// Our certs will only ever have a single URI for now so only check that
   171  	if len(cert.URIs) < 1 {
   172  		return nil, errors.New("peer certificate invalid")
   173  	}
   174  
   175  	return cert.URIs[0], nil
   176  }
   177  
   178  // verifyServerCertMatchesURI is used on tls connections dialled to a connect
   179  // server to ensure that the certificate it presented has the correct identity.
   180  func verifyServerCertMatchesURI(certs []*x509.Certificate,
   181  	expected connect.CertURI) error {
   182  	expectedStr := expected.URI().String()
   183  
   184  	gotURI, err := extractCertURI(certs)
   185  	if err != nil {
   186  		return errors.New("peer certificate mismatch")
   187  	}
   188  
   189  	// Override the hostname since we rely on x509 constraints to limit ability to
   190  	// spoof the trust domain if needed (i.e. because a root is shared with other
   191  	// PKI or Consul clusters). This allows for seamless migrations between trust
   192  	// domains.
   193  	expectURI := expected.URI()
   194  	expectURI.Host = gotURI.Host
   195  	if strings.ToLower(gotURI.String()) == strings.ToLower(expectURI.String()) {
   196  		// OK!
   197  		return nil
   198  	}
   199  
   200  	return fmt.Errorf("peer certificate mismatch got %s, want %s",
   201  		gotURI.String(), expectedStr)
   202  }
   203  
   204  // newServerSideVerifier returns a verifierFunc that wraps the provided
   205  // api.Client to verify the TLS chain and perform AuthZ for the server end of
   206  // the connection. The service name provided is used as the target service name
   207  // for the Authorization.
   208  func newServerSideVerifier(client *api.Client, serviceName string) verifierFunc {
   209  	return func(tlsCfg *tls.Config, rawCerts [][]byte) error {
   210  		leaf, err := verifyChain(tlsCfg, rawCerts, false)
   211  		if err != nil {
   212  			log.Printf("connect: failed TLS verification: %s", err)
   213  			return err
   214  		}
   215  
   216  		// Check leaf is a cert we understand
   217  		if len(leaf.URIs) < 1 {
   218  			log.Printf("connect: invalid leaf certificate")
   219  			return errors.New("connect: invalid leaf certificate")
   220  		}
   221  
   222  		certURI, err := connect.ParseCertURI(leaf.URIs[0])
   223  		if err != nil {
   224  			log.Printf("connect: invalid leaf certificate URI")
   225  			return errors.New("connect: invalid leaf certificate URI")
   226  		}
   227  
   228  		// No AuthZ if there is no client.
   229  		if client == nil {
   230  			log.Printf("connect: nil client")
   231  			return nil
   232  		}
   233  
   234  		// Perform AuthZ
   235  		req := &api.AgentAuthorizeParams{
   236  			Target:           serviceName,
   237  			ClientCertURI:    certURI.URI().String(),
   238  			ClientCertSerial: connect.HexString(leaf.SerialNumber.Bytes()),
   239  		}
   240  		resp, err := client.Agent().ConnectAuthorize(req)
   241  		if err != nil {
   242  			log.Printf("connect: authz call failed: %s", err)
   243  			return errors.New("connect: authz call failed: " + err.Error())
   244  		}
   245  		if !resp.Authorized {
   246  			log.Printf("connect: authz call denied: %s", resp.Reason)
   247  			return errors.New("connect: authz denied: " + resp.Reason)
   248  		}
   249  		return nil
   250  	}
   251  }
   252  
   253  // clientSideVerifier is a verifierFunc that performs verification of certificates
   254  // on the client end of the connection. For now it is just basic TLS
   255  // verification since the identity check needs additional state and becomes
   256  // clunky to customise the callback for every outgoing request. That is done
   257  // within Service.Dial for now.
   258  func clientSideVerifier(tlsCfg *tls.Config, rawCerts [][]byte) error {
   259  	_, err := verifyChain(tlsCfg, rawCerts, true)
   260  	return err
   261  }
   262  
   263  // verifyChain performs standard TLS verification without enforcing remote
   264  // hostname matching.
   265  func verifyChain(tlsCfg *tls.Config, rawCerts [][]byte, client bool) (*x509.Certificate, error) {
   266  
   267  	// Fetch leaf and intermediates. This is based on code form tls handshake.
   268  	if len(rawCerts) < 1 {
   269  		return nil, errors.New("tls: no certificates from peer")
   270  	}
   271  	certs := make([]*x509.Certificate, len(rawCerts))
   272  	for i, asn1Data := range rawCerts {
   273  		cert, err := x509.ParseCertificate(asn1Data)
   274  		if err != nil {
   275  			return nil, errors.New("tls: failed to parse certificate from peer: " + err.Error())
   276  		}
   277  		certs[i] = cert
   278  	}
   279  
   280  	cas := tlsCfg.RootCAs
   281  	if client {
   282  		cas = tlsCfg.ClientCAs
   283  	}
   284  
   285  	opts := x509.VerifyOptions{
   286  		Roots:         cas,
   287  		Intermediates: x509.NewCertPool(),
   288  	}
   289  	if !client {
   290  		// Server side only sets KeyUsages in tls. This defaults to ServerAuth in
   291  		// x509 lib. See
   292  		// https://github.com/golang/go/blob/ee7dd810f9ca4e63ecfc1d3044869591783b8b74/src/crypto/x509/verify.go#L866-L868
   293  		opts.KeyUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
   294  	}
   295  
   296  	// All but the first cert are intermediates
   297  	for _, cert := range certs[1:] {
   298  		opts.Intermediates.AddCert(cert)
   299  	}
   300  	_, err := certs[0].Verify(opts)
   301  	return certs[0], err
   302  }
   303  
   304  // dynamicTLSConfig represents the state for returning a tls.Config that can
   305  // have root and leaf certificates updated dynamically with all existing clients
   306  // and servers automatically picking up the changes. It requires initialising
   307  // with a valid base config from which all the non-certificate and verification
   308  // params are used. The base config passed should not be modified externally as
   309  // it is assumed to be serialised by the embedded mutex.
   310  type dynamicTLSConfig struct {
   311  	base *tls.Config
   312  
   313  	sync.RWMutex
   314  	leaf  *tls.Certificate
   315  	roots *x509.CertPool
   316  	// readyCh is closed when the config first gets both leaf and roots set.
   317  	// Watchers can wait on this via ReadyWait.
   318  	readyCh chan struct{}
   319  }
   320  
   321  type tlsCfgUpdate struct {
   322  	ch   chan struct{}
   323  	next *tlsCfgUpdate
   324  }
   325  
   326  // newDynamicTLSConfig returns a dynamicTLSConfig constructed from base.
   327  // base.Certificates[0] is used as the initial leaf and base.RootCAs is used as
   328  // the initial roots.
   329  func newDynamicTLSConfig(base *tls.Config, logger *log.Logger) *dynamicTLSConfig {
   330  	cfg := &dynamicTLSConfig{
   331  		base: base,
   332  	}
   333  	if len(base.Certificates) > 0 {
   334  		cfg.leaf = &base.Certificates[0]
   335  		// If this does error then future calls to Ready will fail
   336  		// It is better to handle not-Ready rather than failing
   337  		if err := parseLeafX509Cert(cfg.leaf); err != nil && logger != nil {
   338  			logger.Printf("[ERR] Error parsing configured leaf certificate: %v", err)
   339  		}
   340  	}
   341  	if base.RootCAs != nil {
   342  		cfg.roots = base.RootCAs
   343  	}
   344  	if !cfg.Ready() {
   345  		cfg.readyCh = make(chan struct{})
   346  	}
   347  	return cfg
   348  }
   349  
   350  // Get fetches the lastest tls.Config with all the hooks attached to keep it
   351  // loading the most recent roots and certs even after future changes to cfg.
   352  //
   353  // The verifierFunc passed will be attached to the config returned such that it
   354  // runs with the _latest_ config object returned passed to it. That means that a
   355  // client can use this config for a long time and will still verify against the
   356  // latest roots even though the roots in the struct is has can't change.
   357  func (cfg *dynamicTLSConfig) Get(v verifierFunc) *tls.Config {
   358  	cfg.RLock()
   359  	defer cfg.RUnlock()
   360  	copy := cfg.base.Clone()
   361  	copy.RootCAs = cfg.roots
   362  	copy.ClientCAs = cfg.roots
   363  	if v != nil {
   364  		copy.VerifyPeerCertificate = func(rawCerts [][]byte, chains [][]*x509.Certificate) error {
   365  			return v(cfg.Get(nil), rawCerts)
   366  		}
   367  	}
   368  	copy.GetCertificate = func(_ *tls.ClientHelloInfo) (*tls.Certificate, error) {
   369  		leaf := cfg.Leaf()
   370  		if leaf == nil {
   371  			return nil, errors.New("tls: no certificates configured")
   372  		}
   373  		return leaf, nil
   374  	}
   375  	copy.GetClientCertificate = func(_ *tls.CertificateRequestInfo) (*tls.Certificate, error) {
   376  		leaf := cfg.Leaf()
   377  		if leaf == nil {
   378  			return nil, errors.New("tls: no certificates configured")
   379  		}
   380  		return leaf, nil
   381  	}
   382  	copy.GetConfigForClient = func(*tls.ClientHelloInfo) (*tls.Config, error) {
   383  		return cfg.Get(v), nil
   384  	}
   385  	return copy
   386  }
   387  
   388  // SetRoots sets new roots.
   389  func (cfg *dynamicTLSConfig) SetRoots(roots *x509.CertPool) error {
   390  	cfg.Lock()
   391  	defer cfg.Unlock()
   392  	cfg.roots = roots
   393  	cfg.notify()
   394  	return nil
   395  }
   396  
   397  // SetLeaf sets a new leaf.
   398  func (cfg *dynamicTLSConfig) SetLeaf(leaf *tls.Certificate) error {
   399  	cfg.Lock()
   400  	defer cfg.Unlock()
   401  	if err := parseLeafX509Cert(leaf); err != nil {
   402  		return err
   403  	}
   404  	cfg.leaf = leaf
   405  
   406  	cfg.notify()
   407  	return nil
   408  }
   409  
   410  // notify is called under lock during an update to check if we are now ready.
   411  func (cfg *dynamicTLSConfig) notify() {
   412  	if cfg.readyCh != nil && cfg.leaf != nil && cfg.roots != nil && cfg.leaf.Leaf != nil {
   413  		close(cfg.readyCh)
   414  		cfg.readyCh = nil
   415  	}
   416  }
   417  
   418  func (cfg *dynamicTLSConfig) VerifyLeafWithRoots() error {
   419  	cfg.RLock()
   420  	defer cfg.RUnlock()
   421  
   422  	if cfg.roots == nil {
   423  		return fmt.Errorf("No roots are set")
   424  	} else if cfg.leaf == nil {
   425  		return fmt.Errorf("No leaf certificate is set")
   426  	} else if cfg.leaf.Leaf == nil {
   427  		return fmt.Errorf("Leaf certificate has not been parsed")
   428  	}
   429  
   430  	_, err := cfg.leaf.Leaf.Verify(x509.VerifyOptions{Roots: cfg.roots})
   431  	return err
   432  }
   433  
   434  // Roots returns the current CA root CertPool.
   435  func (cfg *dynamicTLSConfig) Roots() *x509.CertPool {
   436  	cfg.RLock()
   437  	defer cfg.RUnlock()
   438  	return cfg.roots
   439  }
   440  
   441  // Leaf returns the current Leaf certificate.
   442  func (cfg *dynamicTLSConfig) Leaf() *tls.Certificate {
   443  	cfg.RLock()
   444  	defer cfg.RUnlock()
   445  	return cfg.leaf
   446  }
   447  
   448  // Ready returns whether or not both roots and a leaf certificate are
   449  // configured. If both are non-nil, they are assumed to be valid and usable.
   450  func (cfg *dynamicTLSConfig) Ready() bool {
   451  	// not locking because VerifyLeafWithRoots will do that
   452  	return cfg.VerifyLeafWithRoots() == nil
   453  }
   454  
   455  // ReadyWait returns a chan that is closed when the the Service becomes ready
   456  // for use for the first time. Note that if the Service is ready when it is
   457  // called it returns a nil chan. Ready means that it has root and leaf
   458  // certificates configured but not that the combination is valid nor that
   459  // the current time is within the validity window of the certificate. The
   460  // service may subsequently stop being "ready" if it's certificates expire
   461  // or are revoked and an error prevents new ones from being loaded but this
   462  // method will not stop returning a nil chan in that case. It is only useful
   463  // for initial startup. For ongoing health Ready() should be used.
   464  func (cfg *dynamicTLSConfig) ReadyWait() <-chan struct{} {
   465  	return cfg.readyCh
   466  }