-
Notifications
You must be signed in to change notification settings - Fork 9.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
*: mitigate DNS rebinding attacks in insecure etcd server #9372
Conversation
For better or for worse I think defaulting to only loopback address is going to break a lot of people. Since we can't really guarantee anything when auth is disabled, does it make sense to only enable this when |
And just to clarify, CVE-2018-5702 isn't about etcd, so it probably shouldn't be the issue title unless you like giving people heart attacks :) |
Also, as @ericchiang pointed out, this might break a lot of etcd clusters with no auth. |
edd327c
to
8009030
Compare
Codecov Report
@@ Coverage Diff @@
## master #9372 +/- ##
=========================================
Coverage ? 72.46%
=========================================
Files ? 362
Lines ? 30779
Branches ? 0
=========================================
Hits ? 22303
Misses ? 6851
Partials ? 1625
Continue to review full report at Codecov.
|
embed/serve.go
Outdated
// https://github.com/transmission/transmission/pull/468 | ||
func errCVE20185702(host string) string { | ||
return fmt.Sprintf(` | ||
etcd received your request, but the hostname was unrecognized. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"but the Host header was unrecognized"
embed/serve.go
Outdated
// 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) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We still want to enforce a Host whitelist even if auth is enabled, right? I'd just recommend
if !m.s.IsHostWhitelisted("*") && !m.s.IsHostWhitelisted(host) {
// error
}
embed/serve.go
Outdated
host := netutil.GetHost(req.Host) | ||
|
||
// allow if loopback address or "*" | ||
ok := netutil.IsLoopback(host) || m.s.IsHostWhitelisted("*") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd recommend dropping the Loopback check. If you've gone through the trouble of specifying a Host header whitelist, then you don't care about loopback (plus non-browser clients can arbitrarily set whatever Host header they want).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are right. I was first replicating the logic in transmission/transmission#468, but this can be simplified as you suggested. Will rework on this.
pkg/netutil/loopback.go
Outdated
// and only returns the host in the address. | ||
func GetHost(addr string) (host string) { | ||
addr = strings.TrimSpace(addr) | ||
u, _ := url.Parse(addr) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think http.Request.Host can be a URL. It can only be a host name or "host:port"
pkg/netutil/loopback.go
Outdated
|
||
// GetHost removes all schemes and ports from address | ||
// and only returns the host in the address. | ||
func GetHost(addr string) (host string) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: GetHost should probably take an http.Request so it's clearer what string it's acting on.
etcdserver/server.go
Outdated
@@ -251,6 +251,9 @@ type EtcdServer struct { | |||
|
|||
leadTimeMu sync.RWMutex | |||
leadElectedTime time.Time | |||
|
|||
hostWhitelistMu sync.RWMutex |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: don't need the mutex because you're only reading from the map?
embed/serve.go
Outdated
|
||
// enforce client origin (CVE-2018-5702) | ||
// https://bugs.chromium.org/p/project-zero/issues/detail?id=1447#c2 | ||
if req != nil { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why can the request be nil? Should it be rejected if there's a Host header whitelist?
@ericchiang All addressed. PTAL. Thanks! |
|
||
`etcdserver.ServerConfig` has added a new field `HostWhitelist` and its default value is empty. If empty and etcd server does not enable TLS, all HTTP client requests will be rejected. This is to prevent [DNS Rebinding](https://en.wikipedia.org/wiki/DNS_rebinding) [attacks](https://bugs.chromium.org/p/project-zero/issues/detail?id=1447). Set it to `"*"` to allow all hostnames. | ||
|
||
Before and after (e.g. [k8s.io/kubernetes/test/e2e_node/services/etcd.go](https://github.com/kubernetes/kubernetes/blob/release-1.10/test/e2e_node/services/etcd.go#L78-L81)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jpbetz FYI, this will be breaking change for Kubernetes testing with etcd v3.4.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we avoid this by having an empty HostWhiteList
mean "allow all" instead of []string{"*"}
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@ericchiang Good idea. Then, we won't have to worry about breaking changes. Will change.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just nits, other than that lgtm.
Thanks for getting this done!
embed/serve.go
Outdated
@@ -265,9 +268,37 @@ 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) | |||
} | |||
|
|||
// client connection is insecure, then enforce client origin policy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: outdated comment
pkg/httputil/httputil.go
Outdated
host, _, err := net.SplitHostPort(req.Host) | ||
if host == "" && | ||
err != nil && | ||
strings.Contains(err.Error(), "missing port in address") { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
strings.Contains
? hmmm
Seems fine since there's pretty thorough testing, but this definitely isn't best practice.
pkg/httputil/httputil.go
Outdated
return "" | ||
} | ||
host, _, err := net.SplitHostPort(req.Host) | ||
if host == "" && |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
err != nil
should probably indicate that any host
value returned is invalid, right? Why not just
h, _, err := net.SplitHostPort(req.Host)
if err != nil {
return req.Host
}
return h
Signed-off-by: Gyuho Lee <gyuhox@gmail.com>
Signed-off-by: Gyuho Lee <gyuhox@gmail.com>
Signed-off-by: Gyuho Lee <gyuhox@gmail.com>
Signed-off-by: Gyuho Lee <gyuhox@gmail.com>
Signed-off-by: Gyuho Lee <gyuhox@gmail.com>
Signed-off-by: Gyuho Lee <gyuhox@gmail.com>
Signed-off-by: Gyuho Lee <gyuhox@gmail.com>
Signed-off-by: Gyuho Lee <gyuhox@gmail.com>
@ericchiang All addressed. Now Host whitelist is empty by default, to allow all hostnames. Merging in greens. Thanks for review! |
Signed-off-by: Gyuho Lee <gyuhox@gmail.com>
Awesome thanks! |
Fix #9353.
Reference
This works
Will add more documentation once reviewed.
/cc @mitake @zelivans @ericchiang