diff --git a/etcdmain/config.go b/etcdmain/config.go index 7f87064e7..7665d1d9d 100644 --- a/etcdmain/config.go +++ b/etcdmain/config.go @@ -172,9 +172,13 @@ func NewConfig() *config { fs.StringVar(&cfg.clientTLSInfo.CAFile, "ca-file", "", "Path to the client server TLS CA file.") fs.StringVar(&cfg.clientTLSInfo.CertFile, "cert-file", "", "Path to the client server TLS cert file.") fs.StringVar(&cfg.clientTLSInfo.KeyFile, "key-file", "", "Path to the client server TLS key file.") + fs.BoolVar(&cfg.clientTLSInfo.ClientCertAuth, "client-cert-auth", false, "Enable client cert authentication.") + fs.StringVar(&cfg.clientTLSInfo.TrustedCAFile, "trusted-ca-file", "", "Path to the client server TLS trusted CA key file.") fs.StringVar(&cfg.peerTLSInfo.CAFile, "peer-ca-file", "", "Path to the peer server TLS CA file.") fs.StringVar(&cfg.peerTLSInfo.CertFile, "peer-cert-file", "", "Path to the peer server TLS cert file.") fs.StringVar(&cfg.peerTLSInfo.KeyFile, "peer-key-file", "", "Path to the peer server TLS key file.") + fs.BoolVar(&cfg.peerTLSInfo.ClientCertAuth, "peer-client-cert-auth", false, "Enable peer client cert authentication.") + fs.StringVar(&cfg.peerTLSInfo.TrustedCAFile, "peer-trusted-ca-file", "", "Path to the peer server TLS trusted CA file.") // unsafe fs.BoolVar(&cfg.forceNewCluster, "force-new-cluster", false, "Force to create a new one member cluster") diff --git a/etcdmain/help.go b/etcdmain/help.go index 346640ff1..782ac95fe 100644 --- a/etcdmain/help.go +++ b/etcdmain/help.go @@ -81,12 +81,20 @@ security flags: path to the client server TLS cert file. --key-file '' path to the client server TLS key file. + --client-cert-auth 'false' + enable client cert authentication. + --trusted-ca-file '' + path to the client server TLS trusted CA key file. --peer-ca-file '' path to the peer server TLS CA file. --peer-cert-file '' path to the peer server TLS cert file. --peer-key-file '' path to the peer server TLS key file. + --peer-client-cert-auth 'false' + enable peer client cert authentication. + --peer-trusted-ca-file '' + path to the peer server TLS trusted CA file. unsafe flags: diff --git a/pkg/transport/listener.go b/pkg/transport/listener.go index d3c0f4d1d..e3f558d41 100644 --- a/pkg/transport/listener.go +++ b/pkg/transport/listener.go @@ -66,9 +66,11 @@ func NewTransport(info TLSInfo) (*http.Transport, error) { } type TLSInfo struct { - CertFile string - KeyFile string - CAFile string + CertFile string + KeyFile string + CAFile string + TrustedCAFile string + ClientCertAuth bool // parseFunc exists to simplify testing. Typically, parseFunc // should be left nil. In that case, tls.X509KeyPair will be used. @@ -115,29 +117,47 @@ func (info TLSInfo) baseConfig() (*tls.Config, error) { return cfg, nil } -// ServerConfig generates a tls.Config object for use by an HTTP server +// cafiles returns a list of CA file paths. +func (info TLSInfo) cafiles() []string { + cs := make([]string, 0) + if info.CAFile != "" { + cs = append(cs, info.CAFile) + } + if info.TrustedCAFile != "" { + cs = append(cs, info.TrustedCAFile) + } + return cs +} + +// ServerConfig generates a tls.Config object for use by an HTTP server. func (info TLSInfo) ServerConfig() (*tls.Config, error) { cfg, err := info.baseConfig() if err != nil { return nil, err } - if info.CAFile != "" { + cfg.ClientAuth = tls.NoClientCert + if info.CAFile != "" || info.ClientCertAuth { cfg.ClientAuth = tls.RequireAndVerifyClientCert - cp, err := newCertPool(info.CAFile) + } + + CAFiles := info.cafiles() + if len(CAFiles) > 0 { + cp, err := newCertPool(CAFiles) if err != nil { return nil, err } cfg.ClientCAs = cp - } else { - cfg.ClientAuth = tls.NoClientCert } return cfg, nil } -// ClientConfig generates a tls.Config object for use by an HTTP client -func (info TLSInfo) ClientConfig() (cfg *tls.Config, err error) { +// ClientConfig generates a tls.Config object for use by an HTTP client. +func (info TLSInfo) ClientConfig() (*tls.Config, error) { + var cfg *tls.Config + var err error + if !info.Empty() { cfg, err = info.baseConfig() if err != nil { @@ -147,34 +167,40 @@ func (info TLSInfo) ClientConfig() (cfg *tls.Config, err error) { cfg = &tls.Config{} } - if info.CAFile != "" { - cfg.RootCAs, err = newCertPool(info.CAFile) - if err != nil { - return - } - } - - return -} - -// newCertPool creates x509 certPool with provided CA file -func newCertPool(CAFile string) (*x509.CertPool, error) { - certPool := x509.NewCertPool() - pemByte, err := ioutil.ReadFile(CAFile) - if err != nil { - return nil, err - } - - for { - var block *pem.Block - block, pemByte = pem.Decode(pemByte) - if block == nil { - return certPool, nil - } - cert, err := x509.ParseCertificate(block.Bytes) + CAFiles := info.cafiles() + if len(CAFiles) > 0 { + cfg.RootCAs, err = newCertPool(CAFiles) if err != nil { return nil, err } - certPool.AddCert(cert) } + + return cfg, nil +} + +// newCertPool creates x509 certPool with provided CA files. +func newCertPool(CAFiles []string) (*x509.CertPool, error) { + certPool := x509.NewCertPool() + + for _, CAFile := range CAFiles { + pemByte, err := ioutil.ReadFile(CAFile) + if err != nil { + return nil, err + } + + for { + var block *pem.Block + block, pemByte = pem.Decode(pemByte) + if block == nil { + break + } + cert, err := x509.ParseCertificate(block.Bytes) + if err != nil { + return nil, err + } + certPool.AddCert(cert) + } + } + + return certPool, nil }