Change:
What happened: As part of creating an e2e test for , it was found that the apiserver proxy server is processing a blank redirect for any for 21 endpoints across pods, services and nodes. For a pod, redirecting for any method other than GET or HEAD is a bug, you expected to happen: This is the current behaviour for the apiserver but with a minimal code change the redirect can apply to only the following endpoints; - - - How to reproduce it (as minimally and precisely as possible): Create any standard pod and service in the default namespace run in another terminal the response from running the following commands produces a can be replaced with other http verbs , , , , and which also produce 301 responses. Anything else we need to know?: networking /sig testing /sig architecture
The label(s) cannot be applied, because the repository doesn't have them <detailsIn response to : What happened: - - - run in another terminal the response from running the following commands produces a testing /sig architecture Instructions for interacting with me using PR comments are available . If you have questions or suggestions related to my behavior, please file an issue against the repository. </details
Does this issue and PR address your concerns about the apiserver proxy server doing a blanket direct of any http call to the root proxy endpoints?
commented on the associated PR, thanks
/remove-sig testing
Issues go stale after 90d of inactivity. Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close. If this issue is safe to close now please do so with . Send feedback to sig-testing, kubernetes/test-infra and/or . /lifecycle stale
/remove-lifecycle stale Will be addressed late in 1.21
Issues go stale after 90d of inactivity. Mark the issue as fresh with . Stale issues rot after an additional 30d of inactivity and eventually close. If this issue is safe to close now please do so with . Send feedback to sig-contributor-experience at . /lifecycle stale
/remove-lifecycle stale
The Kubernetes project currently lacks enough contributors to adequately respond to all issues and PRs. This bot triages issues and PRs according to the following rules: After 90d of inactivity, is applied After 30d of inactivity since was applied, is applied After 30d of inactivity since was applied, the issue is closed You can: Mark this issue or PR as fresh with Mark this issue or PR as rotten with Close this issue or PR with Offer to help out with [Issue Triage][1] Please send feedback to sig-contributor-experience at . /lifecycle stale [1]:
The Kubernetes project currently lacks enough active contributors to adequately respond to all issues and PRs. This bot triages issues and PRs according to the following rules: After 90d of inactivity, is applied After 30d of inactivity since was applied, is applied After 30d of inactivity since was applied, the issue is closed You can: Mark this issue or PR as fresh with Close this issue or PR with Offer to help out with [Issue Triage][1] Please send feedback to sig-contributor-experience at . /lifecycle rotten [1]:
/remove-lifecycle stale
/remove-lifecycle stale

Discussion:
} } <ins> func proxyRedirectsforRootPath(path string, w http.ResponseWriter, req *http.Request) bool { redirect := false method := req.Method // From pkg/genericapiserver/endpoints/handlers/proxy.go#ServeHTTP: // 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(path) == 0 && (method == http.MethodGet || method == http.MethodHead) { var queryPart string if len(req.URL.RawQuery) > 0 { queryPart = "?" + req.URL.RawQuery } w.Header().Set("Location", req.URL.Path+"/"+queryPart) w.WriteHeader(http.StatusMovedPermanently) redirect = true } return redirect } </ins> // ServeHTTP handles the proxy request func (h *UpgradeAwareHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) { if h.tryUpgrade(w, req) {

loc.Path += "/" } <del> // From pkg/genericapiserver/endpoints/handlers/proxy.go#ServeHTTP: // 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 { var queryPart string if len(req.URL.RawQuery) > 0 { queryPart = "?" + req.URL.RawQuery } w.Header().Set("Location", req.URL.Path+"/"+queryPart) w.WriteHeader(http.StatusMovedPermanently) </del> <ins> proxyRedirect := proxyRedirectsforRootPath(loc.Path, w, req) if proxyRedirect { </ins> return }

} } <ins> func TestProxyRedirectsforRootPath(t *testing.T) { tests := []struct { name               string method             string requestPath        string expectedHeader     http.Header expectedStatusCode int redirect           bool }{ { name:               "root path, simple get", method:             "GET", requestPath:        "", redirect:           true, expectedStatusCode: 301, expectedHeader: http.Header{ "Location": []string{"/"}, }, }, { name:               "root path, simple put", method:             "PUT", requestPath:        "", redirect:           false, expectedStatusCode: 200, }, { name:               "root path, simple head", method:             "HEAD", requestPath:        "", redirect:           true, expectedStatusCode: 301, expectedHeader: http.Header{ "Location": []string{"/"}, }, }, { name:               "root path, simple delete with params", method:             "DELETE", requestPath:        "", redirect:           false, expectedStatusCode: 200, }, } for _, test := range tests { func() { w := httptest.NewRecorder() req, err := http.NewRequest(test.method, test.requestPath, nil) if err != nil { t.Fatal(err) } redirect := proxyRedirectsforRootPath(test.requestPath, w, req) if got, want := redirect, test.redirect; got != want { t.Errorf("Expected redirect state %v; got %v", want, got) } res := w.Result() if got, want := res.StatusCode, test.expectedStatusCode; got != want { t.Errorf("Expected status code %d; got %d", want, got) } if res.StatusCode == 301 && !reflect.DeepEqual(res.Header, test.expectedHeader) { t.Errorf("Expected location header to be %v, got %v", test.expectedHeader, res.Header) } }() } } </ins> // exampleCert was generated from crypto/tls/generate_cert.go with the following command: //    go run generate_cert.go  --rsa-bits 1024 --host example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h var exampleCert = []byte(`-----BEGIN CERTIFICATE-----