Try it yourself¶
A hands-on walkthrough with expected output at every step, plus experiments to convince yourself the verification is real. Background for each step is in how-it-works.md.
Prerequisites¶
- Go ≥ 1.26, Docker (running), kind, kubectl, openssl
- A local rookery checkout; set
CILOCK_DIRif it's not at the default path indev/attest-demo.sh, and fix thereplacepaths ingo.modto match.
The one-command version¶
make e2e
Expect the last line:
E2E PASSED: attested image admitted, unattested image denied
Everything below does the same thing manually so you can see each part.
Step 1 — cluster + registry¶
./dev/kind-up.sh
kind cluster 'cilock' ready; registry 'cilock-registry' on localhost:5001
Sanity check the registry: curl -s localhost:5001/v2/_catalog → {"repositories":[]}.
Step 2 — webhook TLS¶
./dev/gen-certs.sh # → dev/.tls/{ca.crt,tls.crt,tls.key}
Step 3 — build, attest, make the policy¶
./dev/attest-demo.sh
In the cilock run summary, find the subjects line and note the
oci/v0.1/imageid: value — that digest is the link the webhook will check:
subjects (8): …, oci/v0.1/imageid:b76c63fdc4d9…, oci/v0.1/manifestdigest:…, …
Poke at the artifacts:
# the DSSE envelope: payload + signature
jq 'keys' dev/.artifacts/build.att.json
# → ["payload","payloadType","signatures"]
# the attestation inside: a collection with material/product/oci
jq -r .payload dev/.artifacts/build.att.json | base64 -d | jq '.predicate.attestations[].type'
# the subjects the webhook searches by
jq -r .payload dev/.artifacts/build.att.json | base64 -d | jq '.subject[].name'
# the (unsigned) policy: which step requires what, signed by whom
jq '.steps' dev/.artifacts/policy.json
Step 4 — deploy the webhook¶
./dev/deploy.sh
deployment "cilock-webhook" successfully rolled out
webhook deployed
Step 5 — watch it decide¶
Terminal 1 — follow the logs:
kubectl -n cilock-system logs deploy/cilock-webhook -f
Terminal 2 — an enforced namespace:
kubectl create ns cilock-demo --dry-run=client -o yaml | kubectl apply -f -
kubectl label ns cilock-demo cilock-policy=enforce --overwrite
Allow case — the attested image:
kubectl -n cilock-demo run attested --image=localhost:5001/cilock-demo:1 --restart=Never
# → pod/attested created
Log shows the subject search finding the envelope, the signature check, and the verdict:
[verified-source] verifying 1 candidate envelope(s) for collection "build"
[verified-source] envelope … signature OK (verifier kid=…)
PASS pod=cilock-demo/attested … imageid=sha256:b76c… steps=[build]
Deny case — plain busybox:
kubectl -n cilock-demo run unattested --image=localhost:5001/unattested:1 --restart=Never
Error from server: admission webhook "cilock-webhook.cilock-system.svc" denied the request:
witness policy verification failed: … failed policy verification
In the logs, the tell-tale line is verifying 0 candidate envelope(s) — no
attestation's subjects contain busybox's digests, so there is no evidence to
verify and the policy step can't be satisfied.
Experiments¶
1. Tampered image is denied. Rebuild with any change, push, admit — the image ID changed, the old attestation no longer applies:
printf 'FROM busybox\nRUN echo tampered > /demo.txt\nCMD ["sleep","3600"]\n' | \
docker build -t localhost:5001/cilock-demo:1 -f - .
docker push localhost:5001/cilock-demo:1
kubectl -n cilock-demo run tampered --image=localhost:5001/cilock-demo:1 --restart=Never
# → denied
(Restore with ./dev/attest-demo.sh && ./dev/deploy.sh — re-attests the new
image and reloads the ConfigMaps.)
2. Unlabeled namespaces are untouched. The webhook is opt-in per namespace, so this works and never even hits the webhook:
kubectl run free --image=busybox --restart=Never -- sleep 60
3. Verify with no cluster at all. The same engine runs as a CLI:
go build -o cilock-k8s .
./cilock-k8s verify \
--policy dev/.artifacts/policy.signed.json \
--policy-key dev/.keys/policy-pub.pem \
--attestation-dir dev/.artifacts/attestations \
--insecure-registry localhost:5001 \
localhost:5001/cilock-demo:1
# → ALLOWED: … steps=[build]
4. Wrong trust anchor is rejected. Hand the webhook a different public key and even the attested image fails (policy signature can't be verified):
openssl ecparam -genkey -name prime256v1 -noout | openssl ec -pubout > /tmp/wrong-pub.pem 2>/dev/null
./cilock-k8s verify \
--policy dev/.artifacts/policy.signed.json \
--policy-key /tmp/wrong-pub.pem \
--attestation-dir dev/.artifacts/attestations \
--insecure-registry localhost:5001 \
localhost:5001/cilock-demo:1
# → DENIED
Troubleshooting¶
| symptom | cause / fix |
|---|---|
pods "attested" already exists |
leftover from a previous run: kubectl -n cilock-demo delete pod attested |
| every pod in the namespace rejected with a TLS/connect error | webhook down or cert mismatch (failurePolicy: Fail is fail-closed) — check kubectl -n cilock-system get pods and redeploy |
| webhook denies with a registry connection error | registry container IP changed (recreated registry) — re-run ./dev/deploy.sh to re-template --registry-alias |
no attestation envelopes (*.json) found on startup |
attestations ConfigMap empty — re-run ./dev/attest-demo.sh && ./dev/deploy.sh |
Teardown¶
make dev-down