Skip to content

v1.6: audit-mode onboarding, managed audit window, agent tooling + WebSocket exec fallback#20

Merged
SoulKyu merged 148 commits into
masterfrom
v1.6-audit-onboarding
Jul 23, 2026
Merged

v1.6: audit-mode onboarding, managed audit window, agent tooling + WebSocket exec fallback#20
SoulKyu merged 148 commits into
masterfrom
v1.6-audit-onboarding

Conversation

@SoulKyu

@SoulKyu SoulKyu commented Jul 23, 2026

Copy link
Copy Markdown
Owner

v1.6 — Audit-Mode Onboarding & cpg-Dedicated Agent Tooling (+ WebSocket exec fallback)

Full v1.6 milestone (phases 20-24, 13/13 requirements, milestone audit passed) plus the AUD-FUT-01 quick task.

What's in

  • --include-audit / include_audit (phase 20): ingest Cilium Verdict_AUDIT flows through the exact same pipeline as DROPPED — opt-in, byte-identical default behavior (regression-pinned), single zero-signal warning.
  • Cilium compat matrix + runtime detection (phase 21): PR-verified floors in README (cilium-dbg ≥ 1.15, enableDefaultDeny ≥ 1.16, proxy-visibility removed at 1.17), warn-and-proceed detection at connect (privilege-neutral), compat verdict surfaced over MCP; fixes the shipped proxy-visibility ≤ 1.19 README bug.
  • cpg bootstrap -n <ns> + readonly MCP get_bootstrap_policy (phase 22): namespaced default-deny CNP that actually enforces — enableDefaultDeny + one-element empty-rule stanzas (ingress: [{}]); the literal ingress: [] form IS CFP: Clarify the intent for policies with default deny specified with no rules cilium/cilium#35558 and is rejected by Sanitize(). Hard refusal below a determined 1.16, warn-and-proceed when undetermined. Stdout-only by design: zero new fsWriteAllowlist entries in the SEC-01 audit.
  • cpg audit-window -n <ns> --ttl (phase 23, CLI-only by explicit decision — MCP stays pure-readonly): supervised foreground window flipping per-endpoint PolicyAuditMode via pods/exec, new-endpoint watcher, revert-only-ours keyed on CiliumEndpoint UID, bounded guaranteed revert on every exit path (SIGTERM, TTL, wedged transport — possibly-stuck endpoints named on timeout). Refuses if daemon-wide audit mode is already active. TestMCPAuditReadonlyReachability stays byte-identical; new TestAuditWindowNotReachableFromMCP structurally proves the exec path is CLI-only.
  • Onboarding runbook (docs/bootstrap-runbook.md): mirrors Cilium's "Creating Policies from Verdicts", opens with a warning against daemon-wide policy-audit-mode (token confined to the warning block, test-pinned).
  • Repo-local agent tooling (phase 24): 5 skills (cpg-triage, cpg-audit-onboard, cpg-policy-review, cpg-health-report, cpg-mcp-smoke) + cpg-operator agent, written as workflow routers over live tools/list; TestSkillsConsistencyTripwire pins the 9-tool registry against drift.
  • WebSocket exec with SPDY fallback (AUD-FUT-01 quick task): audit-window exec transport is now NewFallbackExecutor(WebSocket → SPDY) with kubectl's exact fallback predicate; SEC-01 tripwire extended to all three exec constructors.

Verification

  • Full suite green under -race across all 13 packages, including both whole-program SSA/RTA structural proofs.
  • Zero new go.mod dependencies across the entire milestone.
  • Cross-phase integration audit passed 7/7 with code-path traces (.planning/milestones/v1.6-MILESTONE-AUDIT.md).
  • Every phase went through code review + fix (2 critical revert-path bugs in phase 23 caught and fixed pre-merge).

🤖 Generated with Claude Code

https://claude.ai/code/session_01UrP1o86545Nu9skZJq9BVY

