Files
restic-manager/docs/book/src/security/hardening.md
T
steve bb4ed3502d P5: OSS readiness — docs site, contributor onboarding, e2e harness
P5-01 — Documentation site under docs/book/ rendered with mdBook
(downloaded via Makefile, same static-binary pattern as Tailwind).
Structured chapters: getting started, concepts, operations,
security, reference. `make docs` / `make docs-watch`. Generated
output gitignored.

P5-02 — CONTRIBUTING.md rewritten from placeholder to a full
guide. CODE_OF_CONDUCT.md adapted from Contributor Covenant for a
single-maintainer project. .gitea/issue_template/{bug,feature}.md
and PULL_REQUEST_TEMPLATE.md.

P5-04 — Six README screenshots captured live from a fresh server
bootstrap (login, empty dashboard, add-host, alerts, settings,
audit log). README rewritten to centre the screenshot grid and
link out to the docs site.

P5-05 — SECURITY.md with disclosure policy (3-day ack, 30-day
default window), scope in/out, threat-model summary, operator
hardening checklist. Mirrored as a docs-site chapter.

P5-06 — End-to-end test harness. e2e/compose.e2e.yml brings up
server + sibling Linux agent (alpine + restic) + restic/rest-server.
Agent uses announce-and-approve so Playwright can drive the full
operator flow: bootstrap → login → accept pending → backup →
verify terminal status. Second spec scrapes /metrics to assert
the P6-04 endpoint surface. .gitea/workflows/e2e.yml runs on every
PR; local how-to in docs/e2e.md.
2026-05-08 20:08:23 +01:00

2.7 KiB

Hardening checklist

A baseline for new deployments. Most of these are defaults; the list is here to make audit easy.

Server

  • Reverse proxy in front, TLS terminating at the proxy (Caddy/nginx/Traefik).
  • RM_TRUSTED_PROXY set to the proxy's CIDR.
  • RM_BASE_URL matches the public hostname and the cookie scope you want.
  • RM_COOKIE_SECURE=true (the default; only set false for local HTTP testing).
  • HTTP listener bound to localhost in the compose file, not 0.0.0.0. The reverse proxy is the only thing that should reach it.
  • secret.key backed up separately from the database.
  • Bootstrap token consumed and the printed log line scrubbed from any log archive.

Authentication

  • Admin user has a password ≥ 12 characters (the floor).
  • OIDC enabled if you have an IdP — local password auth stays as a break-glass.
  • Disabled (not deleted) any users who change roles or leave so their session is invalidated immediately.
  • The last-admin guard isn't tripped — there's always at least one enabled admin user.

Repo credentials

  • Append-only credential set as the everyday cred for every host.
  • Admin credential set only where prune cadence is enabled.
  • No credentials reused across hosts. Each host should have its own credential pair so a single host compromise has a single blast radius.
  • If using rest-server, --append-only flag is on for the everyday user; the prune user is a separate identity.

Agent

  • Agent runs as root (Linux) or LocalSystem (Windows) only when the source paths require it. Otherwise pin a service user that has read access to what's backed up and nothing else.
  • systemd unit's sandboxing flags are intact (NoNewPrivileges, Protect*, MemoryDenyWriteExecute).
  • Agent's config file /etc/restic-manager/agent.yaml is mode 0600 and owned by the service user. The bearer token lives in there.

Operations

  • Alerts wired to a real channel (webhook into Slack, ntfy topic, SMTP) — not just sitting in the UI.
  • Test-fire each notification channel after configuring.
  • Audit-log retention is long enough to cover the operator's incident-response window.
  • Prometheus endpoint, if enabled, gated by token AND CIDR where practical (default is opt-in / off).

Recovery

  • A documented procedure for rotating a leaked agent bearer (delete + re-enrol the host).
  • A test-restore done at least once, end-to-end, before relying on the system in anger.
  • secret.key and the SQLite database covered by separate backup paths so neither alone reconstitutes the other.