mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-08-16 04:06:06 +00:00
[release-1.17] Backport #10047: Fix restore-wait init container ignoring pod-level securityContext (#10223)
* Backport PR #10047: Fix restore-wait init container ignoring pod-level securityContext Co-authored-by: kaovilai <11228024+kaovilai@users.noreply.github.com> * Create changelog for unreleased version 10223 Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com> --------- Signed-off-by: Tiger Kaovilai <tkaovila@redhat.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: kaovilai <11228024+kaovilai@users.noreply.github.com> Co-authored-by: Tiger Kaovilai <tkaovila@redhat.com>
This commit is contained in:
co-authored by
kaovilai
copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Tiger Kaovilai
parent
98b5a28884
commit
0fb6a37bbf
@@ -0,0 +1 @@
|
||||
Fix restore-wait init container ignoring pod-level securityContext, falling back to hardcoded runAsUser 1000 instead of the workload's own uid/gid, causing fs-backup restores to deadlock at Init:0/1 on owner-restricted volumes
|
||||
@@ -190,6 +190,25 @@ func (a *PodVolumeRestoreAction) Execute(input *velero.RestoreItemActionExecuteI
|
||||
securityContext = *pod.Spec.Containers[0].SecurityContext.DeepCopy()
|
||||
securityContextSet = true
|
||||
}
|
||||
// if no configmap or container-level securityContext is set, fall back to the pod-level
|
||||
// spec.securityContext runAsUser/runAsGroup: the workload's own identity is the one that
|
||||
// wrote the restored files, so it's the one that can read them back
|
||||
if !securityContextSet && pod.Spec.SecurityContext != nil &&
|
||||
(pod.Spec.SecurityContext.RunAsUser != nil || pod.Spec.SecurityContext.RunAsGroup != nil) {
|
||||
securityContext = defaultSecurityCtx()
|
||||
if pod.Spec.SecurityContext.RunAsUser != nil {
|
||||
securityContext.RunAsUser = pod.Spec.SecurityContext.RunAsUser
|
||||
// defaultSecurityCtx() hardcodes RunAsNonRoot: true, which contradicts a pod-level
|
||||
// RunAsUser of 0 (root); defer to the pod's own RunAsNonRoot setting in that case
|
||||
if *pod.Spec.SecurityContext.RunAsUser == 0 {
|
||||
securityContext.RunAsNonRoot = pod.Spec.SecurityContext.RunAsNonRoot
|
||||
}
|
||||
}
|
||||
if pod.Spec.SecurityContext.RunAsGroup != nil {
|
||||
securityContext.RunAsGroup = pod.Spec.SecurityContext.RunAsGroup
|
||||
}
|
||||
securityContextSet = true
|
||||
}
|
||||
if !securityContextSet {
|
||||
securityContext = defaultSecurityCtx()
|
||||
}
|
||||
|
||||
@@ -154,6 +154,155 @@ func TestPodVolumeRestoreActionExecute(t *testing.T) {
|
||||
|
||||
defaultRestoreHelperImage := "velero/velero:v1.0"
|
||||
|
||||
podLevelUID := int64(999)
|
||||
podLevelGID := int64(999)
|
||||
podLevelSecurityContext := corev1api.SecurityContext{
|
||||
AllowPrivilegeEscalation: boolptr.False(),
|
||||
Capabilities: &corev1api.Capabilities{
|
||||
Drop: []corev1api.Capability{"ALL"},
|
||||
},
|
||||
SeccompProfile: &corev1api.SeccompProfile{
|
||||
Type: corev1api.SeccompProfileTypeRuntimeDefault,
|
||||
},
|
||||
RunAsUser: &podLevelUID,
|
||||
RunAsGroup: &podLevelGID,
|
||||
RunAsNonRoot: boolptr.True(),
|
||||
}
|
||||
|
||||
podWithPodLevelSecurityContext := builder.ForPod("ns-1", "my-pod").
|
||||
ObjectMeta(builder.WithAnnotations("snapshot.velero.io/myvol", "")).
|
||||
Volumes(
|
||||
builder.ForVolume("myvol").PersistentVolumeClaimSource("pvc-1").Result(),
|
||||
).
|
||||
Result()
|
||||
podWithPodLevelSecurityContext.Spec.SecurityContext = &corev1api.PodSecurityContext{RunAsUser: &podLevelUID, RunAsGroup: &podLevelGID}
|
||||
|
||||
wantPodWithPodLevelSecurityContext := builder.ForPod("ns-1", "my-pod").
|
||||
ObjectMeta(builder.WithAnnotations("snapshot.velero.io/myvol", "")).
|
||||
Volumes(
|
||||
builder.ForVolume("myvol").PersistentVolumeClaimSource("pvc-1").Result(),
|
||||
).
|
||||
InitContainers(
|
||||
newRestoreInitContainerBuilder(defaultRestoreHelperImage, "").
|
||||
Resources(&resourceReqs).
|
||||
SecurityContext(&podLevelSecurityContext).
|
||||
VolumeMounts(builder.ForVolumeMount("myvol", "/restores/myvol").Result()).
|
||||
Command([]string{"/velero-restore-helper"}).Result()).
|
||||
Result()
|
||||
wantPodWithPodLevelSecurityContext.Spec.SecurityContext = &corev1api.PodSecurityContext{RunAsUser: &podLevelUID, RunAsGroup: &podLevelGID}
|
||||
|
||||
podLevelRootUID := int64(0)
|
||||
podLevelRootSecurityContext := corev1api.SecurityContext{
|
||||
AllowPrivilegeEscalation: boolptr.False(),
|
||||
Capabilities: &corev1api.Capabilities{
|
||||
Drop: []corev1api.Capability{"ALL"},
|
||||
},
|
||||
SeccompProfile: &corev1api.SeccompProfile{
|
||||
Type: corev1api.SeccompProfileTypeRuntimeDefault,
|
||||
},
|
||||
RunAsUser: &podLevelRootUID,
|
||||
}
|
||||
|
||||
podWithPodLevelRootSecurityContext := builder.ForPod("ns-1", "my-pod").
|
||||
ObjectMeta(builder.WithAnnotations("snapshot.velero.io/myvol", "")).
|
||||
Volumes(
|
||||
builder.ForVolume("myvol").PersistentVolumeClaimSource("pvc-1").Result(),
|
||||
).
|
||||
Result()
|
||||
podWithPodLevelRootSecurityContext.Spec.SecurityContext = &corev1api.PodSecurityContext{RunAsUser: &podLevelRootUID}
|
||||
|
||||
wantPodWithPodLevelRootSecurityContext := builder.ForPod("ns-1", "my-pod").
|
||||
ObjectMeta(builder.WithAnnotations("snapshot.velero.io/myvol", "")).
|
||||
Volumes(
|
||||
builder.ForVolume("myvol").PersistentVolumeClaimSource("pvc-1").Result(),
|
||||
).
|
||||
InitContainers(
|
||||
newRestoreInitContainerBuilder(defaultRestoreHelperImage, "").
|
||||
Resources(&resourceReqs).
|
||||
SecurityContext(&podLevelRootSecurityContext).
|
||||
VolumeMounts(builder.ForVolumeMount("myvol", "/restores/myvol").Result()).
|
||||
Command([]string{"/velero-restore-helper"}).Result()).
|
||||
Result()
|
||||
wantPodWithPodLevelRootSecurityContext.Spec.SecurityContext = &corev1api.PodSecurityContext{RunAsUser: &podLevelRootUID}
|
||||
|
||||
podLevelGroupOnlyGID := int64(777)
|
||||
podLevelGroupOnlySecurityContext := corev1api.SecurityContext{
|
||||
AllowPrivilegeEscalation: boolptr.False(),
|
||||
Capabilities: &corev1api.Capabilities{
|
||||
Drop: []corev1api.Capability{"ALL"},
|
||||
},
|
||||
SeccompProfile: &corev1api.SeccompProfile{
|
||||
Type: corev1api.SeccompProfileTypeRuntimeDefault,
|
||||
},
|
||||
RunAsUser: &id,
|
||||
RunAsGroup: &podLevelGroupOnlyGID,
|
||||
RunAsNonRoot: boolptr.True(),
|
||||
}
|
||||
|
||||
podWithPodLevelGroupOnlySecurityContext := builder.ForPod("ns-1", "my-pod").
|
||||
ObjectMeta(builder.WithAnnotations("snapshot.velero.io/myvol", "")).
|
||||
Volumes(
|
||||
builder.ForVolume("myvol").PersistentVolumeClaimSource("pvc-1").Result(),
|
||||
).
|
||||
Result()
|
||||
podWithPodLevelGroupOnlySecurityContext.Spec.SecurityContext = &corev1api.PodSecurityContext{RunAsGroup: &podLevelGroupOnlyGID}
|
||||
|
||||
wantPodWithPodLevelGroupOnlySecurityContext := builder.ForPod("ns-1", "my-pod").
|
||||
ObjectMeta(builder.WithAnnotations("snapshot.velero.io/myvol", "")).
|
||||
Volumes(
|
||||
builder.ForVolume("myvol").PersistentVolumeClaimSource("pvc-1").Result(),
|
||||
).
|
||||
InitContainers(
|
||||
newRestoreInitContainerBuilder(defaultRestoreHelperImage, "").
|
||||
Resources(&resourceReqs).
|
||||
SecurityContext(&podLevelGroupOnlySecurityContext).
|
||||
VolumeMounts(builder.ForVolumeMount("myvol", "/restores/myvol").Result()).
|
||||
Command([]string{"/velero-restore-helper"}).Result()).
|
||||
Result()
|
||||
wantPodWithPodLevelGroupOnlySecurityContext.Spec.SecurityContext = &corev1api.PodSecurityContext{RunAsGroup: &podLevelGroupOnlyGID}
|
||||
|
||||
bothLevelsPodUID := int64(500)
|
||||
bothLevelsContainerUID := int64(999)
|
||||
bothLevelsContainerSecurityContext := corev1api.SecurityContext{
|
||||
AllowPrivilegeEscalation: boolptr.False(),
|
||||
Capabilities: &corev1api.Capabilities{
|
||||
Drop: []corev1api.Capability{"ALL"},
|
||||
},
|
||||
SeccompProfile: &corev1api.SeccompProfile{
|
||||
Type: corev1api.SeccompProfileTypeRuntimeDefault,
|
||||
},
|
||||
RunAsUser: &bothLevelsContainerUID,
|
||||
RunAsNonRoot: boolptr.True(),
|
||||
}
|
||||
|
||||
podWithBothLevelsSecurityContext := builder.ForPod("ns-1", "my-pod").
|
||||
ObjectMeta(builder.WithAnnotations("snapshot.velero.io/myvol", "")).
|
||||
Volumes(
|
||||
builder.ForVolume("myvol").PersistentVolumeClaimSource("pvc-1").Result(),
|
||||
).
|
||||
Containers(
|
||||
builder.ForContainer("app-container", "app-image").
|
||||
SecurityContext(&bothLevelsContainerSecurityContext).Result()).
|
||||
Result()
|
||||
podWithBothLevelsSecurityContext.Spec.SecurityContext = &corev1api.PodSecurityContext{RunAsUser: &bothLevelsPodUID}
|
||||
|
||||
wantPodWithBothLevelsSecurityContext := builder.ForPod("ns-1", "my-pod").
|
||||
ObjectMeta(builder.WithAnnotations("snapshot.velero.io/myvol", "")).
|
||||
Volumes(
|
||||
builder.ForVolume("myvol").PersistentVolumeClaimSource("pvc-1").Result(),
|
||||
).
|
||||
Containers(
|
||||
builder.ForContainer("app-container", "app-image").
|
||||
SecurityContext(&bothLevelsContainerSecurityContext).Result()).
|
||||
InitContainers(
|
||||
newRestoreInitContainerBuilder(defaultRestoreHelperImage, "").
|
||||
Resources(&resourceReqs).
|
||||
SecurityContext(&bothLevelsContainerSecurityContext).
|
||||
VolumeMounts(builder.ForVolumeMount("myvol", "/restores/myvol").Result()).
|
||||
Command([]string{"/velero-restore-helper"}).Result()).
|
||||
Result()
|
||||
wantPodWithBothLevelsSecurityContext.Spec.SecurityContext = &corev1api.PodSecurityContext{RunAsUser: &bothLevelsPodUID}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
pod *corev1api.Pod
|
||||
@@ -348,6 +497,62 @@ func TestPodVolumeRestoreActionExecute(t *testing.T) {
|
||||
VolumeMounts(builder.ForVolumeMount("myvol", "/restores/myvol").Result()).
|
||||
Command([]string{"/velero-restore-helper"}).Result()).Result(),
|
||||
},
|
||||
{
|
||||
name: "Restoring pod with pod-level securityContext (no container-level SecurityContext) uses pod-level runAsUser/runAsGroup for the restore initContainer",
|
||||
pod: podWithPodLevelSecurityContext,
|
||||
podVolumeBackups: []runtime.Object{
|
||||
builder.ForPodVolumeBackup(veleroNs, "pvb-1").
|
||||
PodName("my-pod").
|
||||
PodNamespace("ns-1").
|
||||
Volume("myvol").
|
||||
ObjectMeta(builder.WithLabels(velerov1api.BackupNameLabel, backupName)).
|
||||
SnapshotID("foo").
|
||||
Result(),
|
||||
},
|
||||
want: wantPodWithPodLevelSecurityContext,
|
||||
},
|
||||
{
|
||||
name: "Restoring pod with pod-level securityContext.runAsUser=0 does not force RunAsNonRoot on the restore initContainer",
|
||||
pod: podWithPodLevelRootSecurityContext,
|
||||
podVolumeBackups: []runtime.Object{
|
||||
builder.ForPodVolumeBackup(veleroNs, "pvb-1").
|
||||
PodName("my-pod").
|
||||
PodNamespace("ns-1").
|
||||
Volume("myvol").
|
||||
ObjectMeta(builder.WithLabels(velerov1api.BackupNameLabel, backupName)).
|
||||
SnapshotID("foo").
|
||||
Result(),
|
||||
},
|
||||
want: wantPodWithPodLevelRootSecurityContext,
|
||||
},
|
||||
{
|
||||
name: "Restoring pod with pod-level securityContext.runAsGroup only (no runAsUser) still applies the group to the restore initContainer",
|
||||
pod: podWithPodLevelGroupOnlySecurityContext,
|
||||
podVolumeBackups: []runtime.Object{
|
||||
builder.ForPodVolumeBackup(veleroNs, "pvb-1").
|
||||
PodName("my-pod").
|
||||
PodNamespace("ns-1").
|
||||
Volume("myvol").
|
||||
ObjectMeta(builder.WithLabels(velerov1api.BackupNameLabel, backupName)).
|
||||
SnapshotID("foo").
|
||||
Result(),
|
||||
},
|
||||
want: wantPodWithPodLevelGroupOnlySecurityContext,
|
||||
},
|
||||
{
|
||||
name: "Restoring pod with both container-level and pod-level SecurityContext set uses the container-level SecurityContext for the restore initContainer (container-level takes priority)",
|
||||
pod: podWithBothLevelsSecurityContext,
|
||||
podVolumeBackups: []runtime.Object{
|
||||
builder.ForPodVolumeBackup(veleroNs, "pvb-1").
|
||||
PodName("my-pod").
|
||||
PodNamespace("ns-1").
|
||||
Volume("myvol").
|
||||
ObjectMeta(builder.WithLabels(velerov1api.BackupNameLabel, backupName)).
|
||||
SnapshotID("foo").
|
||||
Result(),
|
||||
},
|
||||
want: wantPodWithBothLevelsSecurityContext,
|
||||
},
|
||||
}
|
||||
|
||||
veleroDeployment := &appsv1api.Deployment{
|
||||
|
||||
Reference in New Issue
Block a user