github.com/DerekStrickland/consul@v1.4.5/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 dialed 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 customize 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 initializing 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 serialized 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 }