Merge branch 'master' into add-send-only-aliases

This commit is contained in:
Noah Overcash 2025-03-31 21:08:12 -04:00
commit bed51886f4
No known key found for this signature in database
107 changed files with 2727 additions and 3301 deletions

View file

@ -23,7 +23,11 @@ function _setup_opendkim() {
# check if any keys are available
if [[ -e /tmp/docker-mailserver/opendkim/KeyTable ]]; then
cp -a /tmp/docker-mailserver/opendkim/* /etc/opendkim/
_log 'trace' "DKIM keys added for: $(find /etc/opendkim/keys/ -maxdepth 1 -type f -printf '%f ')"
local DKIM_DOMAINS
DKIM_DOMAINS=$(find /etc/opendkim/keys/ -maxdepth 1 -type f -printf '%f ')
_log 'trace' "DKIM keys added for: ${DKIM_DOMAINS}"
chown -R opendkim:opendkim /etc/opendkim/
chmod -R 0700 /etc/opendkim/keys/
else

View file

@ -3,13 +3,54 @@
function _setup_dovecot() {
_log 'debug' 'Setting up Dovecot'
# Protocol support
sedfile -i -e 's|include_try /usr/share/dovecot/protocols.d|include_try /etc/dovecot/protocols.d|g' /etc/dovecot/dovecot.conf
cp -a /usr/share/dovecot/protocols.d /etc/dovecot/
# disable pop3 (it will be eventually enabled later in the script, if requested)
# Disable these protocols by default, they can be enabled later via ENV (ENABLE_POP3, ENABLE_IMAP, ENABLE_MANAGESIEVE)
mv /etc/dovecot/protocols.d/pop3d.protocol /etc/dovecot/protocols.d/pop3d.protocol.disab
# disable imap (it will be eventually enabled later in the script, if requested)
mv /etc/dovecot/protocols.d/imapd.protocol /etc/dovecot/protocols.d/imapd.protocol.disab
mv /etc/dovecot/protocols.d/managesieved.protocol /etc/dovecot/protocols.d/managesieved.protocol.disab
sedfile -i 's|^postmaster_address = .*$|postmaster_address = '"${POSTMASTER_ADDRESS}"'|g' /etc/dovecot/conf.d/15-lda.conf
# NOTE: While Postfix will deliver to Dovecot via LMTP (Previously LDA until DMS v2),
# LDA may be used via other services like Getmail being configured to use /usr/lib/dovecot/deliver
# when mail does not need to go through Postfix.
# `mail_plugins` is scoped to the `protocol lda` config block of this file.
#
# TODO: `postmaster_address` + `hostname` appear to be for the general Dovecot config rather than LDA specific?
# https://doc.dovecot.org/2.3/settings/core/#core_setting-postmaster_address
# https://doc.dovecot.org/2.3/settings/core/#core_setting-hostname
# Dovecot 3.0 docs:
# https://doc.dovecot.org/main/core/summaries/settings.html#postmaster_address
# https://doc.dovecot.org/main/core/summaries/settings.html#postmaster_address
# https://doc.dovecot.org/main/core/config/delivery/lmtp.html#common-delivery-settings
# https://doc.dovecot.org/main/core/config/delivery/lda.html#common-delivery-settings
# https://doc.dovecot.org/main/core/config/sieve/submission.html#postmaster-address
# Shows config example with postmaster_address scoped in a `protocol lda { }` block:
# https://doc.dovecot.org/main/howto/virtual/simple_install.html#delivering-mails
#
# DMS initially copied Dovecot example configs, these were removed from Dovecot 2.4 onwards:
# https://github.com/dovecot/core/commit/5941699b277d762d98c202928cf5b5c8c70bc359
# In favor of a minimal config example:
# https://github.com/dovecot/core/commit/9a6a6aef35bb403fa96f0b5efdb0faff85b1471d
# 2.3 series example config:
# https://github.com/dovecot/core/blob/2.3.21.1/doc/example-config/conf.d/15-lda.conf
# Initial config files committed to DMS in April 2016:
# TODO: Consider housekeeping on config to only represent relevant changes/support by scripts
# https://github.com/docker-mailserver/docker-mailserver/commit/ee0d0853dd672488238eecb0ec2d26719ff45d7d
#
# TODO: `mail_plugins` appending `sieve` should probably be done for both `15-lda.conf` and `20-lmtp.conf`
# Presently DMS replaces the `20-lmtp.conf` from `dovecot-lmtpd` package with our own modified copy from 2016.
# The DMS variant only makes this one change to that file, thus we could adjust it as we do below for `15-lda.conf`
# Reference: https://github.com/docker-mailserver/docker-mailserver/pull/4350#issuecomment-2646736328
# shellcheck disable=SC2016
sedfile -i -r \
-e 's|^(\s*)#?(mail_plugins =).*|\1\2 $mail_plugins sieve|' \
-e 's|^#?(lda_mailbox_autocreate =).*|\1 yes|' \
-e 's|^#?(lda_mailbox_autosubscribe =).*|\1 yes|' \
-e "s|^#?(postmaster_address =).*|\1 ${POSTMASTER_ADDRESS}|" \
-e "s|^#?(hostname =).*|\1 ${HOSTNAME}|" \
/etc/dovecot/conf.d/15-lda.conf
if ! grep -q -E '^stats_writer_socket_path=' /etc/dovecot/dovecot.conf; then
printf '\n%s\n' 'stats_writer_socket_path=' >>/etc/dovecot/dovecot.conf
@ -24,6 +65,7 @@ function _setup_dovecot() {
sedfile -i -E \
"s|^(mail_location =).*|\1 ${DOVECOT_MAILBOX_FORMAT}:/var/mail/%d/%n|" \
/etc/dovecot/conf.d/10-mail.conf
_log 'trace' 'Enabling cron job for dbox purge'
mv /etc/cron.d/dovecot-purge.disabled /etc/cron.d/dovecot-purge
chmod 644 /etc/cron.d/dovecot-purge
@ -55,6 +97,12 @@ function _setup_dovecot() {
[[ -f /tmp/docker-mailserver/dovecot.cf ]] && cp /tmp/docker-mailserver/dovecot.cf /etc/dovecot/local.conf
}
# The `sieve` plugin is always enabled in DMS, this method handles user supplied sieve scripts + ManageSieve protocol
# NOTE: There is a related post-setup step for this sieve support handled at `_setup_post()` (setup-stack.sh)
# TODO: Improved sieve support may be needed in DMS to support this use-case:
# https://github.com/docker-mailserver/docker-mailserver/issues/3904
# TODO: Change detection support + refactor/DRY this sieve logic:
# https://github.com/orgs/docker-mailserver/discussions/2633#discussioncomment-11622955
function _setup_dovecot_sieve() {
mkdir -p /usr/lib/dovecot/sieve-{filter,global,pipe}
mkdir -p /usr/lib/dovecot/sieve-global/{before,after}
@ -192,8 +240,3 @@ function _setup_dovecot_inet_protocols() {
function _setup_dovecot_dhparam() {
_setup_dhparam 'Dovecot' '/etc/dovecot/dh.pem'
}
function _setup_dovecot_hostname() {
_log 'debug' 'Applying hostname to Dovecot'
sedfile -i "s|^#hostname =.*$|hostname = '${HOSTNAME}'|g" /etc/dovecot/conf.d/15-lda.conf
}

View file

@ -4,38 +4,46 @@ function _setup_getmail() {
if [[ ${ENABLE_GETMAIL} -eq 1 ]]; then
_log 'trace' 'Preparing Getmail configuration'
local GETMAILRC ID CONFIGS
local GETMAIL_RC ID GETMAIL_DIR
GETMAILRC='/etc/getmailrc.d'
CONFIGS=0
local GETMAIL_CONFIG_DIR='/tmp/docker-mailserver/getmail'
local GETMAIL_RC_DIR='/etc/getmailrc.d'
local GETMAIL_RC_GENERAL_CF="${GETMAIL_CONFIG_DIR}/getmailrc_general.cf"
local GETMAIL_RC_GENERAL='/etc/getmailrc_general'
mkdir -p "${GETMAILRC}"
# Create the directory /etc/getmailrc.d to place the user config in later.
mkdir -p "${GETMAIL_RC_DIR}"
# Generate getmailrc configs, starting with the `/etc/getmailrc_general` base config,
# Add a unique `message_log` config, then append users own config to the end.
for FILE in /tmp/docker-mailserver/getmail-*.cf; do
if [[ -f ${FILE} ]]; then
CONFIGS=1
ID=$(cut -d '-' -f 3 <<< "${FILE}" | cut -d '.' -f 1)
local GETMAIL_CONFIG="${GETMAILRC}/getmailrc-${ID}"
cat /etc/getmailrc_general >"${GETMAIL_CONFIG}"
echo -e "message_log = /var/log/mail/getmail-${ID}.log\n" >>"${GETMAIL_CONFIG}"
cat "${FILE}" >>"${GETMAIL_CONFIG}"
fi
done
if [[ ${CONFIGS} -eq 1 ]]; then
cat >/etc/cron.d/getmail << EOF
*/${GETMAIL_POLL} * * * * root /usr/local/bin/getmail-cron
EOF
chmod -R 600 "${GETMAILRC}"
# Check if custom getmailrc_general.cf file is present.
if [[ -f "${GETMAIL_RC_GENERAL_CF}" ]]; then
_log 'debug' "Custom 'getmailrc_general.cf' found"
cp "${GETMAIL_RC_GENERAL_CF}" "${GETMAIL_RC_GENERAL}"
fi
# Both the debug command and cron job (that runs getmail) for getmail
# expect this location to exist.
GETMAILDIR=/tmp/docker-mailserver/getmail
mkdir -p "${GETMAILDIR}"
# If no matching filenames are found, and the shell option nullglob is disabled, the word is left unchanged.
# If the nullglob option is set, and no matches are found, the word is removed.
shopt -s nullglob
# Generate getmailrc configs, starting with the `/etc/getmailrc_general` base config, then appending users own config to the end.
for FILE in "${GETMAIL_CONFIG_DIR}"/*.cf; do
if [[ ${FILE} =~ /getmail/(.+)\.cf ]] && [[ ${FILE} != "${GETMAIL_RC_GENERAL_CF}" ]]; then
ID=${BASH_REMATCH[1]}
_log 'debug' "Processing getmail config '${ID}'"
GETMAIL_RC=${GETMAIL_RC_DIR}/${ID}
cat "${GETMAIL_RC_GENERAL}" "${FILE}" >"${GETMAIL_RC}"
fi
done
# Strip read access from non-root due to files containing secrets:
chmod -R 600 "${GETMAIL_RC_DIR}"
# Directory, where "oldmail" files are stored.
# For more information see: https://getmail6.org/faq.html#faq-about-oldmail
# The debug command for getmail expects this location to exist.
GETMAIL_DIR=/var/lib/getmail
_log 'debug' "Creating getmail state-dir '${GETMAIL_DIR}'"
mkdir -p "${GETMAIL_DIR}"
else
_log 'debug' 'Getmail is disabled'
fi

View file

@ -1,15 +1,5 @@
#!/bin/bash
function _setup_logs_general() {
_log 'debug' 'Setting up general log files'
# File/folder permissions are fine when using docker volumes, but may be wrong
# when file system folders are mounted into the container.
# Set the expected values and create missing folders/files just in case.
mkdir -p /var/log/{mail,supervisor}
chown syslog:root /var/log/mail
}
function _setup_logrotate() {
_log 'debug' 'Setting up logrotate'

View file

@ -1,124 +1,138 @@
#!/bin/bash
DMS_STATE_DIR='/var/mail-state'
# Consolidate all states into a single directory
# (/var/mail-state) to allow persistence using docker volumes
function _setup_save_states() {
local DEST DESTDIR STATEDIR SERVICEDIR SERVICEDIRS SERVICEFILE SERVICEFILES
STATEDIR='/var/mail-state'
if [[ -d ${STATEDIR} ]]; then
_log 'debug' "Consolidating all state onto ${STATEDIR}"
# Always enabled features:
SERVICEDIRS=(
lib/logrotate
lib/postfix
spool/postfix
)
# Only consolidate state for services that are enabled
# Notably avoids copying over 200MB for the ClamAV database
[[ ${ENABLE_AMAVIS} -eq 1 ]] && SERVICEDIRS+=('lib/amavis')
[[ ${ENABLE_CLAMAV} -eq 1 ]] && SERVICEDIRS+=('lib/clamav')
[[ ${ENABLE_FAIL2BAN} -eq 1 ]] && SERVICEDIRS+=('lib/fail2ban')
[[ ${ENABLE_FETCHMAIL} -eq 1 ]] && SERVICEDIRS+=('lib/fetchmail')
[[ ${ENABLE_MTA_STS} -eq 1 ]] && SERVICEDIRS+=('lib/mta-sts')
[[ ${ENABLE_POSTGREY} -eq 1 ]] && SERVICEDIRS+=('lib/postgrey')
[[ ${ENABLE_RSPAMD} -eq 1 ]] && SERVICEDIRS+=('lib/rspamd')
[[ ${ENABLE_RSPAMD_REDIS} -eq 1 ]] && SERVICEDIRS+=('lib/redis')
[[ ${ENABLE_SPAMASSASSIN} -eq 1 ]] && SERVICEDIRS+=('lib/spamassassin')
[[ ${ENABLE_SRS} -eq 1 ]] && SERVICEDIRS+=('lib/postsrsd')
[[ ${SMTP_ONLY} -ne 1 ]] && SERVICEDIRS+=('lib/dovecot')
# Single service files
[[ ${ENABLE_SRS} -eq 1 ]] && SERVICEFILES+=('/etc/postsrsd.secret')
for SERVICEFILE in "${SERVICEFILES[@]}"; do
DEST="${STATEDIR}/${SERVICEFILE}"
DESTDIR="${DEST%/*}"
mkdir -p "${DESTDIR}"
if [[ -f ${DEST} ]]; then
_log 'trace' "Destination ${DEST} exists, linking ${SERVICEFILE} to it"
# Original content from image no longer relevant, remove it:
rm -f "${SERVICEFILE}"
elif [[ -f "${SERVICEFILE}" ]]; then
_log 'trace' "Moving ${SERVICEFILE} to ${DEST}"
# Empty volume was mounted, or new content from enabling a feature ENV:
mv "${SERVICEFILE}" "${DEST}"
# Apply SELinux security context to match the state directory, so access
# is not restricted to the current running container:
chcon -R --reference="${STATEDIR}" "${DEST}" 2>/dev/null || true
fi
# Symlink the original file in the container ($SERVICEFILE) to be
# sourced from assocaiated path in /var/mail-state/ ($DEST):
ln -s "${DEST}" "${SERVICEFILE}"
done
for SERVICEDIR in "${SERVICEDIRS[@]}"; do
DEST="${STATEDIR}/${SERVICEDIR//\//-}"
SERVICEDIR="/var/${SERVICEDIR}"
# If relevant content is found in /var/mail-state (presumably a volume mount),
# use it instead. Otherwise copy over any missing directories checked.
if [[ -d ${DEST} ]]; then
_log 'trace' "Destination ${DEST} exists, linking ${SERVICEDIR} to it"
# Original content from image no longer relevant, remove it:
rm -rf "${SERVICEDIR}"
elif [[ -d ${SERVICEDIR} ]]; then
_log 'trace' "Moving contents of ${SERVICEDIR} to ${DEST}"
# An empty volume was mounted, or new content dir now exists from enabling a feature ENV:
mv "${SERVICEDIR}" "${DEST}"
# Apply SELinux security context to match the state directory, so access
# is not restricted to the current running container:
chcon -R --reference="${STATEDIR}" "${DEST}" 2>/dev/null || true
else
_log 'error' "${SERVICEDIR} should exist but is missing"
fi
# Symlink the original path in the container ($SERVICEDIR) to be
# sourced from assocaiated path in /var/mail-state/ ($DEST):
ln -s "${DEST}" "${SERVICEDIR}"
done
# This ensures the user and group of the files from the external mount have their
# numeric ID values in sync. New releases where the installed packages order changes
# can change the values in the Docker image, causing an ownership mismatch.
# NOTE: More details about users and groups added during image builds are documented here:
# https://github.com/docker-mailserver/docker-mailserver/pull/3011#issuecomment-1399120252
_log 'trace' "Fixing ${STATEDIR}/* permissions"
[[ ${ENABLE_AMAVIS} -eq 1 ]] && chown -R amavis:amavis "${STATEDIR}/lib-amavis"
[[ ${ENABLE_CLAMAV} -eq 1 ]] && chown -R clamav:clamav "${STATEDIR}/lib-clamav"
[[ ${ENABLE_FETCHMAIL} -eq 1 ]] && chown -R fetchmail:nogroup "${STATEDIR}/lib-fetchmail"
[[ ${ENABLE_MTA_STS} -eq 1 ]] && chown -R _mta-sts:_mta-sts "${STATEDIR}/lib-mta-sts"
[[ ${ENABLE_POSTGREY} -eq 1 ]] && chown -R postgrey:postgrey "${STATEDIR}/lib-postgrey"
[[ ${ENABLE_RSPAMD} -eq 1 ]] && chown -R _rspamd:_rspamd "${STATEDIR}/lib-rspamd"
[[ ${ENABLE_RSPAMD_REDIS} -eq 1 ]] && chown -R redis:redis "${STATEDIR}/lib-redis"
[[ ${ENABLE_SPAMASSASSIN} -eq 1 ]] && chown -R debian-spamd:debian-spamd "${STATEDIR}/lib-spamassassin"
chown -R root:root "${STATEDIR}/lib-logrotate"
chown -R postfix:postfix "${STATEDIR}/lib-postfix"
# NOTE: The Postfix spool location has mixed owner/groups to take into account:
# UID = postfix(101): active, bounce, corrupt, defer, deferred, flush, hold, incoming, maildrop, private, public, saved, trace
# UID = root(0): dev, etc, lib, pid, usr
# GID = postdrop(103): maildrop, public
# GID for all other directories is root(0)
# NOTE: `spool-postfix/private/` will be set to `postfix:postfix` when Postfix starts / restarts
# Set most common ownership:
chown -R postfix:root "${STATEDIR}/spool-postfix"
chown root:root "${STATEDIR}/spool-postfix"
# These two require the postdrop(103) group:
chgrp -R postdrop "${STATEDIR}"/spool-postfix/{maildrop,public}
# These permissions rely on the `postdrop` binary having the SGID bit set.
# Ref: https://github.com/docker-mailserver/docker-mailserver/pull/3625
chmod 730 "${STATEDIR}/spool-postfix/maildrop"
chmod 710 "${STATEDIR}/spool-postfix/public"
else
_log 'debug' "'${STATEDIR}' is not present; Not consolidating state"
if [[ ! -d ${DMS_STATE_DIR} ]]; then
_log 'debug' "'${DMS_STATE_DIR}' is not present - not consolidating state"
return 0
fi
_log 'debug' "Consolidating all state onto ${DMS_STATE_DIR}"
local DEST SERVICEDIR SERVICEDIRS SERVICEFILE SERVICEFILES
# Always enabled features:
SERVICEDIRS=(
'lib/logrotate'
'lib/postfix'
'spool/postfix'
)
# Only consolidate state for services that are enabled
# Notably avoids copying over 200MB for the ClamAV database
[[ ${ENABLE_AMAVIS} -eq 1 ]] && SERVICEDIRS+=('lib/amavis')
[[ ${ENABLE_CLAMAV} -eq 1 ]] && SERVICEDIRS+=('lib/clamav')
[[ ${ENABLE_FAIL2BAN} -eq 1 ]] && SERVICEDIRS+=('lib/fail2ban')
[[ ${ENABLE_FETCHMAIL} -eq 1 ]] && SERVICEDIRS+=('lib/fetchmail')
[[ ${ENABLE_GETMAIL} -eq 1 ]] && SERVICEDIRS+=('lib/getmail')
[[ ${ENABLE_MTA_STS} -eq 1 ]] && SERVICEDIRS+=('lib/mta-sts')
[[ ${ENABLE_POSTGREY} -eq 1 ]] && SERVICEDIRS+=('lib/postgrey')
[[ ${ENABLE_RSPAMD} -eq 1 ]] && SERVICEDIRS+=('lib/rspamd')
[[ ${ENABLE_RSPAMD_REDIS} -eq 1 ]] && SERVICEDIRS+=('lib/redis')
[[ ${ENABLE_SPAMASSASSIN} -eq 1 ]] && SERVICEDIRS+=('lib/spamassassin')
[[ ${ENABLE_SRS} -eq 1 ]] && SERVICEDIRS+=('lib/postsrsd')
[[ ${SMTP_ONLY} -ne 1 ]] && SERVICEDIRS+=('lib/dovecot')
# Single service files
[[ ${ENABLE_SRS} -eq 1 ]] && SERVICEFILES+=('/etc/postsrsd.secret')
for SERVICEFILE in "${SERVICEFILES[@]}"; do
DEST="${DMS_STATE_DIR}/${SERVICEFILE}"
# Append service parent dir(s) path to the state dir and ensure it exists:
mkdir -p "${DEST%/*}"
if [[ -f ${DEST} ]]; then
_log 'trace' "Destination ${DEST} exists, linking ${SERVICEFILE} to it"
# Original content from image no longer relevant, remove it:
rm -f "${SERVICEFILE}"
elif [[ -f "${SERVICEFILE}" ]]; then
_log 'trace' "Moving ${SERVICEFILE} to ${DEST}"
# Empty volume was mounted, or new content from enabling a feature ENV:
mv "${SERVICEFILE}" "${DEST}"
# Apply SELinux security context to match the state directory, so access
# is not restricted to the current running container:
chcon -R --reference="${DMS_STATE_DIR}" "${DEST}" 2>/dev/null || true
fi
# Symlink the original file in the container ($SERVICEFILE) to be
# sourced from assocaiated path in /var/mail-state/ ($DEST):
ln -s "${DEST}" "${SERVICEFILE}"
done
for SERVICEDIR in "${SERVICEDIRS[@]}"; do
DEST="${DMS_STATE_DIR}/${SERVICEDIR//\//-}"
SERVICEDIR="/var/${SERVICEDIR}"
# If relevant content is found in /var/mail-state (presumably a volume mount),
# use it instead. Otherwise copy over any missing directories checked.
if [[ -d ${DEST} ]]; then
_log 'trace' "Destination ${DEST} exists, linking ${SERVICEDIR} to it"
# Original content from image no longer relevant, remove it:
rm -rf "${SERVICEDIR}"
elif [[ -d ${SERVICEDIR} ]]; then
_log 'trace' "Moving contents of ${SERVICEDIR} to ${DEST}"
# An empty volume was mounted, or new content dir now exists from enabling a feature ENV:
mv "${SERVICEDIR}" "${DEST}"
# Apply SELinux security context to match the state directory, so access
# is not restricted to the current running container:
# https://github.com/docker-mailserver/docker-mailserver/pull/3890
chcon -R --reference="${DMS_STATE_DIR}" "${DEST}" 2>/dev/null || true
else
_log 'error' "${SERVICEDIR} should exist but is missing"
fi
# Symlink the original path in the container ($SERVICEDIR) to be
# sourced from associated path in /var/mail-state/ ($DEST):
ln -s "${DEST}" "${SERVICEDIR}"
done
}
# These corrections are to fix changes to UID/GID values between upgrades,
# or when ownership/permissions were altered externally on the host (eg: migration or system scripts)
function _setup_adjust_state_permissions() {
[[ ! -d ${DMS_STATE_DIR} ]] && return 0
# Parent directories must have executable bit set to descend the file tree for access,
# as each service running as a non-root user requires this to access their state directory,
# `/var/mail-state` must allow all users `+x`:
chmod +x "${DMS_STATE_DIR}"
# This ensures the user and group of the files from the external mount have their
# numeric ID values in sync. New releases where the installed packages order changes
# can change the values in the Docker image, causing an ownership mismatch.
# NOTE: More details about users and groups added during image builds are documented here:
# https://github.com/docker-mailserver/docker-mailserver/pull/3011#issuecomment-1399120252
_log 'trace' "Ensuring correct ownership + permissions for DMS state dir: '${DMS_STATE_DIR}'"
[[ ${ENABLE_AMAVIS} -eq 1 ]] && chown -R amavis:amavis "${DMS_STATE_DIR}/lib-amavis"
[[ ${ENABLE_CLAMAV} -eq 1 ]] && chown -R clamav:clamav "${DMS_STATE_DIR}/lib-clamav"
[[ ${ENABLE_FETCHMAIL} -eq 1 ]] && chown -R fetchmail:nogroup "${DMS_STATE_DIR}/lib-fetchmail"
[[ ${ENABLE_MTA_STS} -eq 1 ]] && chown -R _mta-sts:_mta-sts "${DMS_STATE_DIR}/lib-mta-sts"
[[ ${ENABLE_POSTGREY} -eq 1 ]] && chown -R postgrey:postgrey "${DMS_STATE_DIR}/lib-postgrey"
[[ ${ENABLE_RSPAMD} -eq 1 ]] && chown -R _rspamd:_rspamd "${DMS_STATE_DIR}/lib-rspamd"
[[ ${ENABLE_RSPAMD_REDIS} -eq 1 ]] && chown -R redis:redis "${DMS_STATE_DIR}/lib-redis"
[[ ${ENABLE_SPAMASSASSIN} -eq 1 ]] && chown -R debian-spamd:debian-spamd "${DMS_STATE_DIR}/lib-spamassassin"
chown -R root:root "${DMS_STATE_DIR}/lib-logrotate"
chown -R postfix:postfix "${DMS_STATE_DIR}/lib-postfix"
# NOTE: The Postfix spool location has mixed owner/groups to take into account:
# UID = postfix(101): active, bounce, corrupt, defer, deferred, flush, hold, incoming, maildrop, private, public, saved, trace
# UID = root(0): dev, etc, lib, pid, usr
# GID = postdrop(103): maildrop, public
# GID for all other directories is root(0)
# NOTE: `spool-postfix/private/` will be set to `postfix:postfix` when Postfix starts / restarts
# Set most common ownership:
chown -R postfix:root "${DMS_STATE_DIR}/spool-postfix"
chown root:root "${DMS_STATE_DIR}/spool-postfix"
# These two require the postdrop(103) group:
chgrp -R postdrop "${DMS_STATE_DIR}"/spool-postfix/{maildrop,public}
# These permissions rely on the `postdrop` binary having the SGID bit set.
# Ref: https://github.com/docker-mailserver/docker-mailserver/pull/3625
chmod 730 "${DMS_STATE_DIR}/spool-postfix/maildrop"
chmod 710 "${DMS_STATE_DIR}/spool-postfix/public"
}

View file

@ -79,6 +79,8 @@ EOF
if [[ ${ACCOUNT_PROVISIONER} == 'FILE' ]]; then
postconf 'virtual_mailbox_maps = texthash:/etc/postfix/vmailbox'
fi
# Historical context regarding decision to use LMTP instead of LDA (do not change this):
# https://github.com/docker-mailserver/docker-mailserver/issues/4178#issuecomment-2375489302
postconf 'virtual_transport = lmtp:unix:/var/run/dovecot/lmtp'
fi
@ -91,13 +93,19 @@ EOF
function _setup_postfix_late() {
_log 'debug' 'Configuring Postfix (late setup)'
# These two config files are `access` database tables managed via `setup email restrict`:
# NOTE: Prepends to existing restrictions, thus has priority over other permit/reject policies that follow.
# https://www.postfix.org/postconf.5.html#smtpd_sender_restrictions
# https://www.postfix.org/access.5.html
__postfix__log 'trace' 'Configuring user access'
if [[ -f /tmp/docker-mailserver/postfix-send-access.cf ]]; then
sed -i -E 's|(smtpd_sender_restrictions =)|\1 check_sender_access texthash:/tmp/docker-mailserver/postfix-send-access.cf,|' /etc/postfix/main.cf
# Prefer to prepend to our specialized variant instead:
# https://github.com/docker-mailserver/docker-mailserver/pull/4379
sed -i -E 's|^(dms_smtpd_sender_restrictions =)|\1 check_sender_access texthash:/tmp/docker-mailserver/postfix-send-access.cf,|' /etc/postfix/main.cf
fi
if [[ -f /tmp/docker-mailserver/postfix-receive-access.cf ]]; then
sed -i -E 's|(smtpd_recipient_restrictions =)|\1 check_recipient_access texthash:/tmp/docker-mailserver/postfix-receive-access.cf,|' /etc/postfix/main.cf
sed -i -E 's|^(smtpd_recipient_restrictions =)|\1 check_recipient_access texthash:/tmp/docker-mailserver/postfix-receive-access.cf,|' /etc/postfix/main.cf
fi
__postfix__log 'trace' 'Configuring relay host'
@ -129,7 +137,7 @@ function __postfix__setup_override_configuration() {
# Do not directly output to 'main.cf' as this causes a read-write-conflict.
# `postconf` output is filtered to skip expected warnings regarding overrides:
# https://github.com/docker-mailserver/docker-mailserver/pull/3880#discussion_r1510414576
postconf -n >/tmp/postfix-main-new.cf 2> >(grep -v 'overriding earlier entry')
postconf -n >/tmp/postfix-main-new.cf 2> >(grep -v 'overriding earlier entry' >&2)
mv /tmp/postfix-main-new.cf /etc/postfix/main.cf
_adjust_mtime_for_postfix_maincf

View file

@ -155,13 +155,6 @@ function __setup__security__clamav() {
if [[ ${ENABLE_CLAMAV} -eq 1 ]]; then
_log 'debug' 'Enabling and configuring ClamAV'
local FILE
for FILE in /var/log/mail/{clamav,freshclam}.log; do
touch "${FILE}"
chown clamav:adm "${FILE}"
chmod 640 "${FILE}"
done
if [[ ${CLAMAV_MESSAGE_SIZE_LIMIT} != '25M' ]]; then
_log 'trace' "Setting ClamAV message scan size limit to '${CLAMAV_MESSAGE_SIZE_LIMIT}'"

View file

@ -76,8 +76,9 @@ function __rspamd__run_early_setup_and_checks() {
mkdir -p /var/lib/rspamd/
: >/var/lib/rspamd/stats.ucl
if [[ -d ${RSPAMD_DMS_OVERRIDE_D} ]]; then
cp "${RSPAMD_DMS_OVERRIDE_D}"/* "${RSPAMD_OVERRIDE_D}"
# Copy if directory exists and is not empty
if [[ -d ${RSPAMD_DMS_OVERRIDE_D} ]] && [[ -z $(find "${RSPAMD_DMS_OVERRIDE_D}" -maxdepth 0 -empty) ]]; then
cp "${RSPAMD_DMS_OVERRIDE_D}/"* "${RSPAMD_OVERRIDE_D}"
fi
if [[ ${ENABLE_AMAVIS} -eq 1 ]] || [[ ${ENABLE_SPAMASSASSIN} -eq 1 ]]; then
@ -319,8 +320,7 @@ function __rspamd__setup_check_authenticated() {
local MODULE_FILE="${RSPAMD_LOCAL_D}/settings.conf"
readonly MODULE_FILE
if _env_var_expect_zero_or_one 'RSPAMD_CHECK_AUTHENTICATED' \
&& [[ ${RSPAMD_CHECK_AUTHENTICATED} -eq 0 ]]
then
&& [[ ${RSPAMD_CHECK_AUTHENTICATED} -eq 0 ]]; then
__rspamd__log 'debug' 'Content checks for authenticated users are disabled'
else
__rspamd__log 'debug' 'Enabling content checks for authenticated users'
@ -330,34 +330,24 @@ function __rspamd__setup_check_authenticated() {
fi
}
# This function performs a simple check: go through DKIM configuration files, acquire
# all private key file locations and check whether they exist and whether they can be
# accessed by Rspamd.
# This function performs a simple check on the queried rspamd DKIM configuration:
# - Acquire all private key file locations and check whether they exist and can be accessed by Rspamd.
# - We are not checking paths that contain the '$' symbol.
function __rspamd__check_dkim_permissions() {
local DKIM_CONF_FILES DKIM_KEY_FILES
[[ -f ${RSPAMD_LOCAL_D}/dkim_signing.conf ]] && DKIM_CONF_FILES+=("${RSPAMD_LOCAL_D}/dkim_signing.conf")
[[ -f ${RSPAMD_OVERRIDE_D}/dkim_signing.conf ]] && DKIM_CONF_FILES+=("${RSPAMD_OVERRIDE_D}/dkim_signing.conf")
# Here, we populate DKIM_KEY_FILES which we later iterate over. DKIM_KEY_FILES
# contains all keys files configured by the user.
local FILE
for FILE in "${DKIM_CONF_FILES[@]}"; do
readarray -t DKIM_KEY_FILES_TMP < <(grep -o -E 'path = .*' "${FILE}" | cut -d '=' -f 2 | tr -d ' ";')
DKIM_KEY_FILES+=("${DKIM_KEY_FILES_TMP[@]}")
done
for FILE in "${DKIM_KEY_FILES[@]}"; do
if [[ -f ${FILE} ]]; then
__rspamd__log 'trace' "Checking DKIM file '${FILE}'"
local KEY_FILE
while read -r KEY_FILE; do
if [[ -f ${KEY_FILE} ]]; then
__rspamd__log 'trace' "Checking DKIM file '${KEY_FILE}'"
# See https://serverfault.com/a/829314 for an explanation on `-exec false {} +`
# We additionally resolve symbolic links to check the permissions of the actual files
if find "$(realpath -eL "${FILE}")" \( -user _rspamd -or -group _rspamd -or -perm -o=r \) -exec false {} +; then
__rspamd__log 'warn' "Rspamd DKIM private key file '${FILE}' does not appear to have correct permissions/ownership for Rspamd to use it"
if find "$(realpath -L "${KEY_FILE}")" \( -user _rspamd -or -group _rspamd -or -perm -o=r \) \
-exec false {} +; then
__rspamd__log 'warn' "Rspamd DKIM private key file '${KEY_FILE}' does not appear to have correct permissions/ownership for Rspamd to use it"
else
__rspamd__log 'trace' "DKIM file '${FILE}' permissions and ownership appear correct"
__rspamd__log 'trace' "DKIM file '${KEY_FILE}' permissions and ownership appear correct"
fi
else
__rspamd__log 'warn' "Rspamd DKIM private key file '${FILE}' is configured for usage, but does not appear to exist"
__rspamd__log 'warn' "Rspamd DKIM private key file '${KEY_FILE}' is configured for usage, but does not appear to exist"
fi
done
done < <(rspamadm configdump dkim_signing | grep 'path =' | grep -v -F '$' | awk '{print $3}' | tr -d ';"')
}