EMRY-061: API token auth + TLS for the web dashboard #113

Merged
vxfemboy merged 4 commits from emry-061-web-auth into main 2026-07-02 23:04:55 +00:00
vxfemboy commented 2026-07-02 22:49:06 +00:00 (Migrated from github.com)

What

Opt-in transport security for emry web, off by default (loopback, plain HTTP, no auth — existing behavior is unchanged).

Flag Effect
--auth-token <TOK> (or EMRY_AUTH_TOKEN) Require Authorization: Bearer <TOK> on every route except /healthz. The WebSocket (which can't set headers) accepts ?token=<TOK>. Constant-time comparison.
--tls-cert <PEM> / --tls-key <PEM> Serve HTTPS from your own PEM files (axum-server + rustls). Emry never generates certificates.
--host <IP> Bind address (default 127.0.0.1); use 0.0.0.0 to expose the dashboard, e.g. in a container.

How

New emry-web module security.rs:

  • WebSecurity { token, tls } / TlsConfig { cert, key } (default = fully open plain HTTP).
  • A token middleware (/healthz always open) and a single serve_router that applies the token layer and chooses TLS vs plain.
  • serve_with_labels / serve_with_baseline / serve_project now take a WebSecurity; the CLI builds one from the flags (with the EMRY_AUTH_TOKEN env fallback, empty string treated as unset).

The --host flag also fixes that the dashboard was previously hardcoded to 127.0.0.1, which is needed for the Helm chart (k8s probes/Service hit the pod IP).

Tests

  • Token gate: valid bearer → 200; missing/wrong → 401; ?token= query accepted; /healthz open without a token; no token configured ⇒ open.
  • presented_token header-vs-query precedence; constant-time ct_eq.
  • CLI: --host/--auth-token/--tls-* parse; --tls-cert without --tls-key rejected; web_security env fallback + TLS pairing.

Coverage 90.38% (gate 90%). README gains a "securing the dashboard" note.

Closes #42

## What Opt-in transport security for `emry web`, **off by default** (loopback, plain HTTP, no auth — existing behavior is unchanged). | Flag | Effect | |------|--------| | `--auth-token <TOK>` (or `EMRY_AUTH_TOKEN`) | Require `Authorization: Bearer <TOK>` on every route except `/healthz`. The WebSocket (which can't set headers) accepts `?token=<TOK>`. Constant-time comparison. | | `--tls-cert <PEM>` / `--tls-key <PEM>` | Serve HTTPS from your own PEM files (axum-server + rustls). Emry never generates certificates. | | `--host <IP>` | Bind address (default `127.0.0.1`); use `0.0.0.0` to expose the dashboard, e.g. in a container. | ## How New `emry-web` module `security.rs`: - `WebSecurity { token, tls }` / `TlsConfig { cert, key }` (default = fully open plain HTTP). - A token middleware (`/healthz` always open) and a single `serve_router` that applies the token layer and chooses TLS vs plain. - `serve_with_labels` / `serve_with_baseline` / `serve_project` now take a `WebSecurity`; the CLI builds one from the flags (with the `EMRY_AUTH_TOKEN` env fallback, empty string treated as unset). The `--host` flag also fixes that the dashboard was previously hardcoded to `127.0.0.1`, which is needed for the Helm chart (k8s probes/Service hit the pod IP). ## Tests - Token gate: valid bearer → 200; missing/wrong → 401; `?token=` query accepted; `/healthz` open without a token; no token configured ⇒ open. - `presented_token` header-vs-query precedence; constant-time `ct_eq`. - CLI: `--host`/`--auth-token`/`--tls-*` parse; `--tls-cert` without `--tls-key` rejected; `web_security` env fallback + TLS pairing. Coverage 90.38% (gate 90%). README gains a "securing the dashboard" note. Closes #42
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
femboy/emry!113
No description provided.