Skip to content

Commit 93df250

Browse files
stubbiclaude
andauthored
fix: add SELinux relabel init container for persistent volumes (#41)
Kubernetes may assign MCS categories to volumes that differ from the pod's SELinux level, making data inaccessible (EACCES on mkdir/read). This is unavoidable with shared PVCs where the kubelet relabels volumes with random categories on each pod creation. Adds a privileged init container that runs chcon -R on the data mount before the app starts, ensuring labels always match the pod's context. Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent dc26f4f commit 93df250

1 file changed

Lines changed: 25 additions & 0 deletions

File tree

internal/resources/statefulset.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,31 @@ func BuildStatefulSet(instance *paperclipv1alpha1.Instance, extraPodAnnotations
6363
podSpec.TopologySpreadConstraints = instance.Spec.Availability.TopologySpreadConstraints
6464
}
6565

66+
// SELinux relabel init container: ensures the data volume labels match
67+
// the pod's SELinux context. Required because Kubernetes may assign MCS
68+
// categories to the volume that differ from the pod's level, making
69+
// the data inaccessible. Runs as privileged to perform chcon.
70+
if instance.Spec.Storage.Persistence.Enabled {
71+
seLevel := "s0"
72+
if instance.Spec.Security.PodSecurityContext != nil &&
73+
instance.Spec.Security.PodSecurityContext.SELinuxOptions != nil &&
74+
instance.Spec.Security.PodSecurityContext.SELinuxOptions.Level != "" {
75+
seLevel = instance.Spec.Security.PodSecurityContext.SELinuxOptions.Level
76+
}
77+
podSpec.InitContainers = append(podSpec.InitContainers, corev1.Container{
78+
Name: "selinux-relabel",
79+
Image: "fedora:latest",
80+
Command: []string{"chcon", "-R", "system_u:object_r:container_file_t:" + seLevel, DataMountPath},
81+
VolumeMounts: []corev1.VolumeMount{
82+
{Name: DataVolumeName, MountPath: DataMountPath},
83+
},
84+
SecurityContext: &corev1.SecurityContext{
85+
Privileged: Ptr(true),
86+
RunAsUser: Ptr(int64(0)),
87+
},
88+
})
89+
}
90+
6691
// Onboarding init container: runs non-interactive setup and admin bootstrap
6792
// before the server starts. Only runs when config doesn't exist yet.
6893
podSpec.InitContainers = append(podSpec.InitContainers, buildOnboardInitContainer(instance))

0 commit comments

Comments
 (0)