Browse Source

embed: enforce client origin policy

Signed-off-by: Gyuho Lee <gyuhox@gmail.com>
Gyuho Lee 7 years ago
parent
commit
8edaecadc4
2 changed files with 39 additions and 4 deletions
  1. 6 0
      embed/etcd.go
  2. 33 4
      embed/serve.go

+ 6 - 0
embed/etcd.go

@@ -174,9 +174,15 @@ func StartEtcd(inCfg *Config) (e *Etcd, err error) {
 		Debug:                   cfg.Debug,
 	}
 
+	srvcfg.HostWhitelist = make(map[string]struct{}, len(cfg.HostWhitelist))
+	for _, h := range cfg.HostWhitelist {
+		srvcfg.HostWhitelist[h] = struct{}{}
+	}
+
 	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))

+ 33 - 4
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/httputil"
 	"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,12 @@ 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 +268,35 @@ 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)
 	}
+
+	if req.TLS == nil { // check origin if client connection is not secure
+		host := httputil.GetHostname(req)
+		if !m.s.IsHostWhitelisted(host) {
+			plog.Warningf("rejecting HTTP 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 Host header was unrecognized.
+
+To fix this, choose one of the following options:
+- Enable TLS, then any HTTPS request will be 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)