Skip to content

Commit

Permalink
embed: enforce client origin policy
Browse files Browse the repository at this point in the history
Signed-off-by: Gyuho Lee <gyuhox@gmail.com>
  • Loading branch information
gyuho committed Feb 28, 2018
1 parent 9c20d00 commit eeab945
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 4 deletions.
1 change: 1 addition & 0 deletions embed/etcd.go
Original file line number Diff line number Diff line change
Expand Up @@ -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))
Expand Down
58 changes: 54 additions & 4 deletions embed/serve.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package embed

import (
"context"
"fmt"
"io/ioutil"
defaultLog "log"
"net"
Expand All @@ -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"
Expand Down Expand Up @@ -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())
Expand Down Expand Up @@ -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
}
Expand Down Expand Up @@ -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
}

Expand All @@ -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)
Expand Down

0 comments on commit eeab945

Please sign in to comment.