diff --git a/0001-fix-compile-options.patch b/0001-fix-compile-options.patch index 5daef9057c74a8420c6a8753909dd9088b39c369..5d49abcdaa4520bc1b7061d815e73e63286328b1 100644 --- a/0001-fix-compile-options.patch +++ b/0001-fix-compile-options.patch @@ -1,18 +1,18 @@ -From d49390e67b2d3027c8c0ad799bf294a89d4d149f Mon Sep 17 00:00:00 2001 -From: zhangxiaoyu -Date: Thu, 20 Jul 2023 17:27:05 +0800 +From ed51f4be525cbaf5d2fa508260e1ecc80c95dd25 Mon Sep 17 00:00:00 2001 +From: liuxu +Date: Fri, 26 Apr 2024 10:30:18 +0800 Subject: [PATCH] fix compile options -Signed-off-by: zhangxiaoyu +Signed-off-by: liuxu --- hack/lib/golang.sh | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hack/lib/golang.sh b/hack/lib/golang.sh -index 232c17b3..c6cbce44 100755 +index f3466ff7..0cc59787 100755 --- a/hack/lib/golang.sh +++ b/hack/lib/golang.sh -@@ -744,6 +744,7 @@ kube::golang::build_binaries_for_platform() { +@@ -781,6 +781,7 @@ kube::golang::build_binaries_for_platform() { -installsuffix=static ${goflags:+"${goflags[@]}"} -gcflags="${gogcflags}" @@ -20,7 +20,7 @@ index 232c17b3..c6cbce44 100755 -asmflags="${goasmflags}" -ldflags="${goldflags}" -tags="${gotags:-}" -@@ -755,6 +756,7 @@ kube::golang::build_binaries_for_platform() { +@@ -792,6 +793,7 @@ kube::golang::build_binaries_for_platform() { build_args=( ${goflags:+"${goflags[@]}"} -gcflags="${gogcflags}" @@ -28,7 +28,7 @@ index 232c17b3..c6cbce44 100755 -asmflags="${goasmflags}" -ldflags="${goldflags}" -tags="${gotags:-}" -@@ -844,7 +846,7 @@ kube::golang::build_binaries() { +@@ -881,7 +883,7 @@ kube::golang::build_binaries() { goasmflags="" fi @@ -38,5 +38,5 @@ index 232c17b3..c6cbce44 100755 # Not debugging - disable symbols and DWARF. goldflags="${goldflags} -s -w" -- -2.25.1 +2.34.1 diff --git a/0002-kubelet-support-exec-and-attach-websocket-protocol.patch b/0002-kubelet-support-exec-and-attach-websocket-protocol.patch index 782e8693415f48a8ce41099501fca5fce99ff792..bc0730a3efbcb0eb95e3fe80c9bc44cd3d2e9d4e 100644 --- a/0002-kubelet-support-exec-and-attach-websocket-protocol.patch +++ b/0002-kubelet-support-exec-and-attach-websocket-protocol.patch @@ -1,20 +1,111 @@ -From cb4f12375beffd61f3b2d666ca3f92d574ceb597 Mon Sep 17 00:00:00 2001 -From: zhangxiaoyu -Date: Tue, 3 Jan 2023 14:20:10 +0800 +From 5ad72e2b135afb5fbe5112f901bdec79f8943611 Mon Sep 17 00:00:00 2001 +From: liuxu +Date: Fri, 26 Apr 2024 10:49:14 +0800 Subject: [PATCH] kubelet support exec and attach websocket protocol -Signed-off-by: zhangxiaoyu +Signed-off-by: liuxu --- - .../cri/streaming/remotecommand/proxy.go | 212 ++++++++++++++++++ pkg/kubelet/server/server.go | 43 +++- + .../pkg/cri/streaming/remotecommand/proxy.go | 212 ++++++++++++++++++ 2 files changed, 247 insertions(+), 8 deletions(-) - create mode 100644 pkg/kubelet/cri/streaming/remotecommand/proxy.go + create mode 100644 staging/src/k8s.io/kubelet/pkg/cri/streaming/remotecommand/proxy.go -diff --git a/pkg/kubelet/cri/streaming/remotecommand/proxy.go b/pkg/kubelet/cri/streaming/remotecommand/proxy.go +diff --git a/pkg/kubelet/server/server.go b/pkg/kubelet/server/server.go +index 87a017f9..887e663a 100644 +--- a/pkg/kubelet/server/server.go ++++ b/pkg/kubelet/server/server.go +@@ -834,51 +834,78 @@ func proxyStream(w http.ResponseWriter, r *http.Request, url *url.URL) { + + // getAttach handles requests to attach to a container. + func (s *Server) getAttach(request *restful.Request, response *restful.Response) { +- params := getExecRequestParams(request) + streamOpts, err := remotecommandserver.NewOptions(request.Request) + if err != nil { + utilruntime.HandleError(err) + response.WriteError(http.StatusBadRequest, err) + return + } ++ ++ url, err := s.getAttachUrl(request, response, streamOpts) ++ if err != nil { ++ klog.Errorf("failed to get backend url %v", err) ++ return ++ } ++ if url.Scheme == "ws" || url.Scheme == "wss" { ++ remotecommandserver.ProxyToWebSocket(response.ResponseWriter, request.Request, url, streamOpts) ++ } else { ++ proxyStream(response.ResponseWriter, request.Request, url) ++ } ++} ++ ++func (s *Server) getAttachUrl(request *restful.Request, response *restful.Response, streamOpts *remotecommandserver.Options) (*url.URL, error) { ++ params := getExecRequestParams(request) + pod, ok := s.host.GetPodByName(params.podNamespace, params.podName) + if !ok { + response.WriteError(http.StatusNotFound, fmt.Errorf("pod does not exist")) +- return ++ return nil, fmt.Errorf("pod not found") + } + + podFullName := kubecontainer.GetPodFullName(pod) + url, err := s.host.GetAttach(request.Request.Context(), podFullName, params.podUID, params.containerName, *streamOpts) + if err != nil { + streaming.WriteError(err, response.ResponseWriter) +- return ++ return nil, err + } + +- proxyStream(response.ResponseWriter, request.Request, url) ++ return url, nil + } + + // getExec handles requests to run a command inside a container. + func (s *Server) getExec(request *restful.Request, response *restful.Response) { +- params := getExecRequestParams(request) + streamOpts, err := remotecommandserver.NewOptions(request.Request) + if err != nil { + utilruntime.HandleError(err) + response.WriteError(http.StatusBadRequest, err) + return + } ++ url, err := s.getExecUrl(request, response, streamOpts) ++ if err != nil { ++ klog.Errorf("failed to get backend url %v", err) ++ return ++ } ++ if url.Scheme == "ws" || url.Scheme == "wss" { ++ remotecommandserver.ProxyToWebSocket(response.ResponseWriter, request.Request, url, streamOpts) ++ } else { ++ proxyStream(response.ResponseWriter, request.Request, url) ++ } ++} ++ ++func (s *Server) getExecUrl(request *restful.Request, response *restful.Response, streamOpts *remotecommandserver.Options) (*url.URL, error) { ++ params := getExecRequestParams(request) + pod, ok := s.host.GetPodByName(params.podNamespace, params.podName) + if !ok { + response.WriteError(http.StatusNotFound, fmt.Errorf("pod does not exist")) +- return ++ return nil, fmt.Errorf("pod not found") + } + + podFullName := kubecontainer.GetPodFullName(pod) + url, err := s.host.GetExec(request.Request.Context(), podFullName, params.podUID, params.containerName, params.cmd, *streamOpts) + if err != nil { + streaming.WriteError(err, response.ResponseWriter) +- return ++ return nil, err + } +- proxyStream(response.ResponseWriter, request.Request, url) ++ return url, nil + } + + // getRun handles requests to run a command inside a container. +diff --git a/staging/src/k8s.io/kubelet/pkg/cri/streaming/remotecommand/proxy.go b/staging/src/k8s.io/kubelet/pkg/cri/streaming/remotecommand/proxy.go new file mode 100644 index 00000000..179d8183 --- /dev/null -+++ b/pkg/kubelet/cri/streaming/remotecommand/proxy.go ++++ b/staging/src/k8s.io/kubelet/pkg/cri/streaming/remotecommand/proxy.go @@ -0,0 +1,212 @@ +package remotecommand + @@ -228,97 +319,6 @@ index 00000000..179d8183 + } + return len(p), nil +} -diff --git a/pkg/kubelet/server/server.go b/pkg/kubelet/server/server.go -index 84dbd545..73627d5b 100644 ---- a/pkg/kubelet/server/server.go -+++ b/pkg/kubelet/server/server.go -@@ -797,51 +797,78 @@ func proxyStream(w http.ResponseWriter, r *http.Request, url *url.URL) { - - // getAttach handles requests to attach to a container. - func (s *Server) getAttach(request *restful.Request, response *restful.Response) { -- params := getExecRequestParams(request) - streamOpts, err := remotecommandserver.NewOptions(request.Request) - if err != nil { - utilruntime.HandleError(err) - response.WriteError(http.StatusBadRequest, err) - return - } -+ -+ url, err := s.getAttachUrl(request, response, streamOpts) -+ if err != nil { -+ klog.Errorf("failed to get backend url %v", err) -+ return -+ } -+ if url.Scheme == "ws" || url.Scheme == "wss" { -+ remotecommandserver.ProxyToWebSocket(response.ResponseWriter, request.Request, url, streamOpts) -+ } else { -+ proxyStream(response.ResponseWriter, request.Request, url) -+ } -+} -+ -+func (s *Server) getAttachUrl(request *restful.Request, response *restful.Response, streamOpts *remotecommandserver.Options) (*url.URL, error) { -+ params := getExecRequestParams(request) - pod, ok := s.host.GetPodByName(params.podNamespace, params.podName) - if !ok { - response.WriteError(http.StatusNotFound, fmt.Errorf("pod does not exist")) -- return -+ return nil, fmt.Errorf("pod not found") - } - - podFullName := kubecontainer.GetPodFullName(pod) - url, err := s.host.GetAttach(podFullName, params.podUID, params.containerName, *streamOpts) - if err != nil { - streaming.WriteError(err, response.ResponseWriter) -- return -+ return nil, err - } - -- proxyStream(response.ResponseWriter, request.Request, url) -+ return url, nil - } - - // getExec handles requests to run a command inside a container. - func (s *Server) getExec(request *restful.Request, response *restful.Response) { -- params := getExecRequestParams(request) - streamOpts, err := remotecommandserver.NewOptions(request.Request) - if err != nil { - utilruntime.HandleError(err) - response.WriteError(http.StatusBadRequest, err) - return - } -+ url, err := s.getExecUrl(request, response, streamOpts) -+ if err != nil { -+ klog.Errorf("failed to get backend url %v", err) -+ return -+ } -+ if url.Scheme == "ws" || url.Scheme == "wss" { -+ remotecommandserver.ProxyToWebSocket(response.ResponseWriter, request.Request, url, streamOpts) -+ } else { -+ proxyStream(response.ResponseWriter, request.Request, url) -+ } -+} -+ -+func (s *Server) getExecUrl(request *restful.Request, response *restful.Response, streamOpts *remotecommandserver.Options) (*url.URL, error) { -+ params := getExecRequestParams(request) - pod, ok := s.host.GetPodByName(params.podNamespace, params.podName) - if !ok { - response.WriteError(http.StatusNotFound, fmt.Errorf("pod does not exist")) -- return -+ return nil, fmt.Errorf("pod not found") - } - - podFullName := kubecontainer.GetPodFullName(pod) - url, err := s.host.GetExec(podFullName, params.podUID, params.containerName, params.cmd, *streamOpts) - if err != nil { - streaming.WriteError(err, response.ResponseWriter) -- return -+ return nil, err - } -- proxyStream(response.ResponseWriter, request.Request, url) -+ return url, nil - } - - // getRun handles requests to run a command inside a container. -- -2.25.1 +2.34.1 diff --git a/0003-Add-envFrom-to-serviceaccount-admission-plugin.patch b/0003-Add-envFrom-to-serviceaccount-admission-plugin.patch new file mode 100644 index 0000000000000000000000000000000000000000..9af2e71a06ebd0a5dcb69a3ccc5bfa4d0d1c44b3 --- /dev/null +++ b/0003-Add-envFrom-to-serviceaccount-admission-plugin.patch @@ -0,0 +1,236 @@ +From 3f0922513d235d8bdebe79f0d07da769c04211b8 Mon Sep 17 00:00:00 2001 +From: Rita Zhang +Date: Mon, 25 Mar 2024 10:33:41 -0700 +Subject: [PATCH] Add envFrom to serviceaccount admission plugin + +Signed-off-by: Rita Zhang +--- + .../pkg/admission/serviceaccount/admission.go | 21 +++ + .../serviceaccount/admission_test.go | 122 ++++++++++++++++-- + 2 files changed, 132 insertions(+), 11 deletions(-) + +diff --git a/plugin/pkg/admission/serviceaccount/admission.go b/plugin/pkg/admission/serviceaccount/admission.go +index c844a051c24..3f4338128e5 100644 +--- a/plugin/pkg/admission/serviceaccount/admission.go ++++ b/plugin/pkg/admission/serviceaccount/admission.go +@@ -337,6 +337,13 @@ func (s *Plugin) limitSecretReferences(serviceAccount *corev1.ServiceAccount, po + } + } + } ++ for _, envFrom := range container.EnvFrom { ++ if envFrom.SecretRef != nil { ++ if !mountableSecrets.Has(envFrom.SecretRef.Name) { ++ return fmt.Errorf("init container %s with envFrom referencing secret.secretName=\"%s\" is not allowed because service account %s does not reference that secret", container.Name, envFrom.SecretRef.Name, serviceAccount.Name) ++ } ++ } ++ } + } + + for _, container := range pod.Spec.Containers { +@@ -347,6 +354,13 @@ func (s *Plugin) limitSecretReferences(serviceAccount *corev1.ServiceAccount, po + } + } + } ++ for _, envFrom := range container.EnvFrom { ++ if envFrom.SecretRef != nil { ++ if !mountableSecrets.Has(envFrom.SecretRef.Name) { ++ return fmt.Errorf("container %s with envFrom referencing secret.secretName=\"%s\" is not allowed because service account %s does not reference that secret", container.Name, envFrom.SecretRef.Name, serviceAccount.Name) ++ } ++ } ++ } + } + + // limit pull secret references as well +@@ -388,6 +402,13 @@ func (s *Plugin) limitEphemeralContainerSecretReferences(pod *api.Pod, a admissi + } + } + } ++ for _, envFrom := range container.EnvFrom { ++ if envFrom.SecretRef != nil { ++ if !mountableSecrets.Has(envFrom.SecretRef.Name) { ++ return fmt.Errorf("ephemeral container %s with envFrom referencing secret.secretName=\"%s\" is not allowed because service account %s does not reference that secret", container.Name, envFrom.SecretRef.Name, serviceAccount.Name) ++ } ++ } ++ } + } + return nil + } +diff --git a/plugin/pkg/admission/serviceaccount/admission_test.go b/plugin/pkg/admission/serviceaccount/admission_test.go +index bf15f870d75..4dba6cd8b13 100644 +--- a/plugin/pkg/admission/serviceaccount/admission_test.go ++++ b/plugin/pkg/admission/serviceaccount/admission_test.go +@@ -521,6 +521,25 @@ func TestAllowsReferencedSecret(t *testing.T) { + t.Errorf("Unexpected error: %v", err) + } + ++ pod2 = &api.Pod{ ++ Spec: api.PodSpec{ ++ Containers: []api.Container{ ++ { ++ Name: "container-1", ++ EnvFrom: []api.EnvFromSource{ ++ { ++ SecretRef: &api.SecretEnvSource{ ++ LocalObjectReference: api.LocalObjectReference{ ++ Name: "foo"}}}}, ++ }, ++ }, ++ }, ++ } ++ attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil) ++ if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil { ++ t.Errorf("Unexpected error: %v", err) ++ } ++ + pod2 = &api.Pod{ + Spec: api.PodSpec{ + InitContainers: []api.Container{ +@@ -545,6 +564,25 @@ func TestAllowsReferencedSecret(t *testing.T) { + t.Errorf("Unexpected error: %v", err) + } + ++ pod2 = &api.Pod{ ++ Spec: api.PodSpec{ ++ InitContainers: []api.Container{ ++ { ++ Name: "container-1", ++ EnvFrom: []api.EnvFromSource{ ++ { ++ SecretRef: &api.SecretEnvSource{ ++ LocalObjectReference: api.LocalObjectReference{ ++ Name: "foo"}}}}, ++ }, ++ }, ++ }, ++ } ++ attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil) ++ if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil { ++ t.Errorf("Unexpected error: %v", err) ++ } ++ + pod2 = &api.Pod{ + Spec: api.PodSpec{ + ServiceAccountName: DefaultServiceAccountName, +@@ -572,6 +610,28 @@ func TestAllowsReferencedSecret(t *testing.T) { + if err := admit.Validate(context.TODO(), attrs, nil); err != nil { + t.Errorf("Unexpected error: %v", err) + } ++ ++ pod2 = &api.Pod{ ++ Spec: api.PodSpec{ ++ ServiceAccountName: DefaultServiceAccountName, ++ EphemeralContainers: []api.EphemeralContainer{ ++ { ++ EphemeralContainerCommon: api.EphemeralContainerCommon{ ++ Name: "container-2", ++ EnvFrom: []api.EnvFromSource{{ ++ SecretRef: &api.SecretEnvSource{ ++ LocalObjectReference: api.LocalObjectReference{ ++ Name: "foo"}}}}, ++ }, ++ }, ++ }, ++ }, ++ } ++ // validate enforces restrictions on secret mounts when operation==update and subresource==ephemeralcontainers" ++ attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "ephemeralcontainers", admission.Update, &metav1.UpdateOptions{}, false, nil) ++ if err := admit.Validate(context.TODO(), attrs, nil); err != nil { ++ t.Errorf("Unexpected error: %v", err) ++ } + } + + func TestRejectsUnreferencedSecretVolumes(t *testing.T) { +@@ -628,25 +688,20 @@ func TestRejectsUnreferencedSecretVolumes(t *testing.T) { + + pod2 = &api.Pod{ + Spec: api.PodSpec{ +- InitContainers: []api.Container{ ++ Containers: []api.Container{ + { + Name: "container-1", +- Env: []api.EnvVar{ ++ EnvFrom: []api.EnvFromSource{ + { +- Name: "env-1", +- ValueFrom: &api.EnvVarSource{ +- SecretKeyRef: &api.SecretKeySelector{ +- LocalObjectReference: api.LocalObjectReference{Name: "foo"}, +- }, +- }, +- }, +- }, ++ SecretRef: &api.SecretEnvSource{ ++ LocalObjectReference: api.LocalObjectReference{ ++ Name: "foo"}}}}, + }, + }, + }, + } + attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil) +- if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err == nil || !strings.Contains(err.Error(), "with envVar") { ++ if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err == nil || !strings.Contains(err.Error(), "with envFrom") { + t.Errorf("Unexpected error: %v", err) + } + +@@ -679,6 +734,30 @@ func TestRejectsUnreferencedSecretVolumes(t *testing.T) { + t.Errorf("validate only enforces restrictions on secret mounts when operation==create and subresource==''. Unexpected error: %v", err) + } + ++ pod2 = &api.Pod{ ++ Spec: api.PodSpec{ ++ ServiceAccountName: DefaultServiceAccountName, ++ InitContainers: []api.Container{ ++ { ++ Name: "container-1", ++ EnvFrom: []api.EnvFromSource{ ++ { ++ SecretRef: &api.SecretEnvSource{ ++ LocalObjectReference: api.LocalObjectReference{ ++ Name: "foo"}}}}, ++ }, ++ }, ++ }, ++ } ++ attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil) ++ if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil { ++ t.Errorf("admit only enforces restrictions on secret mounts when operation==create. Unexpected error: %v", err) ++ } ++ attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil) ++ if err := admit.Validate(context.TODO(), attrs, nil); err == nil || !strings.Contains(err.Error(), "with envFrom") { ++ t.Errorf("validate only enforces restrictions on secret mounts when operation==create and subresource==''. Unexpected error: %v", err) ++ } ++ + pod2 = &api.Pod{ + Spec: api.PodSpec{ + ServiceAccountName: DefaultServiceAccountName, +@@ -709,6 +788,27 @@ func TestRejectsUnreferencedSecretVolumes(t *testing.T) { + if err := admit.Validate(context.TODO(), attrs, nil); err == nil || !strings.Contains(err.Error(), "with envVar") { + t.Errorf("validate enforces restrictions on secret mounts when operation==update and subresource==ephemeralcontainers. Unexpected error: %v", err) + } ++ ++ pod2 = &api.Pod{ ++ Spec: api.PodSpec{ ++ ServiceAccountName: DefaultServiceAccountName, ++ EphemeralContainers: []api.EphemeralContainer{ ++ { ++ EphemeralContainerCommon: api.EphemeralContainerCommon{ ++ Name: "container-2", ++ EnvFrom: []api.EnvFromSource{{ ++ SecretRef: &api.SecretEnvSource{ ++ LocalObjectReference: api.LocalObjectReference{ ++ Name: "foo"}}}}, ++ }, ++ }, ++ }, ++ }, ++ } ++ attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "ephemeralcontainers", admission.Update, &metav1.UpdateOptions{}, false, nil) ++ if err := admit.Validate(context.TODO(), attrs, nil); err == nil || !strings.Contains(err.Error(), "with envFrom") { ++ t.Errorf("validate enforces restrictions on secret mounts when operation==update and subresource==ephemeralcontainers. Unexpected error: %v", err) ++ } + } + + func TestAllowUnreferencedSecretVolumesForPermissiveSAs(t *testing.T) { +-- +2.34.1 + diff --git a/0003-Escape-terminal-special-characters-in-kubectl-112553.patch b/0003-Escape-terminal-special-characters-in-kubectl-112553.patch deleted file mode 100644 index 1ddb04bdd3da6f7f029a624a0868d6d8873eaa0d..0000000000000000000000000000000000000000 --- a/0003-Escape-terminal-special-characters-in-kubectl-112553.patch +++ /dev/null @@ -1,215 +0,0 @@ -From 98128bcf1bd9c9ea86c1c8d48c0b1acda4ec7e73 Mon Sep 17 00:00:00 2001 -From: David Leadbeater -Date: Mon, 31 Oct 2022 01:08:43 +1000 -Subject: [PATCH] Escape terminal special characters in kubectl (#112553) - -* Escape terminal special characters in kubectl - -* Add escaping for kubectl alpha events ---- - .../cli-runtime/pkg/printers/tableprinter.go | 13 ++++--- - .../pkg/printers/tableprinter_test.go | 12 ++++++ - .../cli-runtime/pkg/printers/terminal.go | 39 +++++++++++++++++++ - .../kubectl/pkg/cmd/get/customcolumn.go | 2 +- - .../kubectl/pkg/cmd/get/customcolumn_test.go | 16 ++++++++ - .../k8s.io/kubectl/pkg/describe/describe.go | 7 +++- - .../kubectl/pkg/describe/describe_test.go | 19 +++++++++ - 7 files changed, 99 insertions(+), 9 deletions(-) - create mode 100644 staging/src/k8s.io/cli-runtime/pkg/printers/terminal.go - -diff --git a/staging/src/k8s.io/cli-runtime/pkg/printers/tableprinter.go b/staging/src/k8s.io/cli-runtime/pkg/printers/tableprinter.go -index 87bc3f41..54859665 100644 ---- a/staging/src/k8s.io/cli-runtime/pkg/printers/tableprinter.go -+++ b/staging/src/k8s.io/cli-runtime/pkg/printers/tableprinter.go -@@ -212,18 +212,19 @@ func printTable(table *metav1.Table, output io.Writer, options PrintOptions) err - case string: - print := val - truncated := false -- // truncate at newlines -- newline := strings.Index(print, "\n") -- if newline >= 0 { -+ // Truncate at the first newline, carriage return or formfeed -+ // (treated as a newline by tabwriter). -+ breakchar := strings.IndexAny(print, "\f\n\r") -+ if breakchar >= 0 { - truncated = true -- print = print[:newline] -+ print = print[:breakchar] - } -- fmt.Fprint(output, print) -+ WriteEscaped(output, print) - if truncated { - fmt.Fprint(output, "...") - } - default: -- fmt.Fprint(output, val) -+ WriteEscaped(output, fmt.Sprint(val)) - } - } - } -diff --git a/staging/src/k8s.io/cli-runtime/pkg/printers/tableprinter_test.go b/staging/src/k8s.io/cli-runtime/pkg/printers/tableprinter_test.go -index b2caaa12..edbff3ff 100644 ---- a/staging/src/k8s.io/cli-runtime/pkg/printers/tableprinter_test.go -+++ b/staging/src/k8s.io/cli-runtime/pkg/printers/tableprinter_test.go -@@ -769,6 +769,18 @@ test1 20h This is first line which is long and goes for on and on and on an - }, - expected: `NAME AGE DESCRIPTION - test1 20h This is first... -+`, -+ }, -+ // terminal special character, should be escaped -+ { -+ columns: []metav1.TableColumnDefinition{ -+ {Name: "Name", Type: "string"}, -+ }, -+ rows: []metav1.TableRow{ -+ {Cells: []interface{}{"test1\x1b"}}, -+ }, -+ expected: `NAME -+test1^[ - `, - }, - } -diff --git a/staging/src/k8s.io/cli-runtime/pkg/printers/terminal.go b/staging/src/k8s.io/cli-runtime/pkg/printers/terminal.go -new file mode 100644 -index 00000000..5a59491e ---- /dev/null -+++ b/staging/src/k8s.io/cli-runtime/pkg/printers/terminal.go -@@ -0,0 +1,39 @@ -+/* -+Copyright 2022 The Kubernetes Authors. -+ -+Licensed under the Apache License, Version 2.0 (the "License"); -+you may not use this file except in compliance with the License. -+You may obtain a copy of the License at -+ -+ http://www.apache.org/licenses/LICENSE-2.0 -+ -+Unless required by applicable law or agreed to in writing, software -+distributed under the License is distributed on an "AS IS" BASIS, -+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -+See the License for the specific language governing permissions and -+limitations under the License. -+*/ -+ -+package printers -+ -+import ( -+ "io" -+ "strings" -+) -+ -+// terminalEscaper replaces ANSI escape sequences and other terminal special -+// characters to avoid terminal escape character attacks (issue #101695). -+var terminalEscaper = strings.NewReplacer("\x1b", "^[", "\r", "\\r") -+ -+// WriteEscaped replaces unsafe terminal characters with replacement strings -+// and writes them to the given writer. -+func WriteEscaped(writer io.Writer, output string) error { -+ _, err := terminalEscaper.WriteString(writer, output) -+ return err -+} -+ -+// EscapeTerminal escapes terminal special characters in a human readable (but -+// non-reversible) format. -+func EscapeTerminal(in string) string { -+ return terminalEscaper.Replace(in) -+} -diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/get/customcolumn.go b/staging/src/k8s.io/kubectl/pkg/cmd/get/customcolumn.go -index 2b205667..38024cfa 100644 ---- a/staging/src/k8s.io/kubectl/pkg/cmd/get/customcolumn.go -+++ b/staging/src/k8s.io/kubectl/pkg/cmd/get/customcolumn.go -@@ -252,7 +252,7 @@ func (s *CustomColumnsPrinter) printOneObject(obj runtime.Object, parsers []*jso - } - for arrIx := range values { - for valIx := range values[arrIx] { -- valueStrings = append(valueStrings, fmt.Sprintf("%v", values[arrIx][valIx].Interface())) -+ valueStrings = append(valueStrings, printers.EscapeTerminal(fmt.Sprint(values[arrIx][valIx].Interface()))) - } - } - columns[ix] = strings.Join(valueStrings, ",") -diff --git a/staging/src/k8s.io/kubectl/pkg/cmd/get/customcolumn_test.go b/staging/src/k8s.io/kubectl/pkg/cmd/get/customcolumn_test.go -index e4fb17a8..de403142 100644 ---- a/staging/src/k8s.io/kubectl/pkg/cmd/get/customcolumn_test.go -+++ b/staging/src/k8s.io/kubectl/pkg/cmd/get/customcolumn_test.go -@@ -311,6 +311,22 @@ foo baz - obj: &corev1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}, TypeMeta: metav1.TypeMeta{APIVersion: "baz"}}, - expectedOutput: `NAME API_VERSION NOT_FOUND - foo baz -+`, -+ }, -+ { -+ columns: []Column{ -+ { -+ Header: "NAME", -+ FieldSpec: "{.metadata.name}", -+ }, -+ }, -+ obj: &corev1.PodList{ -+ Items: []corev1.Pod{ -+ {ObjectMeta: metav1.ObjectMeta{Name: "\x1b \r"}}, -+ }, -+ }, -+ expectedOutput: `NAME -+^[ \r - `, - }, - } -diff --git a/staging/src/k8s.io/kubectl/pkg/describe/describe.go b/staging/src/k8s.io/kubectl/pkg/describe/describe.go -index 484ed09f..d3b65132 100644 ---- a/staging/src/k8s.io/kubectl/pkg/describe/describe.go -+++ b/staging/src/k8s.io/kubectl/pkg/describe/describe.go -@@ -65,6 +65,7 @@ import ( - "k8s.io/apimachinery/pkg/util/intstr" - "k8s.io/apimachinery/pkg/util/sets" - "k8s.io/cli-runtime/pkg/genericclioptions" -+ "k8s.io/cli-runtime/pkg/printers" - runtimeresource "k8s.io/cli-runtime/pkg/resource" - "k8s.io/client-go/dynamic" - clientset "k8s.io/client-go/kubernetes" -@@ -148,11 +149,13 @@ func (pw *prefixWriter) Write(level int, format string, a ...interface{}) { - for i := 0; i < level; i++ { - prefix += levelSpace - } -- fmt.Fprintf(pw.out, prefix+format, a...) -+ output := fmt.Sprintf(prefix+format, a...) -+ printers.WriteEscaped(pw.out, output) - } - - func (pw *prefixWriter) WriteLine(a ...interface{}) { -- fmt.Fprintln(pw.out, a...) -+ output := fmt.Sprintln(a...) -+ printers.WriteEscaped(pw.out, output) - } - - func (pw *prefixWriter) Flush() { -diff --git a/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go b/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go -index 5225ec2d..a3e2feba 100644 ---- a/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go -+++ b/staging/src/k8s.io/kubectl/pkg/describe/describe_test.go -@@ -5520,3 +5520,22 @@ func TestControllerRef(t *testing.T) { - t.Errorf("unexpected out: %s", out) - } - } -+ -+func TestDescribeTerminalEscape(t *testing.T) { -+ fake := fake.NewSimpleClientset(&corev1.ConfigMap{ -+ ObjectMeta: metav1.ObjectMeta{ -+ Name: "mycm", -+ Namespace: "foo", -+ Annotations: map[string]string{"annotation1": "terminal escape: \x1b"}, -+ }, -+ }) -+ c := &describeClient{T: t, Namespace: "foo", Interface: fake} -+ d := ConfigMapDescriber{c} -+ out, err := d.Describe("foo", "mycm", DescriberSettings{ShowEvents: true}) -+ if err != nil { -+ t.Errorf("unexpected error: %v", err) -+ } -+ if strings.Contains(out, "\x1b") || !strings.Contains(out, "^[") { -+ t.Errorf("unexpected out: %s", out) -+ } -+} --- -2.25.1 - diff --git a/0004-Return-error-for-localhost-seccomp-type-with-no-loca.patch b/0004-Return-error-for-localhost-seccomp-type-with-no-loca.patch deleted file mode 100644 index 7799506636077ee32d2643f17099cdc5c8866994..0000000000000000000000000000000000000000 --- a/0004-Return-error-for-localhost-seccomp-type-with-no-loca.patch +++ /dev/null @@ -1,439 +0,0 @@ -From 604ad21799c43d87456cc76d3e591487de0a5152 Mon Sep 17 00:00:00 2001 -From: Craig Ingram -Date: Fri, 24 Feb 2023 15:24:49 -0500 -Subject: [PATCH] Return error for localhost seccomp type with no localhost - profile defined - ---- - pkg/kubelet/kuberuntime/helpers.go | 60 +++++----- - pkg/kubelet/kuberuntime/helpers_test.go | 120 +++++++++++++------- - pkg/kubelet/kuberuntime/security_context.go | 11 +- - 3 files changed, 121 insertions(+), 70 deletions(-) - -diff --git a/pkg/kubelet/kuberuntime/helpers.go b/pkg/kubelet/kuberuntime/helpers.go -index 9343b78335a..14ed0dd3bfb 100644 ---- a/pkg/kubelet/kuberuntime/helpers.go -+++ b/pkg/kubelet/kuberuntime/helpers.go -@@ -210,32 +210,36 @@ func toKubeRuntimeStatus(status *runtimeapi.RuntimeStatus) *kubecontainer.Runtim - return &kubecontainer.RuntimeStatus{Conditions: conditions} - } - --func fieldProfile(scmp *v1.SeccompProfile, profileRootPath string, fallbackToRuntimeDefault bool) string { -+func fieldProfile(scmp *v1.SeccompProfile, profileRootPath string, fallbackToRuntimeDefault bool) (string, error) { - if scmp == nil { - if fallbackToRuntimeDefault { -- return v1.SeccompProfileRuntimeDefault -+ return v1.SeccompProfileRuntimeDefault, nil - } -- return "" -+ return "", nil - } - if scmp.Type == v1.SeccompProfileTypeRuntimeDefault { -- return v1.SeccompProfileRuntimeDefault -+ return v1.SeccompProfileRuntimeDefault, nil - } -- if scmp.Type == v1.SeccompProfileTypeLocalhost && scmp.LocalhostProfile != nil && len(*scmp.LocalhostProfile) > 0 { -- fname := filepath.Join(profileRootPath, *scmp.LocalhostProfile) -- return v1.SeccompLocalhostProfileNamePrefix + fname -+ if scmp.Type == v1.SeccompProfileTypeLocalhost { -+ if scmp.LocalhostProfile != nil && len(*scmp.LocalhostProfile) > 0 { -+ fname := filepath.Join(profileRootPath, *scmp.LocalhostProfile) -+ return v1.SeccompLocalhostProfileNamePrefix + fname, nil -+ } else { -+ return "", fmt.Errorf("localhostProfile must be set if seccompProfile type is Localhost.") -+ } - } - if scmp.Type == v1.SeccompProfileTypeUnconfined { -- return v1.SeccompProfileNameUnconfined -+ return v1.SeccompProfileNameUnconfined, nil - } - - if fallbackToRuntimeDefault { -- return v1.SeccompProfileRuntimeDefault -+ return v1.SeccompProfileRuntimeDefault, nil - } -- return "" -+ return "", nil - } - - func (m *kubeGenericRuntimeManager) getSeccompProfilePath(annotations map[string]string, containerName string, -- podSecContext *v1.PodSecurityContext, containerSecContext *v1.SecurityContext, fallbackToRuntimeDefault bool) string { -+ podSecContext *v1.PodSecurityContext, containerSecContext *v1.SecurityContext, fallbackToRuntimeDefault bool) (string, error) { - // container fields are applied first - if containerSecContext != nil && containerSecContext.SeccompProfile != nil { - return fieldProfile(containerSecContext.SeccompProfile, m.seccompProfileRoot, fallbackToRuntimeDefault) -@@ -247,42 +251,46 @@ func (m *kubeGenericRuntimeManager) getSeccompProfilePath(annotations map[string - } - - if fallbackToRuntimeDefault { -- return v1.SeccompProfileRuntimeDefault -+ return v1.SeccompProfileRuntimeDefault, nil - } - -- return "" -+ return "", nil - } - --func fieldSeccompProfile(scmp *v1.SeccompProfile, profileRootPath string, fallbackToRuntimeDefault bool) *runtimeapi.SecurityProfile { -+func fieldSeccompProfile(scmp *v1.SeccompProfile, profileRootPath string, fallbackToRuntimeDefault bool) (*runtimeapi.SecurityProfile, error) { - if scmp == nil { - if fallbackToRuntimeDefault { - return &runtimeapi.SecurityProfile{ - ProfileType: runtimeapi.SecurityProfile_RuntimeDefault, -- } -+ }, nil - } - return &runtimeapi.SecurityProfile{ - ProfileType: runtimeapi.SecurityProfile_Unconfined, -- } -+ }, nil - } - if scmp.Type == v1.SeccompProfileTypeRuntimeDefault { - return &runtimeapi.SecurityProfile{ - ProfileType: runtimeapi.SecurityProfile_RuntimeDefault, -- } -+ }, nil - } -- if scmp.Type == v1.SeccompProfileTypeLocalhost && scmp.LocalhostProfile != nil && len(*scmp.LocalhostProfile) > 0 { -- fname := filepath.Join(profileRootPath, *scmp.LocalhostProfile) -- return &runtimeapi.SecurityProfile{ -- ProfileType: runtimeapi.SecurityProfile_Localhost, -- LocalhostRef: fname, -+ if scmp.Type == v1.SeccompProfileTypeLocalhost { -+ if scmp.LocalhostProfile != nil && len(*scmp.LocalhostProfile) > 0 { -+ fname := filepath.Join(profileRootPath, *scmp.LocalhostProfile) -+ return &runtimeapi.SecurityProfile{ -+ ProfileType: runtimeapi.SecurityProfile_Localhost, -+ LocalhostRef: fname, -+ }, nil -+ } else { -+ return nil, fmt.Errorf("localhostProfile must be set if seccompProfile type is Localhost.") - } - } - return &runtimeapi.SecurityProfile{ - ProfileType: runtimeapi.SecurityProfile_Unconfined, -- } -+ }, nil - } - - func (m *kubeGenericRuntimeManager) getSeccompProfile(annotations map[string]string, containerName string, -- podSecContext *v1.PodSecurityContext, containerSecContext *v1.SecurityContext, fallbackToRuntimeDefault bool) *runtimeapi.SecurityProfile { -+ podSecContext *v1.PodSecurityContext, containerSecContext *v1.SecurityContext, fallbackToRuntimeDefault bool) (*runtimeapi.SecurityProfile, error) { - // container fields are applied first - if containerSecContext != nil && containerSecContext.SeccompProfile != nil { - return fieldSeccompProfile(containerSecContext.SeccompProfile, m.seccompProfileRoot, fallbackToRuntimeDefault) -@@ -296,10 +304,10 @@ func (m *kubeGenericRuntimeManager) getSeccompProfile(annotations map[string]str - if fallbackToRuntimeDefault { - return &runtimeapi.SecurityProfile{ - ProfileType: runtimeapi.SecurityProfile_RuntimeDefault, -- } -+ }, nil - } - - return &runtimeapi.SecurityProfile{ - ProfileType: runtimeapi.SecurityProfile_Unconfined, -- } -+ }, nil - } -diff --git a/pkg/kubelet/kuberuntime/helpers_test.go b/pkg/kubelet/kuberuntime/helpers_test.go -index 83a8da8793a..5fbe640b44a 100644 ---- a/pkg/kubelet/kuberuntime/helpers_test.go -+++ b/pkg/kubelet/kuberuntime/helpers_test.go -@@ -242,17 +242,18 @@ func TestFieldProfile(t *testing.T) { - scmpProfile *v1.SeccompProfile - rootPath string - expectedProfile string -+ expectedError string - }{ - { - description: "no seccompProfile should return empty", - expectedProfile: "", - }, - { -- description: "type localhost without profile should return empty", -+ description: "type localhost without profile should return error", - scmpProfile: &v1.SeccompProfile{ - Type: v1.SeccompProfileTypeLocalhost, - }, -- expectedProfile: "", -+ expectedError: "localhostProfile must be set if seccompProfile type is Localhost.", - }, - { - description: "unknown type should return empty", -@@ -279,7 +280,7 @@ func TestFieldProfile(t *testing.T) { - description: "SeccompProfileTypeLocalhost should return localhost", - scmpProfile: &v1.SeccompProfile{ - Type: v1.SeccompProfileTypeLocalhost, -- LocalhostProfile: utilpointer.StringPtr("profile.json"), -+ LocalhostProfile: utilpointer.String("profile.json"), - }, - rootPath: "/test/", - expectedProfile: "localhost//test/profile.json", -@@ -287,8 +288,13 @@ func TestFieldProfile(t *testing.T) { - } - - for i, test := range tests { -- seccompProfile := fieldProfile(test.scmpProfile, test.rootPath, false) -- assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description) -+ seccompProfile, err := fieldProfile(test.scmpProfile, test.rootPath, false) -+ if test.expectedError != "" { -+ assert.EqualError(t, err, test.expectedError, "TestCase[%d]: %s", i, test.description) -+ } else { -+ assert.NoError(t, err, "TestCase[%d]: %s", i, test.description) -+ assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description) -+ } - } - } - -@@ -298,17 +304,18 @@ func TestFieldProfileDefaultSeccomp(t *testing.T) { - scmpProfile *v1.SeccompProfile - rootPath string - expectedProfile string -+ expectedError string - }{ - { - description: "no seccompProfile should return runtime/default", - expectedProfile: v1.SeccompProfileRuntimeDefault, - }, - { -- description: "type localhost without profile should return runtime/default", -+ description: "type localhost without profile should return error", - scmpProfile: &v1.SeccompProfile{ - Type: v1.SeccompProfileTypeLocalhost, - }, -- expectedProfile: v1.SeccompProfileRuntimeDefault, -+ expectedError: "localhostProfile must be set if seccompProfile type is Localhost.", - }, - { - description: "unknown type should return runtime/default", -@@ -335,7 +342,7 @@ func TestFieldProfileDefaultSeccomp(t *testing.T) { - description: "SeccompProfileTypeLocalhost should return localhost", - scmpProfile: &v1.SeccompProfile{ - Type: v1.SeccompProfileTypeLocalhost, -- LocalhostProfile: utilpointer.StringPtr("profile.json"), -+ LocalhostProfile: utilpointer.String("profile.json"), - }, - rootPath: "/test/", - expectedProfile: "localhost//test/profile.json", -@@ -343,8 +350,13 @@ func TestFieldProfileDefaultSeccomp(t *testing.T) { - } - - for i, test := range tests { -- seccompProfile := fieldProfile(test.scmpProfile, test.rootPath, true) -- assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description) -+ seccompProfile, err := fieldProfile(test.scmpProfile, test.rootPath, true) -+ if test.expectedError != "" { -+ assert.EqualError(t, err, test.expectedError, "TestCase[%d]: %s", i, test.description) -+ } else { -+ assert.NoError(t, err, "TestCase[%d]: %s", i, test.description) -+ assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description) -+ } - } - } - -@@ -359,6 +371,7 @@ func TestGetSeccompProfilePath(t *testing.T) { - containerSc *v1.SecurityContext - containerName string - expectedProfile string -+ expectedError string - }{ - { - description: "no seccomp should return empty", -@@ -395,14 +408,14 @@ func TestGetSeccompProfilePath(t *testing.T) { - expectedProfile: seccompLocalhostPath("filename"), - }, - { -- description: "pod seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns empty", -- podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}}, -- expectedProfile: "", -+ description: "pod seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns error", -+ podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}}, -+ expectedError: "localhostProfile must be set if seccompProfile type is Localhost.", - }, - { -- description: "container seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns empty", -- containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}}, -- expectedProfile: "", -+ description: "container seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns error", -+ containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}}, -+ expectedError: "localhostProfile must be set if seccompProfile type is Localhost.", - }, - { - description: "container seccomp profile set to SeccompProfileTypeLocalhost returns 'localhost/' + LocalhostProfile", -@@ -418,8 +431,13 @@ func TestGetSeccompProfilePath(t *testing.T) { - } - - for i, test := range tests { -- seccompProfile := m.getSeccompProfilePath(test.annotation, test.containerName, test.podSc, test.containerSc, false) -- assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description) -+ seccompProfile, err := m.getSeccompProfilePath(test.annotation, test.containerName, test.podSc, test.containerSc, false) -+ if test.expectedError != "" { -+ assert.EqualError(t, err, test.expectedError, "TestCase[%d]: %s", i, test.description) -+ } else { -+ assert.NoError(t, err, "TestCase[%d]: %s", i, test.description) -+ assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description) -+ } - } - } - -@@ -434,6 +452,7 @@ func TestGetSeccompProfilePathDefaultSeccomp(t *testing.T) { - containerSc *v1.SecurityContext - containerName string - expectedProfile string -+ expectedError string - }{ - { - description: "no seccomp should return runtime/default", -@@ -470,14 +489,14 @@ func TestGetSeccompProfilePathDefaultSeccomp(t *testing.T) { - expectedProfile: seccompLocalhostPath("filename"), - }, - { -- description: "pod seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns runtime/default", -- podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}}, -- expectedProfile: v1.SeccompProfileRuntimeDefault, -+ description: "pod seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns error", -+ podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}}, -+ expectedError: "localhostProfile must be set if seccompProfile type is Localhost.", - }, - { -- description: "container seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns runtime/default", -- containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}}, -- expectedProfile: v1.SeccompProfileRuntimeDefault, -+ description: "container seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns error", -+ containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}}, -+ expectedError: "localhostProfile must be set if seccompProfile type is Localhost.", - }, - { - description: "container seccomp profile set to SeccompProfileTypeLocalhost returns 'localhost/' + LocalhostProfile", -@@ -493,8 +512,13 @@ func TestGetSeccompProfilePathDefaultSeccomp(t *testing.T) { - } - - for i, test := range tests { -- seccompProfile := m.getSeccompProfilePath(test.annotation, test.containerName, test.podSc, test.containerSc, true) -- assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description) -+ seccompProfile, err := m.getSeccompProfilePath(test.annotation, test.containerName, test.podSc, test.containerSc, true) -+ if test.expectedError != "" { -+ assert.EqualError(t, err, test.expectedError, "TestCase[%d]: %s", i, test.description) -+ } else { -+ assert.NoError(t, err, "TestCase[%d]: %s", i, test.description) -+ assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description) -+ } - } - } - -@@ -517,6 +541,7 @@ func TestGetSeccompProfile(t *testing.T) { - containerSc *v1.SecurityContext - containerName string - expectedProfile *runtimeapi.SecurityProfile -+ expectedError string - }{ - { - description: "no seccomp should return unconfined", -@@ -551,14 +576,14 @@ func TestGetSeccompProfile(t *testing.T) { - }, - }, - { -- description: "pod seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns unconfined", -- podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}}, -- expectedProfile: unconfinedProfile, -+ description: "pod seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns error", -+ podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}}, -+ expectedError: "localhostProfile must be set if seccompProfile type is Localhost.", - }, - { -- description: "container seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns unconfined", -- containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}}, -- expectedProfile: unconfinedProfile, -+ description: "container seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns error", -+ containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}}, -+ expectedError: "localhostProfile must be set if seccompProfile type is Localhost.", - }, - { - description: "container seccomp profile set to SeccompProfileTypeLocalhost returns 'localhost/' + LocalhostProfile", -@@ -587,8 +612,13 @@ func TestGetSeccompProfile(t *testing.T) { - } - - for i, test := range tests { -- seccompProfile := m.getSeccompProfile(test.annotation, test.containerName, test.podSc, test.containerSc, false) -- assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description) -+ seccompProfile, err := m.getSeccompProfile(test.annotation, test.containerName, test.podSc, test.containerSc, false) -+ if test.expectedError != "" { -+ assert.EqualError(t, err, test.expectedError, "TestCase[%d]: %s", i, test.description) -+ } else { -+ assert.NoError(t, err, "TestCase[%d]: %s", i, test.description) -+ assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description) -+ } - } - } - -@@ -611,6 +641,7 @@ func TestGetSeccompProfileDefaultSeccomp(t *testing.T) { - containerSc *v1.SecurityContext - containerName string - expectedProfile *runtimeapi.SecurityProfile -+ expectedError string - }{ - { - description: "no seccomp should return RuntimeDefault", -@@ -645,14 +676,14 @@ func TestGetSeccompProfileDefaultSeccomp(t *testing.T) { - }, - }, - { -- description: "pod seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns unconfined", -- podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}}, -- expectedProfile: unconfinedProfile, -+ description: "pod seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns error", -+ podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}}, -+ expectedError: "localhostProfile must be set if seccompProfile type is Localhost.", - }, - { -- description: "container seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns unconfined", -- containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}}, -- expectedProfile: unconfinedProfile, -+ description: "container seccomp profile set to SeccompProfileTypeLocalhost with empty LocalhostProfile returns error", -+ containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost}}, -+ expectedError: "localhostProfile must be set if seccompProfile type is Localhost.", - }, - { - description: "container seccomp profile set to SeccompProfileTypeLocalhost returns 'localhost/' + LocalhostProfile", -@@ -681,8 +712,13 @@ func TestGetSeccompProfileDefaultSeccomp(t *testing.T) { - } - - for i, test := range tests { -- seccompProfile := m.getSeccompProfile(test.annotation, test.containerName, test.podSc, test.containerSc, true) -- assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description) -+ seccompProfile, err := m.getSeccompProfile(test.annotation, test.containerName, test.podSc, test.containerSc, true) -+ if test.expectedError != "" { -+ assert.EqualError(t, err, test.expectedError, "TestCase[%d]: %s", i, test.description) -+ } else { -+ assert.NoError(t, err, "TestCase[%d]: %s", i, test.description) -+ assert.Equal(t, test.expectedProfile, seccompProfile, "TestCase[%d]: %s", i, test.description) -+ } - } - } - -diff --git a/pkg/kubelet/kuberuntime/security_context.go b/pkg/kubelet/kuberuntime/security_context.go -index 5e6f05b4e18..d933a710424 100644 ---- a/pkg/kubelet/kuberuntime/security_context.go -+++ b/pkg/kubelet/kuberuntime/security_context.go -@@ -37,9 +37,16 @@ func (m *kubeGenericRuntimeManager) determineEffectiveSecurityContext(pod *v1.Po - - // TODO: Deprecated, remove after we switch to Seccomp field - // set SeccompProfilePath. -- synthesized.SeccompProfilePath = m.getSeccompProfilePath(pod.Annotations, container.Name, pod.Spec.SecurityContext, container.SecurityContext, m.seccompDefault) -+ var err error -+ synthesized.SeccompProfilePath, err = m.getSeccompProfilePath(pod.Annotations, container.Name, pod.Spec.SecurityContext, container.SecurityContext, m.seccompDefault) -+ if err != nil { -+ return nil, err -+ } - -- synthesized.Seccomp = m.getSeccompProfile(pod.Annotations, container.Name, pod.Spec.SecurityContext, container.SecurityContext, m.seccompDefault) -+ synthesized.Seccomp, err = m.getSeccompProfile(pod.Annotations, container.Name, pod.Spec.SecurityContext, container.SecurityContext, m.seccompDefault) -+ if err != nil { -+ return nil, err -+ } - - // set ApparmorProfile. - synthesized.ApparmorProfile = apparmor.GetProfileNameFromPodAnnotations(pod.Annotations, container.Name) --- -2.25.1 - diff --git a/0005-Validate-etcd-paths.patch b/0005-Validate-etcd-paths.patch deleted file mode 100644 index c600d726ca1c6095080c389cac6bdec76edc46f0..0000000000000000000000000000000000000000 --- a/0005-Validate-etcd-paths.patch +++ /dev/null @@ -1,806 +0,0 @@ -From 6775c99cd008c457ce3eed401ac1c60c3812dbfa Mon Sep 17 00:00:00 2001 -From: Tim Allclair -Date: Mon, 10 Oct 2022 18:15:22 -0700 -Subject: [PATCH] Validate etcd paths - ---- - .../pkg/storage/etcd3/linearized_read_test.go | 5 +- - .../apiserver/pkg/storage/etcd3/store.go | 138 +++++++++++------ - .../apiserver/pkg/storage/etcd3/store_test.go | 140 ++++++++++++++---- - .../pkg/storage/testing/store_tests.go | 24 +-- - 4 files changed, 218 insertions(+), 89 deletions(-) - -diff --git a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/linearized_read_test.go b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/linearized_read_test.go -index bb1b9df7818..7331c8245ad 100644 ---- a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/linearized_read_test.go -+++ b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/linearized_read_test.go -@@ -37,7 +37,8 @@ func TestLinearizedReadRevisionInvariant(t *testing.T) { - // [1] https://etcd.io/docs/v3.5/learning/api_guarantees/#isolation-level-and-consistency-of-replicas - ctx, store, etcdClient := testSetup(t) - -- key := "/testkey" -+ dir := "/testing" -+ key := dir + "/testkey" - out := &example.Pod{} - obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo", SelfLink: "testlink"}} - -@@ -53,7 +54,7 @@ func TestLinearizedReadRevisionInvariant(t *testing.T) { - } - - list := &example.PodList{} -- if err := store.GetList(ctx, "/", storage.ListOptions{Predicate: storage.Everything, Recursive: true}, list); err != nil { -+ if err := store.GetList(ctx, dir, storage.ListOptions{Predicate: storage.Everything, Recursive: true}, list); err != nil { - t.Errorf("Unexpected List error: %v", err) - } - finalRevision := list.ResourceVersion -diff --git a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go -index 163eb111bcb..b2e4c674687 100644 ---- a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go -+++ b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store.go -@@ -98,16 +98,21 @@ func New(c *clientv3.Client, codec runtime.Codec, newFunc func() runtime.Object, - - func newStore(c *clientv3.Client, codec runtime.Codec, newFunc func() runtime.Object, prefix string, groupResource schema.GroupResource, transformer value.Transformer, pagingEnabled bool, leaseManagerConfig LeaseManagerConfig) *store { - versioner := storage.APIObjectVersioner{} -+ // for compatibility with etcd2 impl. -+ // no-op for default prefix of '/registry'. -+ // keeps compatibility with etcd2 impl for custom prefixes that don't start with '/' -+ pathPrefix := path.Join("/", prefix) -+ if !strings.HasSuffix(pathPrefix, "/") { -+ // Ensure the pathPrefix ends in "/" here to simplify key concatenation later. -+ pathPrefix += "/" -+ } - result := &store{ -- client: c, -- codec: codec, -- versioner: versioner, -- transformer: transformer, -- pagingEnabled: pagingEnabled, -- // for compatibility with etcd2 impl. -- // no-op for default prefix of '/registry'. -- // keeps compatibility with etcd2 impl for custom prefixes that don't start with '/' -- pathPrefix: path.Join("/", prefix), -+ client: c, -+ codec: codec, -+ versioner: versioner, -+ transformer: transformer, -+ pagingEnabled: pagingEnabled, -+ pathPrefix: pathPrefix, - groupResource: groupResource, - groupResourceString: groupResource.String(), - watcher: newWatcher(c, codec, newFunc, versioner, transformer), -@@ -123,9 +128,12 @@ func (s *store) Versioner() storage.Versioner { - - // Get implements storage.Interface.Get. - func (s *store) Get(ctx context.Context, key string, opts storage.GetOptions, out runtime.Object) error { -- key = path.Join(s.pathPrefix, key) -+ preparedKey, err := s.prepareKey(key) -+ if err != nil { -+ return err -+ } - startTime := time.Now() -- getResp, err := s.client.KV.Get(ctx, key) -+ getResp, err := s.client.KV.Get(ctx, preparedKey) - metrics.RecordEtcdRequestLatency("get", getTypeName(out), startTime) - if err != nil { - return err -@@ -138,11 +146,11 @@ func (s *store) Get(ctx context.Context, key string, opts storage.GetOptions, ou - if opts.IgnoreNotFound { - return runtime.SetZeroValue(out) - } -- return storage.NewKeyNotFoundError(key, 0) -+ return storage.NewKeyNotFoundError(preparedKey, 0) - } - kv := getResp.Kvs[0] - -- data, _, err := s.transformer.TransformFromStorage(ctx, kv.Value, authenticatedDataString(key)) -+ data, _, err := s.transformer.TransformFromStorage(ctx, kv.Value, authenticatedDataString(preparedKey)) - if err != nil { - return storage.NewInternalError(err.Error()) - } -@@ -152,6 +160,10 @@ func (s *store) Get(ctx context.Context, key string, opts storage.GetOptions, ou - - // Create implements storage.Interface.Create. - func (s *store) Create(ctx context.Context, key string, obj, out runtime.Object, ttl uint64) error { -+ preparedKey, err := s.prepareKey(key) -+ if err != nil { -+ return err -+ } - trace := utiltrace.New("Create etcd3", - utiltrace.Field{"audit-id", endpointsrequest.GetAuditIDTruncated(ctx)}, - utiltrace.Field{"key", key}, -@@ -170,14 +182,13 @@ func (s *store) Create(ctx context.Context, key string, obj, out runtime.Object, - if err != nil { - return err - } -- key = path.Join(s.pathPrefix, key) - - opts, err := s.ttlOpts(ctx, int64(ttl)) - if err != nil { - return err - } - -- newData, err := s.transformer.TransformToStorage(ctx, data, authenticatedDataString(key)) -+ newData, err := s.transformer.TransformToStorage(ctx, data, authenticatedDataString(preparedKey)) - trace.Step("TransformToStorage finished", utiltrace.Field{"err", err}) - if err != nil { - return storage.NewInternalError(err.Error()) -@@ -185,9 +196,9 @@ func (s *store) Create(ctx context.Context, key string, obj, out runtime.Object, - - startTime := time.Now() - txnResp, err := s.client.KV.Txn(ctx).If( -- notFound(key), -+ notFound(preparedKey), - ).Then( -- clientv3.OpPut(key, string(newData), opts...), -+ clientv3.OpPut(preparedKey, string(newData), opts...), - ).Commit() - metrics.RecordEtcdRequestLatency("create", getTypeName(obj), startTime) - trace.Step("Txn call finished", utiltrace.Field{"err", err}) -@@ -196,7 +207,7 @@ func (s *store) Create(ctx context.Context, key string, obj, out runtime.Object, - } - - if !txnResp.Succeeded { -- return storage.NewKeyExistsError(key, 0) -+ return storage.NewKeyExistsError(preparedKey, 0) - } - - if out != nil { -@@ -212,12 +223,15 @@ func (s *store) Create(ctx context.Context, key string, obj, out runtime.Object, - func (s *store) Delete( - ctx context.Context, key string, out runtime.Object, preconditions *storage.Preconditions, - validateDeletion storage.ValidateObjectFunc, cachedExistingObject runtime.Object) error { -+ preparedKey, err := s.prepareKey(key) -+ if err != nil { -+ return err -+ } - v, err := conversion.EnforcePtr(out) - if err != nil { - return fmt.Errorf("unable to convert output object to pointer: %v", err) - } -- key = path.Join(s.pathPrefix, key) -- return s.conditionalDelete(ctx, key, out, v, preconditions, validateDeletion, cachedExistingObject) -+ return s.conditionalDelete(ctx, preparedKey, out, v, preconditions, validateDeletion, cachedExistingObject) - } - - func (s *store) conditionalDelete( -@@ -330,6 +344,10 @@ func (s *store) conditionalDelete( - func (s *store) GuaranteedUpdate( - ctx context.Context, key string, destination runtime.Object, ignoreNotFound bool, - preconditions *storage.Preconditions, tryUpdate storage.UpdateFunc, cachedExistingObject runtime.Object) error { -+ preparedKey, err := s.prepareKey(key) -+ if err != nil { -+ return err -+ } - trace := utiltrace.New("GuaranteedUpdate etcd3", - utiltrace.Field{"audit-id", endpointsrequest.GetAuditIDTruncated(ctx)}, - utiltrace.Field{"key", key}, -@@ -340,16 +358,15 @@ func (s *store) GuaranteedUpdate( - if err != nil { - return fmt.Errorf("unable to convert output object to pointer: %v", err) - } -- key = path.Join(s.pathPrefix, key) - - getCurrentState := func() (*objState, error) { - startTime := time.Now() -- getResp, err := s.client.KV.Get(ctx, key) -+ getResp, err := s.client.KV.Get(ctx, preparedKey) - metrics.RecordEtcdRequestLatency("get", getTypeName(destination), startTime) - if err != nil { - return nil, err - } -- return s.getState(ctx, getResp, key, v, ignoreNotFound) -+ return s.getState(ctx, getResp, preparedKey, v, ignoreNotFound) - } - - var origState *objState -@@ -365,9 +382,9 @@ func (s *store) GuaranteedUpdate( - } - trace.Step("initial value restored") - -- transformContext := authenticatedDataString(key) -+ transformContext := authenticatedDataString(preparedKey) - for { -- if err := preconditions.Check(key, origState.obj); err != nil { -+ if err := preconditions.Check(preparedKey, origState.obj); err != nil { - // If our data is already up to date, return the error - if origStateIsCurrent { - return err -@@ -453,11 +470,11 @@ func (s *store) GuaranteedUpdate( - - startTime := time.Now() - txnResp, err := s.client.KV.Txn(ctx).If( -- clientv3.Compare(clientv3.ModRevision(key), "=", origState.rev), -+ clientv3.Compare(clientv3.ModRevision(preparedKey), "=", origState.rev), - ).Then( -- clientv3.OpPut(key, string(newData), opts...), -+ clientv3.OpPut(preparedKey, string(newData), opts...), - ).Else( -- clientv3.OpGet(key), -+ clientv3.OpGet(preparedKey), - ).Commit() - metrics.RecordEtcdRequestLatency("update", getTypeName(destination), startTime) - trace.Step("Txn call finished", utiltrace.Field{"err", err}) -@@ -467,8 +484,8 @@ func (s *store) GuaranteedUpdate( - trace.Step("Transaction committed") - if !txnResp.Succeeded { - getResp := (*clientv3.GetResponse)(txnResp.Responses[0].GetResponseRange()) -- klog.V(4).Infof("GuaranteedUpdate of %s failed because of a conflict, going to retry", key) -- origState, err = s.getState(ctx, getResp, key, v, ignoreNotFound) -+ klog.V(4).Infof("GuaranteedUpdate of %s failed because of a conflict, going to retry", preparedKey) -+ origState, err = s.getState(ctx, getResp, preparedKey, v, ignoreNotFound) - if err != nil { - return err - } -@@ -502,18 +519,21 @@ func getNewItemFunc(listObj runtime.Object, v reflect.Value) func() runtime.Obje - } - - func (s *store) Count(key string) (int64, error) { -- key = path.Join(s.pathPrefix, key) -+ preparedKey, err := s.prepareKey(key) -+ if err != nil { -+ return 0, err -+ } - - // We need to make sure the key ended with "/" so that we only get children "directories". - // e.g. if we have key "/a", "/a/b", "/ab", getting keys with prefix "/a" will return all three, - // while with prefix "/a/" will return only "/a/b" which is the correct answer. -- if !strings.HasSuffix(key, "/") { -- key += "/" -+ if !strings.HasSuffix(preparedKey, "/") { -+ preparedKey += "/" - } - - startTime := time.Now() -- getResp, err := s.client.KV.Get(context.Background(), key, clientv3.WithRange(clientv3.GetPrefixRangeEnd(key)), clientv3.WithCountOnly()) -- metrics.RecordEtcdRequestLatency("listWithCount", key, startTime) -+ getResp, err := s.client.KV.Get(context.Background(), preparedKey, clientv3.WithRange(clientv3.GetPrefixRangeEnd(preparedKey)), clientv3.WithCountOnly()) -+ metrics.RecordEtcdRequestLatency("listWithCount", preparedKey, startTime) - if err != nil { - return 0, err - } -@@ -522,6 +542,10 @@ func (s *store) Count(key string) (int64, error) { - - // GetList implements storage.Interface. - func (s *store) GetList(ctx context.Context, key string, opts storage.ListOptions, listObj runtime.Object) error { -+ preparedKey, err := s.prepareKey(key) -+ if err != nil { -+ return err -+ } - recursive := opts.Recursive - resourceVersion := opts.ResourceVersion - match := opts.ResourceVersionMatch -@@ -542,16 +566,15 @@ func (s *store) GetList(ctx context.Context, key string, opts storage.ListOption - if err != nil || v.Kind() != reflect.Slice { - return fmt.Errorf("need ptr to slice: %v", err) - } -- key = path.Join(s.pathPrefix, key) - - // For recursive lists, we need to make sure the key ended with "/" so that we only - // get children "directories". e.g. if we have key "/a", "/a/b", "/ab", getting keys - // with prefix "/a" will return all three, while with prefix "/a/" will return only - // "/a/b" which is the correct answer. -- if recursive && !strings.HasSuffix(key, "/") { -- key += "/" -+ if recursive && !strings.HasSuffix(preparedKey, "/") { -+ preparedKey += "/" - } -- keyPrefix := key -+ keyPrefix := preparedKey - - // set the appropriate clientv3 options to filter the returned data set - var limitOption *clientv3.OpOption -@@ -590,7 +613,7 @@ func (s *store) GetList(ctx context.Context, key string, opts storage.ListOption - - rangeEnd := clientv3.GetPrefixRangeEnd(keyPrefix) - options = append(options, clientv3.WithRange(rangeEnd)) -- key = continueKey -+ preparedKey = continueKey - - // If continueRV > 0, the LIST request needs a specific resource version. - // continueRV==0 is invalid. -@@ -657,7 +680,7 @@ func (s *store) GetList(ctx context.Context, key string, opts storage.ListOption - }() - for { - startTime := time.Now() -- getResp, err = s.client.KV.Get(ctx, key, options...) -+ getResp, err = s.client.KV.Get(ctx, preparedKey, options...) - if recursive { - metrics.RecordEtcdRequestLatency("list", getTypeName(listPtr), startTime) - } else { -@@ -729,7 +752,7 @@ func (s *store) GetList(ctx context.Context, key string, opts storage.ListOption - } - *limitOption = clientv3.WithLimit(limit) - } -- key = string(lastKey) + "\x00" -+ preparedKey = string(lastKey) + "\x00" - if withRev == 0 { - withRev = returnedRV - options = append(options, clientv3.WithRev(withRev)) -@@ -794,12 +817,15 @@ func growSlice(v reflect.Value, maxCapacity int, sizes ...int) { - - // Watch implements storage.Interface.Watch. - func (s *store) Watch(ctx context.Context, key string, opts storage.ListOptions) (watch.Interface, error) { -+ preparedKey, err := s.prepareKey(key) -+ if err != nil { -+ return nil, err -+ } - rev, err := s.versioner.ParseResourceVersion(opts.ResourceVersion) - if err != nil { - return nil, err - } -- key = path.Join(s.pathPrefix, key) -- return s.watcher.Watch(ctx, key, int64(rev), opts.Recursive, opts.ProgressNotify, opts.Predicate) -+ return s.watcher.Watch(ctx, preparedKey, int64(rev), opts.Recursive, opts.ProgressNotify, opts.Predicate) - } - - func (s *store) getState(ctx context.Context, getResp *clientv3.GetResponse, key string, v reflect.Value, ignoreNotFound bool) (*objState, error) { -@@ -911,6 +937,30 @@ func (s *store) validateMinimumResourceVersion(minimumResourceVersion string, ac - return nil - } - -+func (s *store) prepareKey(key string) (string, error) { -+ if key == ".." || -+ strings.HasPrefix(key, "../") || -+ strings.HasSuffix(key, "/..") || -+ strings.Contains(key, "/../") { -+ return "", fmt.Errorf("invalid key: %q", key) -+ } -+ if key == "." || -+ strings.HasPrefix(key, "./") || -+ strings.HasSuffix(key, "/.") || -+ strings.Contains(key, "/./") { -+ return "", fmt.Errorf("invalid key: %q", key) -+ } -+ if key == "" || key == "/" { -+ return "", fmt.Errorf("empty key: %q", key) -+ } -+ // We ensured that pathPrefix ends in '/' in construction, so skip any leading '/' in the key now. -+ startIndex := 0 -+ if key[0] == '/' { -+ startIndex = 1 -+ } -+ return s.pathPrefix + key[startIndex:], nil -+} -+ - // decode decodes value of bytes into object. It will also set the object resource version to rev. - // On success, objPtr would be set to the object. - func decode(codec runtime.Codec, versioner storage.Versioner, value []byte, objPtr runtime.Object, rev int64) error { -diff --git a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store_test.go b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store_test.go -index 84ee65a1fac..2f0dfe6e588 100644 ---- a/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store_test.go -+++ b/staging/src/k8s.io/apiserver/pkg/storage/etcd3/store_test.go -@@ -54,6 +54,7 @@ var scheme = runtime.NewScheme() - var codecs = serializer.NewCodecFactory(scheme) - - const defaultTestPrefix = "test!" -+const basePath = "/keybase" - - func init() { - metav1.AddToGroupVersion(scheme, metav1.SchemeGroupVersion) -@@ -446,13 +447,13 @@ func TestTransformationFailure(t *testing.T) { - obj *example.Pod - storedObj *example.Pod - }{{ -- key: "/one-level/test", -+ key: basePath + "/one-level/test", - obj: &example.Pod{ - ObjectMeta: metav1.ObjectMeta{Name: "bar"}, - Spec: storagetesting.DeepEqualSafePodSpec(), - }, - }, { -- key: "/two-level/1/test", -+ key: basePath + "/two-level/1/test", - obj: &example.Pod{ - ObjectMeta: metav1.ObjectMeta{Name: "baz"}, - Spec: storagetesting.DeepEqualSafePodSpec(), -@@ -484,7 +485,7 @@ func TestTransformationFailure(t *testing.T) { - Predicate: storage.Everything, - Recursive: true, - } -- if err := store.GetList(ctx, "/", storageOpts, &got); !storage.IsInternalError(err) { -+ if err := store.GetList(ctx, basePath, storageOpts, &got); !storage.IsInternalError(err) { - t.Errorf("Unexpected error %v", err) - } - -@@ -531,7 +532,7 @@ func TestListContinuation(t *testing.T) { - etcdClient.KV = recorder - - // Setup storage with the following structure: -- // / -+ // /keybase/ - // - one-level/ - // | - test - // | -@@ -548,15 +549,15 @@ func TestListContinuation(t *testing.T) { - storedObj *example.Pod - }{ - { -- key: "/one-level/test", -+ key: basePath + "/one-level/test", - obj: &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}, - }, - { -- key: "/two-level/1/test", -+ key: basePath + "/two-level/1/test", - obj: &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}, - }, - { -- key: "/two-level/2/test", -+ key: basePath + "/two-level/2/test", - obj: &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar"}}, - }, - } -@@ -588,7 +589,7 @@ func TestListContinuation(t *testing.T) { - Predicate: pred(1, ""), - Recursive: true, - } -- if err := store.GetList(ctx, "/", options, out); err != nil { -+ if err := store.GetList(ctx, basePath, options, out); err != nil { - t.Fatalf("Unable to get initial list: %v", err) - } - if len(out.Continue) == 0 { -@@ -613,13 +614,13 @@ func TestListContinuation(t *testing.T) { - Predicate: pred(0, continueFromSecondItem), - Recursive: true, - } -- if err := store.GetList(ctx, "/", options, out); err != nil { -+ if err := store.GetList(ctx, basePath, options, out); err != nil { - t.Fatalf("Unable to get second page: %v", err) - } - if len(out.Continue) != 0 { - t.Fatalf("Unexpected continuation token set") - } -- key, rv, err := storage.DecodeContinue(continueFromSecondItem, "/") -+ key, rv, err := storage.DecodeContinue(continueFromSecondItem, basePath) - t.Logf("continue token was %d %s %v", rv, key, err) - storagetesting.ExpectNoDiff(t, "incorrect second page", []example.Pod{*preset[1].storedObj, *preset[2].storedObj}, out.Items) - if transformer.reads != 2 { -@@ -638,7 +639,7 @@ func TestListContinuation(t *testing.T) { - Predicate: pred(1, continueFromSecondItem), - Recursive: true, - } -- if err := store.GetList(ctx, "/", options, out); err != nil { -+ if err := store.GetList(ctx, basePath, options, out); err != nil { - t.Fatalf("Unable to get second page: %v", err) - } - if len(out.Continue) == 0 { -@@ -662,7 +663,7 @@ func TestListContinuation(t *testing.T) { - Predicate: pred(1, continueFromThirdItem), - Recursive: true, - } -- if err := store.GetList(ctx, "/", options, out); err != nil { -+ if err := store.GetList(ctx, basePath, options, out); err != nil { - t.Fatalf("Unable to get second page: %v", err) - } - if len(out.Continue) != 0 { -@@ -688,7 +689,7 @@ func TestListPaginationRareObject(t *testing.T) { - podCount := 1000 - var pods []*example.Pod - for i := 0; i < podCount; i++ { -- key := fmt.Sprintf("/one-level/pod-%d", i) -+ key := basePath + fmt.Sprintf("/one-level/pod-%d", i) - obj := &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: fmt.Sprintf("pod-%d", i)}} - storedObj := &example.Pod{} - err := store.Create(ctx, key, obj, storedObj, 0) -@@ -711,7 +712,7 @@ func TestListPaginationRareObject(t *testing.T) { - }, - Recursive: true, - } -- if err := store.GetList(ctx, "/", options, out); err != nil { -+ if err := store.GetList(ctx, basePath, options, out); err != nil { - t.Fatalf("Unable to get initial list: %v", err) - } - if len(out.Continue) != 0 { -@@ -782,7 +783,7 @@ func TestListContinuationWithFilter(t *testing.T) { - - for i, ps := range preset { - preset[i].storedObj = &example.Pod{} -- err := store.Create(ctx, ps.key, ps.obj, preset[i].storedObj, 0) -+ err := store.Create(ctx, basePath+ps.key, ps.obj, preset[i].storedObj, 0) - if err != nil { - t.Fatalf("Set failed: %v", err) - } -@@ -810,7 +811,7 @@ func TestListContinuationWithFilter(t *testing.T) { - Predicate: pred(2, ""), - Recursive: true, - } -- if err := store.GetList(ctx, "/", options, out); err != nil { -+ if err := store.GetList(ctx, basePath, options, out); err != nil { - t.Errorf("Unable to get initial list: %v", err) - } - if len(out.Continue) == 0 { -@@ -842,7 +843,7 @@ func TestListContinuationWithFilter(t *testing.T) { - Predicate: pred(2, cont), - Recursive: true, - } -- if err := store.GetList(ctx, "/", options, out); err != nil { -+ if err := store.GetList(ctx, basePath, options, out); err != nil { - t.Errorf("Unable to get second page: %v", err) - } - if len(out.Continue) != 0 { -@@ -863,7 +864,7 @@ func TestListInconsistentContinuation(t *testing.T) { - ctx, store, client := testSetup(t) - - // Setup storage with the following structure: -- // / -+ // /keybase/ - // - one-level/ - // | - test - // | -@@ -880,15 +881,15 @@ func TestListInconsistentContinuation(t *testing.T) { - storedObj *example.Pod - }{ - { -- key: "/one-level/test", -+ key: basePath + "/one-level/test", - obj: &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}, - }, - { -- key: "/two-level/1/test", -+ key: basePath + "/two-level/1/test", - obj: &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}, - }, - { -- key: "/two-level/2/test", -+ key: basePath + "/two-level/2/test", - obj: &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar"}}, - }, - } -@@ -920,7 +921,7 @@ func TestListInconsistentContinuation(t *testing.T) { - Predicate: pred(1, ""), - Recursive: true, - } -- if err := store.GetList(ctx, "/", options, out); err != nil { -+ if err := store.GetList(ctx, basePath, options, out); err != nil { - t.Fatalf("Unable to get initial list: %v", err) - } - if len(out.Continue) == 0 { -@@ -964,7 +965,7 @@ func TestListInconsistentContinuation(t *testing.T) { - Predicate: pred(0, continueFromSecondItem), - Recursive: true, - } -- err = store.GetList(ctx, "/", options, out) -+ err = store.GetList(ctx, basePath, options, out) - if err == nil { - t.Fatalf("unexpected no error") - } -@@ -986,7 +987,7 @@ func TestListInconsistentContinuation(t *testing.T) { - Predicate: pred(1, inconsistentContinueFromSecondItem), - Recursive: true, - } -- if err := store.GetList(ctx, "/", options, out); err != nil { -+ if err := store.GetList(ctx, basePath, options, out); err != nil { - t.Fatalf("Unable to get second page: %v", err) - } - if len(out.Continue) == 0 { -@@ -1005,7 +1006,7 @@ func TestListInconsistentContinuation(t *testing.T) { - Predicate: pred(1, continueFromThirdItem), - Recursive: true, - } -- if err := store.GetList(ctx, "/", options, out); err != nil { -+ if err := store.GetList(ctx, basePath, options, out); err != nil { - t.Fatalf("Unable to get second page: %v", err) - } - if len(out.Continue) != 0 { -@@ -1127,9 +1128,9 @@ func testSetup(t *testing.T, opts ...setupOption) (context.Context, *store, *cli - - func TestPrefix(t *testing.T) { - testcases := map[string]string{ -- "custom/prefix": "/custom/prefix", -- "/custom//prefix//": "/custom/prefix", -- "/registry": "/registry", -+ "custom/prefix": "/custom/prefix/", -+ "/custom//prefix//": "/custom/prefix/", -+ "/registry": "/registry/", - } - for configuredPrefix, effectivePrefix := range testcases { - _, store, _ := testSetup(t, withPrefix(configuredPrefix)) -@@ -1302,7 +1303,7 @@ func TestConsistentList(t *testing.T) { - Predicate: predicate, - Recursive: true, - } -- if err := store.GetList(ctx, "/", options, &result1); err != nil { -+ if err := store.GetList(ctx, basePath, options, &result1); err != nil { - t.Fatalf("failed to list objects: %v", err) - } - -@@ -1315,7 +1316,7 @@ func TestConsistentList(t *testing.T) { - } - - result2 := example.PodList{} -- if err := store.GetList(ctx, "/", options, &result2); err != nil { -+ if err := store.GetList(ctx, basePath, options, &result2); err != nil { - t.Fatalf("failed to list objects: %v", err) - } - -@@ -1325,7 +1326,7 @@ func TestConsistentList(t *testing.T) { - options.ResourceVersionMatch = metav1.ResourceVersionMatchNotOlderThan - - result3 := example.PodList{} -- if err := store.GetList(ctx, "/", options, &result3); err != nil { -+ if err := store.GetList(ctx, basePath, options, &result3); err != nil { - t.Fatalf("failed to list objects: %v", err) - } - -@@ -1333,7 +1334,7 @@ func TestConsistentList(t *testing.T) { - options.ResourceVersionMatch = metav1.ResourceVersionMatchExact - - result4 := example.PodList{} -- if err := store.GetList(ctx, "/", options, &result4); err != nil { -+ if err := store.GetList(ctx, basePath, options, &result4); err != nil { - t.Fatalf("failed to list objects: %v", err) - } - -@@ -1384,3 +1385,78 @@ func TestLeaseMaxObjectCount(t *testing.T) { - } - } - } -+ -+func TestValidateKey(t *testing.T) { -+ validKeys := []string{ -+ "/foo/bar/baz/a.b.c/", -+ "/foo", -+ "foo/bar/baz", -+ "/foo/bar..baz/", -+ "/foo/bar..", -+ "foo", -+ "foo/bar", -+ "/foo/bar/", -+ } -+ invalidKeys := []string{ -+ "/foo/bar/../a.b.c/", -+ "..", -+ "/..", -+ "../", -+ "/foo/bar/..", -+ "../foo/bar", -+ "/../foo", -+ "/foo/bar/../", -+ ".", -+ "/.", -+ "./", -+ "/./", -+ "/foo/.", -+ "./bar", -+ "/foo/./bar/", -+ } -+ const ( -+ pathPrefix = "/first/second" -+ expectPrefix = pathPrefix + "/" -+ ) -+ _, store, _ := testSetup(t, withPrefix(pathPrefix)) -+ -+ for _, key := range validKeys { -+ k, err := store.prepareKey(key) -+ if err != nil { -+ t.Errorf("key %q should be valid; unexpected error: %v", key, err) -+ } else if !strings.HasPrefix(k, expectPrefix) { -+ t.Errorf("key %q should have prefix %q", k, expectPrefix) -+ } -+ } -+ -+ for _, key := range invalidKeys { -+ _, err := store.prepareKey(key) -+ if err == nil { -+ t.Errorf("key %q should be invalid", key) -+ } -+ } -+} -+ -+func TestInvalidKeys(t *testing.T) { -+ const invalidKey = "/foo/bar/../baz" -+ expectedError := fmt.Sprintf("invalid key: %q", invalidKey) -+ -+ expectInvalidKey := func(methodName string, err error) { -+ if err == nil { -+ t.Errorf("[%s] expected invalid key error; got nil", methodName) -+ } else if err.Error() != expectedError { -+ t.Errorf("[%s] expected invalid key error; got %v", methodName, err) -+ } -+ } -+ -+ ctx, store, _ := testSetup(t) -+ expectInvalidKey("Create", store.Create(ctx, invalidKey, nil, nil, 0)) -+ expectInvalidKey("Delete", store.Delete(ctx, invalidKey, nil, nil, nil, nil)) -+ _, watchErr := store.Watch(ctx, invalidKey, storage.ListOptions{}) -+ expectInvalidKey("Watch", watchErr) -+ expectInvalidKey("Get", store.Get(ctx, invalidKey, storage.GetOptions{}, nil)) -+ expectInvalidKey("GetList", store.GetList(ctx, invalidKey, storage.ListOptions{}, nil)) -+ expectInvalidKey("GuaranteedUpdate", store.GuaranteedUpdate(ctx, invalidKey, nil, true, nil, nil, nil)) -+ _, countErr := store.Count(invalidKey) -+ expectInvalidKey("Count", countErr) -+} -diff --git a/staging/src/k8s.io/apiserver/pkg/storage/testing/store_tests.go b/staging/src/k8s.io/apiserver/pkg/storage/testing/store_tests.go -index c83ee833ee6..df7078f6bd9 100644 ---- a/staging/src/k8s.io/apiserver/pkg/storage/testing/store_tests.go -+++ b/staging/src/k8s.io/apiserver/pkg/storage/testing/store_tests.go -@@ -42,6 +42,8 @@ import ( - - type KeyValidation func(ctx context.Context, t *testing.T, key string) - -+const basePath = "/keybase" -+ - func RunTestCreate(ctx context.Context, t *testing.T, store storage.Interface, validation KeyValidation) { - key := "/testkey" - out := &example.Pod{} -@@ -445,11 +447,11 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { - Predicate: storage.Everything, - Recursive: true, - } -- if err := store.GetList(ctx, "/two-level", storageOpts, list); err != nil { -+ if err := store.GetList(ctx, basePath+"/two-level", storageOpts, list); err != nil { - t.Errorf("Unexpected error: %v", err) - } - continueRV, _ := strconv.Atoi(list.ResourceVersion) -- secondContinuation, err := storage.EncodeContinue("/two-level/2", "/two-level/", int64(continueRV)) -+ secondContinuation, err := storage.EncodeContinue(basePath+"/two-level/2", basePath+"/two-level/", int64(continueRV)) - if err != nil { - t.Fatal(err) - } -@@ -827,7 +829,7 @@ func RunTestList(ctx context.Context, t *testing.T, store storage.Interface) { - Predicate: tt.pred, - Recursive: true, - } -- err = store.GetList(ctx, tt.prefix, storageOpts, out) -+ err = store.GetList(ctx, basePath+tt.prefix, storageOpts, out) - if tt.expectRVTooLarge { - if err == nil || !storage.IsTooLargeResourceVersion(err) { - t.Fatalf("expecting resource version too high error, but get: %s", err) -@@ -926,7 +928,7 @@ func RunTestListWithoutPaging(ctx context.Context, t *testing.T, store storage.I - Recursive: true, - } - -- if err := store.GetList(ctx, tt.prefix, storageOpts, out); err != nil { -+ if err := store.GetList(ctx, basePath+tt.prefix, storageOpts, out); err != nil { - t.Fatalf("GetList failed: %v", err) - return - } -@@ -952,7 +954,7 @@ func RunTestListWithoutPaging(ctx context.Context, t *testing.T, store storage.I - // from before any were created along with the full set of objects that were persisted - func seedMultiLevelData(ctx context.Context, store storage.Interface) (string, []*example.Pod, error) { - // Setup storage with the following structure: -- // / -+ // /keybase/ - // - one-level/ - // | - test - // | -@@ -975,30 +977,30 @@ func seedMultiLevelData(ctx context.Context, store storage.Interface) (string, [ - storedObj *example.Pod - }{ - { -- key: "/one-level/test", -+ key: basePath + "/one-level/test", - obj: &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}, - }, - { -- key: "/two-level/1/test", -+ key: basePath + "/two-level/1/test", - obj: &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "foo"}}, - }, - { -- key: "/two-level/2/test", -+ key: basePath + "/two-level/2/test", - obj: &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar"}}, - }, - { -- key: "/z-level/3/test", -+ key: basePath + "/z-level/3/test", - obj: &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "fourth"}}, - }, - { -- key: "/z-level/3/test-2", -+ key: basePath + "/z-level/3/test-2", - obj: &example.Pod{ObjectMeta: metav1.ObjectMeta{Name: "bar"}}, - }, - } - - // we want to figure out the resourceVersion before we create anything - initialList := &example.PodList{} -- if err := store.GetList(ctx, "/", storage.ListOptions{Predicate: storage.Everything, Recursive: true}, initialList); err != nil { -+ if err := store.GetList(ctx, basePath, storage.ListOptions{Predicate: storage.Everything, Recursive: true}, initialList); err != nil { - return "", nil, fmt.Errorf("failed to determine starting resourceVersion: %w", err) - } - initialRV := initialList.ResourceVersion --- -2.25.1 - diff --git a/0006-fix-node-address-validation.patch b/0006-fix-node-address-validation.patch deleted file mode 100644 index 540447ecdfc13fdf665afa47938e39d48b70d73d..0000000000000000000000000000000000000000 --- a/0006-fix-node-address-validation.patch +++ /dev/null @@ -1,245 +0,0 @@ -From f85027fcc12d02f79a0649115bdbaf7c9d8aab17 Mon Sep 17 00:00:00 2001 -From: Andrew Sy Kim -Date: Mon, 7 Nov 2022 10:22:44 -0500 -Subject: [PATCH] fix node address validation - -Signed-off-by: Andrew Sy Kim ---- - .../core/node/storage/storage_test.go | 171 +++++++++++++++++- - pkg/registry/core/node/strategy.go | 8 +- - 2 files changed, 174 insertions(+), 5 deletions(-) - -diff --git a/pkg/registry/core/node/storage/storage_test.go b/pkg/registry/core/node/storage/storage_test.go -index af228b8a43a..89df2edf16b 100644 ---- a/pkg/registry/core/node/storage/storage_test.go -+++ b/pkg/registry/core/node/storage/storage_test.go -@@ -17,6 +17,7 @@ limitations under the License. - package storage - - import ( -+ "fmt" - "testing" - - "k8s.io/apimachinery/pkg/api/resource" -@@ -24,11 +25,14 @@ import ( - "k8s.io/apimachinery/pkg/fields" - "k8s.io/apimachinery/pkg/labels" - "k8s.io/apimachinery/pkg/runtime" -+ genericapirequest "k8s.io/apiserver/pkg/endpoints/request" - "k8s.io/apiserver/pkg/registry/generic" - genericregistrytest "k8s.io/apiserver/pkg/registry/generic/testing" -+ "k8s.io/apiserver/pkg/registry/rest" - etcd3testing "k8s.io/apiserver/pkg/storage/etcd3/testing" - api "k8s.io/kubernetes/pkg/apis/core" - kubeletclient "k8s.io/kubernetes/pkg/kubelet/client" -+ proxyutil "k8s.io/kubernetes/pkg/proxy/util" - "k8s.io/kubernetes/pkg/registry/registrytest" - ) - -@@ -40,7 +44,10 @@ func newStorage(t *testing.T) (*REST, *etcd3testing.EtcdTestServer) { - DeleteCollectionWorkers: 1, - ResourcePrefix: "nodes", - } -- storage, err := NewStorage(restOptions, kubeletclient.KubeletClientConfig{}, nil) -+ storage, err := NewStorage(restOptions, kubeletclient.KubeletClientConfig{ -+ Port: 10250, -+ PreferredAddressTypes: []string{string(api.NodeInternalIP)}, -+ }, nil) - if err != nil { - t.Fatal(err) - } -@@ -156,3 +163,165 @@ func TestShortNames(t *testing.T) { - expected := []string{"no"} - registrytest.AssertShortNames(t, storage, expected) - } -+ -+func TestResourceLocation(t *testing.T) { -+ testCases := []struct { -+ name string -+ node api.Node -+ query string -+ location string -+ err error -+ }{ -+ { -+ name: "proxyable hostname with default port", -+ node: api.Node{ -+ ObjectMeta: metav1.ObjectMeta{Name: "node0"}, -+ Status: api.NodeStatus{ -+ Addresses: []api.NodeAddress{ -+ { -+ Type: api.NodeInternalIP, -+ Address: "10.0.0.1", -+ }, -+ }, -+ }, -+ }, -+ query: "node0", -+ location: "10.0.0.1:10250", -+ err: nil, -+ }, -+ { -+ name: "proxyable hostname with kubelet port in query", -+ node: api.Node{ -+ ObjectMeta: metav1.ObjectMeta{Name: "node0"}, -+ Status: api.NodeStatus{ -+ Addresses: []api.NodeAddress{ -+ { -+ Type: api.NodeInternalIP, -+ Address: "10.0.0.1", -+ }, -+ }, -+ }, -+ }, -+ query: "node0:5000", -+ location: "10.0.0.1:5000", -+ err: nil, -+ }, -+ { -+ name: "proxyable hostname with kubelet port in status", -+ node: api.Node{ -+ ObjectMeta: metav1.ObjectMeta{Name: "node0"}, -+ Status: api.NodeStatus{ -+ Addresses: []api.NodeAddress{ -+ { -+ Type: api.NodeInternalIP, -+ Address: "10.0.0.1", -+ }, -+ }, -+ DaemonEndpoints: api.NodeDaemonEndpoints{ -+ KubeletEndpoint: api.DaemonEndpoint{ -+ Port: 5000, -+ }, -+ }, -+ }, -+ }, -+ query: "node0", -+ location: "10.0.0.1:5000", -+ err: nil, -+ }, -+ { -+ name: "non-proxyable hostname with default port", -+ node: api.Node{ -+ ObjectMeta: metav1.ObjectMeta{Name: "node0"}, -+ Status: api.NodeStatus{ -+ Addresses: []api.NodeAddress{ -+ { -+ Type: api.NodeInternalIP, -+ Address: "127.0.0.1", -+ }, -+ }, -+ }, -+ }, -+ query: "node0", -+ location: "", -+ err: proxyutil.ErrAddressNotAllowed, -+ }, -+ { -+ name: "non-proxyable hostname with kubelet port in query", -+ node: api.Node{ -+ ObjectMeta: metav1.ObjectMeta{Name: "node0"}, -+ Status: api.NodeStatus{ -+ Addresses: []api.NodeAddress{ -+ { -+ Type: api.NodeInternalIP, -+ Address: "127.0.0.1", -+ }, -+ }, -+ }, -+ }, -+ query: "node0:5000", -+ location: "", -+ err: proxyutil.ErrAddressNotAllowed, -+ }, -+ { -+ name: "non-proxyable hostname with kubelet port in status", -+ node: api.Node{ -+ ObjectMeta: metav1.ObjectMeta{Name: "node0"}, -+ Status: api.NodeStatus{ -+ Addresses: []api.NodeAddress{ -+ { -+ Type: api.NodeInternalIP, -+ Address: "127.0.0.1", -+ }, -+ }, -+ DaemonEndpoints: api.NodeDaemonEndpoints{ -+ KubeletEndpoint: api.DaemonEndpoint{ -+ Port: 443, -+ }, -+ }, -+ }, -+ }, -+ query: "node0", -+ location: "", -+ err: proxyutil.ErrAddressNotAllowed, -+ }, -+ } -+ -+ for _, testCase := range testCases { -+ t.Run(testCase.name, func(t *testing.T) { -+ storage, server := newStorage(t) -+ defer server.Terminate(t) -+ defer storage.Store.DestroyFunc() -+ -+ ctx := genericapirequest.WithNamespace(genericapirequest.NewDefaultContext(), fmt.Sprintf("namespace-%s", testCase.name)) -+ key, _ := storage.KeyFunc(ctx, testCase.node.Name) -+ if err := storage.Storage.Create(ctx, key, &testCase.node, nil, 0, false); err != nil { -+ t.Fatalf("unexpected error: %v", err) -+ } -+ -+ redirector := rest.Redirector(storage) -+ location, _, err := redirector.ResourceLocation(ctx, testCase.query) -+ -+ if err != nil && testCase.err != nil { -+ if err.Error() != testCase.err.Error() { -+ t.Fatalf("Unexpected error: %v, expected: %v", err, testCase.err) -+ } -+ -+ return -+ } -+ -+ if err != nil && testCase.err == nil { -+ t.Fatalf("Unexpected error: %v, expected: %v", err, testCase.err) -+ } else if err == nil && testCase.err != nil { -+ t.Fatalf("Expected error but got none, err: %v", testCase.err) -+ } -+ -+ if location == nil { -+ t.Errorf("Unexpected nil resource location: %v", location) -+ } -+ -+ if location.Host != testCase.location { -+ t.Errorf("Unexpected host: expected %v, but got %v", testCase.location, location.Host) -+ } -+ }) -+ } -+} -diff --git a/pkg/registry/core/node/strategy.go b/pkg/registry/core/node/strategy.go -index bf6b6a57708..f879f327279 100644 ---- a/pkg/registry/core/node/strategy.go -+++ b/pkg/registry/core/node/strategy.go -@@ -249,6 +249,10 @@ func ResourceLocation(getter ResourceGetter, connection client.ConnectionInfoGet - return nil, nil, err - } - -+ if err := proxyutil.IsProxyableHostname(ctx, &net.Resolver{}, info.Hostname); err != nil { -+ return nil, nil, errors.NewBadRequest(err.Error()) -+ } -+ - // We check if we want to get a default Kubelet's transport. It happens if either: - // - no port is specified in request (Kubelet's port is default) - // - the requested port matches the kubelet port for this node -@@ -261,10 +265,6 @@ func ResourceLocation(getter ResourceGetter, connection client.ConnectionInfoGet - nil - } - -- if err := proxyutil.IsProxyableHostname(ctx, &net.Resolver{}, info.Hostname); err != nil { -- return nil, nil, errors.NewBadRequest(err.Error()) -- } -- - // Otherwise, return the requested scheme and port, and the proxy transport - return &url.URL{Scheme: schemeReq, Host: net.JoinHostPort(info.Hostname, portReq)}, proxyTransport, nil - } --- -2.25.1 - diff --git a/0007-Add-ephemeralcontainer-to-imagepolicy-securityaccoun.patch b/0007-Add-ephemeralcontainer-to-imagepolicy-securityaccoun.patch deleted file mode 100644 index f781cb3866a71fa2a4b636a80fc86e72fe193188..0000000000000000000000000000000000000000 --- a/0007-Add-ephemeralcontainer-to-imagepolicy-securityaccoun.patch +++ /dev/null @@ -1,554 +0,0 @@ -From 8781d503a08ff8a1a29179c80252470ac77b3bd5 Mon Sep 17 00:00:00 2001 -From: Rita Zhang -Date: Tue, 30 May 2023 20:35:33 +0000 -Subject: [PATCH] Add ephemeralcontainer to imagepolicy securityaccount - admission plugin - -Signed-off-by: Rita Zhang ---- - plugin/pkg/admission/imagepolicy/admission.go | 28 ++-- - .../admission/imagepolicy/admission_test.go | 148 +++++++++++++++++- - .../pkg/admission/serviceaccount/admission.go | 57 ++++++- - .../serviceaccount/admission_test.go | 88 +++++++++++ - 4 files changed, 297 insertions(+), 24 deletions(-) - -diff --git a/plugin/pkg/admission/imagepolicy/admission.go b/plugin/pkg/admission/imagepolicy/admission.go -index 6fd7f0dfad7..f1f88fef3b0 100644 ---- a/plugin/pkg/admission/imagepolicy/admission.go -+++ b/plugin/pkg/admission/imagepolicy/admission.go -@@ -46,6 +46,7 @@ import ( - - // PluginName indicates name of admission plugin. - const PluginName = "ImagePolicyWebhook" -+const ephemeralcontainers = "ephemeralcontainers" - - // AuditKeyPrefix is used as the prefix for all audit keys handled by this - // pluggin. Some well known suffixes are listed below. -@@ -132,8 +133,9 @@ func (a *Plugin) webhookError(pod *api.Pod, attributes admission.Attributes, err - - // Validate makes an admission decision based on the request attributes - func (a *Plugin) Validate(ctx context.Context, attributes admission.Attributes, o admission.ObjectInterfaces) (err error) { -- // Ignore all calls to subresources or resources other than pods. -- if attributes.GetSubresource() != "" || attributes.GetResource().GroupResource() != api.Resource("pods") { -+ // Ignore all calls to subresources other than ephemeralcontainers or calls to resources other than pods. -+ subresource := attributes.GetSubresource() -+ if (subresource != "" && subresource != ephemeralcontainers) || attributes.GetResource().GroupResource() != api.Resource("pods") { - return nil - } - -@@ -144,13 +146,21 @@ func (a *Plugin) Validate(ctx context.Context, attributes admission.Attributes, - - // Build list of ImageReviewContainerSpec - var imageReviewContainerSpecs []v1alpha1.ImageReviewContainerSpec -- containers := make([]api.Container, 0, len(pod.Spec.Containers)+len(pod.Spec.InitContainers)) -- containers = append(containers, pod.Spec.Containers...) -- containers = append(containers, pod.Spec.InitContainers...) -- for _, c := range containers { -- imageReviewContainerSpecs = append(imageReviewContainerSpecs, v1alpha1.ImageReviewContainerSpec{ -- Image: c.Image, -- }) -+ if subresource == "" { -+ containers := make([]api.Container, 0, len(pod.Spec.Containers)+len(pod.Spec.InitContainers)) -+ containers = append(containers, pod.Spec.Containers...) -+ containers = append(containers, pod.Spec.InitContainers...) -+ for _, c := range containers { -+ imageReviewContainerSpecs = append(imageReviewContainerSpecs, v1alpha1.ImageReviewContainerSpec{ -+ Image: c.Image, -+ }) -+ } -+ } else if subresource == ephemeralcontainers { -+ for _, c := range pod.Spec.EphemeralContainers { -+ imageReviewContainerSpecs = append(imageReviewContainerSpecs, v1alpha1.ImageReviewContainerSpec{ -+ Image: c.Image, -+ }) -+ } - } - imageReview := v1alpha1.ImageReview{ - Spec: v1alpha1.ImageReviewSpec{ -diff --git a/plugin/pkg/admission/imagepolicy/admission_test.go b/plugin/pkg/admission/imagepolicy/admission_test.go -index d1f81d51950..a9188462fb9 100644 ---- a/plugin/pkg/admission/imagepolicy/admission_test.go -+++ b/plugin/pkg/admission/imagepolicy/admission_test.go -@@ -37,7 +37,6 @@ import ( - api "k8s.io/kubernetes/pkg/apis/core" - - "fmt" -- "io/ioutil" - "os" - "path/filepath" - "text/template" -@@ -67,7 +66,7 @@ imagePolicy: - ` - - func TestNewFromConfig(t *testing.T) { -- dir, err := ioutil.TempDir("", "") -+ dir, err := os.MkdirTemp("", "") - if err != nil { - t.Fatal(err) - } -@@ -92,7 +91,7 @@ func TestNewFromConfig(t *testing.T) { - {data.Key, clientKey}, - } - for _, file := range files { -- if err := ioutil.WriteFile(file.name, file.data, 0400); err != nil { -+ if err := os.WriteFile(file.name, file.data, 0400); err != nil { - t.Fatal(err) - } - } -@@ -196,7 +195,7 @@ current-context: default - // Use a closure so defer statements trigger between loop iterations. - t.Run(tt.msg, func(t *testing.T) { - err := func() error { -- tempfile, err := ioutil.TempFile("", "") -+ tempfile, err := os.CreateTemp("", "") - if err != nil { - return err - } -@@ -211,7 +210,7 @@ current-context: default - return fmt.Errorf("failed to execute test template: %v", err) - } - -- tempconfigfile, err := ioutil.TempFile("", "") -+ tempconfigfile, err := os.CreateTemp("", "") - if err != nil { - return err - } -@@ -359,7 +358,7 @@ func (m *mockService) HTTPStatusCode() int { return m.statusCode } - // newImagePolicyWebhook creates a temporary kubeconfig file from the provided arguments and attempts to load - // a new newImagePolicyWebhook from it. - func newImagePolicyWebhook(callbackURL string, clientCert, clientKey, ca []byte, cacheTime time.Duration, defaultAllow bool) (*Plugin, error) { -- tempfile, err := ioutil.TempFile("", "") -+ tempfile, err := os.CreateTemp("", "") - if err != nil { - return nil, err - } -@@ -381,7 +380,7 @@ func newImagePolicyWebhook(callbackURL string, clientCert, clientKey, ca []byte, - return nil, err - } - -- tempconfigfile, err := ioutil.TempFile("", "") -+ tempconfigfile, err := os.CreateTemp("", "") - if err != nil { - return nil, err - } -@@ -595,17 +594,23 @@ func TestContainerCombinations(t *testing.T) { - test string - pod *api.Pod - wantAllowed, wantErr bool -+ subresource string -+ operation admission.Operation - }{ - { - test: "Single container allowed", - pod: goodPod("good"), - wantAllowed: true, -+ subresource: "", -+ operation: admission.Create, - }, - { - test: "Single container denied", - pod: goodPod("bad"), - wantAllowed: false, - wantErr: true, -+ subresource: "", -+ operation: admission.Create, - }, - { - test: "One good container, one bad", -@@ -627,6 +632,8 @@ func TestContainerCombinations(t *testing.T) { - }, - wantAllowed: false, - wantErr: true, -+ subresource: "", -+ operation: admission.Create, - }, - { - test: "Multiple good containers", -@@ -648,6 +655,8 @@ func TestContainerCombinations(t *testing.T) { - }, - wantAllowed: true, - wantErr: false, -+ subresource: "", -+ operation: admission.Create, - }, - { - test: "Multiple bad containers", -@@ -669,6 +678,8 @@ func TestContainerCombinations(t *testing.T) { - }, - wantAllowed: false, - wantErr: true, -+ subresource: "", -+ operation: admission.Create, - }, - { - test: "Good container, bad init container", -@@ -692,6 +703,8 @@ func TestContainerCombinations(t *testing.T) { - }, - wantAllowed: false, - wantErr: true, -+ subresource: "", -+ operation: admission.Create, - }, - { - test: "Bad container, good init container", -@@ -715,6 +728,8 @@ func TestContainerCombinations(t *testing.T) { - }, - wantAllowed: false, - wantErr: true, -+ subresource: "", -+ operation: admission.Create, - }, - { - test: "Good container, good init container", -@@ -738,6 +753,123 @@ func TestContainerCombinations(t *testing.T) { - }, - wantAllowed: true, - wantErr: false, -+ subresource: "", -+ operation: admission.Create, -+ }, -+ { -+ test: "Good container, good init container, bad ephemeral container when updating ephemeralcontainers subresource", -+ pod: &api.Pod{ -+ Spec: api.PodSpec{ -+ ServiceAccountName: "default", -+ SecurityContext: &api.PodSecurityContext{}, -+ Containers: []api.Container{ -+ { -+ Image: "good", -+ SecurityContext: &api.SecurityContext{}, -+ }, -+ }, -+ InitContainers: []api.Container{ -+ { -+ Image: "good", -+ SecurityContext: &api.SecurityContext{}, -+ }, -+ }, -+ EphemeralContainers: []api.EphemeralContainer{ -+ { -+ EphemeralContainerCommon: api.EphemeralContainerCommon{ -+ Image: "bad", -+ SecurityContext: &api.SecurityContext{}, -+ }, -+ }, -+ }, -+ }, -+ }, -+ wantAllowed: false, -+ wantErr: true, -+ subresource: "ephemeralcontainers", -+ operation: admission.Update, -+ }, -+ { -+ test: "Good container, good init container, bad ephemeral container when updating subresource=='' which sets initContainer and container only", -+ pod: &api.Pod{ -+ Spec: api.PodSpec{ -+ ServiceAccountName: "default", -+ SecurityContext: &api.PodSecurityContext{}, -+ Containers: []api.Container{ -+ { -+ Image: "good", -+ SecurityContext: &api.SecurityContext{}, -+ }, -+ }, -+ InitContainers: []api.Container{ -+ { -+ Image: "good", -+ SecurityContext: &api.SecurityContext{}, -+ }, -+ }, -+ EphemeralContainers: []api.EphemeralContainer{ -+ { -+ EphemeralContainerCommon: api.EphemeralContainerCommon{ -+ Image: "bad", -+ SecurityContext: &api.SecurityContext{}, -+ }, -+ }, -+ }, -+ }, -+ }, -+ wantAllowed: true, -+ wantErr: false, -+ subresource: "", -+ operation: admission.Update, -+ }, -+ -+ { -+ test: "Bad container, good ephemeral container when updating subresource=='ephemeralcontainers' which sets ephemeralcontainers only", -+ pod: &api.Pod{ -+ Spec: api.PodSpec{ -+ ServiceAccountName: "default", -+ SecurityContext: &api.PodSecurityContext{}, -+ Containers: []api.Container{ -+ { -+ Image: "bad", -+ SecurityContext: &api.SecurityContext{}, -+ }, -+ }, -+ EphemeralContainers: []api.EphemeralContainer{ -+ { -+ EphemeralContainerCommon: api.EphemeralContainerCommon{ -+ Image: "good", -+ SecurityContext: &api.SecurityContext{}, -+ }, -+ }, -+ }, -+ }, -+ }, -+ wantAllowed: true, -+ wantErr: false, -+ subresource: "ephemeralcontainers", -+ operation: admission.Update, -+ }, -+ { -+ test: "Good ephemeral container", -+ pod: &api.Pod{ -+ Spec: api.PodSpec{ -+ ServiceAccountName: "default", -+ SecurityContext: &api.PodSecurityContext{}, -+ EphemeralContainers: []api.EphemeralContainer{ -+ { -+ EphemeralContainerCommon: api.EphemeralContainerCommon{ -+ Image: "good", -+ SecurityContext: &api.SecurityContext{}, -+ }, -+ }, -+ }, -+ }, -+ }, -+ wantAllowed: true, -+ wantErr: false, -+ subresource: "ephemeralcontainers", -+ operation: admission.Update, - }, - } - for _, tt := range tests { -@@ -759,7 +891,7 @@ func TestContainerCombinations(t *testing.T) { - return - } - -- attr := admission.NewAttributesRecord(tt.pod, nil, api.Kind("Pod").WithVersion("version"), "namespace", "", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, &user.DefaultInfo{}) -+ attr := admission.NewAttributesRecord(tt.pod, nil, api.Kind("Pod").WithVersion("version"), "namespace", "", api.Resource("pods").WithVersion("version"), tt.subresource, tt.operation, &metav1.CreateOptions{}, false, &user.DefaultInfo{}) - - err = wh.Validate(context.TODO(), attr, nil) - if tt.wantAllowed { -diff --git a/plugin/pkg/admission/serviceaccount/admission.go b/plugin/pkg/admission/serviceaccount/admission.go -index 769a115aef5..c844a051c24 100644 ---- a/plugin/pkg/admission/serviceaccount/admission.go -+++ b/plugin/pkg/admission/serviceaccount/admission.go -@@ -99,7 +99,7 @@ var _ = genericadmissioninitializer.WantsExternalKubeInformerFactory(&Plugin{}) - // 5. If MountServiceAccountToken is true, it adds a VolumeMount with the pod's ServiceAccount's api token secret to containers - func NewServiceAccount() *Plugin { - return &Plugin{ -- Handler: admission.NewHandler(admission.Create), -+ Handler: admission.NewHandler(admission.Create, admission.Update), - // TODO: enable this once we've swept secret usage to account for adding secret references to service accounts - LimitSecretReferences: false, - // Auto mount service account API token secrets -@@ -139,7 +139,10 @@ func (s *Plugin) Admit(ctx context.Context, a admission.Attributes, o admission. - if shouldIgnore(a) { - return nil - } -- -+ if a.GetOperation() != admission.Create { -+ // we only mutate pods during create requests -+ return nil -+ } - pod := a.GetObject().(*api.Pod) - - // Don't modify the spec of mirror pods. -@@ -156,7 +159,7 @@ func (s *Plugin) Admit(ctx context.Context, a admission.Attributes, o admission. - - serviceAccount, err := s.getServiceAccount(a.GetNamespace(), pod.Spec.ServiceAccountName) - if err != nil { -- return admission.NewForbidden(a, fmt.Errorf("error looking up service account %s/%s: %v", a.GetNamespace(), pod.Spec.ServiceAccountName, err)) -+ return admission.NewForbidden(a, fmt.Errorf("error looking up service account %s/%s: %w", a.GetNamespace(), pod.Spec.ServiceAccountName, err)) - } - if s.MountServiceAccountToken && shouldAutomount(serviceAccount, pod) { - s.mountServiceAccountToken(serviceAccount, pod) -@@ -179,6 +182,15 @@ func (s *Plugin) Validate(ctx context.Context, a admission.Attributes, o admissi - - pod := a.GetObject().(*api.Pod) - -+ if a.GetOperation() == admission.Update && a.GetSubresource() == "ephemeralcontainers" { -+ return s.limitEphemeralContainerSecretReferences(pod, a) -+ } -+ -+ if a.GetOperation() != admission.Create { -+ // we only validate pod specs during create requests -+ return nil -+ } -+ - // Mirror pods have restrictions on what they can reference - if _, isMirrorPod := pod.Annotations[api.MirrorPodAnnotationKey]; isMirrorPod { - if len(pod.Spec.ServiceAccountName) != 0 { -@@ -204,6 +216,10 @@ func (s *Plugin) Validate(ctx context.Context, a admission.Attributes, o admissi - return nil - } - -+ // Require container pods to have service accounts -+ if len(pod.Spec.ServiceAccountName) == 0 { -+ return admission.NewForbidden(a, fmt.Errorf("no service account specified for pod %s/%s", a.GetNamespace(), pod.Name)) -+ } - // Ensure the referenced service account exists - serviceAccount, err := s.getServiceAccount(a.GetNamespace(), pod.Spec.ServiceAccountName) - if err != nil { -@@ -220,10 +236,7 @@ func (s *Plugin) Validate(ctx context.Context, a admission.Attributes, o admissi - } - - func shouldIgnore(a admission.Attributes) bool { -- if a.GetResource().GroupResource() != api.Resource("pods") { -- return true -- } -- if a.GetSubresource() != "" { -+ if a.GetResource().GroupResource() != api.Resource("pods") || (a.GetSubresource() != "" && a.GetSubresource() != "ephemeralcontainers") { - return true - } - obj := a.GetObject() -@@ -349,6 +362,36 @@ func (s *Plugin) limitSecretReferences(serviceAccount *corev1.ServiceAccount, po - return nil - } - -+func (s *Plugin) limitEphemeralContainerSecretReferences(pod *api.Pod, a admission.Attributes) error { -+ // Require ephemeral container pods to have service accounts -+ if len(pod.Spec.ServiceAccountName) == 0 { -+ return admission.NewForbidden(a, fmt.Errorf("no service account specified for pod %s/%s", a.GetNamespace(), pod.Name)) -+ } -+ // Ensure the referenced service account exists -+ serviceAccount, err := s.getServiceAccount(a.GetNamespace(), pod.Spec.ServiceAccountName) -+ if err != nil { -+ return admission.NewForbidden(a, fmt.Errorf("error looking up service account %s/%s: %w", a.GetNamespace(), pod.Spec.ServiceAccountName, err)) -+ } -+ if !s.enforceMountableSecrets(serviceAccount) { -+ return nil -+ } -+ // Ensure all secrets the ephemeral containers reference are allowed by the service account -+ mountableSecrets := sets.NewString() -+ for _, s := range serviceAccount.Secrets { -+ mountableSecrets.Insert(s.Name) -+ } -+ for _, container := range pod.Spec.EphemeralContainers { -+ for _, env := range container.Env { -+ if env.ValueFrom != nil && env.ValueFrom.SecretKeyRef != nil { -+ if !mountableSecrets.Has(env.ValueFrom.SecretKeyRef.Name) { -+ return fmt.Errorf("ephemeral container %s with envVar %s referencing secret.secretName=\"%s\" is not allowed because service account %s does not reference that secret", container.Name, env.Name, env.ValueFrom.SecretKeyRef.Name, serviceAccount.Name) -+ } -+ } -+ } -+ } -+ return nil -+} -+ - func (s *Plugin) mountServiceAccountToken(serviceAccount *corev1.ServiceAccount, pod *api.Pod) { - // Find the volume and volume name for the ServiceAccountTokenSecret if it already exists - tokenVolumeName := "" -diff --git a/plugin/pkg/admission/serviceaccount/admission_test.go b/plugin/pkg/admission/serviceaccount/admission_test.go -index d50f321a8be..bf15f870d75 100644 ---- a/plugin/pkg/admission/serviceaccount/admission_test.go -+++ b/plugin/pkg/admission/serviceaccount/admission_test.go -@@ -544,6 +544,34 @@ func TestAllowsReferencedSecret(t *testing.T) { - if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil { - t.Errorf("Unexpected error: %v", err) - } -+ -+ pod2 = &api.Pod{ -+ Spec: api.PodSpec{ -+ ServiceAccountName: DefaultServiceAccountName, -+ EphemeralContainers: []api.EphemeralContainer{ -+ { -+ EphemeralContainerCommon: api.EphemeralContainerCommon{ -+ Name: "container-2", -+ Env: []api.EnvVar{ -+ { -+ Name: "env-1", -+ ValueFrom: &api.EnvVarSource{ -+ SecretKeyRef: &api.SecretKeySelector{ -+ LocalObjectReference: api.LocalObjectReference{Name: "foo"}, -+ }, -+ }, -+ }, -+ }, -+ }, -+ }, -+ }, -+ }, -+ } -+ // validate enforces restrictions on secret mounts when operation==create and subresource=='' or operation==update and subresource==ephemeralcontainers" -+ attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "ephemeralcontainers", admission.Update, &metav1.UpdateOptions{}, false, nil) -+ if err := admit.Validate(context.TODO(), attrs, nil); err != nil { -+ t.Errorf("Unexpected error: %v", err) -+ } - } - - func TestRejectsUnreferencedSecretVolumes(t *testing.T) { -@@ -621,6 +649,66 @@ func TestRejectsUnreferencedSecretVolumes(t *testing.T) { - if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err == nil || !strings.Contains(err.Error(), "with envVar") { - t.Errorf("Unexpected error: %v", err) - } -+ -+ pod2 = &api.Pod{ -+ Spec: api.PodSpec{ -+ ServiceAccountName: DefaultServiceAccountName, -+ InitContainers: []api.Container{ -+ { -+ Name: "container-1", -+ Env: []api.EnvVar{ -+ { -+ Name: "env-1", -+ ValueFrom: &api.EnvVarSource{ -+ SecretKeyRef: &api.SecretKeySelector{ -+ LocalObjectReference: api.LocalObjectReference{Name: "foo"}, -+ }, -+ }, -+ }, -+ }, -+ }, -+ }, -+ }, -+ } -+ attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil) -+ if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil { -+ t.Errorf("admit only enforces restrictions on secret mounts when operation==create. Unexpected error: %v", err) -+ } -+ attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Create, &metav1.CreateOptions{}, false, nil) -+ if err := admit.Validate(context.TODO(), attrs, nil); err == nil || !strings.Contains(err.Error(), "with envVar") { -+ t.Errorf("validate only enforces restrictions on secret mounts when operation==create and subresource==''. Unexpected error: %v", err) -+ } -+ -+ pod2 = &api.Pod{ -+ Spec: api.PodSpec{ -+ ServiceAccountName: DefaultServiceAccountName, -+ EphemeralContainers: []api.EphemeralContainer{ -+ { -+ EphemeralContainerCommon: api.EphemeralContainerCommon{ -+ Name: "container-2", -+ Env: []api.EnvVar{ -+ { -+ Name: "env-1", -+ ValueFrom: &api.EnvVarSource{ -+ SecretKeyRef: &api.SecretKeySelector{ -+ LocalObjectReference: api.LocalObjectReference{Name: "foo"}, -+ }, -+ }, -+ }, -+ }, -+ }, -+ }, -+ }, -+ }, -+ } -+ attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "", admission.Update, &metav1.UpdateOptions{}, false, nil) -+ if err := admissiontesting.WithReinvocationTesting(t, admit).Admit(context.TODO(), attrs, nil); err != nil { -+ t.Errorf("admit only enforces restrictions on secret mounts when operation==create and subresource==''. Unexpected error: %v", err) -+ } -+ attrs = admission.NewAttributesRecord(pod2, nil, api.Kind("Pod").WithVersion("version"), ns, "myname", api.Resource("pods").WithVersion("version"), "ephemeralcontainers", admission.Update, &metav1.UpdateOptions{}, false, nil) -+ if err := admit.Validate(context.TODO(), attrs, nil); err == nil || !strings.Contains(err.Error(), "with envVar") { -+ t.Errorf("validate enforces restrictions on secret mounts when operation==update and subresource==ephemeralcontainers. Unexpected error: %v", err) -+ } - } - - func TestAllowUnreferencedSecretVolumesForPermissiveSAs(t *testing.T) { --- -2.25.1 - diff --git a/kubernetes.spec b/kubernetes.spec index de727551ddf96be59e88d361b80318ef38c4e3a1..f4023e11d4f33d3002e6ebe0a7b662786dc47183 100644 --- a/kubernetes.spec +++ b/kubernetes.spec @@ -3,7 +3,7 @@ Name: kubernetes Version: 1.29.1 -Release: 2 +Release: 4 Summary: Container cluster management License: ASL 2.0 URL: https://k8s.io/kubernetes @@ -24,6 +24,10 @@ Source13: kubernetes-accounting.conf Source14: kubeadm.conf Source15: kubernetes.conf +Patch0001: 0001-fix-compile-options.patch +Patch0002: 0002-kubelet-support-exec-and-attach-websocket-protocol.patch +Patch0003: 0003-Add-envFrom-to-serviceaccount-admission-plugin.patch + %description Container cluster management. @@ -261,6 +265,18 @@ getent passwd kube >/dev/null || useradd -r -g kube -d / -s /sbin/nologin \ %systemd_postun kubelet kube-proxy %changelog +* Fri Apr 26 2024 liuxu - 1.29.1-4 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:fix CVE-2024-3177 + +* Fri Apr 26 2024 liuxu - 1.29.1-3 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:adapt old patch + * Tue Apr 23 2024 liuxu - 1.29.1-2 - Type:bugfix - CVE:NA