entrypoint: /data/opendkim removed, as planned for the release after 2.8.0

2.8.x copied the keys from /data/opendkim to /data/dkim and kept the old
tree for a rollback to 2.7.x "until the next release"
(docs/plans/panel-dkim-signing.md, Q2); the owner's "потом режь 2.9.0"
(2026-10-16) makes this that release. The entrypoint now removes the tree
once /data/dkim holds a key, and says so. A /data with keys under the
old tree only - one that never ran 2.8.x - stops the start with a message
to run 2.8.x first, rather than let the panel give the server's own name
a new key its published record would not match; the tree is left as it
was. Gone with it: the copy into /data/dkim.new, the tree's
normalisation, its exemption from the general hand-over, its key link in
drop_links, and the backup's exclusion of "opendkim" (dead: the panel
never sees the tree now).

Tests: e2e TestStartsOnA28Data replaces TestStartsOnA27Data - a /data as
2.8.x left it (both trees, a queue of foreign uids, a rotation due): the
old tree is gone, the key under /data/dkim is the one left, the selector
table names it, the queue is Postfix's by name; TestRefusesA27Data - the
start stops, says why, leaves the old key and makes no new one. go vet +
go test (26 packages) and make e2e (all pass) on the test server for this
tree.

Docs: guide § Upgrading "To 2.9.0" (rollback to 2.8.x needs nothing, to
2.7.x only from a 2.7.x backup; the 2.7.x rollback paragraph says it is
for 2.8.x only and keeps naming 2.7.0), guide § backup, architecture
(the old tree, § Persistence's table and the restore line), security
§ Backup, development's e2e paragraph, the plan's key step; CHANGELOG
[Unreleased] § Removed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mix
2026-10-16 13:46:04 +03:00
co-authored by Claude Opus 5.5
parent 3f5cd9adf0
commit 9420d8c03d
10 changed files with 161 additions and 110 deletions
+9 -11
View File
@@ -309,14 +309,13 @@ then OpenDKIM did, in a program of its own
lines; nothing is written to `mail.log` (nor did OpenDKIM write there).
- **The old tree,** `/data/opendkim` - the keys, `KeyTable` and
`SigningTable` until `2.8.0`, shared with OpenDKIM through group
`selfpost`. While `/data/dkim` holds no key and the old tree does, the
entrypoint copies `/data/opendkim/keys` to `/data/dkim/keys` as the panel
user (`setpriv`), into `/data/dkim.new`, renamed when whole; a copy that
fails stops the start - the server's name would otherwise get a new key
and its published record would no longer match. Keys, selectors and DNS
records do not change. The old tree is left for a rollback to a 2.7.x
image until the next release, made the panel's alone at every start like
the new one, and left out of backups (§ Persistence).
`selfpost`. `2.8.x` copied its keys to `/data/dkim/keys` at its first
start and kept it for a rollback to a 2.7.x image; since `2.9.0` the
entrypoint removes it once `/data/dkim` holds a key, and a `/data` with
keys under the old tree only - one that never ran 2.8.x - stops the start
(the server's name would otherwise get a new key and its published record
would no longer match). Under 2.8.x it was the panel's alone, like the
new one, and left out of backups.
### Panel binary ([cmd/panel](../cmd/panel))
@@ -1100,7 +1099,6 @@ single-connection trade-off that follows from it.
| `selfpost.db` | SQLite panel state (users, sessions, domains, apps, send log, L2 limits, log-tailer offset, inbound relay, DMARC reports) — see [Database](#database) and [schema-migrations.md](schema-migrations.md) |
| `setup-token` | First-run setup token file |
| `dkim/` | DKIM keys (`keys/<name>/<selector>.private`) and the selector table (`selectors`) the signer reads; the panel's alone, `0700` / `0600` — § DKIM signing |
| `opendkim/` | The key tree OpenDKIM read until `2.8.0` (keys, `KeyTable`, `SigningTable`): copied to `dkim/` once, then kept only for a rollback to 2.7.x until the next release; the panel's alone, excluded from backups |
| `sasl/sasldb2` | Application SASL credentials |
| `postfix/sender_login_maps` | Login → From binding |
| `postfix/relay_domains` | Inbound domains accepted on port 25 |
@@ -1180,8 +1178,8 @@ the stored log lines follow;
`postfix reload` in `postrotate` — see § Log tailer above).
**Restore:** panel button or `selfpost-backup` CLI — self-contained archive:
`data/` (SQLite snapshot + tree minus `log/`, the setup token, the old
`opendkim/` tree and any `tls/` under `/data`), `docker-compose.yml`, `.env`,
`data/` (SQLite snapshot + tree minus `log/`, the setup token and any
`tls/` under `/data`), `docker-compose.yml`, `.env`,
and `certs/` when present;
version check on restore. Requires the project directory mounted read-only at
`SELFPOST_DEPLOY_ROOT` (`/selfpost-deploy` in the default compose file). On the
+6 -5
View File
@@ -503,12 +503,13 @@ record as a receiver would. `signature_over_awkward_headers`
([test/e2e/dkim_shapes_test.go](../test/e2e/dkim_shapes_test.go)) sends the
header shapes Postfix hands a milter in its own way - folded, tab-led, no
space after the colon, two `To:` - and an 8-bit body, and dkimpy verifies
what was delivered. `TestStartsOnA27Data`
what was delivered. `TestStartsOnA28Data`
([test/e2e/upgrade_test.go](../test/e2e/upgrade_test.go)) starts the image on
a `/data` a 2.7.x image left: the key under `/data/opendkim` is copied to
`/data/dkim` unchanged and the selector table names it, the old tree becomes
the panel's alone, and a queue owned by other uids is handed to Postfix's
users by name.
a `/data` a 2.8.x image left: the old key tree `/data/opendkim` is removed,
the key under `/data/dkim` is kept unchanged and the selector table names
it, and a queue owned by other uids is handed to Postfix's users by name;
`TestRefusesA27Data` has the start stop on a `/data` whose keys are under
the old tree only, which is left as it was.
Waits poll with a timeout (`waitFor`); a fixed sleep is the exception and says
why where it stands (`sender_check_test.go`: a downtime worth a second or two,
and 35 s to show a row is not written again).
+12 -4
View File
@@ -684,7 +684,17 @@ was, for a rollback, until the next release, and which a full backup leaves
out. The selector table, `data/dkim/selectors`, is written from the database
at every start, before the signer serves.
**Rolling back to 2.7.x** after that: the 2.7.x image signs with OpenDKIM
**To 2.9.0** from 2.8.x there is nothing to do: the first start removes
`data/opendkim`, the key tree 2.7.x read, which 2.8.x kept for a rollback,
and says so (`entrypoint: removed /data/opendkim, the key tree of 2.7.x;
the keys are under /data/dkim`); the keys under `data/dkim` stay as they
are. From 2.7.x, start 2.8.x once first: an image of 2.9.0 on a `data`
whose keys are under `data/opendkim` only stops at the start and says so,
rather than give the server's own name a new key. Rolling back from 2.9.x
to 2.8.x needs nothing; to 2.7.x, the old tree is gone - only a full backup
of 2.7.x brings it back.
**Rolling back to 2.7.x** from 2.8.x (only): the 2.7.x image signs with OpenDKIM
from `data/opendkim`, which holds the keys as they were at the upgrade. A
domain added under 2.8.x has no key there, and 2.7.x sends its mail
unsigned; one deleted and added again under 2.8.x has its old key there,
@@ -771,9 +781,7 @@ as themselves, the host's IPv6 address and `127.0.0.1` as `172.18.0.1`.
domains' DKIM keys under `data/dkim/`, all applications' SASL credentials,
`manifest.json` with the version that created it), plus `docker-compose.yml`,
`.env`, and `certs/` from the operator directory next to `./data`. Delivery
logs under `data/log/` are excluded, so is `data/opendkim/` (the key tree an
installation upgraded from 2.7.x keeps for a rollback, see
[Upgrading](#upgrading)), and so is the Postfix queue: the archive is taken while the
logs under `data/log/` are excluded, and so is the Postfix queue: the archive is taken while the
instance runs, and a copy of a queue Postfix is working on can hold a message
twice or not at all. Mail still waiting for delivery is therefore not carried
over, and a restored instance starts with an empty queue; to keep the queue,
+3
View File
@@ -171,6 +171,9 @@ dropped connection (a one-hour watchdog closes the signer).
- [x] Keys: `/data/opendkim/keys` -> `/data/dkim/keys` (Q2): the entrypoint
copies the tree once and leaves the old one for a rollback to 2.7.x until
the next release; KeyTable and SigningTable go with the new layout.
*2.9.0:* the entrypoint removes the old tree once `/data/dkim` holds a
key, and stops on a `/data` with keys under the old tree only; the copy
and the backup exclusion are gone.
- [x] Tests: `test/upstream/*.sh`, e2e's OpenDKIM log and process checks,
the 256-byte test rewritten for the new rule.
+4 -5
View File
@@ -410,11 +410,10 @@ operator can act on it.
private key from `certs/` when present; a domain export carries the DKIM key
and **working** application passwords in the clear (otherwise a transfer
without recreating credentials would be impossible). The keys a backup
carries are those of `/data/dkim`; `/data/opendkim`, the tree 2.7.x read
and kept for a rollback, is left out (`internal/backup`,
`excludedFromArchive`, `2.8.0`): an archive restores into its own version
alone, and a second copy of every key - one that outlives a deleted
domain's - has no place in it.
carries are those of `/data/dkim`. `/data/opendkim`, the tree 2.7.x read,
was left out while 2.8.x kept it for a rollback (`2.8.0`); since `2.9.0`
the entrypoint removes it before the panel starts, so no second copy of
every key - one that outlived a deleted domain's - is left on disk.
- Links under `certs/` (and `certs` itself as a link) are followed, in the
container's view: the archive carries whatever regular file they lead to
that the panel's user may read. `certs/` is the operator's, mounted