From a40d0966f133254ff3ebb9d961063f48d9c6abb3 Mon Sep 17 00:00:00 2001 From: zhangxiaoyu Date: Thu, 29 Jun 2023 11:31:10 +0800 Subject: [PATCH] fix CVE-2023-2431 Signed-off-by: zhangxiaoyu --- ...-localhost-seccomp-type-with-no-loca.patch | 859 ++++++++++++++++++ kubernetes.spec | 9 +- 2 files changed, 867 insertions(+), 1 deletion(-) create mode 100644 0006-Return-error-for-localhost-seccomp-type-with-no-loca.patch diff --git a/0006-Return-error-for-localhost-seccomp-type-with-no-loca.patch b/0006-Return-error-for-localhost-seccomp-type-with-no-loca.patch new file mode 100644 index 0000000..308d896 --- /dev/null +++ b/0006-Return-error-for-localhost-seccomp-type-with-no-loca.patch @@ -0,0 +1,859 @@ +From 73174f870735251e7d4240cdc36983d1bef7db5f 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 | 66 ++-- + pkg/kubelet/kuberuntime/helpers_test.go | 350 ++++-------------- + .../kuberuntime_container_linux.go | 16 +- + .../kuberuntime_container_linux_test.go | 22 +- + pkg/kubelet/kuberuntime/security_context.go | 15 +- + 5 files changed, 153 insertions(+), 316 deletions(-) + +diff --git a/pkg/kubelet/kuberuntime/helpers.go b/pkg/kubelet/kuberuntime/helpers.go +index de289836a8b..6138fc958d7 100644 +--- a/pkg/kubelet/kuberuntime/helpers.go ++++ b/pkg/kubelet/kuberuntime/helpers.go +@@ -210,28 +210,32 @@ 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 +- } +- if scmp.Type == v1.SeccompProfileTypeLocalhost && scmp.LocalhostProfile != nil && len(*scmp.LocalhostProfile) > 0 { +- fname := filepath.Join(profileRootPath, *scmp.LocalhostProfile) +- return v1.SeccompLocalhostProfileNamePrefix + fname ++ return v1.SeccompProfileRuntimeDefault, nil ++ } ++ 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 annotationProfile(profile, profileRootPath string) string { +@@ -244,7 +248,7 @@ func annotationProfile(profile, profileRootPath string) string { + } + + 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) +@@ -253,7 +257,7 @@ func (m *kubeGenericRuntimeManager) getSeccompProfilePath(annotations map[string + // if container field does not exist, try container annotation (deprecated) + if containerName != "" { + if profile, ok := annotations[v1.SeccompContainerAnnotationKeyPrefix+containerName]; ok { +- return annotationProfile(profile, m.seccompProfileRoot) ++ return annotationProfile(profile, m.seccompProfileRoot), nil + } + } + +@@ -264,46 +268,50 @@ func (m *kubeGenericRuntimeManager) getSeccompProfilePath(annotations map[string + + // as last resort, try to apply pod annotation (deprecated) + if profile, ok := annotations[v1.SeccompPodAnnotationKey]; ok { +- return annotationProfile(profile, m.seccompProfileRoot) ++ return annotationProfile(profile, m.seccompProfileRoot), nil + } + + 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) +@@ -317,12 +325,12 @@ 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 + } + + func ipcNamespaceForPod(pod *v1.Pod) runtimeapi.NamespaceMode { +diff --git a/pkg/kubelet/kuberuntime/helpers_test.go b/pkg/kubelet/kuberuntime/helpers_test.go +index 25065f30411..70ad7250ce2 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", +@@ -369,91 +382,6 @@ func TestGetSeccompProfilePath(t *testing.T) { + containerName: "container1", + expectedProfile: "", + }, +- { +- description: "annotations: pod runtime/default seccomp profile should return runtime/default", +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: v1.SeccompProfileRuntimeDefault, +- }, +- expectedProfile: "runtime/default", +- }, +- { +- description: "annotations: pod docker/default seccomp profile should return docker/default", +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: v1.DeprecatedSeccompProfileDockerDefault, +- }, +- expectedProfile: "docker/default", +- }, +- { +- description: "annotations: pod runtime/default seccomp profile with containerName should return runtime/default", +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: v1.SeccompProfileRuntimeDefault, +- }, +- containerName: "container1", +- expectedProfile: "runtime/default", +- }, +- { +- description: "annotations: pod docker/default seccomp profile with containerName should return docker/default", +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: v1.DeprecatedSeccompProfileDockerDefault, +- }, +- containerName: "container1", +- expectedProfile: "docker/default", +- }, +- { +- description: "annotations: pod unconfined seccomp profile should return unconfined", +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: v1.SeccompProfileNameUnconfined, +- }, +- expectedProfile: "unconfined", +- }, +- { +- description: "annotations: pod unconfined seccomp profile with containerName should return unconfined", +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: v1.SeccompProfileNameUnconfined, +- }, +- containerName: "container1", +- expectedProfile: "unconfined", +- }, +- { +- description: "annotations: pod localhost seccomp profile should return local profile path", +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: "localhost/chmod.json", +- }, +- expectedProfile: seccompLocalhostPath("chmod.json"), +- }, +- { +- description: "annotations: pod localhost seccomp profile with containerName should return local profile path", +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: "localhost/chmod.json", +- }, +- containerName: "container1", +- expectedProfile: seccompLocalhostPath("chmod.json"), +- }, +- { +- description: "annotations: container localhost seccomp profile with containerName should return local profile path", +- annotation: map[string]string{ +- v1.SeccompContainerAnnotationKeyPrefix + "container1": "localhost/chmod.json", +- }, +- containerName: "container1", +- expectedProfile: seccompLocalhostPath("chmod.json"), +- }, +- { +- description: "annotations: container localhost seccomp profile should override pod profile", +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: v1.SeccompProfileNameUnconfined, +- v1.SeccompContainerAnnotationKeyPrefix + "container1": "localhost/chmod.json", +- }, +- containerName: "container1", +- expectedProfile: seccompLocalhostPath("chmod.json"), +- }, +- { +- description: "annotations: container localhost seccomp profile with unmatched containerName should return empty", +- annotation: map[string]string{ +- v1.SeccompContainerAnnotationKeyPrefix + "container1": "localhost/chmod.json", +- }, +- containerName: "container2", +- expectedProfile: "", +- }, + { + description: "pod seccomp profile set to unconfined returns unconfined", + podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeUnconfined}}, +@@ -480,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", +@@ -500,41 +428,16 @@ func TestGetSeccompProfilePath(t *testing.T) { + containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeRuntimeDefault}}, + expectedProfile: "runtime/default", + }, +- { +- description: "prioritise container field over container annotation, pod field and pod annotation", +- podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost, LocalhostProfile: getLocal("field-pod-profile.json")}}, +- containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost, LocalhostProfile: getLocal("field-cont-profile.json")}}, +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: "localhost/annota-pod-profile.json", +- v1.SeccompContainerAnnotationKeyPrefix + "container1": "localhost/annota-cont-profile.json", +- }, +- containerName: "container1", +- expectedProfile: seccompLocalhostPath("field-cont-profile.json"), +- }, +- { +- description: "prioritise container annotation over pod field", +- podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost, LocalhostProfile: getLocal("field-pod-profile.json")}}, +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: "localhost/annota-pod-profile.json", +- v1.SeccompContainerAnnotationKeyPrefix + "container1": "localhost/annota-cont-profile.json", +- }, +- containerName: "container1", +- expectedProfile: seccompLocalhostPath("annota-cont-profile.json"), +- }, +- { +- description: "prioritise pod field over pod annotation", +- podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost, LocalhostProfile: getLocal("field-pod-profile.json")}}, +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: "localhost/annota-pod-profile.json", +- }, +- containerName: "container1", +- expectedProfile: seccompLocalhostPath("field-pod-profile.json"), +- }, + } + + 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) ++ } + } + } + +@@ -549,6 +452,7 @@ func TestGetSeccompProfilePathDefaultSeccomp(t *testing.T) { + containerSc *v1.SecurityContext + containerName string + expectedProfile string ++ expectedError string + }{ + { + description: "no seccomp should return runtime/default", +@@ -559,91 +463,6 @@ func TestGetSeccompProfilePathDefaultSeccomp(t *testing.T) { + containerName: "container1", + expectedProfile: v1.SeccompProfileRuntimeDefault, + }, +- { +- description: "annotations: pod runtime/default seccomp profile should return runtime/default", +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: v1.SeccompProfileRuntimeDefault, +- }, +- expectedProfile: v1.SeccompProfileRuntimeDefault, +- }, +- { +- description: "annotations: pod docker/default seccomp profile should return docker/default", +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: v1.DeprecatedSeccompProfileDockerDefault, +- }, +- expectedProfile: "docker/default", +- }, +- { +- description: "annotations: pod runtime/default seccomp profile with containerName should return runtime/default", +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: v1.SeccompProfileRuntimeDefault, +- }, +- containerName: "container1", +- expectedProfile: v1.SeccompProfileRuntimeDefault, +- }, +- { +- description: "annotations: pod docker/default seccomp profile with containerName should return docker/default", +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: v1.DeprecatedSeccompProfileDockerDefault, +- }, +- containerName: "container1", +- expectedProfile: "docker/default", +- }, +- { +- description: "annotations: pod unconfined seccomp profile should return unconfined", +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: v1.SeccompProfileNameUnconfined, +- }, +- expectedProfile: "unconfined", +- }, +- { +- description: "annotations: pod unconfined seccomp profile with containerName should return unconfined", +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: v1.SeccompProfileNameUnconfined, +- }, +- containerName: "container1", +- expectedProfile: "unconfined", +- }, +- { +- description: "annotations: pod localhost seccomp profile should return local profile path", +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: "localhost/chmod.json", +- }, +- expectedProfile: seccompLocalhostPath("chmod.json"), +- }, +- { +- description: "annotations: pod localhost seccomp profile with containerName should return local profile path", +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: "localhost/chmod.json", +- }, +- containerName: "container1", +- expectedProfile: seccompLocalhostPath("chmod.json"), +- }, +- { +- description: "annotations: container localhost seccomp profile with containerName should return local profile path", +- annotation: map[string]string{ +- v1.SeccompContainerAnnotationKeyPrefix + "container1": "localhost/chmod.json", +- }, +- containerName: "container1", +- expectedProfile: seccompLocalhostPath("chmod.json"), +- }, +- { +- description: "annotations: container localhost seccomp profile should override pod profile", +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: v1.SeccompProfileNameUnconfined, +- v1.SeccompContainerAnnotationKeyPrefix + "container1": "localhost/chmod.json", +- }, +- containerName: "container1", +- expectedProfile: seccompLocalhostPath("chmod.json"), +- }, +- { +- description: "annotations: container localhost seccomp profile with unmatched containerName should return runtime/default", +- annotation: map[string]string{ +- v1.SeccompContainerAnnotationKeyPrefix + "container1": "localhost/chmod.json", +- }, +- containerName: "container2", +- expectedProfile: v1.SeccompProfileRuntimeDefault, +- }, + { + description: "pod seccomp profile set to unconfined returns unconfined", + podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeUnconfined}}, +@@ -670,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", +@@ -690,41 +509,16 @@ func TestGetSeccompProfilePathDefaultSeccomp(t *testing.T) { + containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeRuntimeDefault}}, + expectedProfile: "runtime/default", + }, +- { +- description: "prioritise container field over container annotation, pod field and pod annotation", +- podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost, LocalhostProfile: getLocal("field-pod-profile.json")}}, +- containerSc: &v1.SecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost, LocalhostProfile: getLocal("field-cont-profile.json")}}, +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: "localhost/annota-pod-profile.json", +- v1.SeccompContainerAnnotationKeyPrefix + "container1": "localhost/annota-cont-profile.json", +- }, +- containerName: "container1", +- expectedProfile: seccompLocalhostPath("field-cont-profile.json"), +- }, +- { +- description: "prioritise container annotation over pod field", +- podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost, LocalhostProfile: getLocal("field-pod-profile.json")}}, +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: "localhost/annota-pod-profile.json", +- v1.SeccompContainerAnnotationKeyPrefix + "container1": "localhost/annota-cont-profile.json", +- }, +- containerName: "container1", +- expectedProfile: seccompLocalhostPath("annota-cont-profile.json"), +- }, +- { +- description: "prioritise pod field over pod annotation", +- podSc: &v1.PodSecurityContext{SeccompProfile: &v1.SeccompProfile{Type: v1.SeccompProfileTypeLocalhost, LocalhostProfile: getLocal("field-pod-profile.json")}}, +- annotation: map[string]string{ +- v1.SeccompPodAnnotationKey: "localhost/annota-pod-profile.json", +- }, +- containerName: "container1", +- expectedProfile: seccompLocalhostPath("field-pod-profile.json"), +- }, + } + + 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) ++ } + } + } + +@@ -747,6 +541,7 @@ func TestGetSeccompProfile(t *testing.T) { + containerSc *v1.SecurityContext + containerName string + expectedProfile *runtimeapi.SecurityProfile ++ expectedError string + }{ + { + description: "no seccomp should return unconfined", +@@ -781,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", +@@ -817,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) ++ } + } + } + +@@ -841,6 +641,7 @@ func TestGetSeccompProfileDefaultSeccomp(t *testing.T) { + containerSc *v1.SecurityContext + containerName string + expectedProfile *runtimeapi.SecurityProfile ++ expectedError string + }{ + { + description: "no seccomp should return RuntimeDefault", +@@ -875,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", +@@ -911,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/kuberuntime_container_linux.go b/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go +index 25917803b1c..1e9a2377b51 100644 +--- a/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go ++++ b/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go +@@ -45,15 +45,23 @@ func (m *kubeGenericRuntimeManager) applyPlatformSpecificContainerConfig(config + libcontainercgroups.IsCgroup2UnifiedMode() { + enforceMemoryQoS = true + } +- config.Linux = m.generateLinuxContainerConfig(container, pod, uid, username, nsTarget, enforceMemoryQoS) ++ cl, err := m.generateLinuxContainerConfig(container, pod, uid, username, nsTarget, enforceMemoryQoS) ++ if err != nil { ++ return err ++ } ++ config.Linux = cl + return nil + } + + // generateLinuxContainerConfig generates linux container config for kubelet runtime v1. +-func (m *kubeGenericRuntimeManager) generateLinuxContainerConfig(container *v1.Container, pod *v1.Pod, uid *int64, username string, nsTarget *kubecontainer.ContainerID, enforceMemoryQoS bool) *runtimeapi.LinuxContainerConfig { ++func (m *kubeGenericRuntimeManager) generateLinuxContainerConfig(container *v1.Container, pod *v1.Pod, uid *int64, username string, nsTarget *kubecontainer.ContainerID, enforceMemoryQoS bool) (*runtimeapi.LinuxContainerConfig, error) { ++ sc, err := m.determineEffectiveSecurityContext(pod, container, uid, username) ++ if err != nil { ++ return nil, err ++ } + lc := &runtimeapi.LinuxContainerConfig{ + Resources: &runtimeapi.LinuxContainerResources{}, +- SecurityContext: m.determineEffectiveSecurityContext(pod, container, uid, username), ++ SecurityContext: sc, + } + + if nsTarget != nil && lc.SecurityContext.NamespaceOptions.Pid == runtimeapi.NamespaceMode_CONTAINER { +@@ -124,7 +132,7 @@ func (m *kubeGenericRuntimeManager) generateLinuxContainerConfig(container *v1.C + } + } + +- return lc ++ return lc, nil + } + + // calculateLinuxResources will create the linuxContainerResources type based on the provided CPU and memory resource requests, limits +diff --git a/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go b/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go +index 29d0bbfc9b4..838ecf8c6f5 100644 +--- a/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go ++++ b/pkg/kubelet/kuberuntime/kuberuntime_container_linux_test.go +@@ -47,6 +47,8 @@ func makeExpectedConfig(m *kubeGenericRuntimeManager, pod *v1.Pod, containerInde + restartCountUint32 := uint32(restartCount) + envs := make([]*runtimeapi.KeyValue, len(opts.Envs)) + ++ l, _ := m.generateLinuxContainerConfig(container, pod, new(int64), "", nil, enforceMemoryQoS) ++ + expectedConfig := &runtimeapi.ContainerConfig{ + Metadata: &runtimeapi.ContainerMetadata{ + Name: container.Name, +@@ -64,7 +66,7 @@ func makeExpectedConfig(m *kubeGenericRuntimeManager, pod *v1.Pod, containerInde + Stdin: container.Stdin, + StdinOnce: container.StdinOnce, + Tty: container.TTY, +- Linux: m.generateLinuxContainerConfig(container, pod, new(int64), "", nil, enforceMemoryQoS), ++ Linux: l, + Envs: envs, + } + return expectedConfig +@@ -215,7 +217,8 @@ func TestGenerateLinuxContainerConfigResources(t *testing.T) { + }, + } + +- linuxConfig := m.generateLinuxContainerConfig(&pod.Spec.Containers[0], pod, new(int64), "", nil, false) ++ linuxConfig, err := m.generateLinuxContainerConfig(&pod.Spec.Containers[0], pod, new(int64), "", nil, false) ++ assert.NoError(t, err) + assert.Equal(t, test.expected.CpuPeriod, linuxConfig.GetResources().CpuPeriod, test.name) + assert.Equal(t, test.expected.CpuQuota, linuxConfig.GetResources().CpuQuota, test.name) + assert.Equal(t, test.expected.CpuShares, linuxConfig.GetResources().CpuShares, test.name) +@@ -329,6 +332,8 @@ func TestGenerateContainerConfigWithMemoryQoSEnforced(t *testing.T) { + memoryLow int64 + memoryHigh int64 + } ++ l1, _ := m.generateLinuxContainerConfig(&pod1.Spec.Containers[0], pod1, new(int64), "", nil, true) ++ l2, _ := m.generateLinuxContainerConfig(&pod2.Spec.Containers[0], pod2, new(int64), "", nil, true) + tests := []struct { + name string + pod *v1.Pod +@@ -338,7 +343,7 @@ func TestGenerateContainerConfigWithMemoryQoSEnforced(t *testing.T) { + name: "Request128MBLimit256MB", + pod: pod1, + expected: &expectedResult{ +- m.generateLinuxContainerConfig(&pod1.Spec.Containers[0], pod1, new(int64), "", nil, true), ++ l1, + 128 * 1024 * 1024, + int64(float64(256*1024*1024) * m.memoryThrottlingFactor), + }, +@@ -347,7 +352,7 @@ func TestGenerateContainerConfigWithMemoryQoSEnforced(t *testing.T) { + name: "Request128MBWithoutLimit", + pod: pod2, + expected: &expectedResult{ +- m.generateLinuxContainerConfig(&pod2.Spec.Containers[0], pod2, new(int64), "", nil, true), ++ l2, + 128 * 1024 * 1024, + int64(pod2MemoryHigh), + }, +@@ -355,7 +360,8 @@ func TestGenerateContainerConfigWithMemoryQoSEnforced(t *testing.T) { + } + + for _, test := range tests { +- linuxConfig := m.generateLinuxContainerConfig(&test.pod.Spec.Containers[0], test.pod, new(int64), "", nil, true) ++ linuxConfig, err := m.generateLinuxContainerConfig(&test.pod.Spec.Containers[0], test.pod, new(int64), "", nil, true) ++ assert.NoError(t, err) + assert.Equal(t, test.expected.containerConfig, linuxConfig, test.name) + assert.Equal(t, linuxConfig.GetResources().GetUnified()["memory.min"], strconv.FormatInt(test.expected.memoryLow, 10), test.name) + assert.Equal(t, linuxConfig.GetResources().GetUnified()["memory.high"], strconv.FormatInt(test.expected.memoryHigh, 10), test.name) +@@ -578,7 +584,8 @@ func TestGenerateLinuxContainerConfigNamespaces(t *testing.T) { + }, + } { + t.Run(tc.name, func(t *testing.T) { +- got := m.generateLinuxContainerConfig(&tc.pod.Spec.Containers[0], tc.pod, nil, "", tc.target, false) ++ got, err := m.generateLinuxContainerConfig(&tc.pod.Spec.Containers[0], tc.pod, nil, "", tc.target, false) ++ assert.NoError(t, err) + if diff := cmp.Diff(tc.want, got.SecurityContext.NamespaceOptions); diff != "" { + t.Errorf("%v: diff (-want +got):\n%v", t.Name(), diff) + } +@@ -669,7 +676,8 @@ func TestGenerateLinuxContainerConfigSwap(t *testing.T) { + } { + t.Run(tc.name, func(t *testing.T) { + m.memorySwapBehavior = tc.swapSetting +- actual := m.generateLinuxContainerConfig(&tc.pod.Spec.Containers[0], tc.pod, nil, "", nil, false) ++ actual, err := m.generateLinuxContainerConfig(&tc.pod.Spec.Containers[0], tc.pod, nil, "", nil, false) ++ assert.NoError(t, err) + assert.Equal(t, tc.expected, actual.Resources.MemorySwapLimitInBytes, "memory swap config for %s", tc.name) + }) + } +diff --git a/pkg/kubelet/kuberuntime/security_context.go b/pkg/kubelet/kuberuntime/security_context.go +index c9d33e44305..3b575c8e974 100644 +--- a/pkg/kubelet/kuberuntime/security_context.go ++++ b/pkg/kubelet/kuberuntime/security_context.go +@@ -24,7 +24,7 @@ import ( + ) + + // determineEffectiveSecurityContext gets container's security context from v1.Pod and v1.Container. +-func (m *kubeGenericRuntimeManager) determineEffectiveSecurityContext(pod *v1.Pod, container *v1.Container, uid *int64, username string) *runtimeapi.LinuxContainerSecurityContext { ++func (m *kubeGenericRuntimeManager) determineEffectiveSecurityContext(pod *v1.Pod, container *v1.Container, uid *int64, username string) (*runtimeapi.LinuxContainerSecurityContext, error) { + effectiveSc := securitycontext.DetermineEffectiveSecurityContext(pod, container) + synthesized := convertToRuntimeSecurityContext(effectiveSc) + if synthesized == nil { +@@ -36,9 +36,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) +@@ -74,7 +81,7 @@ func (m *kubeGenericRuntimeManager) determineEffectiveSecurityContext(pod *v1.Po + synthesized.MaskedPaths = securitycontext.ConvertToRuntimeMaskedPaths(effectiveSc.ProcMount) + synthesized.ReadonlyPaths = securitycontext.ConvertToRuntimeReadonlyPaths(effectiveSc.ProcMount) + +- return synthesized ++ return synthesized, nil + } + + // convertToRuntimeSecurityContext converts v1.SecurityContext to runtimeapi.SecurityContext. +-- +2.25.1 + diff --git a/kubernetes.spec b/kubernetes.spec index dda3f75..b399b75 100644 --- a/kubernetes.spec +++ b/kubernetes.spec @@ -3,7 +3,7 @@ Name: kubernetes Version: 1.24.0 -Release: 1 +Release: 2 Summary: Container cluster management License: ASL 2.0 URL: https://k8s.io/kubernetes @@ -29,6 +29,7 @@ Patch0002: 0002-kubelet-support-exec-and-attach-websocket-protocol.patch Patch0003: 0003-timeout-wait-backend-to-frontend-complete.patch Patch0004: 0004-Add-an-option-for-aggregator.patch Patch0005: 0005-Escape-terminal-special-characters-in-kubectl-112553.patch +Patch0006: 0006-Return-error-for-localhost-seccomp-type-with-no-loca.patch %description Container cluster management. @@ -260,6 +261,12 @@ getent passwd kube >/dev/null || useradd -r -g kube -d / -s /sbin/nologin \ %systemd_postun kubelet kube-proxy %changelog +* Thu Jun 29 2023 zhangxiaoyu - 1.24.0-2 +- Type:bugfix +- CVE:NA +- SUG:NA +- DESC:fix CVE-2023-2431 + * Tue Jan 03 2023 zhangxiaoyu - 1.24.0-1 - Type:update - CVE:NA -- Gitee