Add Support for Consul Connect
Co-authored-by: Florian Apolloner <apollo13@users.noreply.github.com>
This commit is contained in:
parent
3a180e2afc
commit
7e43e5615e
36 changed files with 2118 additions and 644 deletions
|
@ -3,7 +3,9 @@ package tls
|
|||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"sort"
|
||||
"strings"
|
||||
|
@ -273,3 +275,86 @@ func (c *Certificates) Set(value string) error {
|
|||
func (c *Certificates) Type() string {
|
||||
return "certificates"
|
||||
}
|
||||
|
||||
// VerifyPeerCertificate verifies the chain certificates and their URI.
|
||||
func VerifyPeerCertificate(uri string, cfg *tls.Config, rawCerts [][]byte) error {
|
||||
// TODO: Refactor to avoid useless verifyChain (ex: when insecureskipverify is false)
|
||||
cert, err := verifyChain(cfg.RootCAs, rawCerts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(uri) > 0 {
|
||||
return verifyServerCertMatchesURI(uri, cert)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// verifyServerCertMatchesURI is used on tls connections dialed to a server
|
||||
// to ensure that the certificate it presented has the correct URI.
|
||||
func verifyServerCertMatchesURI(uri string, cert *x509.Certificate) error {
|
||||
if cert == nil {
|
||||
return errors.New("peer certificate mismatch: no peer certificate presented")
|
||||
}
|
||||
|
||||
// Our certs will only ever have a single URI for now so only check that
|
||||
if len(cert.URIs) < 1 {
|
||||
return errors.New("peer certificate mismatch: peer certificate invalid")
|
||||
}
|
||||
|
||||
gotURI := cert.URIs[0]
|
||||
|
||||
// Override the hostname since we rely on x509 constraints to limit ability to spoof the trust domain if needed
|
||||
// (i.e. because a root is shared with other PKI or Consul clusters).
|
||||
// This allows for seamless migrations between trust domains.
|
||||
|
||||
expectURI := &url.URL{}
|
||||
id, err := url.Parse(uri)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%q is not a valid URI", uri)
|
||||
}
|
||||
*expectURI = *id
|
||||
expectURI.Host = gotURI.Host
|
||||
|
||||
if strings.EqualFold(gotURI.String(), expectURI.String()) {
|
||||
return nil
|
||||
}
|
||||
|
||||
return fmt.Errorf("peer certificate mismatch got %s, want %s", gotURI, uri)
|
||||
}
|
||||
|
||||
// verifyChain performs standard TLS verification without enforcing remote hostname matching.
|
||||
func verifyChain(rootCAs *x509.CertPool, rawCerts [][]byte) (*x509.Certificate, error) {
|
||||
// Fetch leaf and intermediates. This is based on code form tls handshake.
|
||||
if len(rawCerts) < 1 {
|
||||
return nil, errors.New("tls: no certificates from peer")
|
||||
}
|
||||
|
||||
certs := make([]*x509.Certificate, len(rawCerts))
|
||||
for i, asn1Data := range rawCerts {
|
||||
cert, err := x509.ParseCertificate(asn1Data)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("tls: failed to parse certificate from peer: %w", err)
|
||||
}
|
||||
|
||||
certs[i] = cert
|
||||
}
|
||||
|
||||
opts := x509.VerifyOptions{
|
||||
Roots: rootCAs,
|
||||
Intermediates: x509.NewCertPool(),
|
||||
}
|
||||
|
||||
// All but the first cert are intermediates
|
||||
for _, cert := range certs[1:] {
|
||||
opts.Intermediates.AddCert(cert)
|
||||
}
|
||||
|
||||
_, err := certs[0].Verify(opts)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return certs[0], nil
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue