diff --git a/etcdmain/config.go b/etcdmain/config.go index 614112923632..c96e2c0794a3 100644 --- a/etcdmain/config.go +++ b/etcdmain/config.go @@ -184,6 +184,7 @@ func newConfig() *config { fs.StringVar(&cfg.PeerTLSInfo.TrustedCAFile, "peer-trusted-ca-file", "", "Path to the peer server TLS trusted CA file.") fs.BoolVar(&cfg.PeerAutoTLS, "peer-auto-tls", false, "Peer TLS using generated certificates") fs.StringVar(&cfg.PeerTLSInfo.CRLFile, "peer-crl-file", "", "Path to the peer certificate revocation list file.") + fs.StringVar(&cfg.PeerTLSInfo.RequireCN, "peer-require-cn", "", "CN required for inter peer authentication.") // logging fs.BoolVar(&cfg.Debug, "debug", false, "Enable debug-level logging for etcd.") diff --git a/pkg/transport/listener.go b/pkg/transport/listener.go index 33ba17fe12dd..cc6156d7be07 100644 --- a/pkg/transport/listener.go +++ b/pkg/transport/listener.go @@ -22,6 +22,7 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/pem" + "errors" "fmt" "math/big" "net" @@ -76,6 +77,9 @@ type TLSInfo struct { // parseFunc exists to simplify testing. Typically, parseFunc // should be left nil. In that case, tls.X509KeyPair will be used. parseFunc func([]byte, []byte) (tls.Certificate, error) + + // RequireCN is a CN which must be provided by a client + RequireCN string } func (info TLSInfo) String() string { @@ -174,6 +178,23 @@ func (info TLSInfo) baseConfig() (*tls.Config, error) { MinVersion: tls.VersionTLS12, ServerName: info.ServerName, } + + if info.RequireCN != "" { + cfg.VerifyPeerCertificate = func(rawCerts [][]byte, verifiedChains [][]*x509.Certificate) error { + if len(verifiedChains) != 0 { + chains := verifiedChains[0] + if len(chains) != 0 { + if info.RequireCN == chains[0].Subject.CommonName { + return nil + } else { + return fmt.Errorf("CommonName authentication failed (required: %s, client: %s)", info.RequireCN, chains[0].Subject.CommonName) + } + } + } + return errors.New("CommonName authentication failed") + } + } + // this only reloads certs when there's a client request // TODO: support server-side refresh (e.g. inotify, SIGHUP), caching cfg.GetCertificate = func(clientHello *tls.ClientHelloInfo) (*tls.Certificate, error) {