From 81105031a7bffb54b0e6f5c307866468fc5e19c7 Mon Sep 17 00:00:00 2001 From: Jan Jansen Date: Fri, 16 May 2025 13:31:09 +0200 Subject: [PATCH] copy security context from origin pod Signed-off-by: Jan Jansen --- changelogs/unreleased/8943-farodin91 | 1 + pkg/repository/maintenance/maintenance.go | 8 ++ .../maintenance/maintenance_test.go | 49 ++++++-- pkg/util/velero/velero.go | 14 +++ pkg/util/velero/velero_test.go | 114 ++++++++++++++++++ 5 files changed, 173 insertions(+), 13 deletions(-) create mode 100644 changelogs/unreleased/8943-farodin91 diff --git a/changelogs/unreleased/8943-farodin91 b/changelogs/unreleased/8943-farodin91 new file mode 100644 index 000000000..c77c1b887 --- /dev/null +++ b/changelogs/unreleased/8943-farodin91 @@ -0,0 +1 @@ +Copy security context from origin pod \ No newline at end of file diff --git a/pkg/repository/maintenance/maintenance.go b/pkg/repository/maintenance/maintenance.go index afe7d86aa..aace83d59 100644 --- a/pkg/repository/maintenance/maintenance.go +++ b/pkg/repository/maintenance/maintenance.go @@ -431,6 +431,12 @@ func buildJob(cli client.Client, ctx context.Context, repo *velerov1api.BackupRe // Get the service account from the Velero server deployment serviceAccount := veleroutil.GetServiceAccountFromVeleroServer(deployment) + // Get the security context from the Velero server deployment + securityContext := veleroutil.GetContainerSecurityContextsFromVeleroServer(deployment) + + // Get the pod security context from the Velero server deployment + podSecurityContext := veleroutil.GetPodSecurityContextsFromVeleroServer(deployment) + // Get image image := veleroutil.GetVeleroServerImage(deployment) @@ -506,10 +512,12 @@ func buildJob(cli client.Client, ctx context.Context, repo *velerov1api.BackupRe EnvFrom: envFromSources, VolumeMounts: volumeMounts, Resources: resources, + SecurityContext: securityContext, TerminationMessagePolicy: corev1api.TerminationMessageFallbackToLogsOnError, }, }, RestartPolicy: corev1api.RestartPolicyNever, + SecurityContext: podSecurityContext, Volumes: volumes, ServiceAccountName: serviceAccount, Tolerations: []corev1api.Toleration{ diff --git a/pkg/repository/maintenance/maintenance_test.go b/pkg/repository/maintenance/maintenance_test.go index c77a231f7..ea830391a 100644 --- a/pkg/repository/maintenance/maintenance_test.go +++ b/pkg/repository/maintenance/maintenance_test.go @@ -40,6 +40,7 @@ import ( "github.com/vmware-tanzu/velero/pkg/builder" "github.com/vmware-tanzu/velero/pkg/repository/provider" velerotest "github.com/vmware-tanzu/velero/pkg/test" + "github.com/vmware-tanzu/velero/pkg/util/boolptr" "github.com/vmware-tanzu/velero/pkg/util/kube" "github.com/vmware-tanzu/velero/pkg/util/logging" @@ -875,10 +876,16 @@ func TestBuildJob(t *testing.T) { Spec: appsv1api.DeploymentSpec{ Template: corev1api.PodTemplateSpec{ Spec: corev1api.PodSpec{ + SecurityContext: &corev1api.PodSecurityContext{ + RunAsNonRoot: boolptr.True(), + }, Containers: []corev1api.Container{ { Name: "velero-repo-maintenance-container", Image: "velero-image", + SecurityContext: &corev1api.SecurityContext{ + RunAsNonRoot: boolptr.True(), + }, Env: []corev1api.EnvVar{ { Name: "test-name", @@ -908,21 +915,25 @@ func TestBuildJob(t *testing.T) { }, } - deploy2 := deploy + deploy2 := deploy.DeepCopy() deploy2.Spec.Template.Labels = map[string]string{"azure.workload.identity/use": "fake-label-value"} + deploy2.Spec.Template.Spec.SecurityContext = nil + deploy2.Spec.Template.Spec.Containers[0].SecurityContext = nil testCases := []struct { - name string - m *JobConfigs - deploy *appsv1api.Deployment - logLevel logrus.Level - logFormat *logging.FormatFlag - thirdPartyLabel map[string]string - expectedJobName string - expectedError bool - expectedEnv []corev1api.EnvVar - expectedEnvFrom []corev1api.EnvFromSource - expectedPodLabel map[string]string + name string + m *JobConfigs + deploy *appsv1api.Deployment + logLevel logrus.Level + logFormat *logging.FormatFlag + thirdPartyLabel map[string]string + expectedJobName string + expectedError bool + expectedEnv []corev1api.EnvVar + expectedEnvFrom []corev1api.EnvFromSource + expectedPodLabel map[string]string + expectedSecurityContext *corev1api.SecurityContext + expectedPodSecurityContext *corev1api.PodSecurityContext }{ { name: "Valid maintenance job without third party labels", @@ -964,6 +975,12 @@ func TestBuildJob(t *testing.T) { expectedPodLabel: map[string]string{ RepositoryNameLabel: "test-123", }, + expectedSecurityContext: &corev1api.SecurityContext{ + RunAsNonRoot: boolptr.True(), + }, + expectedPodSecurityContext: &corev1api.PodSecurityContext{ + RunAsNonRoot: boolptr.True(), + }, }, { name: "Valid maintenance job with third party labels", @@ -975,7 +992,7 @@ func TestBuildJob(t *testing.T) { MemoryLimit: "256Mi", }, }, - deploy: &deploy2, + deploy: deploy2, logLevel: logrus.InfoLevel, logFormat: logging.NewFormatFlag(), expectedJobName: "test-123-maintain-job", @@ -1006,6 +1023,8 @@ func TestBuildJob(t *testing.T) { RepositoryNameLabel: "test-123", "azure.workload.identity/use": "fake-label-value", }, + expectedSecurityContext: nil, + expectedPodSecurityContext: nil, }, { name: "Error getting Velero server deployment", @@ -1083,6 +1102,10 @@ func TestBuildJob(t *testing.T) { assert.Equal(t, tc.expectedEnv, container.Env) assert.Equal(t, tc.expectedEnvFrom, container.EnvFrom) + // Check security context + assert.Equal(t, tc.expectedPodSecurityContext, job.Spec.Template.Spec.SecurityContext) + assert.Equal(t, tc.expectedSecurityContext, container.SecurityContext) + // Check resources expectedResources := corev1api.ResourceRequirements{ Requests: corev1api.ResourceList{ diff --git a/pkg/util/velero/velero.go b/pkg/util/velero/velero.go index c0ab9771d..d4e0f70cb 100644 --- a/pkg/util/velero/velero.go +++ b/pkg/util/velero/velero.go @@ -65,6 +65,20 @@ func GetVolumeMountsFromVeleroServer(deployment *appsv1api.Deployment) []corev1a return nil } +// GetPodSecurityContextsFromVeleroServer get the pod security context from the Velero server deployment +func GetPodSecurityContextsFromVeleroServer(deployment *appsv1api.Deployment) *corev1api.PodSecurityContext { + return deployment.Spec.Template.Spec.SecurityContext +} + +// GetContainerSecurityContextsFromVeleroServer get the security context from the Velero server deployment +func GetContainerSecurityContextsFromVeleroServer(deployment *appsv1api.Deployment) *corev1api.SecurityContext { + for _, container := range deployment.Spec.Template.Spec.Containers { + // We only have one container in the Velero server deployment + return container.SecurityContext + } + return nil +} + // GetVolumesFromVeleroServer get the volumes from the Velero server deployment func GetVolumesFromVeleroServer(deployment *appsv1api.Deployment) []corev1api.Volume { return deployment.Spec.Template.Spec.Volumes diff --git a/pkg/util/velero/velero_test.go b/pkg/util/velero/velero_test.go index 4090daeaa..42948d890 100644 --- a/pkg/util/velero/velero_test.go +++ b/pkg/util/velero/velero_test.go @@ -28,6 +28,7 @@ import ( velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1" "github.com/vmware-tanzu/velero/pkg/builder" + "github.com/vmware-tanzu/velero/pkg/util/boolptr" ) func TestGetNodeSelectorFromVeleroServer(t *testing.T) { @@ -521,6 +522,119 @@ func TestGetVolumesFromVeleroServer(t *testing.T) { } } +func TestGetPodSecurityContextsFromVeleroServer(t *testing.T) { + tests := []struct { + name string + deploy *appsv1api.Deployment + want *corev1api.PodSecurityContext + }{ + { + name: "no security context", + deploy: &appsv1api.Deployment{ + Spec: appsv1api.DeploymentSpec{ + Template: corev1api.PodTemplateSpec{ + Spec: corev1api.PodSpec{ + SecurityContext: nil, + }, + }, + }, + }, + want: nil, + }, + { + name: "security context", + deploy: &appsv1api.Deployment{ + Spec: appsv1api.DeploymentSpec{ + Template: corev1api.PodTemplateSpec{ + Spec: corev1api.PodSpec{ + SecurityContext: &corev1api.PodSecurityContext{ + RunAsNonRoot: boolptr.True(), + }, + }, + }, + }, + }, + want: &corev1api.PodSecurityContext{ + RunAsNonRoot: boolptr.True(), + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := GetPodSecurityContextsFromVeleroServer(test.deploy) + assert.Equal(t, test.want, got) + }) + } +} + +func TestGetContainerSecurityContextsFromVeleroServer(t *testing.T) { + tests := []struct { + name string + deploy *appsv1api.Deployment + want *corev1api.SecurityContext + }{ + { + name: "no container", + deploy: &appsv1api.Deployment{ + Spec: appsv1api.DeploymentSpec{ + Template: corev1api.PodTemplateSpec{ + Spec: corev1api.PodSpec{ + Containers: []corev1api.Container{}, + }, + }, + }, + }, + want: nil, + }, + { + name: "no security context", + deploy: &appsv1api.Deployment{ + Spec: appsv1api.DeploymentSpec{ + Template: corev1api.PodTemplateSpec{ + Spec: corev1api.PodSpec{ + Containers: []corev1api.Container{ + { + SecurityContext: nil, + }, + }, + }, + }, + }, + }, + want: nil, + }, + { + name: "security context", + deploy: &appsv1api.Deployment{ + Spec: appsv1api.DeploymentSpec{ + Template: corev1api.PodTemplateSpec{ + Spec: corev1api.PodSpec{ + Containers: []corev1api.Container{ + { + SecurityContext: &corev1api.SecurityContext{ + RunAsNonRoot: boolptr.True(), + }, + }, + }, + }, + }, + }, + }, + want: &corev1api.SecurityContext{ + RunAsNonRoot: boolptr.True(), + }, + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + got := GetContainerSecurityContextsFromVeleroServer(test.deploy) + assert.Equal(t, test.want, got) + }) + } +} + func TestGetServiceAccountFromVeleroServer(t *testing.T) { tests := []struct { name string