Project Argus — Phase 1 Verification Checklist & Evidence Pack

Card: 226 — Define Phase 1 verification checklist and evidence pack
Milestone: Access & Segmentation
Generated: 2026-07-07T23:50:04.929331+00:00
Status: 4/18 items complete (22.2%)

Phase Objective

Tighten the loose inter-VLAN rules, get MAB and MPSK live, stand up the CA. Ensure MAB-allowed ports are strictly bound by OPNsense firewall constraints.

How to use this document

  1. Work through each PENDING item top to bottom.
  2. For each item, capture the listed evidence under posts/drafts/project-argus/evidence/phase1/<id>-*.{md,json,csv,pem,log}.
  3. Update the item status in verification-checklist.json once the evidence is captured.
  4. Re-run the Section F audit jobs after Phase 1 cutover to confirm no regressions.
  5. Raise a Phase 1 close-out MR with this checklist + the evidence pack as the body once every item is COMPLETE.

A. Discovery & Planning

A1: Live network flows captured

Status: COMPLETE
Source: Card 216 (Done)

What: Run ansible-discover-network-flows CI job and produce per-host artifacts covering listening sockets, established connections, Docker containers, komodo stacks, and TrueNAS shares.

Evidence to capture:

  • GitLab CI job URL: <run-job>
  • evidence/network-flows/*.json artifacts in CI job
  • posts/drafts/project-argus/data/argus-phase1-digest.json contains aggregated live state

Verification: Job status=success; 11 host JSON files present with non-empty raw, listening, and komodo_stacks fields.


A2: Inferred allowlist produced

Status: COMPLETE
Source: Phase 1 prep

What: Pre-audit allowlist derived from source configs and design docs.

Evidence to capture:

  • posts/drafts/project-argus/data/argus-phase1-allowlist.md

Verification: File exists with sections for required inter-VLAN paths.


A3: Live OPNsense + MikroTik state captured

Status: COMPLETE
Source: Card 216 (Done)

What: OPNsense REST API audit of filter rules, interfaces, routes, DHCP; MikroTik API-SSL audit of VLAN ports, bridge, IPs.

Evidence to capture:

  • posts/drafts/project-argus/data/opnsense-filter-rules.json
  • posts/drafts/project-argus/data/mikrotik-state.json
  • posts/drafts/project-argus/data/argus-phase1-diff.md

Verification: Both files exist; diff document identifies every temporary/broad rule and legacy bridge IP.


A4: Firewall rules plan written

Status: COMPLETE
Source: Card 217 (Done — closed by user, work retained)

What: Documented the proposed per-service ruleset to replace the broad temporary rules, plus a roll-out sequence and inter-VLAN matrix.

Evidence to capture:

  • posts/drafts/project-argus/planning/phase1/firewall-rules.md
  • posts/drafts/project-argus/planning/phase1/firewall-rules.json

Verification: Files exist; 43 proposed rules covering 31 public services and 12 inter-VLAN paths.


B. Firewall Policy Implementation

B1: All broad TEMPORARY rules removed

Status: PENDING
Depends on: A4
Source: planning/phase1/firewall-rules.md

What: Every “Temporary allow all - migration” rule on every internal interface in OPNsense is deleted. Explicit allowlist rules are in their place.

Evidence to capture:

  • OPNsense API: GET /api/firewall/filter/searchRule — must return zero rules with description containing "temporary" or "migration"
  • OPNsense API diff: rule count drops by 8 (the broad rules)

Verification: curl -sk -u $OPN_KEY:$OPN_SECRET https://10.67.45.1/api/firewall/filter/searchRule | jq "[.rows[] | select(.description | test("temporary|migration"; "i"))] | length" returns 0


B2: All 31 public services still respond 200

Status: PENDING
Depends on: B1
Source: planning/phase1/firewall-rules.md#proposed-firewall-rules

What: Each public hostname exposed via Traefik returns 200 OK over HTTPS after the new rules take effect.

Evidence to capture:

  • Reachability matrix CSV: 31 rows, all status=200
  • Curl commands: for host in traefik-routers; do curl -sI -o /dev/null -w "%{http_code} $host\n" https://$host; done

Verification: Reachability test script exits 0; log archived under evidence/phase1/b2-reachability.csv


B3: Inter-VLAN reachability matrix passes

Status: PENDING
Depends on: B1
Source: planning/phase1/firewall-rules.md#inter-vlan-matrix

What: Each required inter-VLAN path in the proposed ruleset succeeds; each non-allowed path is denied.

Evidence to capture:

  • evidence/phase1/b3-inter-vlan-matrix.csv — 12 positive rows (allowed) all PASS, 6 negative rows (denied) all FAIL

Verification: nmap/nc from each source VLAN to each destination/port matches the matrix.


B4: MikroTik untagged VLAN 45 ports reviewed

Status: PENDING
Source: argus-phase1-diff.md observations

What: VLAN 45 (Management) has 4 untagged access ports (ether1, ether5, ether9, ether12). Confirm they are intentional, not legacy. Document the decision per port.

Evidence to capture:

  • mikrotik-state.json — /interface/bridge/vlan/print section
  • evidence/phase1/b4-mikrotik-vlan-45-decision.md — per-port decision table

Verification: Each of the 4 ports is annotated as “intentional management access” or “remove and reassign”.


B5: MikroTik legacy bridge IP removed

Status: PENDING
Source: argus-phase1-diff.md observations

What: The 10.0.1.34/16 address on the MikroTik bridge is non-routable legacy config. Remove or reassign.

Evidence to capture:

  • mikrotik-state.json — /ip/address/print
  • OPNsense API: confirm no host route for 10.0.1.0/16

Verification: /ip/address/print shows no 10.0.1.34/16 entry.


B6: Hermes read-only users created on OPNsense + MikroTik

Status: PENDING
Source: Card 308 (Backlog)

What: A dedicated hermes user exists on each device with read-only/API-read permissions so future audits do not need temporary admin access.

Evidence to capture:

  • evidence/phase1/b6-hermes-users.md — creation steps + credentials location in OpenBao

Verification: Login as hermes to each device with the read-only API; confirm a write attempt is rejected with permission error.


B7: OPNsense live log shows zero unexpected denies over 7 days

Status: PENDING
Depends on: B1, B2, B3
Source: planning/phase1/firewall-rules.md#evidence-required

What: After the new rules are live, watch the OPNsense live log for 7 days. Any “default deny” hit on the new ruleset indicates a missing rule or a service not yet documented.

Evidence to capture:

  • OPNsense API: GET /api/diagnostics/log/firewall?limit=... filtered by 7-day window
  • evidence/phase1/b7-deny-analysis.md — table of every deny hit, root cause, rule added or marked-as-acceptable

Verification: Every deny hit in the 7-day window has a documented disposition in b7-deny-analysis.md.


C. Wired NAC (MAB)

C1: First MikroTik MAB-protected wired port profile implemented

Status: PENDING
Depends on: B1
Source: Card 222 (Backlog)

What: At least one MikroTik port uses MAB (MAC Authentication Bypass). The port authenticates devices via RADIUS fallback to VLAN assignment.

Evidence to capture:

  • MikroTik config snippet: /interface bridge port print, /radius configuration
  • evidence/phase1/c1-mab-port-config.md

Verification: Plug a test device into the chosen port; it lands in the expected VLAN within 30s. Disconnect/reconnect: re-auth succeeds.


C2: MAB-connected ports are demonstrably constrained by firewall policy

Status: PENDING
Depends on: C1, B3
Source: project-argus.md Phase 1 exit criteria

What: MAC checks alone are not trusted. Even on MAB-authenticated ports, OPNsense must restrict which subnets the port can reach.

Evidence to capture:

  • OPNsense ruleset snippet showing per-MAB-VLAN restrictions
  • evidence/phase1/c2-mab-firewall-constraints.md

Verification: Test device in MAB VLAN cannot reach a subnet it should not (e.g. Storage from a Guest MAB port).


D. Wireless (RADIUS / MPSK)

D1: Validate wireless AP support for RADIUS-driven MPSK

Status: PENDING
Source: Card 218 (To Do)

What: Confirm whether the current AP platform (Omada) supports the intended RADIUS-driven MPSK design. Record any limitations.

Evidence to capture:

  • evidence/phase1/d1-ap-radius-support.md

Verification: Document records Omada controller version, RADIUS support, and any vendor quirks/fallbacks.


D2: First RADIUS-backed wireless authentication path validated

Status: PENDING
Depends on: D1
Source: project-argus.md Phase 1 exit criteria / Card 219

What: At least one wireless client successfully authenticates via RADIUS and lands in the intended VLAN.

Evidence to capture:

  • RADIUS server log: 1 successful auth + VLAN assignment
  • Client device: 1 wifi connection + 1 ip in expected VLAN
  • evidence/phase1/d2-radius-wireless-test.md

Verification: Test client gets an IP in the expected RADIUS-assigned VLAN within 30s of association.


E. PKI / Certificate Authority

E1: Certificate authority is operational for the first intended use case

Status: PENDING
Source: project-argus.md Phase 1 exit criteria / Card 225

What: Stand up the CA (OpenBao PKI or step-ca) and issue at least one certificate to a real service that uses it.

Evidence to capture:

  • CA cert chain (root + intermediate) in evidence/phase1/e1-ca-chain.pem
  • Issued cert + service consuming it (e.g. Traefik dashboard, dbackup, or Infisical)
  • evidence/phase1/e1-ca-first-use.md

Verification: Service successfully serves HTTPS with a certificate issued by the new CA, verifiable via openssl s_client -showcerts.


F. Audit & Verification Automation

F1: Re-run ansible-discover-network-flows to confirm no new unexplained ports

Status: PENDING
Depends on: B1, B7
Source: planning/phase1/firewall-rules.md#evidence-required

What: After Phase 1 firewall changes are live, re-run the discovery job. Diff the new artifacts against the baseline; any new port must have a documented owner.

Evidence to capture:

  • GitLab CI job URL: <re-run>
  • evidence/phase1/f1-port-diff.md — table of new vs removed ports

Verification: Every new listening port in the post-cutover job has an entry in f1-port-diff.md explaining its purpose.


F2: OPNsense API audit job flags any new broad rules within 24h

Status: PENDING
Depends on: B6
Source: planning/phase1/firewall-rules.md#next-steps

What: A scheduled job (cron + ansible) checks OPNsense for any rules matching “any any”, “allow all”, or “temporary” patterns. Alert via ntfy if found.

Evidence to capture:

  • Cron job config + last 7 days of "clean" runs
  • evidence/phase1/f2-audit-job-config.md

Verification: A test that re-adds a broad rule is detected and reported within 24h.


Summary

  • Total items: 18
  • Complete: 4
  • Pending: 14
  • Blocked: 0
  • Percent complete: 22.2%

Tools & Credentials Required

  • ansible-playbook (machines/ansible-playbooks)
  • OPNsense REST API credentials (in OpenBao after Card 308)
  • MikroTik API-SSL credentials (in OpenBao after Card 308)
  • OpenBao or equivalent secret store for new credentials

Remediation Workflow

  1. For each PENDING item: run the verification step, capture evidence under evidence/phase1/-*.{md,json,csv,pem,log}
  2. Update the item status to COMPLETE in this file once evidence is captured
  3. Re-run Section F audit jobs to confirm no regressions
  4. Once all items COMPLETE, raise the Phase 1 close-out MR with this checklist + evidence pack as the body

Verify this post

This page is published as a PGP clearsigned document. You can verify it like this:

gpg --keyserver hkps://keys.openpgp.org --recv-keys CA98D5946FA3A374BA7E2D8FB254FBF3F060B796
curl -fsSL 'https://eddiequinn.xyz/sigs/posts/drafts/project-argus/planning/phase1/verification-checklist.txt' | gpg --verify

whoami

Systems should be predictable. People rarely are.