backup-reauth: the account password before a backup or an export
Agreed with the owner on 2026-10-13, taken up on 2026-10-16 ("Да"); the
mockup is 1888dce. Both secret downloads - the full backup on Server ›
Backup and a domain's export on its settings - ask for "Your account
password": reauth (handlers_backup.go) checks the signed-in user's own
password with bcrypt, as a password change does, under the same limiter
(auth.AllowPasswordChange: ten in fifteen minutes per user, shared with the
Account form). A wrong or missing one is 401 on the same page with "Your
account password is incorrect.", logged; nothing is sent. The import and
the selfpost-backup CLI are not asked.
Also, the owner's phone screenshot of 2026-10-16 ("кнопка выбора файла не
такая, как в макете. Это косяк?"): the import's file field was a bare
<input type="file"> drawn by the browser ("Browse…"), where the mockup has
Bulma's .file - "Choose a file…" and the name chosen. The template now
follows the mockup; panel.js writes the name into .file-name on change and
at load (testdata/filename_test.js, run by TestPanelJS). No other file
field exists.
Tests: TestSecretDownloadsAskForTheAccountPassword (missing, wrong, right,
the limiter), the export refusals of the settings page with the password,
cmd/panel's restore tests and e2e's backups with it, e2e's refusal of a
backup without and with a wrong password. go vet + go test (26 packages)
and make e2e on the test server for this tree.
Docs: security.md § Backup and domain export, guide § Full backup and
restore, help (word for word with the mockup); CHANGELOG [Unreleased];
roadmap row done.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -777,6 +777,12 @@ as themselves, the host's IPv6 address and `127.0.0.1` as `172.18.0.1`.
|
||||
|
||||
### Full backup and restore
|
||||
|
||||
Both downloads - the full backup on **Server › Backup** and a domain's
|
||||
export on its settings - ask for your account password, the one you sign in
|
||||
with, every time: a session left open is not enough to take them. Wrong
|
||||
guesses count against the same limit as a password change (ten in fifteen
|
||||
minutes).
|
||||
|
||||
**Full backup** is a self-contained project archive: `data/` (SQLite, all
|
||||
domains' DKIM keys under `data/dkim/`, all applications' SASL credentials,
|
||||
`manifest.json` with the version that created it), plus `docker-compose.yml`,
|
||||
|
||||
+8
-1
@@ -33,7 +33,7 @@ in `git log` and [CHANGELOG.md](../CHANGELOG.md).
|
||||
| test-email | Send a test message from the panel (was preflight) | agreed (2026-10-13) | — | — |
|
||||
| inbound-antispam-panel | Inbound antispam journal + allow/deny lists | shipped `2.1.0` | 12/12 | [plans/inbound-antispam-panel.md](plans/inbound-antispam-panel.md) |
|
||||
| origin-strict | Refuse a state-changing request that carries neither `Sec-Fetch-Site` nor `Origin` (was csrf-tokens) | agreed (2026-10-13) | — | — |
|
||||
| backup-reauth | Ask for the account password before a backup or a domain export is downloaded | agreed (2026-10-13) | — | — |
|
||||
| backup-reauth | Ask for the account password before a backup or a domain export is downloaded | done 2026-10-16, unreleased | — | — |
|
||||
| review-2026-08-followups | Remaining findings of the 2026-08-19 code review | closed 2026-10-14 (rows placed in [audit-2026-10-12](audit-2026-10-12.md)) | — | — |
|
||||
| panel-notifications | E-mail notifications about important events | shipped `2.3.0` | 10/10 | [plans/panel-notifications.md](plans/panel-notifications.md) |
|
||||
| delivery-log-storage | Keep a message's `mail.log` lines with its send-log row | shipped `2.2.0` | 8/8 | [plans/delivery-log-storage.md](plans/delivery-log-storage.md) |
|
||||
@@ -258,6 +258,13 @@ container.
|
||||
with a test that proves it, attempts are limited, and
|
||||
[security.md](security.md) § Backup says so.
|
||||
|
||||
**Done (2026-10-16):** "Your account password" in both forms (mockup
|
||||
`1888dce`), checked by `reauth` in `internal/web/handlers` with bcrypt and
|
||||
`AllowPasswordChange` (ten in fifteen minutes per user, shared with the
|
||||
Account form); a wrong or missing one is 401 on the same page, logged, and
|
||||
nothing is sent. Handler and e2e tests; security.md § Backup and domain
|
||||
export, guide § Full backup and restore, help.
|
||||
|
||||
**Version:** `2.x` MINOR; `agreed`.
|
||||
|
||||
---
|
||||
|
||||
@@ -428,6 +428,18 @@ operator can act on it.
|
||||
|
||||
### Backup and domain export
|
||||
|
||||
- **A session is not enough to take them** (backup-reauth, owner
|
||||
2026-10-13; built 2026-10-16). The full backup and a domain's export ask
|
||||
for the signed-in user's own account password at every download, checked
|
||||
with bcrypt as a password change checks it and under the same limiter -
|
||||
ten checks in fifteen minutes per user, shared with the Account form -
|
||||
so a stolen session cookie or a panel left open no longer hands over a
|
||||
copy of the server, and the field is no place to guess a password from.
|
||||
A wrong one is refused with 401 and logged; nothing is sent
|
||||
(`internal/web/handlers`, `reauth`). *Encrypt with a password* is the
|
||||
file's own protection, not this one: whoever downloads it chooses that
|
||||
password. The import and the `selfpost-backup` CLI, which runs inside the
|
||||
container, are not asked.
|
||||
- Both files are secrets: a full backup carries DKIM keys, `sasldb2`, the
|
||||
panel users' password hashes, `docker-compose.yml`, `.env`, and the TLS
|
||||
private key from `certs/` when present; a domain export carries the DKIM key
|
||||
|
||||
Reference in New Issue
Block a user