Skip to content

Security

AstorisTheBrave edited this page Jun 21, 2026 · 6 revisions

Security

How Argus is secured, what is exposed, and how to deploy it safely. For reporting a vulnerability, see SECURITY.md (private advisory via the GitHub Security tab, or email).

What is exposed, and how to gate it

Surface Default Gate
Bot /metrics open scrape-only aggregate data; keep open for Prometheus, restrict at the network if needed
Bot dashboard / + /api/* open set dashboard_auth_token (or ARGUS_DASHBOARD_AUTH_TOKEN)
Bot /healthz open liveness only
Fleet control plane (all routes) refuses to start on a public bind without a token ARGUS_FLEET_TOKEN (or split ingest/viewer), /healthz + /readyz open

The no-PII guarantee (invariant 2)

guild_id, user_id, and channel_id are never Prometheus labels - enforced by a test, not convention. Per-entity questions go only to the optional analytical path (ClickHouse), which is separate storage and fails closed without a token. The fleet control plane reads aggregate metrics only and cannot expose per-entity data.

Dashboard (per-bot)

  • Set dashboard_auth_token for anything not strictly localhost. It gates / and every /api/* route with a constant-time comparison; /metrics//healthz stay open so a scraper does not need the token.
  • The browser remembers a ?token= link in localStorage. Query-string tokens can land in proxy/access logs; prefer the Authorization: Bearer header for programmatic clients, and treat the link as a credential.
  • Disable the UI entirely with dashboard=False and rely on Grafana if you only want scraping.

Fleet control plane

Secure-by-default and hardened (see Fleet for the full table):

  • Refuse-insecure bind: a non-loopback bind with no token will not start. Set a token, bind loopback, or ARGUS_FLEET_INSECURE=1 (local testing only).
  • Split tokens (optional): a low-privilege ARGUS_FLEET_INGEST_TOKEN lives on every bot; an ARGUS_FLEET_VIEWER_TOKEN gates the UI/read APIs - so a leaked bot token cannot read the dashboard. Each falls back to the shared token. *_FILE variants read from mounted secrets.
  • Abuse resistance: request body cap (413), per-IP register and per-identity heartbeat rate limits (429), and a ARGUS_FLEET_MAX_CLUSTERS cap.
  • Scanner/fingerprint reduction: the version banner is stripped and security headers (X-Frame-Options: DENY, CSP, nosniff, Referrer-Policy) are sent on every response; unknown paths and bad tokens get flat, detail-free responses.
  • Single writer: an advisory lock on the state file refuses a second instance; the on-disk state is schema-versioned (unknown versions refuse to load).
  • Duplicate-identity detection: a CLUSTER_ID/fleet_id reused from two hosts increments argus_fleet_identity_conflicts_total and logs a warning.
  • Fail-open member: the bot's heartbeat is bounded and never raises into the bot loop, so a fleet outage cannot affect your bot.

CORS

You do not need CORS for the bundled, same-origin fleet UI - the browser only ever talks to the control plane, which serves both the SPA and the API. Hosting the bot and the dashboard on different machines/providers does not change this. CORS is only needed if you serve the SPA from a different origin than the API; then set ARGUS_FLEET_CORS_ORIGINS to an explicit allowlist (never a wildcard on a token-gated surface).

Deployment recommendations

  • Terminate TLS at a reverse proxy (or a tunnel) for any public deployment; tokens are bearer credentials. Behind a proxy, set ARGUS_FLEET_TRUSTED_PROXY=1 so rate limits key on the real client IP, and add Strict-Transport-Security at the proxy.
  • Run as non-root. The published images already do (uid 10001) and ship a HEALTHCHECK; the k8s example uses a read-only root filesystem and drops all capabilities.
  • Restrict exposure. IP-allowlist or VPN-gate the control plane where possible; a non-default port and /metrics behind the token trim opportunistic scanning.
  • Pin versions of the package and images so a mid-development change cannot reach a deployment.
  • Secrets: use *_TOKEN_FILE / Kubernetes Secrets rather than inline env vars where you can; never commit tokens.

Supply chain

  • Releases publish to PyPI via OIDC Trusted Publishing with attestations (no stored long-lived credentials).
  • The GHCR images (argus, argus-fleet) ship an SBOM and max provenance attestation.
  • CodeQL and Dependabot run on the repository.

Clone this wiki locally