From: Bruce Ashfield <bruce.ashfield@gmail.com>
To: soumya.sambu@windriver.com
Cc: meta-virtualization@lists.yoctoproject.org
Subject: Re: [meta-virtualization][kirkstone][PATCH v2 2/2] kubernetes: Fix CVE-2023-2727, CVE-2023-2728
Date: Tue, 21 Nov 2023 04:09:03 +0000 [thread overview]
Message-ID: <ZVwtX7t6Vame2/3o@gmail.com> (raw)
In-Reply-To: <20231113035848.1875360-1-soumya.sambu@windriver.com> <20231113041609.1888228-1-soumya.sambu@windriver.com> <20231113041641.1888686-1-soumya.sambu@windriver.com>
All three are now merged.
Thanks for the CVE information in the commit, it makes it
much easier and faster to merge.
Bruce
In message: [meta-virtualization][kirkstone][PATCH v2 2/2] kubernetes: Fix CVE-2023-2727, CVE-2023-2728
on 13/11/2023 Soumya via lists.yoctoproject.org wrote:
> From: Soumya Sambu <soumya.sambu@windriver.com>
>
> Users may be able to launch containers using images that are restricted by
> ImagePolicyWebhook when using ephemeral containers, Kubernetes clusters are
> only affected if the ImagePolicyWebhook admission plugin is used together
> with ephemeral containers.
>
> Users may be able to launch containers that bypass the mountable secrets
> policy enforced by the ServiceAccount admission plugin when using ephemeral
> containers. The policy ensures pods running with a service account may only
> reference secrets specified in the service account's secrets field. Kuberenetes
> clusters are only affected if the ServiceAccount admission plugin and the
> `kubernetes.io/enforce-mountab'le-secrets` annotation are used teogether with
> ephemeralcontainers.
>
> CVE: CVE-2023-2727, CVE-2023-2728
>
> Affected Versions
> 1.27.0 - v1.27.2
> v1.26.0 - v1.26.5
> v1.25.0 - v1.25.10
> <= v1.24.14
>
> master branch(kubernetes v1.28.2) is not impacted
> mickledore branch(kubernetes v1.27.5) is not impacted
>
> References:
> https://nvd.nist.gov/vuln/detail/CVE-2023-2727
> https://nvd.nist.gov/vuln/detail/CVE-2023-2728
>
> Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
> ---
> .../CVE-2023-2727-CVE-2023-2728.patch | 559 ++++++++++++++++++
> .../kubernetes/kubernetes_git.bb | 1 +
> 2 files changed, 560 insertions(+)
> create mode 100644 recipes-containers/kubernetes/kubernetes/CVE-2023-2727-CVE-2023-2728.patch
>
> diff --git a/recipes-containers/kubernetes/kubernetes/CVE-2023-2727-CVE-2023-2728.patch b/recipes-containers/kubernetes/kubernetes/CVE-2023-2727-CVE-2023-2728.patch
> new file mode 100644
> index 00000000..2a9e8489
> --- /dev/null
> +++ b/recipes-containers/kubernetes/kubernetes/CVE-2023-2727-CVE-2023-2728.patch
> @@ -0,0 +1,559 @@
> +From f754a4dee31455a0d7fc0f51cb85348af9ea5e1f Mon Sep 17 00:00:00 2001
> +From: Rita Zhang <rita.z.zhang@gmail.com>
> +Date: Tue, 30 May 2023 20:35:33 +0000
> +Subject: [PATCH] Add ephemeralcontainer to imagepolicy securityaccount
> + admission plugin
> +
> +Signed-off-by: Rita Zhang <rita.z.zhang@gmail.com>
> +
> +CVE: CVE-2023-2727, CVE-2023-2728
> +
> +Upstream-Status: Backport [https://github.com/kubernetes/kubernetes/commit/f754a4dee31455a0d7fc0f51cb85348af9ea5e1f]
> +
> +Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
> +---
> + 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 aea4f713eb5..3dfcbf95eef 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 035d54ea8ea..f6e25f3c19d 100644
> +--- a/plugin/pkg/admission/serviceaccount/admission.go
> ++++ b/plugin/pkg/admission/serviceaccount/admission.go
> +@@ -100,7 +100,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
> +@@ -140,7 +140,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.
> +@@ -157,7 +160,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)
> +@@ -180,6 +183,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 {
> +@@ -205,6 +217,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 {
> +@@ -221,10 +237,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()
> +@@ -350,6 +363,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 ca43abf9c3f..f5359253985 100644
> +--- a/plugin/pkg/admission/serviceaccount/admission_test.go
> ++++ b/plugin/pkg/admission/serviceaccount/admission_test.go
> +@@ -545,6 +545,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) {
> +@@ -622,6 +650,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.40.0
> diff --git a/recipes-containers/kubernetes/kubernetes_git.bb b/recipes-containers/kubernetes/kubernetes_git.bb
> index dc741bbf..b0c87c47 100644
> --- a/recipes-containers/kubernetes/kubernetes_git.bb
> +++ b/recipes-containers/kubernetes/kubernetes_git.bb
> @@ -31,6 +31,7 @@ SRC_URI:append = " \
> file://0001-build-golang.sh-convert-remaining-go-calls-to-use.patch;patchdir=src/import \
> file://0001-Makefile.generated_files-Fix-race-issue-for-installi.patch;patchdir=src/import \
> file://CVE-2023-2431.patch;patchdir=src/import \
> + file://CVE-2023-2727-CVE-2023-2728.patch;patchdir=src/import \
> file://cni-containerd-net.conflist \
> file://k8s-init \
> file://99-kubernetes.conf \
> --
> 2.40.0
>
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#8458): https://lists.yoctoproject.org/g/meta-virtualization/message/8458
> Mute This Topic: https://lists.yoctoproject.org/mt/102555691/1050810
> Group Owner: meta-virtualization+owner@lists.yoctoproject.org
> Unsubscribe: https://lists.yoctoproject.org/g/meta-virtualization/unsub [bruce.ashfield@gmail.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>
In message: [meta-virtualization][kirkstone][PATCH v2 1/2] kubernetes: Fix CVE-2023-2431
on 13/11/2023 Soumya via lists.yoctoproject.org wrote:
> From: Soumya Sambu <soumya.sambu@windriver.com>
>
> A security issue was discovered in Kubelet that allows pods to bypass the
> seccomp profile enforcement. Pods that use localhost type for seccomp profile
> but specify an empty profile field, are affected by this issue. In this
> scenario, this vulnerability allows the pod to run in unconfined (seccomp
> disabled) mode. This bug affects Kubelet.
>
> CVE: CVE-2023-2431
> Affected Versions
> v1.27.0 - v1.27.1
> v1.26.0 - v1.26.4
> v1.25.0 - v1.25.9
> <= v1.24.13
>
> master branch(kubernetes v1.28.2) is not impacted
> mickledore branch(kubernetes v1.27.5) is not impacted
>
> References:
> https://nvd.nist.gov/vuln/detail/CVE-2023-2431
>
> Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
> ---
> .../kubernetes/kubernetes/CVE-2023-2431.patch | 863 ++++++++++++++++++
> .../kubernetes/kubernetes_git.bb | 1 +
> 2 files changed, 864 insertions(+)
> create mode 100644 recipes-containers/kubernetes/kubernetes/CVE-2023-2431.patch
>
> diff --git a/recipes-containers/kubernetes/kubernetes/CVE-2023-2431.patch b/recipes-containers/kubernetes/kubernetes/CVE-2023-2431.patch
> new file mode 100644
> index 00000000..56c3a6e1
> --- /dev/null
> +++ b/recipes-containers/kubernetes/kubernetes/CVE-2023-2431.patch
> @@ -0,0 +1,863 @@
> +From 73174f870735251e7d4240cdc36983d1bef7db5f Mon Sep 17 00:00:00 2001
> +From: Craig Ingram <cjingram@google.com>
> +Date: Fri, 24 Feb 2023 15:24:49 -0500
> +Subject: [PATCH] Return error for localhost seccomp type with no localhost
> + profile defined
> +
> +CVE: CVE-2023-2431
> +
> +Upstream-Status: Backport [https://github.com/kubernetes/kubernetes/commit/73174f870735251e7d4240cdc36983d1bef7db5f]
> +
> +Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
> +---
> + 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 fa580335cf8..b36e01166f8 100644
> +--- a/pkg/kubelet/kuberuntime/helpers.go
> ++++ b/pkg/kubelet/kuberuntime/helpers.go
> +@@ -209,28 +209,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 {
> +@@ -243,7 +247,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)
> +@@ -252,7 +256,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
> + }
> + }
> +
> +@@ -263,46 +267,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)
> +@@ -316,12 +324,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 6cb9e54729e..54670673bcd 100644
> +--- a/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go
> ++++ b/pkg/kubelet/kuberuntime/kuberuntime_container_linux.go
> +@@ -46,15 +46,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 {
> +@@ -125,7 +133,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 46817e00fb0..98f635cc932 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.40.0
> diff --git a/recipes-containers/kubernetes/kubernetes_git.bb b/recipes-containers/kubernetes/kubernetes_git.bb
> index 59892c92..dc741bbf 100644
> --- a/recipes-containers/kubernetes/kubernetes_git.bb
> +++ b/recipes-containers/kubernetes/kubernetes_git.bb
> @@ -30,6 +30,7 @@ SRC_URI:append = " \
> file://0001-cross-don-t-build-tests-by-default.patch;patchdir=src/import \
> file://0001-build-golang.sh-convert-remaining-go-calls-to-use.patch;patchdir=src/import \
> file://0001-Makefile.generated_files-Fix-race-issue-for-installi.patch;patchdir=src/import \
> + file://CVE-2023-2431.patch;patchdir=src/import \
> file://cni-containerd-net.conflist \
> file://k8s-init \
> file://99-kubernetes.conf \
> --
> 2.40.0
>
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#8457): https://lists.yoctoproject.org/g/meta-virtualization/message/8457
> Mute This Topic: https://lists.yoctoproject.org/mt/102555683/1050810
> Group Owner: meta-virtualization+owner@lists.yoctoproject.org
> Unsubscribe: https://lists.yoctoproject.org/g/meta-virtualization/unsub [bruce.ashfield@gmail.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>
In message: [meta-virtualization][kirkstone][PATCH v2 1/1] kubernetes: Adjust patches to resolve error that occur with devtool
on 13/11/2023 Soumya via lists.yoctoproject.org wrote:
> From: Soumya Sambu <soumya.sambu@windriver.com>
>
> Adjust patches and .bb to fix below error which occurs with devtool modify command -
>
> ERROR: Applying patch '0001-hack-lib-golang.sh-use-CC-from-environment.patch' on
> target directory
> CmdError('sh -c \'PATCHFILE="0001-hack-lib-golang.sh-use-CC-from-environment.patch"
> git -c user.name="OpenEmbedded" -c user.email="oe.patch@oe" commit -F /tmp/tmp_ptvioq3
> --author="Koen Kooi <koen.kooi@linaro.org>"
> --date="Mon, 23 Jul 2018 15:28:02 +0200"\'', 0, 'stdout: On branch devtool
> Changes not staged for commit:
> (use "git add <file>..." to update what will be committed)
> (use "git restore <file>..." to discard changes in working directory)
> (commit or discard the untracked or modified content in submodules)
> \tmodified: src/import (modified content)
>
> no changes added to commit (use "git add" and/or "git commit -a")
>
> stderr: ')
>
> This error is not seen on master branch, fixed with below commit -
> [https://git.yoctoproject.org/meta-virtualization/commit/?id=d9af46db9aa9060c1ec10118b2cccabfc8264904]
>
> Signed-off-by: Soumya Sambu <soumya.sambu@windriver.com>
> ---
> ...rated_files-Fix-race-issue-for-installi.patch | 16 +++++++++-------
> ...ng.sh-convert-remaining-go-calls-to-use.patch | 16 ++++++++--------
> ...0001-cross-don-t-build-tests-by-default.patch | 10 +++++-----
> ...k-lib-golang.sh-use-CC-from-environment.patch | 12 +++++++-----
> recipes-containers/kubernetes/kubernetes_git.bb | 8 ++++----
> 5 files changed, 33 insertions(+), 29 deletions(-)
>
> diff --git a/recipes-containers/kubernetes/kubernetes/0001-Makefile.generated_files-Fix-race-issue-for-installi.patch b/recipes-containers/kubernetes/kubernetes/0001-Makefile.generated_files-Fix-race-issue-for-installi.patch
> index 02bb5e91..1b08b8c3 100644
> --- a/recipes-containers/kubernetes/kubernetes/0001-Makefile.generated_files-Fix-race-issue-for-installi.patch
> +++ b/recipes-containers/kubernetes/kubernetes/0001-Makefile.generated_files-Fix-race-issue-for-installi.patch
> @@ -1,7 +1,7 @@
> From 441df8a24a2c80e320f140b5d9bc352c7ce8a64a Mon Sep 17 00:00:00 2001
> From: Robert Yang <liezhi.yang@windriver.com>
> Date: Thu, 15 Oct 2020 07:27:35 +0000
> -Subject: [PATCH] src/import/build/root/Makefile.generated_files: Fix race issue for installing
> +Subject: [PATCH] src/import/build/root/Makefile.generated_files: Fix race issue for installing
> go2make
>
> The src/import/build/root/Makefile.generated_files are called several times during the build, so the
> @@ -25,14 +25,14 @@ Upstream-Status: Pending
>
> Signed-off-by: Robert Yang <liezhi.yang@windriver.com>
> ---
> - src/import/build/root/Makefile.generated_files | 4 +++-
> + build/root/Makefile.generated_files | 4 +++-
> 1 file changed, 3 insertions(+), 1 deletion(-)
>
> -Index: kubernetes-v1.21.1+git45da3fc33872083fb225c1a8c4d03e530d6f7630/src/import/build/root/Makefile.generated_files
> -===================================================================
> ---- kubernetes-v1.21.1+git45da3fc33872083fb225c1a8c4d03e530d6f7630.orig/src/import/build/root/Makefile.generated_files
> -+++ kubernetes-v1.21.1+git45da3fc33872083fb225c1a8c4d03e530d6f7630/src/import/build/root/Makefile.generated_files
> -@@ -67,7 +67,9 @@
> +diff --git a/build/root/Makefile.generated_files b/build/root/Makefile.generated_files
> +index d86a90cbb39..19a3d332476 100644
> +--- a/build/root/Makefile.generated_files
> ++++ b/build/root/Makefile.generated_files
> +@@ -67,7 +67,9 @@ $(META_DIR)/$(GO_PKGDEPS_FILE): FORCE
> if [[ "$(DBG_CODEGEN)" == 1 ]]; then \
> echo "DBG: calculating Go dependencies"; \
> fi
> @@ -43,3 +43,5 @@ Index: kubernetes-v1.21.1+git45da3fc33872083fb225c1a8c4d03e530d6f7630/src/import
> hack/run-in-gopath.sh go2make \
> k8s.io/kubernetes/... \
> --prune k8s.io/kubernetes/staging \
> +--
> +2.40.0
> diff --git a/recipes-containers/kubernetes/kubernetes/0001-build-golang.sh-convert-remaining-go-calls-to-use.patch b/recipes-containers/kubernetes/kubernetes/0001-build-golang.sh-convert-remaining-go-calls-to-use.patch
> index 8adbafb3..00425c7d 100644
> --- a/recipes-containers/kubernetes/kubernetes/0001-build-golang.sh-convert-remaining-go-calls-to-use.patch
> +++ b/recipes-containers/kubernetes/kubernetes/0001-build-golang.sh-convert-remaining-go-calls-to-use.patch
> @@ -8,11 +8,11 @@ Signed-off-by: Bruce Ashfield <bruce.ashfield@gmail.com>
> hack/lib/golang.sh | 8 ++++----
> 1 file changed, 4 insertions(+), 4 deletions(-)
>
> -diff --git a/src/import/hack/lib/golang.sh b/src/import/hack/lib/golang.sh
> -index e9148ec08fa..71d3c987563 100755
> ---- a/src/import/hack/lib/golang.sh
> -+++ b/src/import/hack/lib/golang.sh
> -@@ -651,7 +651,7 @@ kube::golang::build_some_binaries() {
> +diff --git a/hack/lib/golang.sh b/hack/lib/golang.sh
> +index d0f4b00dadf..cef0c2075a3 100755
> +--- a/hack/lib/golang.sh
> ++++ b/hack/lib/golang.sh
> +@@ -654,7 +654,7 @@ kube::golang::build_some_binaries() {
> kube::golang::create_coverage_dummy_test "${package}"
> kube::util::trap_add "kube::golang::delete_coverage_dummy_test \"${package}\"" EXIT
>
> @@ -21,7 +21,7 @@ index e9148ec08fa..71d3c987563 100755
> -covermode count \
> -coverpkg k8s.io/...,k8s.io/kubernetes/vendor/k8s.io/... \
> "${build_args[@]}" \
> -@@ -663,13 +663,13 @@ kube::golang::build_some_binaries() {
> +@@ -666,13 +666,13 @@ kube::golang::build_some_binaries() {
> done
> if [[ "${#uncovered[@]}" != 0 ]]; then
> V=2 kube::log::info "Building ${uncovered[*]} without coverage..."
> @@ -37,7 +37,7 @@ index e9148ec08fa..71d3c987563 100755
> fi
> }
>
> -@@ -725,7 +725,7 @@ kube::golang::build_binaries_for_platform() {
> +@@ -730,7 +730,7 @@ kube::golang::build_binaries_for_platform() {
> testpkg=$(dirname "${test}")
>
> mkdir -p "$(dirname "${outfile}")"
> @@ -47,5 +47,5 @@ index e9148ec08fa..71d3c987563 100755
> -gcflags "${gogcflags:-}" \
> -asmflags "${goasmflags:-}" \
> --
> -2.19.1
> +2.40.0
>
> diff --git a/recipes-containers/kubernetes/kubernetes/0001-cross-don-t-build-tests-by-default.patch b/recipes-containers/kubernetes/kubernetes/0001-cross-don-t-build-tests-by-default.patch
> index 659e3013..cd5e46f1 100644
> --- a/recipes-containers/kubernetes/kubernetes/0001-cross-don-t-build-tests-by-default.patch
> +++ b/recipes-containers/kubernetes/kubernetes/0001-cross-don-t-build-tests-by-default.patch
> @@ -15,10 +15,10 @@ Signed-off-by: Bruce Ashfield <bruce.ashfield@windriver.com>
> hack/make-rules/cross.sh | 4 ++--
> 1 file changed, 2 insertions(+), 2 deletions(-)
>
> -diff --git a/src/import/hack/make-rules/cross.sh b/hack/make-rules/cross.sh
> -index 8e1e938..0898c5c 100755
> ---- a/src/import/hack/make-rules/cross.sh
> -+++ b/src/import/hack/make-rules/cross.sh
> +diff --git a/hack/make-rules/cross.sh b/hack/make-rules/cross.sh
> +index f8a6d0dbf5e..d22bf52b1cc 100755
> +--- a/hack/make-rules/cross.sh
> ++++ b/hack/make-rules/cross.sh
> @@ -33,6 +33,6 @@ make all WHAT="${KUBE_NODE_TARGETS[*]}" KUBE_BUILD_PLATFORMS="${KUBE_NODE_PLATFO
>
> make all WHAT="${KUBE_CLIENT_TARGETS[*]}" KUBE_BUILD_PLATFORMS="${KUBE_CLIENT_PLATFORMS[*]}"
> @@ -29,5 +29,5 @@ index 8e1e938..0898c5c 100755
> -make all WHAT="${KUBE_TEST_SERVER_TARGETS[*]}" KUBE_BUILD_PLATFORMS="${KUBE_TEST_SERVER_PLATFORMS[*]}"
> +#make all WHAT="${KUBE_TEST_SERVER_TARGETS[*]}" KUBE_BUILD_PLATFORMS="${KUBE_TEST_SERVER_PLATFORMS[*]}"
> --
> -2.7.4
> +2.40.0
>
> diff --git a/recipes-containers/kubernetes/kubernetes/0001-hack-lib-golang.sh-use-CC-from-environment.patch b/recipes-containers/kubernetes/kubernetes/0001-hack-lib-golang.sh-use-CC-from-environment.patch
> index 3a22a2ef..8684a94a 100644
> --- a/recipes-containers/kubernetes/kubernetes/0001-hack-lib-golang.sh-use-CC-from-environment.patch
> +++ b/recipes-containers/kubernetes/kubernetes/0001-hack-lib-golang.sh-use-CC-from-environment.patch
> @@ -11,11 +11,11 @@ Signed-off-by: Koen Kooi <koen.kooi@linaro.org>
> hack/lib/golang.sh | 4 ----
> 1 file changed, 4 deletions(-)
>
> -Index: kubernetes-v1.21.1+git45da3fc33872083fb225c1a8c4d03e530d6f7630/src/import/hack/lib/golang.sh
> -===================================================================
> ---- kubernetes-v1.21.1+git45da3fc33872083fb225c1a8c4d03e530d6f7630.orig/src/import/hack/lib/golang.sh
> -+++ kubernetes-v1.21.1+git45da3fc33872083fb225c1a8c4d03e530d6f7630/src/import/hack/lib/golang.sh
> -@@ -414,19 +414,15 @@
> +diff --git a/hack/lib/golang.sh b/hack/lib/golang.sh
> +index e16a60d1867..d0f4b00dadf 100755
> +--- a/hack/lib/golang.sh
> ++++ b/hack/lib/golang.sh
> +@@ -420,19 +420,15 @@ kube::golang::set_platform_envs() {
> ;;
> "linux/arm")
> export CGO_ENABLED=1
> @@ -35,3 +35,5 @@ Index: kubernetes-v1.21.1+git45da3fc33872083fb225c1a8c4d03e530d6f7630/src/import
> ;;
> esac
> fi
> +--
> +2.40.0
> diff --git a/recipes-containers/kubernetes/kubernetes_git.bb b/recipes-containers/kubernetes/kubernetes_git.bb
> index f475bd73..59892c92 100644
> --- a/recipes-containers/kubernetes/kubernetes_git.bb
> +++ b/recipes-containers/kubernetes/kubernetes_git.bb
> @@ -26,10 +26,10 @@ SRC_URI = "git://github.com/kubernetes/kubernetes.git;branch=release-1.23;name=k
> git://github.com/kubernetes/release;branch=master;name=kubernetes-release;destsuffix=git/release;protocol=https"
>
> SRC_URI:append = " \
> - file://0001-hack-lib-golang.sh-use-CC-from-environment.patch \
> - file://0001-cross-don-t-build-tests-by-default.patch \
> - file://0001-build-golang.sh-convert-remaining-go-calls-to-use.patch \
> - file://0001-Makefile.generated_files-Fix-race-issue-for-installi.patch \
> + file://0001-hack-lib-golang.sh-use-CC-from-environment.patch;patchdir=src/import \
> + file://0001-cross-don-t-build-tests-by-default.patch;patchdir=src/import \
> + file://0001-build-golang.sh-convert-remaining-go-calls-to-use.patch;patchdir=src/import \
> + file://0001-Makefile.generated_files-Fix-race-issue-for-installi.patch;patchdir=src/import \
> file://cni-containerd-net.conflist \
> file://k8s-init \
> file://99-kubernetes.conf \
> --
> 2.40.0
>
>
> -=-=-=-=-=-=-=-=-=-=-=-
> Links: You receive all messages sent to this group.
> View/Reply Online (#8456): https://lists.yoctoproject.org/g/meta-virtualization/message/8456
> Mute This Topic: https://lists.yoctoproject.org/mt/102555494/1050810
> Group Owner: meta-virtualization+owner@lists.yoctoproject.org
> Unsubscribe: https://lists.yoctoproject.org/g/meta-virtualization/unsub [bruce.ashfield@gmail.com]
> -=-=-=-=-=-=-=-=-=-=-=-
>
prev parent reply other threads:[~2023-11-21 4:09 UTC|newest]
Thread overview: 4+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-11-13 3:58 [meta-virtualization][kirkstone][PATCH v2 1/1] kubernetes: Adjust patches to resolve error that occur with devtool ssambu
2023-11-13 4:16 ` [meta-virtualization][kirkstone][PATCH v2 1/2] kubernetes: Fix CVE-2023-2431 ssambu
2023-11-13 4:16 ` [meta-virtualization][kirkstone][PATCH v2 2/2] kubernetes: Fix CVE-2023-2727, CVE-2023-2728 ssambu
2023-11-21 4:09 ` Bruce Ashfield [this message]
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ZVwtX7t6Vame2/3o@gmail.com \
--to=bruce.ashfield@gmail.com \
--cc=meta-virtualization@lists.yoctoproject.org \
--cc=soumya.sambu@windriver.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.