diff --git a/embed/config.go b/embed/config.go index 8d4ce54a6f55..47c94971eac3 100644 --- a/embed/config.go +++ b/embed/config.go @@ -123,6 +123,7 @@ type securityConfig struct { KeyFile string `json:"key-file"` CertAuth bool `json:"client-cert-auth"` TrustedCAFile string `json:"trusted-ca-file"` + CRLFile string `json:"crl-file"` AutoTLS bool `json:"auto-tls"` } @@ -214,6 +215,7 @@ func (cfg *configYAML) configFromFile(path string) error { tls.KeyFile = ysc.KeyFile tls.ClientCertAuth = ysc.CertAuth tls.TrustedCAFile = ysc.TrustedCAFile + tls.CRLFile = ysc.CRLFile } copySecurityDetails(&cfg.ClientTLSInfo, &cfg.ClientSecurityJSON) copySecurityDetails(&cfg.PeerTLSInfo, &cfg.PeerSecurityJSON) diff --git a/embed/etcd.go b/embed/etcd.go index f9cdc0ca9b27..fd00b2a40ac2 100644 --- a/embed/etcd.go +++ b/embed/etcd.go @@ -16,15 +16,18 @@ package embed import ( "crypto/tls" + "errors" "fmt" "net" "net/http" "path" + "github.com/cloudflare/cfssl/revoke" "github.com/coreos/etcd/etcdserver" "github.com/coreos/etcd/etcdserver/api/v2http" "github.com/coreos/etcd/pkg/cors" runtimeutil "github.com/coreos/etcd/pkg/runtime" + "github.com/coreos/etcd/pkg/tlsutil" "github.com/coreos/etcd/pkg/transport" "github.com/coreos/etcd/pkg/types" "github.com/coreos/etcd/rafthttp" @@ -272,6 +275,27 @@ func startClientListeners(cfg *Config) (sctxs map[string]*serveCtx, err error) { return sctxs, nil } +func revokeCheckHandler(req *http.Request, CRLpath string) error { + if req.TLS == nil { + return nil + } + for _, cert := range req.TLS.PeerCertificates { + var revoked, ok bool + if CRLpath != "" { + revoked, ok = revoke.VerifyCertificateByCRLPath(cert, CRLpath) + } else { + revoked, ok = revoke.VerifyCertificate(cert) + } + if !ok { + return errors.New("Cert check failed") + } + if revoked { + return errors.New("Cert if revoked") + } + } + return nil +} + func (e *Etcd) serve() (err error) { var ctlscfg *tls.Config if !e.cfg.ClientTLSInfo.Empty() { @@ -286,16 +310,23 @@ func (e *Etcd) serve() (err error) { } // Start the peer server in a goroutine - ph := v2http.NewPeerHandler(e.Server) + ph := tlsutil.RevocationCheck( + v2http.NewPeerHandler(e.Server), + revokeCheckHandler, + e.cfg.PeerTLSInfo.CRLFile) for _, l := range e.Peers { go func(l net.Listener) { e.errc <- servePeerHTTP(l, ph) }(l) } + clientHandler := tlsutil.RevocationCheck( + v2http.NewClientHandler(e.Server, e.Server.Cfg.ReqTimeout()), + revokeCheckHandler, + e.cfg.ClientTLSInfo.CRLFile) // Start a client server goroutine for each listen address ch := http.Handler(&cors.CORSHandler{ - Handler: v2http.NewClientHandler(e.Server, e.Server.Cfg.ReqTimeout()), + Handler: clientHandler, Info: e.cfg.CorsInfo, }) for _, sctx := range e.sctxs { diff --git a/etcdmain/config.go b/etcdmain/config.go index 5fb4dea4934b..40fc472b53d8 100644 --- a/etcdmain/config.go +++ b/etcdmain/config.go @@ -173,12 +173,14 @@ func newConfig() *config { 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.ClientTLSInfo.CRLFile, "crl-file", "", "Path to the client server certificate revocation list file.") fs.BoolVar(&cfg.ClientAutoTLS, "auto-tls", false, "Client TLS using generated certificates") fs.StringVar(&cfg.PeerTLSInfo.CAFile, "peer-ca-file", "", "DEPRECATED: 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.") + fs.StringVar(&cfg.PeerTLSInfo.CRLFile, "peer-crl-file", "", "Path to the peer server certificate revocation list file.") fs.BoolVar(&cfg.PeerAutoTLS, "peer-auto-tls", false, "Peer TLS using generated certificates") // logging diff --git a/etcdmain/help.go b/etcdmain/help.go index e425a90cd36a..b3fd8ef8ee1e 100644 --- a/etcdmain/help.go +++ b/etcdmain/help.go @@ -118,6 +118,8 @@ security flags: enable client cert authentication. --trusted-ca-file '' path to the client server TLS trusted CA key file. + --crl-file '' + path to the client server certificate revocation list file. --auto-tls 'false' client TLS using generated certificates. --peer-ca-file '' [DEPRECATED] @@ -130,6 +132,8 @@ security flags: enable peer client cert authentication. --peer-trusted-ca-file '' path to the peer server TLS trusted CA file. + --peer-crl-file '' + path to the peer server certificate revocation list file. --peer-auto-tls 'false' peer TLS using self-generated certificates if --peer-key-file and --peer-cert-file are not provided. diff --git a/pkg/tlsutil/tlsutil.go b/pkg/tlsutil/tlsutil.go index 79b1f632ed51..1cca0ae0c22a 100644 --- a/pkg/tlsutil/tlsutil.go +++ b/pkg/tlsutil/tlsutil.go @@ -18,7 +18,11 @@ import ( "crypto/tls" "crypto/x509" "encoding/pem" + "fmt" "io/ioutil" + "net/http" + + etcdErr "github.com/coreos/etcd/error" ) // NewCertPool creates x509 certPool with provided CA files. @@ -70,3 +74,16 @@ func NewCert(certfile, keyfile string, parseFunc func([]byte, []byte) (tls.Certi } return &tlsCert, nil } + +func RevocationCheck(handler http.Handler, checker func(*http.Request, string) error, CRLpath string) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + err := checker(req, CRLpath) + if err == nil { + handler.ServeHTTP(w, req) + return + } + w.WriteHeader(http.StatusForbidden) + e := etcdErr.NewError(etcdErr.EcodeUnauthorized, fmt.Sprint(err), 0) + e.WriteTo(w) + }) +} diff --git a/pkg/transport/listener.go b/pkg/transport/listener.go index d94757b2c52b..3ba4ab82e41b 100644 --- a/pkg/transport/listener.go +++ b/pkg/transport/listener.go @@ -62,6 +62,7 @@ type TLSInfo struct { KeyFile string CAFile string TrustedCAFile string + CRLFile string ClientCertAuth bool selfCert bool @@ -72,7 +73,7 @@ type TLSInfo struct { } func (info TLSInfo) String() string { - return fmt.Sprintf("cert = %s, key = %s, ca = %s, trusted-ca = %s, client-cert-auth = %v", info.CertFile, info.KeyFile, info.CAFile, info.TrustedCAFile, info.ClientCertAuth) + return fmt.Sprintf("cert = %s, key = %s, ca = %s, trusted-ca = %s, cert-auth = %v, crl-file = %s", info.CertFile, info.KeyFile, info.CAFile, info.TrustedCAFile, info.ClientCertAuth, info.CRLFile) } func (info TLSInfo) Empty() bool {