From cd781bf30ce4abcfdb2d3e6a58edcecad66df7d8 Mon Sep 17 00:00:00 2001 From: Anthony Romano Date: Tue, 2 Aug 2016 10:40:20 -0700 Subject: [PATCH] transport: add ServerName to TLSConfig and add ValidateSecureEndpoints ServerName prevents accepting forged SRV records with cross-domain credentials. ValidateSecureEndpoints prevents downgrade attacks from SRV records. --- pkg/transport/listener.go | 8 ++++++- pkg/transport/tls.go | 49 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 pkg/transport/tls.go diff --git a/pkg/transport/listener.go b/pkg/transport/listener.go index 78407ad8b..9d7209c53 100644 --- a/pkg/transport/listener.go +++ b/pkg/transport/listener.go @@ -67,6 +67,9 @@ type TLSInfo struct { TrustedCAFile string ClientCertAuth bool + // ServerName ensures the cert matches the given host in case of discovery / virtual hosting + ServerName string + selfCert bool // parseFunc exists to simplify testing. Typically, parseFunc @@ -167,6 +170,7 @@ func (info TLSInfo) baseConfig() (*tls.Config, error) { cfg := &tls.Config{ Certificates: []tls.Certificate{*tlsCert}, MinVersion: tls.VersionTLS12, + ServerName: info.ServerName, } return cfg, nil } @@ -218,7 +222,7 @@ func (info TLSInfo) ClientConfig() (*tls.Config, error) { return nil, err } } else { - cfg = &tls.Config{} + cfg = &tls.Config{ServerName: info.ServerName} } CAFiles := info.cafiles() @@ -227,6 +231,8 @@ func (info TLSInfo) ClientConfig() (*tls.Config, error) { if err != nil { return nil, err } + // if given a CA, trust any host with a cert signed by the CA + cfg.ServerName = "" } if info.selfCert { diff --git a/pkg/transport/tls.go b/pkg/transport/tls.go new file mode 100644 index 000000000..62fe0d385 --- /dev/null +++ b/pkg/transport/tls.go @@ -0,0 +1,49 @@ +// Copyright 2016 The etcd Authors +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package transport + +import ( + "fmt" + "strings" + "time" +) + +// ValidateSecureEndpoints scans the given endpoints against tls info, returning only those +// endpoints that could be validated as secure. +func ValidateSecureEndpoints(tlsInfo TLSInfo, eps []string) ([]string, error) { + t, err := NewTransport(tlsInfo, 5*time.Second) + if err != nil { + return nil, err + } + var errs []string + var endpoints []string + for _, ep := range eps { + if !strings.HasPrefix(ep, "https://") { + errs = append(errs, fmt.Sprintf("%q is insecure", ep)) + continue + } + conn, cerr := t.Dial("tcp", ep[len("https://"):]) + if cerr != nil { + errs = append(errs, fmt.Sprintf("%q failed to dial (%v)", ep, cerr)) + continue + } + conn.Close() + endpoints = append(endpoints, ep) + } + if len(errs) != 0 { + err = fmt.Errorf("%s", strings.Join(errs, ",")) + } + return endpoints, err +}