diff --git a/deploy/attest-cronjob.yaml b/deploy/attest-cronjob.yaml index 675ab35..8312b33 100644 --- a/deploy/attest-cronjob.yaml +++ b/deploy/attest-cronjob.yaml @@ -105,7 +105,7 @@ spec: type: RuntimeDefault containers: - name: attest - image: forgejo.coulomb.social/coulomb/audit-core@sha256:c82e0442de0fd181342916ae9cd5d6de41d859e1efda637bd93936c67873afa5 + image: forgejo.coulomb.social/coulomb/audit-core@sha256:ec15f63d49226bfe507af2bc38ffbd5e83f549ba2c6d1ae7a338e7e053f34615 imagePullPolicy: IfNotPresent command: - python @@ -126,7 +126,9 @@ spec: - name: AUDIT_CORE_AUTO_MIGRATE value: "0" resources: - requests: {cpu: 50m, memory: 64Mi} + # 10m so a one-shot proof job can schedule on a node packed + # to 99% CPU requests; 50m would Pending (Insufficient cpu). + requests: {cpu: 10m, memory: 64Mi} limits: {cpu: 500m, memory: 256Mi} securityContext: allowPrivilegeEscalation: false @@ -146,18 +148,9 @@ spec: secret: secretName: audit-core-database defaultMode: 0440 ---- -# Created empty by the operator so the CronJob's Role needs no `create`, and -# so the Deployment can mount it before the first run. An absent or undated -# attestation degrades the claim rather than breaking the receiver — that is -# T01's `no_attestation` path, and it is the correct behaviour on day one. -apiVersion: v1 -kind: ConfigMap -metadata: - name: audit-core-chain-head - namespace: audit-core - labels: - app.kubernetes.io/name: audit-core - app.kubernetes.io/component: attest -data: - chain-head.json: "{}" +# ConfigMap audit-core-chain-head is NOT in this file. Applying a placeholder +# `{}` would overwrite a live attestation. Bootstrap once if absent: +# kubectl -n audit-core create configmap audit-core-chain-head \ +# --from-literal=chain-head.json='{}' +# kubectl -n audit-core label configmap audit-core-chain-head \ +# app.kubernetes.io/name=audit-core app.kubernetes.io/component=attest diff --git a/deploy/networkpolicies.yaml b/deploy/networkpolicies.yaml index 9841129..1b3aa2a 100644 --- a/deploy/networkpolicies.yaml +++ b/deploy/networkpolicies.yaml @@ -199,10 +199,10 @@ spec: ports: - {protocol: UDP, port: 53} - {protocol: TCP, port: 53} - # kube-apiserver. On this single-node k3s cluster the API server is the - # host itself, so this is a host-network destination rather than a pod - # selector; narrow it to the API port. + # kube-apiserver. In-cluster clients use kubernetes.default.svc:443; + # the host listener is 6443. Both, and no other ports. - ports: + - {protocol: TCP, port: 443} - {protocol: TCP, port: 6443} --- apiVersion: networking.k8s.io/v1 diff --git a/docs/evidence/2026-09-15-t12-attest-run.json b/docs/evidence/2026-09-15-t12-attest-run.json new file mode 100644 index 0000000..141f0cf --- /dev/null +++ b/docs/evidence/2026-09-15-t12-attest-run.json @@ -0,0 +1,23 @@ +{ + "task": "AUDIT-WP-0009-T12", + "observed_at": "2026-09-15T19:15:40+00:00", + "job": "audit-core-attest-t12", + "job_succeeded": true, + "image": "forgejo.coulomb.social/coulomb/audit-core@sha256:ec15f63d49226bfe507af2bc38ffbd5e83f549ba2c6d1ae7a338e7e053f34615", + "published": true, + "schema": "audit-core.chain-head.v1", + "event_count": 59, + "head_prefix": "bb0937f02a772d1c", + "mounted_readback": true, + "receiver_patch": "URLError", + "rbac": { + "default_sa_can_patch_chain_head": false, + "attest_sa_can_patch_chain_head": true, + "attest_sa_can_create_configmaps": false + }, + "cronjob": "audit-core-attest-chain", + "schedule": "17 3 * * *", + "offsite_copy": "not_written_here", + "readyz_tamper_evidence": false, + "readyz_note": "live image reports last-known claim on /readyz; the mounted file is the live attestation. Offsite copy remains RESOURCE-WP-0002-T06." +} diff --git a/tests/test_layer_conformance.py b/tests/test_layer_conformance.py index 5718405..b68f99d 100644 --- a/tests/test_layer_conformance.py +++ b/tests/test_layer_conformance.py @@ -97,3 +97,6 @@ def test_the_receiver_has_no_api_server_egress(): attest = next(d for d in documents if "name: audit-core-attest-egress" in d) assert "component: attest" in attest assert "6443" in attest + assert "443" in attest + assert "6443" not in receiver + assert "443" not in receiver diff --git a/workplans/AUDIT-WP-0009-evidence-role-conformance.md b/workplans/AUDIT-WP-0009-evidence-role-conformance.md index 9492ace..22d8547 100644 --- a/workplans/AUDIT-WP-0009-evidence-role-conformance.md +++ b/workplans/AUDIT-WP-0009-evidence-role-conformance.md @@ -9,7 +9,7 @@ flavor: implementation owner: claude topic_slug: railiance created: "2026-08-29" -updated: "2026-09-14" +updated: "2026-09-15" depends_on: - AUDIT-WP-0007 state_hub_workstream_id: "46a96b03-bc08-53b5-9c93-4071adabf734" @@ -608,14 +608,28 @@ runtime/spend gates remain separate. Factory attempts and paid calls remain 0. ```task id: AUDIT-WP-0009-T12 -status: todo +status: done priority: high state_hub_task_id: "712ba035-edad-5528-8293-56375553d85a" ``` -Residual from the source-complete T02: the 2026-09-11 receiver rollout found -no native attest CronJob or audit-core-chain-head ConfigMap. The new receiver -correctly reports tamper_evidence=false while its 32-event chain is intact. +Done 2026-09-15. Native attestor is live: ServiceAccount/Role/RoleBinding +`audit-core-attest` (named ConfigMap get/update/patch only, no create), +CronJob `audit-core-attest-chain` at 17:03 UTC, NetworkPolicy +`audit-core-attest-egress` (Postgres, DNS, API 443/6443). Empty ConfigMap +`audit-core-chain-head` was bootstrapped because it was absent; the +placeholder is no longer in the apply path so a later apply cannot clobber +a live head. One-shot Job `audit-core-attest-t12` published +`audit-core.chain-head.v1` for 59 events at 2026-09-15T19:15:40Z. Receiver +mount caught up; default SA cannot patch; attest SA cannot create ConfigMaps; +receiver API PATCH is a URLError (no API egress). CPU request is 10m so the +job schedules on the packed node. + +Offsite copy is still RESOURCE-WP-0002-T06 / operator — this job does not +write Nextcloud. In-cluster attestation is a database-owner boundary only. +`/readyz` still reports last-known `tamper_evidence: false` on the live +image; the mounted file is the live claim. Receipt: +`docs/evidence/2026-09-15-t12-attest-run.json`. Apply the reviewed separate attestor identity, exact-ConfigMap RBAC, component egress and scheduled job; bootstrap an empty ConfigMap only if absent, never overwrite a live attestation with the manifest placeholder. Prove one successful