-
Notifications
You must be signed in to change notification settings - Fork 39.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
By default block service proxy to external IP addresses. #57265
Conversation
2d6e88b
to
66848ba
Compare
d58ab68
to
20bee87
Compare
pkg/registry/core/service/rest.go
Outdated
@@ -81,9 +82,13 @@ func NewStorage(registry Registry, endpoints endpoint.Registry, serviceIPs ipall | |||
serviceNodePorts: serviceNodePorts, | |||
proxyTransport: proxyTransport, | |||
} | |||
redirectOnly := true | |||
if utilfeature.DefaultFeatureGate.Enabled(features.ServiceProxyAllowExternalIPs) { | |||
redirectOnly = false |
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'm not sure we can assume everything talking to this endpoint will follow redirects
pkg/registry/core/service/proxy.go
Outdated
opts := &metainternalversion.ListOptions{ | ||
LabelSelector: labels.Set(svc.Spec.Selector).AsSelector(), | ||
} | ||
obj, err = r.pods.List(r.ctx, opts) |
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.
this doesn't take readiness/availability into account, right?
pkg/registry/core/service/proxy.go
Outdated
} | ||
ix := rand.Intn(len(podList.Items)) | ||
pod := podList.Items[ix] | ||
path := fmt.Sprintf("/apis/v1/namespaces/%s/pods/%s/proxy%s", pod.Namespace, pod.Name, r.path) |
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.
this drops port and protocol info (see logic in service REST#ResourceLocation
):
svcScheme, svcName, portStr, valid := utilnet.SplitSchemeNamePort(id)
rather than switching to a redirect, it seems less disruptive to require the selected endpoint address to actually refer to a real pod in the same namespace with a matching IP kubernetes/pkg/registry/core/service/rest.go Lines 435 to 441 in 85e0ed7
At that point, require the selected Address to have a targetRef, look up the pod, make sure it exists and has a matching IP. Something like: // Pick a random address.
addr := ss.Addresses[rand.Intn(len(ss.Addresses))]
if noExternalAddresses {
ns := genericapirequest.NamespaceFrom(ctx)
if addr.TargetRef == nil {
return nil, nil, errors.NewServiceUnavailable("...")
}
if addr.TargetRef.Kind != "Pod" {
return nil, nil, errors.NewServiceUnavailable("...")
}
if addr.TargetRef.Namespace == "" || addr.TargetRef.Namespace != ns {
return nil, nil, errors.NewServiceUnavailable("...")
}
pod, err := rs.pods.GetPod(ctx, addr.TargetRef.Name)
if err != nil {
return nil, nil, errors.NewServiceUnavailable("...")
}
if pod.Status.PodIP != addr.IP {
return nil, nil, errors.NewServiceUnavailable("...")
}
}
ip := addr.IP
port := int(ss.Ports[i].Port)
return &url.URL{
Scheme: svcScheme,
Host: net.JoinHostPort(ip, strconv.Itoa(port)),
}, rs.proxyTransport, nil |
@liggitt thanks for the feedback, I'll address the readiness and port/scheme comments. Regarding redirect vs. validation, my concern is that as it is currently constructed, the proxy is enabling the end user to cloak their requests with the APIServer's IP address. That makes me fairly uncomfortable from an audit/network ACL perspective. The only valid way to access the Also, in a world where masters are network partitioned from workers, except for master -> kubelet communication, requiring masters to be able to directly communicate with Service/Pod IPs opens another potentially vulnerable network path. |
No, any http request is valid, and many http libraries don't follow redirects automatically. |
if that is the issue, then this PR doesn't really help... it's just redirecting to the pods/proxy API endpoint, which still goes through the API server and cloaks the request with the APIServer's IP address, right? |
The difference is that the pod proxy path tunnels things to the kubelet rather than communicating with the IP address directly. My concern isn't around losing the client IP address (that is going to happen either way as you suggest) but my concern is that the request is coming from the APIServer's IP address which is much more likely to have special permissions in network configuration rules. Also, since the path to the kubelet is an HTTP call, there's a much better audit trail than making a random IP request from the APIServer's network namespace. Basically, as currently written this code enables an arbitrary pod to appear to be traffic from the API server, that doesn't make me super comfortable. |
also wrt to clients following redirects, sure, that's true of course, but if your client isn't handling redirects, you're probably already in a world of hurt (e.g. OAuth relies on redirects), so it doesn't seem that bad to me to say "if you want proxy, you need to handle redirects" Proxy isn't core functionality imho, so adding some limitations doesn't seem that bad. |
Actually, the service proxy subresource communicates with the IP the same way the pod proxy subresource does (see the transport it is given to use, which is the same transport the pod proxy uses). Requiring the resolved IP to be a pod IP seems sufficient to me, and gets you identical protection, without changing behavior to redirect. |
I'll take a look at the transport, if it is indeed tunnelling to the kubelet, then I agree that validation seems reasonable to me. |
Hrm, as far as I read they both just use the default networking. I don't really like either of those... I swear the pod proxy tunneled to kubelet sigh I agree it's no worse that what would happen with a redirect to the Pod, so I'll just do the Pod IP validation... But I think we should also change the default so that we don't drive from the APIServer's IP address... |
20bee87
to
acd2256
Compare
@liggitt re-factored as you suggested to just filter based on Pod TargetRef. Please take another look. |
pkg/registry/core/service/rest.go
Outdated
@@ -419,6 +422,9 @@ func (rs *REST) ResourceLocation(ctx genericapirequest.Context, id string) (*url | |||
if err != nil { | |||
return nil, nil, err | |||
} | |||
if !utilfeature.DefaultFeatureGate.Enabled(features.ServiceProxyAllowExternalIPs) { |
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.
seems like we'd want to do the check on a particular address inside the loop, after we've verified the subset even has the port we want, since it involves a remote call
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.
Do you think it really matters? How many times is there an endpoint that doesn't match the port for the service?
(and is Proxy really a performance-sensitive call?)
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.
if you have a service backed by 400 pods (c.f.#58050 ), this would make it do 400 pod lookups as a pre-filter before selecting a single one and using it.
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.
That's not the point, because you're going to pay that cost no matter what. My point was: how many services do you not pay it either way?
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.
If you find a suitable endpoint address first, then verify the pod as a second step, in the normal case, you only look up one pod, right?
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.
Ok, I see your approach, I can restructure the code in that way, its more complicated then I was thinking...
/retest |
1 similar comment
/retest |
7d96855
to
02f335b
Compare
/retest |
Not sure. Is the intent to deprecate and default off this "feature" eventually (I'd be in favor of that), leaving the feature gate as an escape hatch for people who still wanted this behavior during the deprecation period? If so, I wonder if we'd want the feature gate to sound more perjorative. Just noticed the release-note isn't accurate... it's not defaulted off yet. |
Yes, my plan is to eventually remove the "feature" since it is a bug imho, but I want to leave an escape hatch for at least one release for people whom it breaks. I believe that the release note is correct. The feature is "allow external ips" the default for features is By default disable access to external IP addresses from the apiserver service proxy. Appears to be correct? |
I'd agree
Ah, you're right, though I think you need to add it to the |
Service proxy uses redirects to Pods instead of direct access.
a0efea8
to
dcb9b4b
Compare
@liggitt added to ptal. Thanks |
thanks /lgtm |
/retest |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: brendandburns, liggitt Associated issue: #58761 The full list of commands accepted by this bot can be found here.
Needs approval from an approver in each of these OWNERS Files:
You can indicate your approval by writing |
/test all [submit-queue is verifying that this PR is safe to merge] |
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions here. |
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Categorize deprecated feature gate more accurately related to #58761 follow up from #57265 to clarify the status of the feature gate ```release-note NONE ```
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Categorize deprecated feature gate more accurately related to #58761 follow up from kubernetes/kubernetes#57265 to clarify the status of the feature gate ```release-note NONE ``` Kubernetes-commit: e8225f5618d7bf9251115b9a8be689175bbed52f
…#58878-upstream-release-1.8 Automatic merge from submit-queue. Automated cherry pick of #57265: By default block service proxy to external IP addresses. #58878: Add deprecated stage of feature gates Cherry pick of #57265 #58878 on release-1.8. #57265: By default block service proxy to external IP addresses. #58878: Add deprecated stage of feature gates ```release-note Access to externally managed IP addresses via the kube-apiserver service proxy subresource is no longer allowed by default. This can be re-enabled via the `ServiceProxyAllowExternalIPs` feature gate, but will be disallowed completely in 1.11 ```
…#58878-upstream-release-1.9 Automatic merge from submit-queue. Automated cherry pick of #57265: By default block service proxy to external IP addresses. #58878: Add deprecated stage of feature gates Cherry pick of #57265 #58878 on release-1.9. #57265: By default block service proxy to external IP addresses. #58878: Add deprecated stage of feature gates ```release-note Access to externally managed IP addresses via the kube-apiserver service proxy subresource is no longer allowed by default. This can be re-enabled via the `ServiceProxyAllowExternalIPs` feature gate, but will be disallowed completely in 1.11 ```
…#58878-upstream-release-1.7 Automatic merge from submit-queue. Automated cherry pick of #57265: By default block service proxy to external IP addresses. #58878: Add deprecated stage of feature gates Cherry pick of #57265 #58878 on release-1.7. #57265: By default block service proxy to external IP addresses. #58878: Add deprecated stage of feature gates ```release-note Access to externally managed IP addresses via the kube-apiserver service proxy subresource is no longer allowed by default. This can be re-enabled via the `ServiceProxyAllowExternalIPs` feature gate, but will be disallowed completely in 1.11 ```
I don't agree this is a bug and in fact it's a feature I depend on. |
1). Does this feature block an External Service(or IP), that I am trying to access such as my database that is outside the kubernetes cluster. 2). Also, if we are trying to protect the access of services, present in another namespace. Cannot the same result be achieved by NetworkPolicy objects or even RBAC/ABAC for that matter, instead of introducing the above feature in this PR ? |
What this PR does / why we need it:
Currently, the Service Proxy on the APIServer allows unrestricted access to any IP address that the APIServer machine can reach. This is likely undesirable in many cases.
Update the service proxy so that it filters Endpoints to only those that have a TargetRef that matches a known Pod.
Fixes #58761
Release note: