82931684eb
CI / Test (server-http) (pull_request) Successful in 21s
CI / Test (rest) (pull_request) Successful in 23s
CI / Test (store) (pull_request) Successful in 36s
CI / Lint (pull_request) Successful in 27s
CI / Build (windows/amd64) (pull_request) Successful in 26s
CI / Build (linux/arm64) (pull_request) Successful in 23s
CI / Build (linux/amd64) (pull_request) Successful in 1m24s
e2e / Playwright vs docker-compose (pull_request) Failing after 2m52s
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.
138 lines
5.5 KiB
Markdown
138 lines
5.5 KiB
Markdown
# 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.
|