audit-core/Makefile

73 lines
2.9 KiB
Makefile
Raw Normal View History

2026-06-01 23:44:03 +02:00
SHELL := /usr/bin/env bash
.DEFAULT_GOAL := help
AUDIT_CORE_MOCK_DIR ?= /tmp/audit-core
test: ## Run unit tests
python3 -m pytest -q
mock-audit-smoke: ## Write one non-secret smoke event to the mock audit backend
AUDIT_CORE_MOCK_DIR="$(AUDIT_CORE_MOCK_DIR)" python3 -m audit_core emit \
--source audit-core \
--action audit_core.mock_backend.smoke \
--resource "$(AUDIT_CORE_MOCK_DIR)" \
--outcome success \
--detail backend=mock-file
mock-audit-cleanup: ## Remove mock audit files older than the retention window
AUDIT_CORE_MOCK_DIR="$(AUDIT_CORE_MOCK_DIR)" python3 -m audit_core cleanup
help: ## Show this help
@awk 'BEGIN {FS = ":.*##"; printf "\nUsage:\n make \033[36m<target>\033[0m\n"} \
/^[a-zA-Z_-]+:.*?##/ { printf " \033[36m%-24s\033[0m %s\n", $$1, $$2 }' $(MAKEFILE_LIST)
.PHONY: test test-pg pg-test-up pg-test-down failure-matrix mock-audit-smoke mock-audit-cleanup \
image-build image-publish deploy-dry-run help
IMAGE_REGISTRY ?= forgejo.coulomb.social/coulomb/audit-core
GIT_COMMIT := $(shell git rev-parse HEAD)
GIT_COMMIT_SHORT := $(shell git rev-parse --short HEAD)
# railiance01 k3s API is forwarded by ops-bridge tunnel k3s-api-railiance01.
KUBECONFIG_RAILIANCE ?= $(HOME)/.kube/config-hosteurope
image-build: ## Build the immutable image, labelled with the current commit
docker build -f Containerfile \
--build-arg GIT_COMMIT=$(GIT_COMMIT) \
-t $(IMAGE_REGISTRY):$(GIT_COMMIT_SHORT) \
-t $(IMAGE_REGISTRY):$(GIT_COMMIT) \
.
image-publish: image-build ## Push the commit-tagged image to Forgejo
docker push $(IMAGE_REGISTRY):$(GIT_COMMIT_SHORT)
docker push $(IMAGE_REGISTRY):$(GIT_COMMIT)
@echo "Pin the printed digest in deploy/audit-core.yaml and deploy/migrate-job.yaml"
deploy-dry-run: ## Server-side validate the railiance01 manifests
KUBECONFIG=$(KUBECONFIG_RAILIANCE) kubectl apply --dry-run=server --validate=strict \
-f deploy/audit-core.yaml \
-f deploy/networkpolicies.yaml \
-f deploy/clustersecretstore.yaml \
-f deploy/externalsecrets.yaml \
-f deploy/migrate-job.yaml
Add the PostgreSQL audit backend and a shared conformance suite AUDIT-WP-0005-T01, built and verified against PostgreSQL 16 locally in Docker; the Railiance cluster was not needed. tests/test_backend_conformance.py is one suite run against every backend, so "the Postgres backend is done" means it satisfies the same contract SQLite already does rather than having its own green tests. It skips cleanly with no server reachable; make pg-test-up and make test-pg run it. Suite 50 -> 71. RetentionPolicy declares immutable=True and earns it: migration 0002 installs a trigger rejecting UPDATE and DELETE on the events table, so a leaked runtime credential can append but cannot rewrite or erase the trail. That materially narrows the residual risk ADR-0001 section 5 called out. tamper_evidence stays False because nothing here would prove a database owner had dropped the trigger - hash-chaining or external anchoring would be needed and is not implemented. Idempotency is one statement (INSERT ... ON CONFLICT DO NOTHING RETURNING), verified to behave identically to the SQLite backend under 12 concurrent submissions of the same event. Migrations are ordered, recorded and idempotent. Replay reconciles rather than duplicating - the piece deferred out of WP-0004-T05 - and is tested to leave exactly one custody record. Backend selection is by AUDIT_CORE_DATABASE_URL; the SQLite fallback logs a warning so a deployment that lost its URL is visible rather than quietly running on the wrong store. Also fixed: ingestion had no __main__ guard, so python -m audit_core.ingestion silently did nothing. Found during end-to-end smoke. Counting semantics documented: occurrences counts transmissions, not stored events, so a retry of a secret-shaped field increments it again. That is the sender behaviour being optimized away. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 17:09:46 +02:00
PG_TEST_CONTAINER ?= ac-pg-test
PG_TEST_PORT ?= 55445
PG_TEST_URL ?= postgresql://postgres:test@127.0.0.1:$(PG_TEST_PORT)/audit_core
pg-test-up: ## Start a throwaway PostgreSQL for backend conformance tests
-docker rm -f $(PG_TEST_CONTAINER) 2>/dev/null
docker run -d --name $(PG_TEST_CONTAINER) -e POSTGRES_PASSWORD=test \
-e POSTGRES_DB=audit_core -p 127.0.0.1:$(PG_TEST_PORT):5432 postgres:16-alpine
@for i in $$(seq 1 40); do \
docker exec $(PG_TEST_CONTAINER) pg_isready -U postgres >/dev/null 2>&1 && exit 0; \
sleep 1; done; echo "postgres did not become ready" >&2; exit 1
pg-test-down: ## Remove the throwaway PostgreSQL
-docker rm -f $(PG_TEST_CONTAINER)
test-pg: ## Run the suite including PostgreSQL conformance (needs pg-test-up)
AUDIT_CORE_TEST_DATABASE_URL="$(PG_TEST_URL)" python3 -m pytest -q
Make the failure matrix an executable harness AUDIT-WP-0005-T05 (progress). scripts/failure_matrix.py, make failure-matrix. Two modes: MODE=local stands up PostgreSQL and the receiver in Docker and runs all 15 scenarios including infrastructure disruption; MODE=remote targets a deployed receiver and skips disruption unless DISRUPT=1, since restarting a production database is not this script's call. Rehearsed locally: 15 passed, 0 failed. Delivery and reconciliation, rejection and dead-letter visibility, redaction with per-path counting, correlation lookup, privilege separation both directions, credential rotation mid-ingestion with no delivery gap, operator replay and duplicate replay, receiver unavailability with sender retry, and a database restart mid-ingestion where 5 of 7 attempts were acknowledged and all 5 survived. Two deliberate choices. Stored-record counts are read straight from the database rather than through the API, because the assertion is about what is stored and asking the service to vouch for itself is weaker evidence. The retry policy retries 503/500 and treats 400/401/403/409 as terminal, which is the documented response contract - so what is under test is a sender that follows it. Harness credibility checked rather than assumed: exit 0 on success, exit 2 against an unreachable receiver rather than passing silently, and the evidence JSON carries no tokens, credentials or event payloads so it can go to NK-WP-0024 as-is. The local rehearsal is not a substitute for the live run: it does not exercise CNPG failover, NetworkPolicy enforcement, or OpenBao-leased credentials. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-10 17:49:32 +02:00
failure-matrix: ## Run the delivery/retry/replay failure matrix (AUDIT-WP-0005-T05)
python3 scripts/failure_matrix.py