chore(canary-phase6): kubeconfig generator complete
Phase 6 ships the fifth generator in the canary registry plus the
first fake-API style trigger response. kubeconfig canary tokens are
rendered YAML pointing kubectl at /k/{id}; on any HTTP call to that
path, the handler records kubectl_path/method/query/ua in event.Extra
and responds with a Kubernetes-shaped 403 Status JSON whose message is
parameterized by the resource and the HTTP verb so the attacker sees
real-looking "Error from server (Forbidden): X is forbidden..." output.
Implementation commits in this phase:
- 6f994f37 feat(canary): kubeconfig generator + YAML template
- bd0c6646 feat(canary): kubeconfig fake K8s API handler
(forbidden response)
- ceec4d37 feat(canary): register kubeconfig generator in registry
- 982beafd fix(canary-phase6): address audit nit before rollup
Deliverables:
- text/template-rendered kubeconfig with cluster=prod-cluster,
user=svc-backup-reader, bearer=t.ID, server=baseURL+/k/+t.ID
- template.yaml.tmpl convention (not template.yaml) so pre-commit
check-yaml hook doesn't trip on Go template tokens
- K8s Status JSON 403 response with verb derived from HTTP method
(GET/HEAD → list, POST → create, PUT → update, PATCH → patch,
DELETE → delete, others → list) — richer than spec §9.5's
hardcoded "list", forensic enhancement
- Resource extraction via path.Base with empty/root fallback to
"resource"
- Event.Extra captures kubectl_path/method/query/ua as JSON
- Defense-in-depth: nil-token path returns 403 + valid Status JSON
+ nil event (FK-safe), response shape byte-identical to valid-
token case modulo the resource/verb message slots
- Registry expanded 4 → 5; pending list shrinks to {envfile, mysql}
- ~36 test cases including YAML round-trip parsing, 11-subcase IP
precedence parity with docx/pdf, verb-derivation table, status
JSON shape validation, nil-token shape equivalence
Audit outcome (2 agents in parallel per standing pattern):
- superpowers:code-reviewer: PASS (0 BLOCKER, 0 SHOULD-FIX, 5 NITs
all marked optional / defensible / not-actionable)
- general-purpose spec-adherence vs §9.5 + §8.5: PASS (0 BLOCKER,
0 SHOULD-FIX, 1 actionable NIT cleared in 982beafd, 2 INFO items
documenting sanctioned deferrals to later phases)
Pre-rollup acceptance gates clean at HEAD 982beafd:
- go build ./... + go vet ./... clean
- go test -race -timeout=60s ./... — all packages pass
- go test -tags=integration -race -timeout=300s ./internal/token/...
./internal/event/... — all pass
- golangci-lint run ./... → 0 issues.
- grep -rn "//nolint" --include="*.go" returns nothing
- BACKLOG.md open section still _(none)_
Forward state for Phase 7 (envfile generator + bait recipes): pure-
string-rendering generator using crypto/rand + math/rand seeded from
crypto/rand for shuffled bait sections (aws/stripe/github/db) plus
the actual canary URL line embedded as INTERNAL_METRICS_ENDPOINT.
Trigger pattern returns to webbug-style (/c/{id} GIF). No new HTTP
route shape needed.
This commit is contained in:
parent
982beafdc9
commit
a36292b786