Documentation
¶
Overview ¶
Package k8sperm centralizes obol-stack's cross-platform fix for the one recurring class of Kubernetes failure in this codebase: a non-root workload cannot write a freshly provisioned local-path volume.
Why this exists ¶
obol-stack persists workload state on rancher local-path-provisioner. Those PVs are hostPath-typed, and the kubelet treats hostPath volumes as "unmanaged" — it never runs SetVolumeOwnership for them, so a pod's securityContext.fsGroup and fsGroupChangePolicy are genuine NO-OPs on every local-path volume. (This holds identically on k3d/macOS, where the cluster runs inside Docker, and on native k3s/Linux — it is the same kubelet binary.)
The provisioner's setup script chowns each new volume to a single hardcoded owner (1000:1000). Any workload that runs as a different UID therefore cannot write its own volume unless something re-owns the directory first. The only mechanism that does this deterministically across both backends, regardless of volume type, is a root-privileged init container that chowns the mount to the workload's UID/GID before the main container starts. The master Hermes agent has always relied on exactly this; this package makes the same mechanism reusable so controller-spawned workloads cannot silently drift out of sync with it (the bug that left spawned agents stuck Provisioning).
Pod Security Standard constraint ¶
The returned init container runs as UID 0, which violates the "restricted" Pod Security Standard. Use it ONLY in namespaces that do not enforce restricted PSS (agent-*, hermes-*, openclaw-*, and the cluster-default namespaces). It is rejected at admission in the llm and x402 namespaces; for workloads there, align the container's runAsUser/runAsGroup with the provisioner's owner instead. See plans/volume-permission-hardening.md.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func ChownCommand ¶
ChownCommand returns the shell command a root chown init container runs: a single recursive chown of every path to uid:gid. Exposed so YAML-template renderers (e.g. the master Hermes deployment) can share the exact semantics rather than re-deriving the string and drifting.
func RootChownInitContainer ¶
func RootChownInitContainer(name, image string, uid, gid int64, mounts []ChownMount) map[string]any
RootChownInitContainer returns an init-container spec, as an unstructured map[string]any suitable for controller-rendered manifests, that runs as root and recursively chowns every mount path to uid:gid.
uid/gid MUST equal the main container's runAsUser/runAsGroup, otherwise the main (non-root) container still cannot write the volume. image should reuse the workload's own image so no extra image pull is needed; any image with a POSIX sh and chown works.
See the package documentation for the PSS constraint — do not use this in a namespace that enforces the restricted Pod Security Standard.
Types ¶
type ChownMount ¶
type ChownMount struct {
// Name is the pod volume name (must match an entry in the pod's volumes).
Name string
// MountPath is where the volume is mounted inside the init container.
MountPath string
}
ChownMount identifies a single volume mount whose contents must be re-owned to the workload's UID/GID before the main container starts.