chore(canary-phase8): mysql fake TCP server complete — generator set CLOSED
Phase 8 ships the SEVENTH AND FINAL generator. All 7 token types (webbug, slowredirect, docx, pdf, kubeconfig, envfile, mysql) are now registered; the canary token-type set is closed. Phases 9-18 are service/handler/wire-up/notification/manage-page/admin/geoip/frontend work — no more generators. mysql is the only non-HTTP generator: the trigger fires when an attacker tries to authenticate against our fake MySQL server using the canary-issued connection string. We accept the TCP connection, write a real-looking HandshakeV10 packet (5.7.40-canary banner + mysql_native_password auth plugin + valid capability mask), read the client's HandshakeResponse41, extract the username (which is "canary_"+token.ID per spec §9.7 line 1341), look up the token by stripping the prefix, record an event with the kubectl-equivalent forensic fields (mysql_username + mysql_client_capabilities formatted as 0x%08x + mysql_client_charset), and respond with the canonical ERR_Packet 1045 "Access denied" message before closing. To the attacker the response is byte-indistinguishable from a real MySQL server rejecting bad credentials. Implementation commits in this phase: -712202abfeat(canary): mysql wire protocol (handshake + auth + ERR packets) -4d4d4951feat(canary): mysql server + connection handler -fe80c94afeat(canary): mysql generator + register in registry Deliverables: - protocol.go: byte-exact HandshakeV10 builder per spec §9.7 lines 1366-1379 (3-byte LE length + seq id, protocol version 0x0a, "5.7.40-canary\x00", random conn id, 8+12 split auth-plugin-data, capability flags 0xf7ff/0x81ff, charset 0x21, status 0x0002, auth-plugin-data length 0x15, mysql_native_password); ClientAuth parser for HandshakeResponse41; BuildAccessDeniedErr ERR_Packet with code 1045 + SQL state 28000; crypto/rand-backed NewRandomAuthData (20 bytes) + NewRandomConnectionID (uint32); error sentinels (ErrShortPacket, ErrPacketTooLarge, ErrInvalidPayload, ErrUsernameMissing, ErrHTTPTriggerNotSupported) - handler.go: HandleConnection per spec §9.7 lines 1410-1442 — 10-second deadline, handshake-first, defense-in-depth silent drops for non-canary username / unknown token / lookup error / nil recorder; TokenLookup + EventRecorder interfaces decouple from Phase 9/10 services - server.go: ctx-aware net.ListenConfig listener, per-connection goroutines drained via WaitGroup on shutdown, net.ErrClosed silenced during graceful close - generator.go: New() + NewWithAddress(host, port); Generate writes mysql:// connection string and persists mysql_username into t.Metadata via setMySQLUsername (map[string]json.RawMessage merge — sanctioned deviation from spec §9.7 line 1343's map[string]any indexing because real Token.Metadata is json.RawMessage); Trigger returns ErrHTTPTriggerNotSupported (TCP-only, no HTTP route) - Registry expanded 6 → 7; TestBuild_AllSevenGeneratorsRegistered closes the cardinality-test sequence (no more "pending types" list — the set is complete) ~115 test cases added across 4 test files: - protocol_test.go (~24): byte-exact packet header re-decode, LE conn-id round-trip, 5-username Build/Read round-trip, error paths (short, missing terminator, empty reader, oversize), randomness distinctness for both crypto/rand helpers - handler_test.go (~10): real socket pairs (net.Listen + Dial), handshake-first assertion, non-canary username silent drop, full known-token flow with ERR packet + event Extra capture, unknown token silent, lookup error silent, nil EventRecorder tolerated, bad auth packet silent - server_test.go (~5): nil handler rejection, real-dial dispatch, 10 concurrent connections, ctx cancellation graceful shutdown, invalid address rejection - generator_test.go (~11): Type, KindConnectionString, default + custom address, canary_ prefix invariant, metadata persistence on empty token, existing-fields preservation (non-string field round-trips), malformed-metadata recovery, stale-username overwrite, baseURL irrelevance, Trigger sentinel error path (happy + nil token) Audit outcome (2 agents in parallel per standing pattern): - superpowers:code-reviewer: PASS-WITH-NITS (0 BLOCKER, 0 SHOULD-FIX, 8 NITs all explicitly marked "not worth fixing" / "leave as-is" / "theoretical, never happens") - general-purpose spec-adherence vs §9.7 + sanctioned deviations: PASS (0 BLOCKER, 0 SHOULD-FIX, 0 NITs — confirmed both sanctioned deviations matched the slowredirect/envfile metadata pattern and the TCP-only Trigger shape) No audit-fix commit this phase. Matching Phase 5 / Phase 7 pattern (Phase 6 was the only phase to require an audit-fix commit before rollup). DEFERRED TO PHASE 9 (sanctioned per handoff anti-relitigation): - Task 8.5: wire `go mysql.Run(ctx, addr, handler)` into cmd/canary/main.go via cfg.MySQL.Enabled + cfg.MySQL.Addr. Requires config.Config.MySQL field which lands in Phase 9 alongside config.Config.Canary.BaseURL. Wiring it in Phase 8 would require partial config additions Phase 9 then re-touches. - Task 8.6: manual `mysql -h ... -u canary_<id> -ptest internal_db` smoke test. Operator-deferred per standing rule. Pre-rollup acceptance gate clean at HEAD fe80c94a: - go build ./... + go vet ./... clean - go test -race -timeout=60s ./... all packages pass - go test -tags=integration -race -timeout=300s ./internal/token/... ./internal/event/... pass - golangci-lint run ./... → 0 issues. - grep -rn "//nolint" --include="*.go" returns nothing - BACKLOG.md open section still _(none)_ - go.mod surface unchanged (no new deps for the entire phase) # GENERATOR SET CLOSED webbug (Phase 2) — /c/{id} GIF, simplest case slowredirect (Phase 3) — HTML page + fingerprint endpoint docx (Phase 4) — zip-patched OOXML INCLUDEPICTURE pdf (Phase 5) — byte-replace fixed-width URI action kubeconfig (Phase 6) — YAML + fake K8s /k/{id}/* handler envfile (Phase 7) — shuffled bait + INTERNAL_METRICS_ENDPOINT mysql (Phase 8) — TCP wire protocol + ERR_Packet 1045 Forward state for Phase 9 (token service + handlers + Turnstile + fingerprint mw): wires the registry into cmd/canary/main.go, brings config.Config.Canary{BaseURL} + config.Config.MySQL{Enabled,Addr, PublicHost,PublicPort}, defines token.Service.Create (id+manage_id generation, registry dispatch, repository persistence) and token.Handler (POST /api/tokens, GET /tokens/types, route mounting for /c/{id} + /c/{id}/fingerprint + /k/{id}/*), promotes the realIP helper from webbug into middleware, adds turnstile verification + fingerprint rate-limit middleware. Closes Phase 8's deferred task 8.5 by spawning `go mysql.Run(...)` from main.go.
This commit is contained in:
parent
fe80c94a73
commit
47907a3eb4