VACVAC
Operations

Security

The Security dashboard — posture checklist, live traffic monitoring, and host hardening — plus the model behind it.

3 min readUpdated June 15, 2026Edition · v0.5.xBeta

VAC's Security tab is "your box at a glance": a posture checklist, a live traffic-anomaly monitor, and read-only firewall/fail2ban state. It turns VAC's two best vantage points — Caddy's access log and VAC's own config and store — into one screen.

Read-only by design

Nothing on this tab mutates host state. The posture checks and host readers are pure reads; the traffic monitor only holds bounded in-memory counters fed by the access log. VAC observes and advises — it doesn't reconfigure your firewall.

Posture checklist

A checklist that re-evaluates on each load. Each row is OK, a warning, or an error, with the reason and the fix. Host-hardening checks lead the list, since an exposed box with no firewall is the most dangerous state.

CheckFlags when
Firewallno firewall detected, or installed but inactive (error); state stale (warn).
fail2bannot detected or inactive.
Master keyVAC_MASTER_KEY is missing (error — secrets can't be sealed).
Metrics tokenVAC_METRICS_TOKEN is unset, leaving /metrics reachable if exposed (warn).
Exposure moderunning in local exposure rather than public HTTPS (warn).
Base domainno base domain configured, so traffic isn't on HTTPS (warn).
Request monitoringthe Caddy access log isn't being read, so traffic analysis is off (warn).
Host portsa service publishes a host port, bypassing Caddy (warn).

Firewall and fail2ban checks depend on the host agent. If you deliberately don't run one, opt out (VAC_SECURITY_EXPECT_FIREWALL=false / vac security-check) and the row becomes an informational "disabled by operator" line instead of a warning.

Traffic anomaly monitor

Always on (VAC_SECURITY_MONITOR, default enabled) and cheap — it reads the access log VAC already tails and keeps bounded per-IP counters. The panel shows:

  • Top talkers — the busiest source IPs, with request and error counts.
  • Recent requests — a live tail of what's hitting the box right now.
  • Recent anomalies — a ring of the latest flagged events.

It flags two kinds of anomaly from a single IP within a sliding window:

SignalDefault threshold
Request spike300 requests from one IP per minute
Error surge100 4xx/5xx responses from one IP per minute

(Window, thresholds, and cooldown are tunable via VAC_SECURITY_* env vars.) A breach fires a traffic_anomaly notification, debounced per IP so you aren't spammed. Memory stays bounded: the per-IP table caps at ~1024 entries (least-recently-seen evicted), so an attacker churning addresses can't grow it without limit.

Host security agent

The firewall and fail2ban panels are powered by an opt-in host agent — a small read-only collector that snapshots posture (active firewall backend, fail2ban jails and ban counts) for the dashboard. It's off by default because it installs a host-level component.

Enable it at install time (VAC_SECURITY_AGENT=true) or later:

bashroot@your-vps:~#
vac security-agent on
It only reads

The agent never bans, unbans, or changes firewall rules — it reports what's there. Acting on what you see (adjusting UFW, tuning fail2ban) stays a host-level task you do yourself.

The security model behind it

The dashboard surfaces a few deliberate design choices worth knowing:

  • The control plane is sandboxed. vac-api stays off the vac-edge network, so your app containers — which run code you may not fully trust — can't reach the API that holds the Docker socket. See Architecture.
  • Secrets are encrypted at rest. Env vars, deploy keys, TOTP secrets, and webhook URLs are sealed with AES-256-GCM. See Environment variables.
  • Auth has teeth. Operator login supports TOTP, and destructive actions require a recent step-up re-verification.
  • Cookies are Secure per request — set only when the request actually arrived over TLS, so the http://<host>:9393 fallback stays usable without weakening real HTTPS traffic.
  • Outbound webhooks are SSRF-guarded — notification URLs resolving to private or loopback addresses are refused.
  • Risky surfaces are opt-in — the in-dashboard container shell (VAC_ENABLE_SHELL) and the host agent are off unless you turn them on.

API

The tab reads from four endpoints (session-authenticated, like the rest of the API):

GET /api/security/posture     ## the checklist
GET /api/security/traffic     ## live traffic snapshot
GET /api/security/firewall    ## firewall state (needs the host agent)
GET /api/security/fail2ban    ## fail2ban state (needs the host agent)

Next

On this page