From 1f984dfea8c983086e0e523ca22cc49b03dc0621 Mon Sep 17 00:00:00 2001 From: Armon Dadgar Date: Mon, 11 May 2015 15:14:56 -0700 Subject: [PATCH] tlsutil: Adding wrappers for hostname verification --- tlsutil/config.go | 69 ++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 66 insertions(+), 3 deletions(-) diff --git a/tlsutil/config.go b/tlsutil/config.go index b65f9a4fd..0b3f42d46 100644 --- a/tlsutil/config.go +++ b/tlsutil/config.go @@ -9,9 +9,14 @@ import ( "time" ) -// Wrapper is a function that is used to wrap a non-TLS connection -// and returns an appropriate TLS connection or error -type Wrapper func(net.Conn) (net.Conn, error) +// DCWrapper is a function that is used to wrap a non-TLS connection +// and returns an appropriate TLS connection or error. This takes +// a datacenter as an argument. +type DCWrapper func(dc string, conn net.Conn) (net.Conn, error) + +// Wrapper is a variant of DCWrapper, where the DC is provided as +// a constant value. This is usually done by currying DCWrapper. +type Wrapper func(conn net.Conn) (net.Conn, error) // Config used to create tls.Config type Config struct { @@ -26,6 +31,14 @@ type Config struct { // server nodes. VerifyOutgoing bool + // VerifyServerHostname is used to enable hostname verification of servers. This + // ensures that the certificate presented is valid for server... + // This prevents a compromised client from being restarted as a server, and then + // intercepting request traffic as well as being added as a raft peer. This should be + // enabled by default with VerifyOutgoing, but for legacy reasons we cannot break + // existing clients. + VerifyServerHostname bool + // CAFile is a path to a certificate authority file. This is used with VerifyIncoming // or VerifyOutgoing to verify the TLS connection. CAFile string @@ -44,6 +57,9 @@ type Config struct { // ServerName is used with the TLS certificate to ensure the name we // provide matches the certificate ServerName string + + // Domain is the Consul TLD being used. Defaults to "consul." + Domain string } // AppendCA opens and parses the CA file and adds the certificates to @@ -94,6 +110,11 @@ func (c *Config) OutgoingTLSConfig() (*tls.Config, error) { tlsConfig.ServerName = c.ServerName tlsConfig.InsecureSkipVerify = false } + if c.VerifyServerHostname { + // ServerName is filled in dynamically based on the target DC + tlsConfig.ServerName = "VerifyServerHostname" + tlsConfig.InsecureSkipVerify = false + } // Ensure we have a CA if VerifyOutgoing is set if c.VerifyOutgoing && c.CAFile == "" { @@ -117,6 +138,48 @@ func (c *Config) OutgoingTLSConfig() (*tls.Config, error) { return tlsConfig, nil } +// OutgoingTLSWrapper returns a a DCWrapper based on the OutgoingTLS +// configuration. If hostname verification is on, the wrapper +// will properly generate the dynamic server name for verification. +func (c *Config) OutgoingTLSWrapper() (DCWrapper, error) { + // Get the TLS config + tlsConfig, err := c.OutgoingTLSConfig() + if err != nil { + return nil, err + } + + // Check if TLS is not enabled + if tlsConfig == nil { + return nil, nil + } + + // Generate the wrapper based on hostname verification + if c.VerifyServerHostname { + wrapper := func(dc string, conn net.Conn) (net.Conn, error) { + conf := *tlsConfig + conf.ServerName = "server." + dc + "." + c.Domain + return WrapTLSClient(conn, &conf) + } + return wrapper, nil + } else { + wrapper := func(dc string, c net.Conn) (net.Conn, error) { + return WrapTLSClient(c, tlsConfig) + } + return wrapper, nil + } +} + +// SpecificDC is used to invoke a static datacenter +// and turns a DCWrapper into a Wrapper type. +func SpecificDC(dc string, tlsWrap DCWrapper) Wrapper { + if tlsWrap == nil { + return nil + } + return func(conn net.Conn) (net.Conn, error) { + return tlsWrap(dc, conn) + } +} + // Wrap a net.Conn into a client tls connection, performing any // additional verification as needed. //