From eeab94505eedd41aed2322f007675c45c59f1e93 Mon Sep 17 00:00:00 2001 From: Gyuho Lee Date: Tue, 27 Feb 2018 15:52:24 -0800 Subject: [PATCH] embed: enforce client origin policy Signed-off-by: Gyuho Lee --- embed/etcd.go | 1 + embed/serve.go | 58 ++++++++++++++++++++++++++++++++++++++++++++++---- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/embed/etcd.go b/embed/etcd.go index a9936c2d7638..9c9ef7383541 100644 --- a/embed/etcd.go +++ b/embed/etcd.go @@ -182,6 +182,7 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) { if e.Server, err = etcdserver.NewServer(srvcfg); err != nil { return e, err } + plog.Infof("%s starting with host whitelist %q", e.Server.ID(), cfg.HostWhitelist) // buffer channel so goroutines on closed connections won't wait forever e.errc = make(chan error, len(e.Peers)+len(e.Clients)+2*len(e.sctxs)) diff --git a/embed/serve.go b/embed/serve.go index 3b8868cb5a97..cd88ccf4f8f7 100644 --- a/embed/serve.go +++ b/embed/serve.go @@ -16,6 +16,7 @@ package embed import ( "context" + "fmt" "io/ioutil" defaultLog "log" "net" @@ -33,6 +34,7 @@ import ( "github.com/coreos/etcd/etcdserver/api/v3rpc" etcdservergw "github.com/coreos/etcd/etcdserver/etcdserverpb/gw" "github.com/coreos/etcd/pkg/debugutil" + "github.com/coreos/etcd/pkg/netutil" "github.com/coreos/etcd/pkg/transport" gw "github.com/grpc-ecosystem/grpc-gateway/runtime" @@ -114,7 +116,7 @@ func (sctx *serveCtx) serve( httpmux := sctx.createMux(gwmux, handler) srvhttp := &http.Server{ - Handler: wrapMux(httpmux), + Handler: wrapMux(s, httpmux), ErrorLog: logger, // do not log user error } httpl := m.Match(cmux.HTTP1()) @@ -157,7 +159,7 @@ func (sctx *serveCtx) serve( httpmux := sctx.createMux(gwmux, handler) srv := &http.Server{ - Handler: wrapMux(httpmux), + Handler: wrapMux(s, httpmux), TLSConfig: tlscfg, ErrorLog: logger, // do not log user error } @@ -252,11 +254,15 @@ func (sctx *serveCtx) createMux(gwmux *gw.ServeMux, handler http.Handler) *http. // - mutate gRPC gateway request paths // - check hostname whitelist // client HTTP requests goes here first -func wrapMux(mux *http.ServeMux) http.Handler { - return &httpWrapper{mux: mux} +func wrapMux(s *etcdserver.EtcdServer, mux *http.ServeMux) http.Handler { + return &httpWrapper{ + s: s, + mux: mux, + } } type httpWrapper struct { + s *etcdserver.EtcdServer mux *http.ServeMux } @@ -265,9 +271,53 @@ func (m *httpWrapper) ServeHTTP(rw http.ResponseWriter, req *http.Request) { if req != nil && req.URL != nil && strings.HasPrefix(req.URL.Path, "/v3beta/") { req.URL.Path = strings.Replace(req.URL.Path, "/v3beta/", "/v3/", 1) } + + // enforce client origin (CVE-2018-5702) + // https://bugs.chromium.org/p/project-zero/issues/detail?id=1447#c2 + if req != nil { + // 1. If client connection is secured via HTTPS or auth is enabled, + // then allow any hostnames. + // 2. If client connection is not secured or auth is not enabled, + // then allow only hostnames of loopback address (e.g. localhost) + // or the hostname exists in given host whitelist. + host := netutil.GetHost(req.Host) + + // allow if loopback address or "*" + ok := netutil.IsLoopback(host) || m.s.IsHostWhitelisted("*") + if !ok { + // connection is not secure, or auth is not enabled + if req.TLS == nil || !m.s.AuthStore().IsAuthEnabled() { + // allow if host is in whitelist + ok = m.s.IsHostWhitelisted(host) + } + } + if !ok { + plog.Warningf("rejecting HTTP(s) request from %q to prevent DNS rebinding attacks", host) + + // TODO: use Go's "http.StatusMisdirectedRequest" (421) + // https://github.com/golang/go/commit/4b8a7eafef039af1834ef9bfa879257c4a72b7b5 + http.Error(rw, errCVE20185702(host), 421) + return + } + } + m.mux.ServeHTTP(rw, req) } +// https://github.com/transmission/transmission/pull/468 +func errCVE20185702(host string) string { + return fmt.Sprintf(` +etcd received your request, but the hostname was unrecognized. + +To fix this, choose one of the following options: +- Enable password authentication, then any hostname is allowed. +- Add the hostname you want to use to the whitelist in settings. + - e.g. etcd --host-whitelist %q + +This requirement has been added to help prevent "DNS Rebinding" attacks (CVE-2018-5702). +`, host) +} + func (sctx *serveCtx) registerUserHandler(s string, h http.Handler) { if sctx.userHandlers[s] != nil { plog.Warningf("path %s already registered by user handler", s)