-
Notifications
You must be signed in to change notification settings - Fork 1.5k
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
KEP: hardened exec requests #1899
Conversation
@tallclair: The label(s) In response to this:
Instructions for interacting with me using PR comments are available here. If you have questions or suggestions related to my behavior, please file an issue against the kubernetes/test-infra repository. |
The second path format includes the pod's UID, and is never called by the kube-apiserver. We should | ||
remove these endpoints as they're unused, to reduce code complexity. |
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.
Aside: if you wanted to preserve the UID checking behavior, it might be feasible to make up a conditional header If-UID:
and fail requests where that's set and not the same as the running Pod's UID.
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 it's used anywhere. It would be easy enough to add this approach later if a use case arose.
endpoints](#2-require-a--request-to-the-streaming-endpoints) and [4. Require the request options to | ||
be provided in the request body](#4-require-the-request-options-to-be-provided-in-the-request-body). | ||
|
||
#### 3. Require the websocket protocol for GET requests. |
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.
Clients should use the SPDY protocols if their clients have native support, or WebSockets as a fallback.
…
In the future, an HTTP/2 implementation will be exposed that deprecates SPDY.
Is that still accurate?
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 clear on why some clients (browsers?) aren't able to use POST + WebSocket - it'd be nice to have that clarified.
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.
correct. browsers don't allow controlling the http method when opening a websocket
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 think I get it. If I'm right, the challenge here is that the client sends a single request to the API server that is both an upgrade request and request for a command execution. But I'm not sure I've understood, even after re-reading several times.
I haven't looked at the API here because I don't think there's documentation on how this works. The upgrade to WebSocket includes the exec request in its query string. So the options could include:
- Retain current behavior because it's not vulnerable to this attack (?)
- POST requesting an exec, API server returns a WebSocket URI, client send a (GET)
Upgrade:
to open that WebSocket, continue conversation over WebSocket - Client requests a WebSocket, upgrade happens, and then within the WebSocket stream request an exec
I couldn't be sure I've understood what this section is calling for. With that in mind, would you be willing @tallclair to reword this to clarify?
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.
- It is vulnerable to attack, but by locking it down to only websocket requests, it makes it harder to exploit (especially in environments where websockets aren't used).
- This is similar to the approach we previously took for the apiserver <-> CRI communications. I like the idea, but it's also a breaking change. We could skip the caching layer that we used in the CRI implementation by just signing the request parameters (i.e. POST request to get the signed query params, follow up with the GET including the signature)
- I'm not familiar enough with the websocket protocol to know how complicated this would be. It's certainly feasible, but as it's also a breaking change I'm not sure how it compares with Role-based access control (RBAC) #2
I'll update the KEP with these alternatives.
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.
Updated with these options.
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.
What does a browser do if it is redirected from an https://... endpoint to a wss://... endpoint? Does it open a websocket request?
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.
GET
(and other HTTP verbs) don't have any meaning for WebSockets, so I think the browser will error out.
(untested)
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 haven't tested it, but the redirected request would be missing the required headers for the opening handshake, so I assume it wouldn't work. https://tools.ietf.org/html/rfc6455#section-1.3
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.
Thinking about this more, I have a strong preference for "alternative 1" (hash of request as a subprotocol). Alternative 2 (exec token request) would require managing a signing key and option 3 (make request over the websocket connection) would be a much larger change. Since headers can't be modified on a redirect, I think the hash approach provides sufficient protection with minimal changes on the client & server side. We just need a well-defined serialized input for the hash function. Then the question is whether we want to tie this to the HardenedExecRequests feature gate.
@tallclair I just noticed this KEP when I am drafting #1908. Would it help if we make kubectl not follow redirects by default? |
Yes, I think these 2 approaches are complementary, and we should pursue both. |
/assign @liggitt |
/assign I won’t be able to read in depth until next week. |
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.
a few initial questions
|
||
- If only body options are provided, use those. | ||
- If only query options are provided, use those. | ||
- If both body and query options are provided, require that they be identical. |
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.
does this match current behavior?
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.
The current behavior completely ignores the request body. Only the query parameters are used.
endpoints](#2-require-a--request-to-the-streaming-endpoints) and [4. Require the request options to | ||
be provided in the request body](#4-require-the-request-options-to-be-provided-in-the-request-body). | ||
|
||
#### 3. Require the websocket protocol for GET requests. |
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.
What does a browser do if it is redirected from an https://... endpoint to a wss://... endpoint? Does it open a websocket request?
A few thoughts:
i can setup a quick test to answer @liggitt question about what browsers will do on a redirect from a GET to wss://. My guess is it will just follow it. |
Thanks @mlbiam !
If you get a chance to test this, that would be great! I think a redirect from a https:// URL to a wss:// URL will be rejected because it doesn't have the correct headers, but I'm guessing a redirect from wss:// to wss:// will work. |
That brings up a really good question. Does it matter if a redirect works? A redirect from a GET to wss:// would only ever happen from a js library inside of a browser, right? just a simple 302 from the url bar wouldn't do much to your point. |
If the redirect works, it's more important that we get redirect protection on websocket requests. If the redirect doesn't work, we might be able to treat them as their own thing. |
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 think the open questions are addressable after the alpha phase, and this has sufficient detail to move forward.
/approve
/lgtm
### Non-Goals | ||
|
||
- Protecting proxy requests | ||
- Maintaining backwards compatibility of the Kubelet API, which is only intended for consumption by |
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 agree with this goal, my fear is that it may not have been honored ;-)
|
||
The changes to the Kubelet API are: | ||
|
||
1. Remove the `/run` endpoint, which is unused in core Kubernetes. |
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.
for the history buffs, i think this links back to 2016 when we moved away from it.
kubernetes/kubernetes#24921
The changes to the Kubelet API are: | ||
|
||
1. Remove the `/run` endpoint, which is unused in core Kubernetes. | ||
2. Require a `POST` request for the `/exec`, `/attach` and `/portForward` endpoints. |
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 agree that POST
is also semantically better because a GET
should not have changed system state.
request. This endpoint is not used by any Kubernetes components, and should be removed to reduce the | ||
attack surface. | ||
|
||
**Risk:** Although this endpoint is not exposed in the Kubernetes API, it is still reachable via a |
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 am inclined to just remove it. We gave no version guarantee on the endpoint.
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: derekwaynecarr, tallclair The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
If that comment refers to the kubelet API, we can do that assuming the kube-apiserver is the only client. If that comment refers to the kube-apiserver API, then requiring a POST breaks existing browser websocket clients with no recourse. |
Harden exec requests against SSRF vulnerabilities. See kubernetes/kubernetes#92914 for motivation.
For #1898.
/sig node api-machinery
/area security
/cc @liggitt @cjcullen @mtaufen