Skip to content
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

Limit Apiserver Proxy Redirects #95128

Merged
merged 1 commit into from Oct 12, 2021

Conversation

Riaankl
Copy link
Contributor

@Riaankl Riaankl commented Sep 28, 2020

What type of PR is this?*
/kind cleanup

What this PR does / why we need it:

Which issue(s) this PR fixes:
Fixes #95129

Testgrid Link:

Special notes for your reviewer:

NONE

Does this PR introduce a user-facing change?:

Yes

Release note:

kube-apiserver: requests to node, service, and pod `/proxy` subresources with no additional URL path now only automatically redirect GET and HEAD requests.

Additional documentation e.g., KEPs (Kubernetes Enhancement Proposals), usage docs, etc.:

NONE

/sig testing
/sig architecture
/networking

@k8s-ci-robot k8s-ci-robot added release-note-none Denotes a PR that doesn't merit a release note. kind/cleanup Categorizes issue or PR as related to cleaning up code, process, or technical debt. size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. sig/testing Categorizes an issue or PR as relevant to SIG Testing. sig/architecture Categorizes an issue or PR as relevant to SIG Architecture. area/conformance Issues or PRs related to kubernetes conformance tests cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. needs-priority Indicates a PR lacks a `priority/foo` label and requires one. sig/api-machinery Categorizes an issue or PR as relevant to SIG API Machinery. labels Sep 28, 2020
@Riaankl Riaankl changed the title Extend Agnhost Image: Support Conformance testing of PodProxy Endpoints Limit Apiserver Proxy Redirects Sep 28, 2020
@Riaankl Riaankl added this to Issue with PR's soaking in conformance-definition Sep 28, 2020
@Riaankl Riaankl moved this from Issue with PR's soaking to In Progress PRs in conformance-definition Sep 28, 2020
@fedebongio
Copy link
Contributor

/assign @deads2k

