# Security policy

restic-manager handles credentials that grant access to backup
repositories — losing them means an attacker can read or destroy a
fleet's backups. We take security reports seriously even at this
project's small scale.

## Supported versions

Pre-1.0, only the latest tagged release on `main` is supported.
Backporting fixes to older tags is not currently offered.

| Version            | Supported      |
|--------------------|----------------|
| `main` HEAD        | Yes            |
| Latest released tag| Yes            |
| Anything older     | No             |

## Reporting a vulnerability

**Please don't open a public issue for security problems.**

Instead, use one of these private channels:

1. **Gitea private message** to the repository owner. The
   instance is at <https://gitea.dcglab.co.uk> and the owner's
   profile (`steve`) has direct-message contact set up.
2. **Email** to the address on the maintainer's Gitea profile.
   Use a subject like `[SECURITY] restic-manager: <one-line summary>`
   so it doesn't get lost. PGP optional — if you want to encrypt,
   ask for a key first.

If you don't get an acknowledgement within **3 working days**,
please escalate through the other channel — solo maintainers do
miss things, and the goal here is to fix the problem, not to
preserve protocol.

### What to include

- A description of the issue and the impact (what does an attacker
  gain? confidentiality, integrity, availability?).
- Affected component (server, agent, install script, docs).
- Affected version (`restic-manager-server --version`).
- Reproduction steps if you have them. A working PoC is welcome
  but not required — a credible threat model is enough.
- Whether you intend to publish a writeup, and any timing
  preferences.

### What we'll do

1. Acknowledge receipt within 3 working days.
2. Confirm or refute the issue, and agree a rough severity (CVSS
   or just "this is bad / this isn't"). Asking clarifying
   questions is normal at this stage — please don't read it as
   foot-dragging.
3. Develop a fix on a private branch, test it, and prepare a
   release.
4. Coordinate disclosure timing with you. The default is **30
   days from confirmed report to public disclosure**, with a
   patched release published before the disclosure date. Faster
   if a workable PoC is already circulating; slower only by
   mutual agreement.
5. Credit the reporter in the release notes (or omit the credit
   if you'd rather stay anonymous — your choice).

## Scope

In scope:

- The server binary (`cmd/server`) and any HTTP, WebSocket, or CLI
  surface it exposes.
- The agent binary (`cmd/agent`) and the way it consumes commands
  from the server.
- The install scripts (`deploy/install/install.sh`, `install.ps1`)
  and the systemd unit shipped with them.
- The docker-compose reference deployment and the docker image we
  publish.
- Any cryptographic primitive choice or implementation detail
  (AEAD, token hashing, session handling, OIDC handshake).
- Documentation that, if followed, leads operators into an
  insecure configuration.

Out of scope (not because they aren't real problems, just not ones
this report channel can act on):

- Vulnerabilities in restic itself — report those upstream at
  <https://github.com/restic/restic>.
- Vulnerabilities in third-party dependencies that haven't yet been
  patched upstream — report upstream first.
- Issues that require pre-authenticated admin access on the control
  plane (admins can already do everything; that's not a privilege
  escalation, that's the design).
- DoS via resource exhaustion on a deployment without the
  recommended reverse proxy / rate limiting in front (see
  `docs/reverse-proxy.md`).
- Social-engineering scenarios that don't have a technical hook
  into the project's own surfaces.

## Threat model summary

For context (longer version in [`spec.md`](./spec.md) §11):

- The server is **HTTP-only**; TLS termination, ACME, HSTS, and
  edge rate-limiting are the reverse proxy's job.
- Credentials are encrypted at rest with an AEAD key loaded from
  `RM_SECRET_KEY_FILE`. The same key encrypts agent secrets that
  travel to the agent over the WS channel.
- Agents authenticate with bearer tokens issued at enrolment and
  hashed at rest. Compromise of the server DB does **not** leak
  bearer tokens in plaintext, but does leak the hashes (which is
  enough to log in *as* the agent until the operator revokes —
  see [NS-01 / NS-02](./tasks.md) for the revoke + regenerate
  flows).
- The control plane intentionally **never touches backup bytes** —
  the agent runs `restic` directly against the repo. A
  compromised control plane can dispatch new jobs but cannot
  exfiltrate snapshot contents in-band.
- Append-only credentials are first-class. Forget/prune jobs use a
  separate, admin-marked credential that the server only pushes
  for the duration of a maintenance dispatch.

## Hardening checklist for operators

- Run behind a TLS-terminating reverse proxy (Caddy/nginx/Traefik).
- Set `RM_TRUSTED_PROXY` to the proxy's CIDR so request IPs aren't
  spoofable.
- Back up `RM_SECRET_KEY_FILE` separately from the database.
  Without it the encrypted creds are unrecoverable.
- Use append-only credentials for the everyday backup path; only
  the optional admin credential should have write/forget/prune
  power.
- Disable users (don't delete) when staff change roles — bearer
  tokens stay valid until rotated.
- Watch the alert and audit-log views during enrolment of new
  hosts.

Thanks for helping keep restic-manager users safe.
