Skip to content

fix: add spec.security.seLinuxRelabel opt-out for relabel init container - #98

Merged
stubbi merged 3 commits into
mainfrom
fix/selinux-relabel-opt-out
Jul 12, 2026
Merged

fix: add spec.security.seLinuxRelabel opt-out for relabel init container#98
stubbi merged 3 commits into
mainfrom
fix/selinux-relabel-opt-out

Conversation

@stubbi

@stubbi stubbi commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Root cause

internal/resources/podtemplate.go unconditionally appends a privileged selinux-relabel init container whenever persistence is enabled. The only gate was PersistenceEnabled(instance).

The init container runs chcon -R system_u:object_r:container_file_t:<level> /paperclip to make the data volume's MCS categories match the pod's SELinux level. On SELinux-enforcing nodes this is required. But on clusters where relabeling does not apply, chcon fails permanently:

  • NFS-backed storage (relabeling is not supported on NFS)
  • Nodes that are not SELinux-enforcing, e.g. Ubuntu with AppArmor

In those cases chcon returns Operation not supported, the init container never succeeds, the pod is stuck in Init:CrashLoopBackOff, and the Service returns 503. There was no CRD field to disable it (issue #96).

The fix: an opt-out CRD field

Added a backward-compatible opt-out field:

  • Field: spec.security.seLinuxRelabel (*bool)
  • Location: SecuritySpec in api/v1alpha1/paperclipinstance_types.go. Chosen because the relabel is a security concern and the init container already reads its SELinux level from spec.security.podSecurityContext.seLinuxOptions -- this is the least-surprising home and sits next to the sibling networkPolicy.enabled / persistence enabled opt-out fields.
  • Default: true (kubebuilder default), which preserves today's behavior: the init container is still added whenever persistence is enabled. Existing SELinux-enforcing clusters are therefore completely unaffected.
  • Pointer (*bool) so an explicit false survives marshaling instead of being dropped by omitempty and re-defaulted to true by the API server on every controller update (same bug class already documented for PersistenceSpec.Enabled and NetworkPolicySpec.Enabled).

CRD field

// +kubebuilder:default=true
// +optional
SELinuxRelabel *bool `json:"seLinuxRelabel,omitempty"`

Gate

New resolver in internal/resources/common.go (nil => true), mirroring NetworkPolicyEnabled:

func SELinuxRelabelEnabled(instance *paperclipv1alpha1.Instance) bool {
    if instance.Spec.Security.SELinuxRelabel == nil {
        return true
    }
    return *instance.Spec.Security.SELinuxRelabel
}

Podtemplate gate changed from:

if PersistenceEnabled(instance) {

to:

if PersistenceEnabled(instance) && SELinuxRelabelEnabled(instance) {

Everything else about the init container is identical.

Why existing SELinux clusters stay unaffected

Unset (the default for every existing CR) resolves to true, so the init container is added exactly as before. We deliberately did not make chcon || true -- on genuinely SELinux-enforcing clusters that would mask real relabel failures and let pods start with mislabeled data. The explicit opt-out is the non-surprising fix.

Future enhancement (out of scope for this PR): auto-detect applicability (storage class / node SELinux mode) instead of requiring the operator to opt out.

Tests

Table-driven unit test TestBuildStatefulSetSELinuxRelabelOptOut in internal/resources/resources_test.go:

  • persistence enabled + field unset => selinux-relabel init container present (legacy preserved)
  • persistence enabled + field=false => init container absent
  • persistence enabled + field=true => present
  • persistence disabled + field unset => absent (no PVC to relabel)

E2E: added a low-effort assertion + stsInitContainerNames helper in test/e2e/e2e_test.go on the existing e2e-boot spec (persistence on, field unset) that the selinux-relabel init container is present. Not run locally (requires a Kind cluster + real app image); CI e2e will exercise it.

Docs

README.md: new "SELinux Relabel" subsection under Security documenting the field, its true default, and when to set it false (NFS / non-SELinux clusters).

Generated files

Ran make generate && make manifests && make sync-chart-crds; committed:

  • api/v1alpha1/zz_generated.deepcopy.go
  • config/crd/bases/paperclip.inc_instances.yaml
  • charts/paperclip-operator/templates/crds/paperclip.inc_instances.yaml

Re-running the make targets produces no drift.

Verification

Command Result
gofmt -l . PASS (no files)
go vet ./... PASS
go build ./... PASS
go test ./internal/resources/ PASS (incl. new table test)
git status after re-running make targets clean, no drift
golangci-lint run ./... SKIPPED (not installed locally; CI runs v2.1.0)
e2e / envtest / scorecard SKIPPED (need Kind cluster / envtest binaries; CI runs them)

Closes #96

🤖 Generated with Claude Code

stubbi and others added 3 commits July 2, 2026 09:56
The operator unconditionally adds a privileged "selinux-relabel" init
container whenever persistence is enabled. On clusters where SELinux
relabeling does not apply (NFS-backed storage, or nodes that are not
SELinux-enforcing such as Ubuntu with AppArmor), the container's chcon
fails permanently with "Operation not supported", leaving the pod stuck
in Init:CrashLoopBackOff and the Service returning 503. There was no way
to disable it via the Instance CRD.

Add a backward-compatible opt-out field, spec.security.seLinuxRelabel
(*bool, default true). The init container is now gated on both
PersistenceEnabled and SELinuxRelabelEnabled. Unset preserves today's
behavior so existing SELinux-enforcing clusters are unaffected; an
explicit false skips the init container entirely.

Closes #96

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@stubbi
stubbi enabled auto-merge (squash) July 12, 2026 20:36
@stubbi
stubbi merged commit d91ccb0 into main Jul 12, 2026
14 checks passed
@stubbi
stubbi deleted the fix/selinux-relabel-opt-out branch July 12, 2026 20:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

selinux-relabel init container breaks pods on NFS / non-SELinux nodes (no opt-out)

1 participant