base-trixie: findings of the reviews
test / test (push) Canceled after 0s

Security review (1c6e04b):
- The panel's database and its -wal/-shm were 0644 under a traversable
  /data, holding the panel users' password hashes and session token
  hashes. store.Open now makes them 0600 (SQLite gives the next -wal/-shm
  the database's mode: findCreateFileMode(), unixOpenSharedMemory());
  TestOpenKeepsTheDatabasePrivate.
- The entrypoint removes a symbolic link the panel user could leave where
  a /data directory, table, map, the queue or mail.log is expected, before
  its root steps chmod or create them (drop_links).
- The panel user is 10999:10999, outside the system range: in trixie
  useradd gave it group 998, the host's systemd-network on the test
  server; bookworm's 999 was the host's systemd-journal.
- security.md said the database was 0640; it was 0644.

Technical review (1c6e04b):
- mail_helpers.go: smtp-sink does add a Received: header, "(smtp-sink)"
  (smtp-sink.c:520-531); the "(Postfix)" filter leaves it out.
- CHANGELOG/roadmap: OpenDKIM's signing and From:-parsing code is
  unchanged, not all of it; the retry-policy bug predates 3.9 (any
  postconf warning); three entries moved from Fixed to Changed, as no
  released image had them.
- internal/app/sasl.go: db_dump's stdout alone is parsed.
- mynetworks comment: postinst:180, fset_all_changed.

Tests: on the server, make e2e exit 0 (712 s), go vet + go test 27
packages ok.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
mix
2026-10-15 20:05:01 +03:00
co-authored by Claude Opus 5.5
parent 1c6e04b7d3
commit a3050afb66
10 changed files with 164 additions and 31 deletions
+8 -4
View File
@@ -85,10 +85,14 @@ RUN echo "postfix postfix/mailname string localhost" | debconf-set-selections \
# it off (debian/configure-instance.in:22-24 and :44; debian/README.Debian:136-138).
RUN printf 'SYNC_CHROOT=\n' > /etc/default/postfix
# Unprivileged user for the panel process. Its ids are left to useradd: the
# entrypoint gives /data back to it by name on every start, so a number
# that differs from one image to the next changes nothing.
RUN useradd --system --no-create-home --shell /usr/sbin/nologin panel
# Unprivileged user for the panel process, with ids of its own outside the
# system range for the same reason as selfpost's below: its files under
# /data show on the host by number, and a number from that range is a
# system group of the host's too (useradd gave the panel group 998 in the
# trixie image, systemd-network on the test server's host; bookworm's
# 999:999 was the host's systemd-journal group). The entrypoint gives /data
# back to it by name on every start, so files of the old ids follow.
RUN groupadd --system --gid 10999 panel && useradd --system --uid 10999 --gid panel --no-create-home --shell /usr/sbin/nologin panel
# Shared group bridging the unprivileged services: the panel generates
# per-domain DKIM keys, application SASL accounts (sasldb2) and the Postfix
+26 -1
View File
@@ -66,6 +66,31 @@ esac
# mode 0o700".)
chown panel:panel /data
chmod 755 /data
# What follows runs as root over a tree the panel user writes: /data is the
# panel's, and so is /data/postfix. A symbolic link the panel put where a
# directory or a file of its own is expected would lead root's mkdir, ": >",
# chmod and chgrp to whatever it names (chmod follows a link; so do a
# redirect and mkdir -p; chown -R and find do not descend into one given as
# their operand). None of these paths is ever a link in a working
# installation, so a link there is removed and the path made anew below.
# (Security review of base-trixie, 2026-10-15: no escalation found, as the
# TLS key copy and the read-only mounts stand, but nothing for root to
# follow either.)
drop_links() {
for p in "$@"; do
if [ -L "$p" ]; then
echo "entrypoint: removing the symbolic link $p" >&2
rm -f "$p"
fi
done
}
drop_links /data/opendkim /data/sasl /data/postfix /data/antispam /data/log \
/data/opendkim/keys /data/opendkim/KeyTable /data/opendkim/SigningTable \
/data/sasl/sasldb2 /data/postfix/queue \
/data/postfix/sender_login_maps /data/postfix/relay_domains \
/data/postfix/transport /data/postfix/relay_recipients /data/postfix/tls_policy \
/data/log/mail.log
# Restored backups or previously-created state may contain panel-owned files
# under /data; make sure they stay writable without disturbing anything that a
# later phase deliberately hands to another service. /data/log is exempt: it is
@@ -73,7 +98,7 @@ chmod 755 /data
# normalised on its own below, as are the trees shared through group selfpost.
# A file's owner is a number: an entry whose user or group is not the panel's
# by that number - unpacked by root, or written under another image whose
# useradd gave the panel other ids (bookworm's 999:999, until 2.7.0) - is
# image gave the panel other ids (999:999 until 2.7.0, 10999:10999 since) - is
# handed over whole.
find /data -mindepth 1 -maxdepth 1 ! -name log ! -name postfix ! -name opendkim ! -name sasl ! -name antispam \
\( ! -user panel -o ! -group panel \) -exec chown -R panel:panel {} +
+2 -1
View File
@@ -387,7 +387,8 @@ postconf -e \
# what the exceptions above and smtpd_forbid_bare_newline_exclusions (below)
# spare. The value is the one Debian's debconf default gives (postfix
# 3.10.13-0+deb13u1, debian/templates:124-126, "postfix/mynetworks", written
# by debian/postfix.postinst:249-262; debian/main.cf.in:28 has 127.0.0.0/8
# by debian/postfix.postinst:249-262 on a new main.cf, where :180,
# fset_all_changed, marks every debconf value to be written; main.cf.in:28 has 127.0.0.0/8
# alone), so on this image it changes nothing.
postconf -e \
"mynetworks=127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128" \