fix(logtail): keep mail.log in /data and reconcile stuck rows (v1.x closure phase 2)
test / test (push) Has been cancelled
test / test (push) Has been cancelled
Move the delivery log from the ephemeral /var/log to /data/log/mail.log so the lines that resolve a queued send-log row survive a container recreate. postlogd writes it as postfix, the panel reads it through the selfpost group (dir 2750, file 0640, normalised every start); backups exclude log/. Close the residual gap with a queue sweep: rows queued for over two minutes whose id postqueue -p no longer lists are marked bounced. The sweep waits until the tailer has read the log to its end and does nothing when the queue cannot be listed, so a message in flight is never touched. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+21
-2
@@ -11,8 +11,10 @@ set -e
|
||||
chown panel:panel /data
|
||||
# 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.
|
||||
find /data -mindepth 1 -maxdepth 1 ! -user panel -exec chown -R panel:panel {} +
|
||||
# later phase deliberately hands to another service. /data/log is exempt: it is
|
||||
# deliberately owned by postfix (postlogd writes the delivery log there) and is
|
||||
# normalised on its own below.
|
||||
find /data -mindepth 1 -maxdepth 1 ! -user panel ! -name log -exec chown -R panel:panel {} +
|
||||
|
||||
# DKIM key tree (spec 6, 9). The panel (user `panel`) generates keys and writes
|
||||
# the OpenDKIM tables; OpenDKIM (user `opendkim`) must read them. Normalise the
|
||||
@@ -51,6 +53,23 @@ chown -R panel:selfpost /data/postfix
|
||||
chmod 2750 /data/postfix
|
||||
chmod 0640 /data/postfix/sender_login_maps
|
||||
|
||||
# Delivery log (architecture.md § Log tailer). postlogd writes it as user
|
||||
# `postfix`; the panel reads it for the log-tailer and the System log page. It
|
||||
# lives under /data — not the ephemeral /var/log — so the delivery lines that
|
||||
# resolve a "queued" send-log row survive a container recreate.
|
||||
#
|
||||
# postlogd creates a missing log itself, but at 0600, which the unprivileged
|
||||
# panel cannot read; so create it here (and re-normalise an existing one, plus
|
||||
# whatever logrotate left behind) at 0640 owned postfix:selfpost. The setgid
|
||||
# directory keeps the shared group on anything created inside it later, and
|
||||
# 2750 keeps it group-traversable but not group-writable — logrotate refuses to
|
||||
# rotate a log whose directory is writable by a non-root group.
|
||||
mkdir -p /data/log
|
||||
[ -e /data/log/mail.log ] || : > /data/log/mail.log
|
||||
chown -R postfix:selfpost /data/log
|
||||
chmod 2750 /data/log
|
||||
find /data/log -type f -exec chmod 0640 {} +
|
||||
|
||||
# Milter socket directories (spec 5 p.3, 7.3). Postfix (user `postfix`) must
|
||||
# actually CONNECT to both milter sockets — OpenDKIM's and the panel's
|
||||
# journal-milter — not just probe them at start-up. The sockets are
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
#!/bin/sh
|
||||
# Periodic logrotate for /var/log/mail.log (spec 9, 10). Rotation renames the
|
||||
# file, recreates it (`create 0644 root root`, matching a cold container
|
||||
# Periodic logrotate for /data/log/mail.log (spec 9, 10). Rotation renames the
|
||||
# file, recreates it (`create 0640 postfix selfpost`, matching a cold container
|
||||
# start), then runs `postfix reload` (the same mechanism `postfix logrotate`
|
||||
# uses): postlogd keeps writing to the renamed inode until reload, and the
|
||||
# panel's log-tailer holds its own descriptor on that inode, so nothing
|
||||
# written before the reload is lost. `create` (rather than `nocreate`) matters
|
||||
# here beyond timing: a reload-triggered recreate lands the file at 0600,
|
||||
# which the unprivileged panel process cannot read — confirmed on a live
|
||||
# container — so logrotate must be the one to create it at 0644.
|
||||
# here beyond timing: a postlogd-triggered recreate lands the file at 0600
|
||||
# owned by postfix, which the unprivileged panel process cannot read —
|
||||
# confirmed on a live container — so logrotate must be the one to create it.
|
||||
#
|
||||
# logrotate itself only rotates once the configured "daily" period has elapsed
|
||||
# (tracked in /var/lib/logrotate/status), so it is safe to invoke this more
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
/var/log/mail.log {
|
||||
# The delivery log lives under the persistent /data, not the ephemeral
|
||||
# /var/log, so the lines that resolve a "queued" send-log row outlive the
|
||||
# container (architecture.md § Log tailer). Path and ownership match
|
||||
# build/postfix-config.sh (maillog_file) and build/entrypoint.sh: postlogd
|
||||
# writes it as user postfix, the unprivileged panel reads it through the shared
|
||||
# selfpost group, hence create 0640 postfix selfpost rather than 0644 root root.
|
||||
/data/log/mail.log {
|
||||
daily
|
||||
rotate 14
|
||||
missingok
|
||||
notifempty
|
||||
compress
|
||||
delaycompress
|
||||
create 0644 root root
|
||||
create 0640 postfix selfpost
|
||||
postrotate
|
||||
/usr/sbin/postfix reload
|
||||
endscript
|
||||
|
||||
@@ -45,10 +45,18 @@ SASLDB_PATH="${SASL_DB_PATH:-/data/sasl/sasldb2}"
|
||||
# when a client library needs STARTTLS on 587 instead of implicit TLS on 465).
|
||||
SUBMISSION_ENABLE="${SUBMISSION_ENABLE:-false}"
|
||||
|
||||
# Delivery log, written by postlogd and read by the panel's log-tailer. It lives
|
||||
# under the persistent /data (not the ephemeral /var/log) so the delivery lines
|
||||
# for messages still marked "queued" survive a container recreate — without
|
||||
# them those rows could never be resolved (architecture.md § Log tailer). The
|
||||
# default must match cmd/panel/main.go's MAIL_LOG; entrypoint.sh creates the
|
||||
# directory and the file with the ownership postlogd writes and the panel reads.
|
||||
MAIL_LOG_PATH="${MAIL_LOG:-/data/log/mail.log}"
|
||||
|
||||
# --- main.cf -----------------------------------------------------------------
|
||||
postconf -e \
|
||||
"myhostname=${HOSTNAME_VALUE}" \
|
||||
"maillog_file=/var/log/mail.log" \
|
||||
"maillog_file=${MAIL_LOG_PATH}" \
|
||||
"mydestination=" \
|
||||
"relayhost=" \
|
||||
"inet_interfaces=all" \
|
||||
|
||||
@@ -103,7 +103,7 @@ stdout_logfile_maxbytes=0
|
||||
stderr_logfile=/dev/stderr
|
||||
stderr_logfile_maxbytes=0
|
||||
|
||||
; Periodic logrotate for /var/log/mail.log (spec 9, 10: daily, 7-14 files kept
|
||||
; Periodic logrotate for /data/log/mail.log (spec 9, 10: daily, 7-14 files kept
|
||||
; in the image). Runs as root so logrotate can read/rotate the log; never exits
|
||||
; non-zero, so it neither trips the crashexit listener nor needs restarting.
|
||||
[program:logrotate]
|
||||
|
||||
Reference in New Issue
Block a user