SoulKyu added 30 commits July 22, 2026 09:04
- TestAggregator_AuditVerdictCount_Increments: 1 AUDIT + 1 DROPPED flow -> counter==1
- TestAggregator_AuditVerdictCount_IndependentOfIncludeAudit: counter moves with includeAudit unset (default false)
- RED: AuditVerdictCount()/SetIncludeAudit() do not exist yet (compile failure)
- includeAudit bool field + SetIncludeAudit setter (mirrors l7Enabled/SetL7Enabled)
- auditVerdictCount uint64 field + AuditVerdictCount accessor (mirrors l7DNSCount/L7DNSCount)
- Run() increments auditVerdictCount unconditionally on Verdict_AUDIT, regardless of includeAudit
- GREEN: both new counter tests pass; all pre-existing aggregator tests unaffected
- TestAggregator_ClassifiesAuditWhenEnabled: AUDIT infra flow suppressed like DROPPED when includeAudit=true (RED — gate not widened yet)
- TestAggregator_AuditNotClassifiedWhenDisabled: byte-identical default-disabled path (already holds; proves no-regression invariant)
- Site-5 gate now matches DROPPED || (includeAudit && AUDIT), non-zero DropReasonDesc unchanged
- switch class body left untouched -- downstream logic keys off DropReasonDesc/class, never Verdict
- Byte-identical default (includeAudit=false): AUDIT flows never enter classification
- GREEN: both new gate tests pass; all 30 pre-existing + new aggregator tests green under -race
- Site 5 of 5 AUD-01 verdict-filter sites: aggregator classification gate + diagnostic counter
- 2 tasks, 4 TDD commits (test/feat pairs), 30/30 aggregator tests green under -race
- Full module build clean; no ripple to other packages
…cludeAudit through pipeline

- FlowSource.StreamDroppedFlows gains includeAudit bool 4th param
- client.go buildFilters rebuilt around single verdicts slice (sites 1-3)
- file.go replay gate (site 4) widened, nonDroppedSkipped kept inside skip branch
- PipelineConfig.IncludeAudit field threads to source + agg.SetIncludeAudit
- AUD-01 warning: bare single post-g.Wait() check, no dedup map
…nterface

- 7 test-double StreamDroppedFlows implementations gain ignored 4th _ bool param
- 8 file_test.go call sites gain trailing false 4th arg
- Deviation (Rule 3, blocking): pulled forward client_test.go's 4 buildFilters
  regression-arg edits (nil/false 3rd arg) from Task 3 scope — required for
  go vet/go test ./pkg/hubble/ to pass at this task boundary, since Task 1's
  buildFilters arity change and the FlowSource interface change land in the
  same package. Task 3 adds only the new _WithAudit siblings.
- go build ./... and go vet on all 3 packages green; all pre-existing tests
  pass under -race
- TestBuildFilters_*_WithAudit asserts {DROPPED, AUDIT} (order pinned) for
  AllNamespaces, SingleNamespace, MultipleNamespaces, EmptyNamespaces
- Regression half (existing 4 tests calling buildFilters(..., false)) landed
  in the prior commit as a Task 2 blocking-issue fix; this commit adds only
  the new widened-behavior siblings
- 8/8 TestBuildFilters tests green under -race
…4 plan

- SUMMARY.md documents 3 tasks, 1 auto-fixed blocking deviation (Rule 3)
- REQUIREMENTS.md untouched: AUD-01 remains Pending (CLI/MCP flag threading
  is plan 20-04's deliverable; matches plan 20-01's precedent)
- STATE.md/ROADMAP.md excluded — orchestrator owns those writes post-wave
- 2-line fixture: DROPPED flow on port 8080, AUDIT flow on port 9090
- Both target the same production/api-server workload so both land
  in the same output CNP file, enabling byte-identical / generated-
  like-DROPPED assertions in pipeline_audit_test.go (Task 2)
…udit

- runReplayPipelineAudit helper mirrors runReplayPipeline
  (pipeline_l7_test.go) scoped to IncludeAudit, reusing newObservedLogger
  and the existing l4OnlyFixture/emptyFixture package-level consts
- TestPipeline_AuditEmpty_FiresWarning: AUD-01 warning fires exactly once
  when flag set + zero AUDIT flows observed (AC-3)
- TestPipeline_AuditDisabled_NoWarning: warning never fires when flag unset
- TestPipeline_AuditDisabled_AuditFlowsIgnored: flag-off output is
  byte-identical / AUDIT flow invisible, end-to-end (AC-2)
- TestPipeline_AuditEnabled_NoFlows_NoWarning: warning never fires on zero
  flows at all
- TestPipeline_AuditIngested_GeneratedLikeDropped: AUDIT flow generates a
  CNP rule exactly like DROPPED when flag is set (AC-1)
- All 5 pass under -race; whole pkg/hubble suite stays green
- commonflags.go: includeAudit field + f.Bool("include-audit", false, ...) registration + GetBool parse
- generate.go: IncludeAudit: f.includeAudit, in PipelineConfig literal (no preflight added, plain passthrough)
- replay.go: IncludeAudit: f.includeAudit, in PipelineConfig literal
- commonflags_test.go: TestIncludeAuditFlagParses covers generate/replay parse-true and default-false
- .gitignore: /cpg, prevents the stray root binary this task's own go build ./cmd/cpg/ verify step leaves behind
…neConfig

