Merge pull request #11210 from hashicorp/dnephin/fix-raft-authz-error

rpc: include error for AuthorizeServerConn failures
This commit is contained in:
Daniel Nephin 2021-10-04 16:18:57 -04:00 committed by GitHub
commit a18d298c6b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 126 additions and 2 deletions

View File

@ -893,6 +893,10 @@ func (c *Configurator) wrapALPNTLSClient(dc, nodeName, alpnProto string, conn ne
return tlsConn, nil return tlsConn, nil
} }
type TLSConn interface {
ConnectionState() tls.ConnectionState
}
// AuthorizeServerConn is used to validate that the connection is being established // AuthorizeServerConn is used to validate that the connection is being established
// by a Consul server in the same datacenter. // by a Consul server in the same datacenter.
// //
@ -902,7 +906,7 @@ func (c *Configurator) wrapALPNTLSClient(dc, nodeName, alpnProto string, conn ne
// //
// Note this check is only performed if VerifyServerHostname is enabled, otherwise // Note this check is only performed if VerifyServerHostname is enabled, otherwise
// it does no authorization. // it does no authorization.
func (c *Configurator) AuthorizeServerConn(dc string, conn *tls.Conn) error { func (c *Configurator) AuthorizeServerConn(dc string, conn TLSConn) error {
if !c.VerifyServerHostname() { if !c.VerifyServerHostname() {
return nil return nil
} }
@ -931,7 +935,7 @@ func (c *Configurator) AuthorizeServerConn(dc string, conn *tls.Conn) error {
if err == nil { if err == nil {
return nil return nil
} }
multierror.Append(errs, err) errs = multierror.Append(errs, err)
} }
return fmt.Errorf("AuthorizeServerConn failed certificate validation for certificate with a SAN.DNSName of %v: %w", expected, errs) return fmt.Errorf("AuthorizeServerConn failed certificate validation for certificate with a SAN.DNSName of %v: %w", expected, errs)

View File

@ -7,6 +7,7 @@ import (
"io" "io"
"io/ioutil" "io/ioutil"
"net" "net"
"path/filepath"
"reflect" "reflect"
"strings" "strings"
"testing" "testing"
@ -1270,6 +1271,125 @@ func TestConfigurator_AutoEncryptCert(t *testing.T) {
require.Equal(t, int64(4679716209), c.AutoEncryptCert().NotAfter.Unix()) require.Equal(t, int64(4679716209), c.AutoEncryptCert().NotAfter.Unix())
} }
func TestConfigurator_AuthorizeServerConn_Error(t *testing.T) {
caPEM, caPK, err := GenerateCA(CAOpts{Days: 5, Domain: "consul"})
require.NoError(t, err)
dir := testutil.TempDir(t, "ca")
caPath := filepath.Join(dir, "ca.pem")
err = ioutil.WriteFile(caPath, []byte(caPEM), 0600)
require.NoError(t, err)
cfg := Config{
VerifyServerHostname: true,
Domain: "consul",
CAFile: caPath,
}
c, err := NewConfigurator(cfg, hclog.New(nil))
require.NoError(t, err)
t.Run("wrong DNSName", func(t *testing.T) {
signer, err := ParseSigner(caPK)
require.NoError(t, err)
pem, _, err := GenerateCert(CertOpts{
Signer: signer,
CA: caPEM,
Name: "server.dc1.consul",
Days: 5,
DNSNames: []string{"this-name-is-wrong", "localhost"},
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
})
require.NoError(t, err)
s := fakeTLSConn{
state: tls.ConnectionState{
VerifiedChains: [][]*x509.Certificate{certChain(t, pem, caPEM)},
PeerCertificates: certChain(t, pem, caPEM),
},
}
err = c.AuthorizeServerConn("dc1", s)
testutil.RequireErrorContains(t, err, "is valid for this-name-is-wrong, localhost, not server.dc1.consul")
})
t.Run("wrong CA", func(t *testing.T) {
caPEM, caPK, err := GenerateCA(CAOpts{Days: 5, Domain: "consul"})
require.NoError(t, err)
dir := testutil.TempDir(t, "other")
caPath := filepath.Join(dir, "ca.pem")
err = ioutil.WriteFile(caPath, []byte(caPEM), 0600)
require.NoError(t, err)
signer, err := ParseSigner(caPK)
require.NoError(t, err)
pem, _, err := GenerateCert(CertOpts{
Signer: signer,
CA: caPEM,
Name: "server.dc1.consul",
Days: 5,
DNSNames: []string{"server.dc1.consul", "localhost"},
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth},
})
require.NoError(t, err)
s := fakeTLSConn{
state: tls.ConnectionState{
VerifiedChains: [][]*x509.Certificate{certChain(t, pem, caPEM)},
PeerCertificates: certChain(t, pem, caPEM),
},
}
err = c.AuthorizeServerConn("dc1", s)
testutil.RequireErrorContains(t, err, "signed by unknown authority")
})
t.Run("missing ext key usage", func(t *testing.T) {
signer, err := ParseSigner(caPK)
require.NoError(t, err)
pem, _, err := GenerateCert(CertOpts{
Signer: signer,
CA: caPEM,
Name: "server.dc1.consul",
Days: 5,
DNSNames: []string{"server.dc1.consul", "localhost"},
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageEmailProtection},
})
require.NoError(t, err)
s := fakeTLSConn{
state: tls.ConnectionState{
VerifiedChains: [][]*x509.Certificate{certChain(t, pem, caPEM)},
PeerCertificates: certChain(t, pem, caPEM),
},
}
err = c.AuthorizeServerConn("dc1", s)
testutil.RequireErrorContains(t, err, "certificate specifies an incompatible key usage")
})
}
type fakeTLSConn struct {
state tls.ConnectionState
}
func (f fakeTLSConn) ConnectionState() tls.ConnectionState {
return f.state
}
func certChain(t *testing.T, certs ...string) []*x509.Certificate {
t.Helper()
result := make([]*x509.Certificate, 0, len(certs))
for i, c := range certs {
cert, err := parseCert(c)
require.NoError(t, err, "cert %d", i)
result = append(result, cert)
}
return result
}
func TestConfig_tlsVersions(t *testing.T) { func TestConfig_tlsVersions(t *testing.T) {
require.Equal(t, []string{"tls10", "tls11", "tls12", "tls13"}, tlsVersions()) require.Equal(t, []string{"tls10", "tls11", "tls12", "tls13"}, tlsVersions())
expected := "tls10, tls11, tls12, tls13" expected := "tls10, tls11, tls12, tls13"