@@ -207,7 +208,7 @@ func (h *UpgradeAwareHandler) ServeHTTP(w http.ResponseWriter, req *http.Request
// Redirect requests with an empty path to a location that ends with a '/'
// This is essentially a hack for http://issue.k8s.io/4958.
// Note: Keep this code after tryUpgrade to not break that flow.
if len(loc.Path) == 0 {
if len(loc.Path) == 0 && (method == http.MethodGet || method == http.MethodHead) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

limiting the redirect to GET/HEAD requests looks good to me.

by continuing with the normal proxy path for other methods (e.g. PUT/POST), what is the HTTP request this change would end up passing to the underlying pod if len(loc.Path) was 0 here?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From my tests the other methods will pass through to the pod unaffected. Using kubectl proxy, porter and curl I can get 200 OK response back from the pod for the other http methods.

$ curl -I -X POST http://localhost:9999/api/v1/namespaces/default/pods/agnhost-7bb97c7fb7-6k9dq/proxy
HTTP/1.1 200 OK
Cache-Control: no-cache, private
Content-Length: 3
Content-Type: text/plain; charset=utf-8
Date: Wed, 30 Sep 2020 18:43:36 GMT

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using this PR with an updated e2e test for NamespacedPodProxy endpoints gives

STEP: Creating a Pod
Oct  1 01:17:33.579: INFO: pod created
Oct  1 01:17:33.591: INFO: Pod Quantity: 1 Status: Pending
Oct  1 01:17:34.596: INFO: Pod Quantity: 1 Status: Pending
Oct  1 01:17:35.597: INFO: Pod Quantity: 1 Status: Pending
Oct  1 01:17:36.596: INFO: Pod Status: Running
Oct  1 01:17:36.596: INFO: Starting http.Client for https://127.0.0.1:41813/api/v1/namespaces/proxy-9664/pods/proxy-pod-target/proxy
Oct  1 01:17:36.600: INFO: http.Client request:GET StatusCode:301
Oct  1 01:17:36.603: INFO: http.Client request:HEAD StatusCode:301
Oct  1 01:17:36.608: INFO: http.Client request:DELETE StatusCode:200
Oct  1 01:17:36.612: INFO: http.Client request:OPTIONS StatusCode:200
Oct  1 01:17:36.616: INFO: http.Client request:PATCH StatusCode:200
Oct  1 01:17:36.621: INFO: http.Client request:POST StatusCode:200
Oct  1 01:17:36.624: INFO: http.Client request:PUT StatusCode:200

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the HTTP request this change would end up passing to the underlying pod if len(loc.Path) was 0 here?

I wanted to make sure the request sent to the underlying pod was a valid HTTP request (especially that a non-empty request-uri was sent to the backing pod)

https://www.w3.org/Protocols/HTTP/1.1/rfc2616bis/draft-lafon-rfc2616bis-03.html#request-uri

Note that the absolute path cannot be empty; if none is present in the original URI, it must be given as "/" (the server root).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Updated PR so 'absolute path cannot be empty' now.
  • Checked how the pod handles the request & payload sent back to the client.
  • Tested with kubectl proxy, curl, porter and tcpdump
    10.244.0.1.42256 > 10.244.0.5.80: Flags [P.], cksum 0x16e4 (incorrect -> 0x7749), seq 211:419, ack 120, win 502, options [nop,nop,TS val 1754840149 ecr 1101893751], length 208: HTTP, length: 208
        DELETE / HTTP/1.1
        Host: localhost:9999
        User-Agent: via-proxy
        Accept: */*
        Accept-Encoding: gzip
        X-Forwarded-For: 127.0.0.1, 172.18.0.1
        X-Forwarded-Uri: /api/v1/namespaces/default/pods/agnhost2/proxy/

02:58:21.755520 IP (tos 0x0, ttl 64, id 11616, offset 0, flags [DF], proto TCP (6), length 52)
    10.244.0.5.80 > 10.244.0.1.42256: Flags [.], cksum 0x1614 (incorrect -> 0x25f4), ack 419, win 507, options [nop,nop,TS val 1101914757 ecr 1754840149], length 0
02:58:21.755774 IP (tos 0x0, ttl 64, id 11617, offset 0, flags [DF], proto TCP (6), length 171)
    10.244.0.5.80 > 10.244.0.1.42256: Flags [P.], cksum 0x168b (incorrect -> 0x5f0d), seq 120:239, ack 419, win 507, options [nop,nop,TS val 1101914758 ecr 1754840149], length 119: HTTP, length: 119
        HTTP/1.1 200 OK
        Date: Tue, 06 Oct 2020 02:58:21 GMT
        Content-Length: 3
        Content-Type: text/plain; charset=utf-8

        foo[!http]

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why would we limit to GET & HEAD? Why couldn't people be using POST PUT etc? Isn't this a compatibility breakage?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The request body associated with a PUT/POST/PATCH/etc request doesn't get sent to the backend, and the clients I know of drop their PUT/POST/PATCH content in response to a 301 request, and switch to making a GET request to the new location (including golang and browsers, which is what this redirect was originally added for)

That makes returning a 301 redirect for anything other than GET/HEAD lose data and not be usable in the scenarios I can see

@k8s-ci-robot k8s-ci-robot added size/S Denotes a PR that changes 10-29 lines, ignoring generated files. and removed size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. labels Oct 6, 2020
return
default:
path := req.URL.Path
req.URL.Path = path + "/" + queryPart
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this is doing a few things that don't seem quite right:

  1. it is adding duplicating query data into the path (I think a request with query parameters would get an incorrect path, need to add a unit test to exercise this)
  2. it is modifying req.URL.Path but not loc.Path (which means the newReq.URL = &loc code path below is still wrong... need a unit test to exercise that path as well)

My question in #95128 (comment) was more of a question about behavior ... I'm not actually sure we need to append a / ... the go http client might default to sending / if there is not path present... if this default case is simply dropped, does the pod still get a $METHOD / HTTP/1.1 call?

@fejta-bot
Copy link

Issues go stale after 90d of inactivity.
Mark the issue as fresh with /remove-lifecycle stale.
Stale issues rot after an additional 30d of inactivity and eventually close.

If this issue is safe to close now please do so with /close.

Send feedback to sig-testing, kubernetes/test-infra and/or fejta.
/lifecycle stale

@k8s-ci-robot k8s-ci-robot added the lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. label Jan 4, 2021
@Riaankl
Copy link
Contributor Author

Riaankl commented Jan 6, 2021

/remove-lifecycle stale

@k8s-ci-robot k8s-ci-robot removed the lifecycle/stale Denotes an issue or PR has remained open with no activity and has become stale. label Jan 6, 2021
@heyste
Copy link
Member

heyste commented Aug 19, 2021

/test pull-kubernetes-node-e2e-containerd
unrelated flake E2eNode Suite: [sig-storage] HostPath should support r/w [NodeConformance]

@heyste
Copy link
Member

heyste commented Aug 19, 2021

After retesting the original fix with kind, kubectl proxy and a custom porter, the following output shows that no empty request is sent to the pod (i.e a missing slash).

$ curl -XPOST http://127.0.0.1:11111/api/v1/namespaces/default/pods/debug-proxy/proxy && echo
{"Method":"POST","RequestURI":"/","Body":"foo"}
 
$ curl -XPOST http://127.0.0.1:11111/api/v1/namespaces/default/pods/debug-proxy/proxy?id=123 && echo
{"Method":"POST","RequestURI":"/?id=123","Body":"foo"}

$ curl -XDELETE http://127.0.0.1:11111/api/v1/namespaces/default/pods/debug-proxy/proxy && echo
{"Method":"DELETE","RequestURI":"/","Body":"foo"}

$ curl -XDELETE http://127.0.0.1:11111/api/v1/namespaces/default/pods/debug-proxy/proxy?id=123 && echo
{"Method":"DELETE","RequestURI":"/?id=123","Body":"foo"}

Checking APISnoop results

#+begin_src sql-mode :eval never-export :exports both :session none
select distinct  endpoint, right(useragent,83) AS useragent
from testing.audit_event
where endpoint ilike '%proxy%'
order by endpoint
limit 10;
#+end_src

#+RESULTS:
#+begin_SRC example
               endpoint                |  useragent
---------------------------------------+-------------
 connectCoreV1DeleteNamespacedPodProxy | curl/7.68.0
 connectCoreV1PostNamespacedPodProxy   | curl/7.68.0
(2 rows)

#+end_SRC

@liggitt Can you please confirm that the logs above do address your outstanding query and the PR can now be lgtm & approved? TIA

@liggitt
Copy link
Member

liggitt commented Aug 24, 2021

thanks for checking the impact on backend behavior, the change lgtm

unit test coverage of client side behavior (redirect received for get/head, no redirect received for put/post/delete/etc) and backend behavior (request with / received for all assuming the client followed redirects) would ensure the behavior you manually verified doesn't regress in the future

@smarterclayton
Copy link
Contributor

If jordan's question is answered to his satisfaction this is approved and also lgtm, will tag as such unless he comments otherwise in next day.

/approve

@k8s-ci-robot k8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label Sep 2, 2021
@liggitt
Copy link
Member

liggitt commented Sep 2, 2021

the change lgtm but without test coverage nothing would catch a regression

@heyste
Copy link
Member

heyste commented Sep 21, 2021

@liggitt For the new unit test to check the status code (either 301/200) for various methods I'm having an issue getting passed the following code block

if !strings.HasSuffix(loc.Path, "/") && strings.HasSuffix(req.URL.Path, "/") {
loc.Path += "/"
}

Using the current TestServeHTTP scenario with a requestPath: "" it will get replaced with a / which means it will then skips the next check

Question: Based on the output below does that get processed somewhere inside of pkg/registry/core/pod/rest/subresources.go or have I missed something in the process of getting from curl to the pod ?

curl -I http://localhost:12345/api/v1/namespaces/default/pods/agnhost2-5b47dc9979-tfdj5/proxy
HTTP/1.1 301 Moved Permanently
Audit-Id: 42beb19b-50e8-4491-a7de-9ed5c9d6a842
Cache-Control: no-cache, private
Date: Tue, 21 Sep 2021 23:33:39 GMT
Location: /api/v1/namespaces/default/pods/agnhost2-5b47dc9979-tfdj5/proxy/

TIA for any tips you can provide.

@liggitt
Copy link
Member

liggitt commented Sep 28, 2021

Using the current TestServeHTTP scenario with a requestPath: "" it will get replaced with a /

You can't send a completely empty path to a server. If the test server roots the proxy handler at /, I don't think there's a way to entirely omit a path in a request to it.

In real use, the proxy handler is rooted at /api/v1/namespaces/$ns/pods/$pod/proxy, so a request to ``/api/v1/namespaces/$ns/pods/$pod/proxy(no trailing slash) would be handled by the proxy handler, would have the/api/v1/namespaces/$ns/pods/$pod/proxy` prefix removed, and would end up with an empty path with no trailing slash it was expected to proxy through.

@k8s-ci-robot k8s-ci-robot added size/L Denotes a PR that changes 100-499 lines, ignoring generated files. and removed size/XS Denotes a PR that changes 0-9 lines, ignoring generated files. labels Oct 6, 2021
@heyste
Copy link
Member

heyste commented Oct 6, 2021

/test pull-kubernetes-node-e2e-containerd
unrelated flake

W1006 03:17:04.446] Could not detect Kubernetes master node.  Make sure you've launched a cluster with 'kube-up.sh'
I1006 03:17:04.546] Master not detected. Is the cluster up? 

- Extract the current redirect code into a function
  (proxyRedirectsforRootPath) and redirect for only
  http.MethodGet or http.MethodHead
- Create a unit test that confirms that only GET &
  HEAD methods are redirected
@heyste
Copy link
Member

heyste commented Oct 6, 2021

@liggitt I've moved the redirect code into a function so I have a more direct way of unit testing the various methods. The unit test checks GET, PUT, HEAD and DELETE. Manual testing the latest changes with porter still works as shown below.

$ curl -I -XGET http://127.0.0.1:11111/api/v1/namespaces/default/pods/debug-proxy/proxy
HTTP/1.1 301 Moved Permanently
Audit-Id: 6eee31cd-67b1-43d6-adc5-3f1f9015f953
Cache-Control: no-cache, private
Content-Length: 0
Date: Wed, 06 Oct 2021 18:56:02 GMT
Location: /api/v1/namespaces/default/pods/debug-proxy/proxy/

$ curl  -XDELETE http://127.0.0.1:11111/api/v1/namespaces/default/pods/debug-proxy/proxy
{"Method":"DELETE","RequestURI":"/","Body":"foo"}
$ curl  -XPUT http://127.0.0.1:11111/api/v1/namespaces/default/pods/debug-proxy/proxy
{"Method":"PUT","RequestURI":"/","Body":"foo"}
$ curl  -XPOST http://127.0.0.1:11111/api/v1/namespaces/default/pods/debug-proxy/proxy
{"Method":"POST","RequestURI":"/","Body":"foo"}
$ curl  -XPATCH http://127.0.0.1:11111/api/v1/namespaces/default/pods/debug-proxy/proxy && echo
{"Method":"PATCH","RequestURI":"/","Body":"foo"}
$ curl  -XOPTIONS http://127.0.0.1:11111/api/v1/namespaces/default/pods/debug-proxy/proxy
{"Method":"OPTIONS","RequestURI":"/","Body":"foo"}

$ curl -I -XHEAD http://127.0.0.1:11111/api/v1/namespaces/default/pods/debug-proxy/proxy
HTTP/1.1 301 Moved Permanently
Audit-Id: f6302f94-2d8b-4126-b232-bc15619ce385
Cache-Control: no-cache, private
Date: Wed, 06 Oct 2021 18:57:42 GMT
Location: /api/v1/namespaces/default/pods/debug-proxy/proxy/

Is this okay to lgtm now?

@k8s-ci-robot k8s-ci-robot added release-note Denotes a PR that will be considered when it comes time to generate release notes. and removed release-note-none Denotes a PR that doesn't merit a release note. labels Oct 11, 2021
@liggitt
Copy link
Member

liggitt commented Oct 11, 2021

/lgtm
/approve

@k8s-ci-robot k8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label Oct 11, 2021
@k8s-ci-robot
Copy link
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: liggitt, Riaankl, smarterclayton

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 /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@Riaankl
Copy link
Contributor Author

Riaankl commented Oct 11, 2021

/test pull-kubernetes-e2e-gce-ubuntu-containerd

Unreleated flake

[Fail] [sig-api-machinery] CustomResourcePublishOpenAPI [Privileged:ClusterAdmin] [It] works for CRD with validation schema [Conformance] 
I1011 19:51:16.925] /go/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/vendor/github.com/onsi/ginkgo/internal/leafnodes/runner.go:113

@Riaankl
Copy link
Contributor Author

Riaankl commented Oct 11, 2021

/test pull-kubernetes-e2e-gce-ubuntu-containerd

Unrelated flake

 [Fail] [sig-node] PreStop [It] graceful pod terminated should wait until preStop hook completes the process 
I1011 21:01:43.621] /go/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/test/e2e/node/pre_stop.go:213

@heyste
Copy link
Member

heyste commented Oct 11, 2021

/test pull-kubernetes-e2e-gce-ubuntu-containerd
unrelated flake

Kubernetes  e2e suite: [sig-storage] In-tree Volumes [Driver:  local][LocalVolumeType: dir] [Testpattern: Pre-provisioned PV (default  fs)] subPath should support existing directory

/go/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/test/e2e/storage/testsuites/subpath.go:205
 Oct 11 22:48:06.345: Unexpected error:     <*errors.errorString \| 0xc00237b2a0>: {
         s: "failed to get logs from pod-subpath-test-preprovisionedpv-bsgb for test-container-volume-preprovisionedpv-bsgb: an error on the server (\"unknown\") has prevented the request from succeeding (get pods pod-subpath-test-preprovisionedpv-bsgb)",
     }
     failed to get logs from pod-subpath-test-preprovisionedpv-bsgb for test-container-volume-preprovisionedpv-bsgb: an error on the server ("unknown") has prevented the request from succeeding (get pods pod-subpath-test-preprovisionedpv-bsgb)
 occurred
 /go/src/k8s.io/kubernetes/_output/dockerized/go/src/k8s.io/kubernetes/test/e2e/framework/util.go:743

@heyste
Copy link
Member

heyste commented Oct 11, 2021

/test pull-kubernetes-e2e-gce-100-performance
unrelated flake e2e.go:Up

scp: /var/log/cluster-autoscaler.log*: No such file or directory
scp: /var/log/fluentd.log*: No such file or directory
scp: /var/log/kubelet.cov*: No such file or directory
scp: /var/log/cl2-**: No such file or directory
scp: /var/log/startupscript.log*: No such file or directory
ERROR: (gcloud.compute.scp) [/usr/bin/scp] exited with return code [1]. 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
approved Indicates a PR has been approved by an approver from all required OWNERS files. area/conformance Issues or PRs related to kubernetes conformance tests cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. kind/cleanup Categorizes issue or PR as related to cleaning up code, process, or technical debt. lgtm "Looks good to me", indicates that a PR is ready to be merged. needs-priority Indicates a PR lacks a `priority/foo` label and requires one. release-note Denotes a PR that will be considered when it comes time to generate release notes. sig/api-machinery Categorizes an issue or PR as relevant to SIG API Machinery. sig/architecture Categorizes an issue or PR as relevant to SIG Architecture. sig/testing Categorizes an issue or PR as relevant to SIG Testing. size/L Denotes a PR that changes 100-499 lines, ignoring generated files.
Projects
Development

Successfully merging this pull request may close these issues.

Bug: Limit Apiserver Proxy Redirects
10 participants