Restore the 0.2 TLS verification behavior.

Namely, don't check the DNS names in TLS certificates when connecting to
other servers.

As of golang 1.3, crypto/tls no longer natively supports doing partial
verification (verifying the cert issuer but not the hostname), so we
have to disable verification entirely and then do the issuer
verification ourselves. Fortunately, crypto/x509 makes this relatively
straightforward.

If the "server_name" configuration option is passed, we preserve the
existing behavior of checking that server name everywhere.

No option is provided to retain the current behavior of checking the
remote certificate against the local node name, since that behavior
seems clearly buggy and unintentional, and I have difficulty imagining
it is actually being used anywhere. It would be relatively
straightforward to restore if desired, however.
This commit is contained in:
Nelson Elhage 2014-06-22 12:49:51 -07:00
parent df637c7070
commit 0a2476b20e
6 changed files with 109 additions and 28 deletions

View File

@ -88,10 +88,8 @@ func NewClient(config *Config) (*Client, error) {
// Create the tlsConfig
var tlsConfig *tls.Config
var err error
if config.VerifyOutgoing {
if tlsConfig, err = config.OutgoingTLSConfig(); err != nil {
return nil, err
}
if tlsConfig, err = config.OutgoingTLSConfig(); err != nil {
return nil, err
}
// Create a logger

View File

@ -172,16 +172,21 @@ func (c *Config) KeyPair() (*tls.Certificate, error) {
return &cert, err
}
// OutgoingTLSConfig generates a TLS configuration for outgoing requests
// OutgoingTLSConfig generates a TLS configuration for outgoing
// requests. It will return a nil config if this configuration should
// not use TLS for outgoing connections.
func (c *Config) OutgoingTLSConfig() (*tls.Config, error) {
if !c.VerifyOutgoing {
return nil, nil
}
// Create the tlsConfig
tlsConfig := &tls.Config{
ServerName: c.ServerName,
RootCAs: x509.NewCertPool(),
InsecureSkipVerify: !c.VerifyOutgoing,
InsecureSkipVerify: true,
}
if tlsConfig.ServerName == "" {
tlsConfig.ServerName = c.NodeName
if c.ServerName != "" {
tlsConfig.ServerName = c.ServerName
tlsConfig.InsecureSkipVerify = false
}
// Ensure we have a CA if VerifyOutgoing is set
@ -206,6 +211,59 @@ func (c *Config) OutgoingTLSConfig() (*tls.Config, error) {
return tlsConfig, nil
}
// Wrap a net.Conn into a tls connection, performing any additional
// verification as needed.
//
// As of go 1.3, crypto/tls only supports either doing no certificate
// verification, or doing full verification including of the peer's
// DNS name. For consul, we want to validate that the certificate is
// signed by a known CA, but because consul doesn't use DNS names for
// node names, we don't verify the certificate DNS names. Since go 1.3
// no longer supports this mode of operation, we have to do it
// manually.
func wrapTLSClient(conn net.Conn, tlsConfig *tls.Config) (net.Conn, error) {
var err error
var tlsConn *tls.Conn
tlsConn = tls.Client(conn, tlsConfig)
// If crypto/tls is doing verification, there's no need to do
// our own.
if tlsConfig.InsecureSkipVerify == false {
return tlsConn, nil
}
if err = tlsConn.Handshake(); err != nil {
tlsConn.Close()
return nil, err
}
// The following is lightly-modified from the doFullHandshake
// method in crypto/tls's handshake_client.go.
opts := x509.VerifyOptions{
Roots: tlsConfig.RootCAs,
CurrentTime: time.Now(),
DNSName: "",
Intermediates: x509.NewCertPool(),
}
certs := tlsConn.ConnectionState().PeerCertificates
for i, cert := range certs {
if i == 0 {
continue
}
opts.Intermediates.AddCert(cert)
}
_, err = certs[0].Verify(opts)
if err != nil {
tlsConn.Close()
return nil, err
}
return tlsConn, err
}
// IncomingTLSConfig generates a TLS configuration for incoming requests
func (c *Config) IncomingTLSConfig() (*tls.Config, error) {
// Create the tlsConfig

View File

@ -78,14 +78,8 @@ func TestConfig_OutgoingTLS_OnlyCA(t *testing.T) {
if err != nil {
t.Fatalf("err: %v", err)
}
if tls == nil {
t.Fatalf("expected config")
}
if len(tls.RootCAs.Subjects()) != 1 {
t.Fatalf("expect root cert")
}
if !tls.InsecureSkipVerify {
t.Fatalf("expect to skip verification")
if tls != nil {
t.Fatalf("expected no config")
}
}
@ -104,8 +98,35 @@ func TestConfig_OutgoingTLS_VerifyOutgoing(t *testing.T) {
if len(tls.RootCAs.Subjects()) != 1 {
t.Fatalf("expect root cert")
}
if tls.ServerName != "" {
t.Fatalf("expect no server name verification")
}
if !tls.InsecureSkipVerify {
t.Fatalf("should skip built-in verification")
}
}
func TestConfig_OutgoingTLS_ServerName(t *testing.T) {
conf := &Config{
VerifyOutgoing: true,
CAFile: "../test/ca/root.cer",
ServerName: "consul.example.com",
}
tls, err := conf.OutgoingTLSConfig()
if err != nil {
t.Fatalf("err: %v", err)
}
if tls == nil {
t.Fatalf("expected config")
}
if len(tls.RootCAs.Subjects()) != 1 {
t.Fatalf("expect root cert")
}
if tls.ServerName != "consul.example.com" {
t.Fatalf("expect server name")
}
if tls.InsecureSkipVerify {
t.Fatalf("should not skip verification")
t.Fatalf("should not skip built-in verification")
}
}
@ -126,8 +147,8 @@ func TestConfig_OutgoingTLS_WithKeyPair(t *testing.T) {
if len(tls.RootCAs.Subjects()) != 1 {
t.Fatalf("expect root cert")
}
if tls.InsecureSkipVerify {
t.Fatalf("should not skip verification")
if !tls.InsecureSkipVerify {
t.Fatalf("should skip verification")
}
if len(tls.Certificates) != 1 {
t.Fatalf("expected client cert")

View File

@ -221,7 +221,11 @@ func (p *ConnPool) getNewConn(addr net.Addr, version int) (*Conn, error) {
}
// Wrap the connection in a TLS client
conn = tls.Client(conn, p.tlsConfig)
conn, err = wrapTLSClient(conn, p.tlsConfig)
if err != nil {
conn.Close()
return nil, err
}
}
// Switch the multiplexing based on version

View File

@ -94,7 +94,10 @@ func (l *RaftLayer) Dial(address string, timeout time.Duration) (net.Conn, error
}
// Wrap the connection in a TLS client
conn = tls.Client(conn, l.tlsConfig)
conn, err = wrapTLSClient(conn, l.tlsConfig)
if err != nil {
return nil, err
}
}
// Write the Raft byte to set the mode

View File

@ -145,12 +145,9 @@ func NewServer(config *Config) (*Server, error) {
}
// Create the tlsConfig for outgoing connections
var tlsConfig *tls.Config
var err error
if config.VerifyOutgoing {
if tlsConfig, err = config.OutgoingTLSConfig(); err != nil {
return nil, err
}
tlsConfig, err := config.OutgoingTLSConfig()
if err != nil {
return nil, err
}
// Get the incoming tls config