security: reduce privilege in puppet namespace workloads (root init containers, redundant caps, APE) #307
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Problem
A cluster-wide security sweep (
kubectl get pods -A -o json, read-only) flagged thepuppetnamespace as the only Argo-managed application whose privilege is lazy/reducible rather than intrinsically required (CNI, ceph-csi, purelb, vault IPC_LOCK, traefik NET_BIND_SERVICE, node-feature-discovery, intel-gpu are all legitimately privileged and out of scope). All findings trace to raw manifests underapps/base/puppet/. Concretely:deployment_puppetboard.yaml—cert-generatorinit container runs as root with escalation enabled.securityContext: { runAsUser: 0, runAsGroup: 0, allowPrivilegeEscalation: true }(lines 132-135). The container only generates an SSL keypair into thepuppetboard-certsPVC and finishes withchown -R 1000:1000. The main puppetboard container is already fully hardened (runAsNonRoot: true, uid 1000,allowPrivilegeEscalation: false,capabilities.drop: [all]). Nothing in the init script is setuid, soallowPrivilegeEscalation: truegrants escalation for no reason, and root is only used to satisfy the trailingchown.deployment_puppetdb.yaml—create-log-dirinit container runs as root only to chown.runAsUser: 0(line 109) exists solely tomkdir … && chown 999:999 …/logs. The pod has nofsGroup, so the volume is not group-owned by 999 and the chown is the workaround.Redundant / over-broad added capabilities on the OpenVox containers.
puppetdb,puppetserver-master, andpuppetserver-compilermain containers, bothperms-and-dirsinit containers, and thegenerate-typesCronJob alldrop: [all]thenadd:the same cap set listed twice — onceCAP_-prefixed and once bare (e.g.CAP_CHOWNandCHOWN). Kubernetes normalises both spellings to the identical kernel capability, so every add-list is 100% duplicated noise. The server/compiler/cronjob lists also includeAUDIT_WRITE, which OpenVox does not use (no auditd writes).Root main containers on the OpenVox workloads.
puppetdb/puppetserver-master/puppetserver-compilermain containers set norunAsUser, so they run as the image default (root) and rely onCHOWN/SETUID/SETGID/DAC_OVERRIDEfor the entrypoint's privilege-drop to thepuppetuser. Theperms-and-dirsinit already chowns the data dirs topuppet:puppetandfsGroup: 999is set, so running the main process directly as uid 999 (removing the need for any added caps) is likely feasible.Why it matters: these are the Puppet control-plane pods (CA keys, eyaml keys, compiled catalogs). Root +
allowPrivilegeEscalation+ broad caps widen the blast radius of any RCE in puppetboard/puppetserver and violate the estate's baseline (non-root,allowPrivilegeEscalation: false, least-capability). Findings 1 and 2 are class A (privilege removable outright); 3 and 4 are class B (reducible).Proposal
All changes are in
apps/base/puppet/:deployment_puppetboard.yaml: add pod-levelspec.template.spec.securityContext.fsGroup: 1000; change thecert-generatorinitsecurityContexttorunAsUser: 1000,runAsGroup: 1000,runAsNonRoot: true,allowPrivilegeEscalation: false,capabilities.drop: [all]; delete the trailingchown -R 1000:1000 ${CERT_DIR}line (redundant oncefsGroupowns the PVC).deployment_puppetdb.yaml: add pod-levelsecurityContext.fsGroup: 999; drop thecreate-log-dirinit container torunAsUser: 999 / runAsGroup: 999 / runAsNonRoot: true / allowPrivilegeEscalation: falseand remove thechown 999:999from its args (log dir inherits the group viafsGroup).deployment_puppetdb.yaml,deployment_puppetserver-master.yaml,deployment_puppetserver-compiler.yamlmain +perms-and-dirs,cronjob_generate-types.yaml): delete the duplicateCAP_-prefixed entries (keep one canonical spelling), and dropAUDIT_WRITE.puppetdb,puppetserver-master,puppetserver-compiler): attemptrunAsUser: 999,runAsNonRoot: true,capabilities.drop: [all]with no adds; gate on the entrypoint tolerating a non-root start (OpenVox images checkid -ubefore dropping). If a workload regresses, fall back to keeping the minimalCHOWN/SETUID/SETGID/DAC_OVERRIDE/FOWNERset (de-duplicated) for that container only and document why in a comment.Acceptance
kubectl get pods -n puppet -o jsonshows: puppetboardcert-generatorand puppetdbcreate-log-dirrunning as non-root withallowPrivilegeEscalation: false; no container in the namespace carries a duplicated orAUDIT_WRITEcapability./returns 200 via its httproute); puppetdb reachesrunningat/status/v1/simple; puppetserver master and a compiler reachrunning; thegenerate-typesCronJob completes without error.privilegedor hasallowPrivilegeEscalation: true.make/ kubeconform + the repo's pre-commit and Woodpecker CI are green.