- mcp_tools.go: startSessionArgs.IncludeAudit with json/jsonschema tags + StartArgs threading (bool passthrough, no new validation)
- pkg/session/session.go: StartArgs.IncludeAudit field
- pkg/session/pipeline_config.go: buildPipelineConfig sets IncludeAudit from args
- pipeline_config_test.go: extends TestBuildPipelineConfig with IncludeAudit:true + new TestBuildPipelineConfig_IncludeAuditDefaultsFalse
- cmd/cpg/mcp_audit_test.go verified byte-identical (SEC-01 readonly proof untouched, zero new write verbs)
- with_audit.jsonl fixture + pipeline_audit_test.go (5 tests, all -race
  green) prove AC-1/AC-2/AC-3 for --include-audit end-to-end
- Zero production code touched; AUD-01 left Pending in REQUIREMENTS.md
  (shared across all 4 plans in phase 20 — orchestrator marks complete
  once 20-04 also lands)
… (MCP)

- Flags table: --include-audit row in the Filtering block, alongside --ignore-protocol/--ignore-drop-reason
- Offline replay section: --include-audit added to the shared-flags list + skip-description caveat
- MCP section: start_session row documents the new include_audit arg, wording matches the jsonschema tag in mcp_tools.go verbatim (Pitfall 6 — no drifting third copy)
SoulKyu added 29 commits July 22, 2026 19:38
- cpg-operator: single repo-local agent driving MCP session lifecycle,
  Bash+Read only, spawned by cpg-triage and cpg-audit-onboard
- cpg-triage: routes the live-session tool sequence (start_session ->
  get_status -> list_dropped_flows -> per-workload list_policies/get_policy/
  get_evidence -> stop_session -> get_cluster_health), delegates driving to
  cpg-operator, notes the stop-before-health ordering requirement
- cpg-audit-onboard: guides cpg bootstrap and cpg audit-window as human-run
  CLI steps (never executes them), drives capture via cpg-operator/
  start_session, ends the enforce checklist with the human kubectl-applying
  (no apply tool referenced), routes to docs/bootstrap-runbook.md sections
- cpg-policy-review: routes an offline CNP audit checklist through cpg
  explain + get_evidence, pointing at README's Explain policies and
  L7 Prerequisites sections instead of restating them
… tooling

- cpg-health-report: routes cluster-health.json (disk or get_cluster_health
  tool, states which) into a self-contained escaped inline-styled HTML
  report with node/workload breakdown and Cilium remediation links
- cpg-mcp-smoke: routes to TestMCPE2EGracefulLifecycle WITHOUT -short,
  asserts the 9-tool handshake, backtick-mentions all 9 registered tools
  (coverage floor), notes TestMCPE2EUngracefulDisconnect as companion
- README: add "## Agent tooling" section listing all 5 skills + cpg-operator
  between the MCP Server and Label selection sections
- TestSkillsConsistencyTripwire enumerates live 9-tool MCP registry via startInMemoryMCPSession
- phantom-check: backtick-quoted verb-prefixed tool refs in cpg-*/SKILL.md + cpg-operator.md must exist in registry
- coverage floor: cpg-mcp-smoke/SKILL.md must mention every registered tool
- count pin at 9 forces a skill sweep on any registry change
- README pins: '## Agent tooling' section + all 5 skill names
…ec path

- newFallbackExecutor mirrors kubectl's WebSocket-primary/SPDY-secondary construction
- shouldFallbackToSPDY extracted as a named, unit-testable predicate (kubectl's exact rule)
- adds TestExecFallbackPredicate covering both branches
…tructors

- execConstructorSymbols set watches NewSPDYExecutor, NewWebSocketExecutor, NewFallbackExecutor
- negative half: none of the three genuinely reachable from runMCPServer
- positive half: NewFallbackExecutor AND NewSPDYExecutor genuinely reachable from runAuditWindow (non-vacuous)
- TestMCPAuditReadonlyReachability body untouched
- audit-window RBAC paragraph now names the transport (WebSocket w/ SPDY fallback, matching kubectl 1.30+)
…(zero real drops) + README onboarding section
@SoulKyu
SoulKyu merged commit 2ceca9d into master Jul 23, 2026
4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant