Merge branch 'master' into mta-sts-support

This commit is contained in:
Georg Lauterbach 2023-11-01 20:29:11 +01:00 committed by GitHub
commit 83f87885bc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 1557 additions and 1322 deletions

View File

@ -10,12 +10,6 @@ permissions:
pull-requests: write
statuses: write
env:
# Assign commit authorship to official Github Actions bot:
GIT_USER: github-actions[bot]
GIT_EMAIL: 41898282+github-actions[bot]@users.noreply.github.com
BRANCH_NAME: contributors-update
jobs:
add-contributors:
name: 'Add Contributors'
@ -24,64 +18,15 @@ jobs:
- name: 'Checkout'
uses: actions/checkout@v4
- name: 'Checkout New Branch and Push It'
run: |
git checkout -b ${{ env.BRANCH_NAME }}
git push --force https://${GITHUB_ACTOR}:${GITHUB_TOKEN}@github.com/${GITHUB_REPOSITORY}.git HEAD:${{ env.BRANCH_NAME }}
git checkout master
# See https://github.com/marketplace/actions/auto-add-contributors for reference of the action.
#
# This action is not well documented, but it does the job for now. We pin the version in order
# to not have any issues in the future.
- name: 'Update CONTRIBUTORS.md'
uses: BobAnkh/add-contributors@v0.2.2
uses: akhilmhdh/contributors-readme-action@v2.3.6
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
ACCESS_TOKEN: ${{secrets.GITHUB_TOKEN}}
BRANCH: ${{ env.BRANCH_NAME }}
COMMIT_MESSAGE: 'docs: update `CONTRIBUTORS.md`'
PATH: /CONTRIBUTORS.md
CONTRIBUTOR: '## Contributors'
COLUMN_PER_ROW: 6
IMG_WIDTH: 100
FONT_SIZE: 14
AVATAR_SHAPE: round
# See https://github.com/marketplace/actions/create-pull-request for reference of the action.
- name: 'Create Pull Request'
uses: peter-evans/create-pull-request@v5.0.2
id: create-pr
with:
token: ${{ secrets.GITHUB_TOKEN }}
base: master
branch: ${{ env.BRANCH_NAME }}
title: 'docs: update `CONTRIBUTORS.md`'
commit-message: 'docs: update `CONTRIBUTORS.md`'
delete-branch: true
committer: ${{ env.GIT_USER }} <${{ env.GIT_EMAIL }}>
author: ${{ env.GIT_USER }} <${{ env.GIT_EMAIL }}>
signoff: true
body: |
Updated `CONTRIBUTORS.md` via the CI workflow: [`contributors.yml`][workflow].
[workflow]: https://github.com/docker-mailserver/docker-mailserver/blob/master/.github/workflows/contributors.yml
# See https://github.com/marketplace/actions/set-commit-status for reference of the action.
#
# GH Actions are limited when it comes to actions triggering other actions. Hence,
# this whole workflow will not trigger a `pull_request` event without a PAT. The lint
# workflow, which is required due to branch protection, is not important for this type
# of PR, so we skip it and pretend it was successful.
- name: 'Set Status for Linting Actions to Success (Skipped)'
uses: myrotvorets/set-commit-status-action@v2.0.0
continue-on-error: true
with:
token: ${{ secrets.GITHUB_TOKEN }}
# Skipped workflows are still assigned a "success" status:
status: success
# This should be the correct commit SHA on ${{ env.BRANCH_NAME }}:
sha: ${{ steps.create-pr.outputs.pull-request-head-sha }}
# Name of status check to add/update:
context: lint
# Optional message/note we can inline to the right of the context name in the UI:
description: Lint skipped. Not relevant.
readme_path: CONTRIBUTORS.md
collaborators: all
commit_message: 'docs: updated `CONTRIBUTORS.md`'
committer_username: github-actions[bot]
committer_email: 41898282+github-actions[bot]@users.noreply.github.com
pr_title_on_protected: 'docs: update `CONTRIBUTORS.md'
auto_detect_branch_protection: true

View File

@ -9,6 +9,19 @@ All notable changes to this project will be documented in this file. The format
### Breaking
- The environment variable `ENABLE_LDAP=1` has been changed to `ACCOUNT_PROVISIONER=LDAP`.
- Postfix now defaults to supporting DSNs (_[Delivery Status Notifications](https://github.com/docker-mailserver/docker-mailserver/pull/3572#issuecomment-1751880574)_) only for authenticated users. This is a security measure to reduce spammer abuse of your DMS instance as a backscatter source.
- If you need to modify this change, please let us know by opening an issue / discussion.
- You can [opt-out (_enable DSNs_) via the `postfix-main.cf` override support](https://docker-mailserver.github.io/docker-mailserver/v12.1/config/advanced/override-defaults/postfix/) using the contents: `smtpd_discard_ehlo_keywords =`.
- Likewise for authenticated users, the submission(s) ports (465 + 587) are configured internally via `master.cf` to keep DSNs enabled (_since authentication protects from abuse_).
If necessary, DSNs for authenticated users can be disabled via the `postfix-master.cf` override with the following contents:
```cf
submission/inet/smtpd_discard_ehlo_keywords=silent-discard,dsn
submissions/inet/smtpd_discard_ehlo_keywords=silent-discard,dsn
```
- using the old path for the Rspamd custom commands file (`/tmp/docker-mailserver/rspamd-modules.conf`), which was deprecated, will now prevent startup; use `/tmp/docker-mailserver/rspamd/custom-commands.conf` instead
### Added

File diff suppressed because it is too large Load Diff

View File

@ -107,11 +107,11 @@ DMS brings sane default settings for Rspamd. They are located at `/etc/rspamd/lo
!!! question "What is [`docker-data/dms/config/`][docs-dms-config-volume]?"
If you want to overwrite the default settings and / or provide your own settings, you can place files at `docker-data/dms/config/rspamd/override.d/` (a directory that is linked to `/etc/rspamd/override.d/`, if it exists) to override Rspamd and DMS default settings. This directory will not do a complete file override, but a [forced override of the specific settings in that file][rspamd-docs-override-dir].
If you want to overwrite the default settings and / or provide your own settings, you can place files at `docker-data/dms/config/rspamd/override.d/`. Files from this directory are copied to `/etc/rspamd/override.d/` during startup. These files [forcibly override][rspamd-docs-override-dir] Rspamd and DMS default settings.
!!! warning "Clashing Overrides"
Note that when also [using the `rspamd-commands` file](#with-the-help-of-a-custom-file), files in `override.d` may be overwritten in case you adjust them manually and with the help of the file.
Note that when also [using the `custom-commands.conf` file](#with-the-help-of-a-custom-file), files in `override.d` may be overwritten in case you adjust them manually and with the help of the file.
[rspamd-docs-override-dir]: https://www.rspamd.com/doc/faq.html#what-are-the-locald-and-overrided-directories
[docs-dms-config-volume]: ../../faq.md#what-about-the-docker-datadmsconfig-directory

View File

@ -33,6 +33,9 @@ ${ORANGE}EXAMPLES${RESET}
${LWHITE}./setup.sh alias add alias@example.com recipient@example.com${RESET}
Add the alias 'alias@example.com' for the mail account 'recipient@example.com'.
${LWHITE}./setup.sh alias add alias@example.com 'recipient@example.com, another-recipient@example.com'${RESET}
Multiple recipients are separated by comma.
${ORANGE}EXIT STATUS${RESET}
Exit status is 0 if command was successful. If wrong arguments are provided
or arguments contain errors, the script will exit early with exit status 1.

View File

@ -27,9 +27,10 @@ ${ORANGE}DESCRIPTION${RESET}
${ORANGE}OPTIONS${RESET}
${BLUE}Generic Program Information${RESET}
-v Enable verbose logging (setting the log level to 'debug').
-vv Enable very verbose logging (setting the log level to 'trace').
help Print the usage information.
-f | --force Overwrite existing files if there are any
-v Enable verbose logging (setting the log level to 'debug').
-vv Enable very verbose logging (setting the log level to 'trace').
help Print the usage information.
${BLUE}Configuration adjustments${RESET}
keytype Set the type of key you want to use.
@ -69,6 +70,7 @@ function __do_as_rspamd_user() {
}
function _parse_arguments() {
FORCE=0
KEYTYPE='rsa'
KEYSIZE='2048'
SELECTOR='mail'
@ -112,6 +114,12 @@ function _parse_arguments() {
exit 0
;;
( '-f' | '--force' )
FORCE=1
shift 1
continue
;;
( '-vv' )
# shellcheck disable=SC2034
LOG_LEVEL='trace'
@ -132,30 +140,36 @@ function _parse_arguments() {
__usage
_exit_with_error "Unknown option(s) '${1}' ${2:+"and '${2}'"}"
;;
esac
shift 2
done
return 0
}
function _preflight_checks() {
if [[ ${KEYTYPE} == 'ed25519' ]] && [[ ${KEYSIZE} -ne 2048 ]]; then
_exit_with_error "Chosen keytype does not accept the 'keysize' argument"
fi
return 0
if [[ ! -d /tmp/docker-mailserver ]]; then
_log 'warn' "The directory '/tmp/docker-mailserver' does not seem to be mounted by a volume - the Rspamd (DKIM) configuration will not be persisted"
fi
_rspamd_get_envs
mkdir -p "${RSPAMD_DMS_DKIM_D}" "${RSPAMD_DMS_OVERRIDE_D}"
chown _rspamd:_rspamd "${RSPAMD_DMS_DKIM_D}"
}
function _create_keys() {
# Note: Variables not marked with `local` are used
# in other functions (after this function was called).
BASE_DIR='/tmp/docker-mailserver/rspamd/dkim'
if [[ ${KEYTYPE} == 'rsa' ]]; then
local BASE_FILE_NAME="${BASE_DIR}/${KEYTYPE}-${KEYSIZE}-${SELECTOR}-${DOMAIN}"
local BASE_FILE_NAME="${RSPAMD_DMS_DKIM_D}/${KEYTYPE}-${KEYSIZE}-${SELECTOR}-${DOMAIN}"
KEYTYPE_OPTIONS=('-b' "${KEYSIZE}")
_log 'info' "Creating DKIM keys of type '${KEYTYPE}' and length '${KEYSIZE}' with selector '${SELECTOR}' for domain '${DOMAIN}'"
else
local BASE_FILE_NAME="${BASE_DIR}/${KEYTYPE}-${SELECTOR}-${DOMAIN}"
local BASE_FILE_NAME="${RSPAMD_DMS_DKIM_D}/${KEYTYPE}-${SELECTOR}-${DOMAIN}"
KEYTYPE_OPTIONS=('-t' "${KEYTYPE}")
_log 'info' "Creating DKIM keys of type '${KEYTYPE}' with selector '${SELECTOR}' for domain '${DOMAIN}'"
fi
@ -164,8 +178,15 @@ function _create_keys() {
PUBLIC_KEY_DNS_FILE="${BASE_FILE_NAME}.public.dns.txt"
PRIVATE_KEY_FILE="${BASE_FILE_NAME}.private.txt"
mkdir -p "${BASE_DIR}"
chown _rspamd:_rspamd "${BASE_DIR}"
if [[ -f ${PUBLIC_KEY_FILE} ]] || [[ -f ${PUBLIC_KEY_DNS_FILE} ]] || [[ -f ${PRIVATE_KEY_FILE} ]]; then
if [[ ${FORCE} -eq 0 ]]; then
_log 'error' "Not overwriting existing files (use '--force' to overwrite existing files)"
exit 1
else
_log 'info' "Overwriting existing files as the '--force' option was supplied"
rm "${PUBLIC_KEY_FILE}" "${PUBLIC_KEY_DNS_FILE}" "${PRIVATE_KEY_FILE}"
fi
fi
# shellcheck disable=SC2310
if __do_as_rspamd_user rspamadm \
@ -186,8 +207,8 @@ function _create_keys() {
function _check_permissions() {
# shellcheck disable=SC2310
if ! __do_as_rspamd_user ls "${BASE_DIR}" >/dev/null; then
_log 'warn' "The Rspamd user ('_rspamd') seems to be unable to list files in the keys directory ('${BASE_DIR}') - Rspamd may experience permission errors later"
if ! __do_as_rspamd_user ls "${RSPAMD_DMS_DKIM_D}" >/dev/null; then
_log 'warn' "The Rspamd user ('_rspamd') seems to be unable to list files in the keys directory ('${RSPAMD_DMS_DKIM_D}') - Rspamd may experience permission errors later"
elif ! __do_as_rspamd_user cat "${PRIVATE_KEY_FILE}" >/dev/null; then
_log 'warn' "The Rspamd user ('_rspamd') seems to be unable to read the private key file - Rspamd may experience permission errors later"
else
@ -196,11 +217,11 @@ function _check_permissions() {
}
function _setup_default_signing_conf() {
local DEFAULT_CONFIG_FILE='/etc/rspamd/override.d/dkim_signing.conf'
local DEFAULT_CONFIG_FILE="${RSPAMD_DMS_OVERRIDE_D}/dkim_signing.conf"
if [[ -f ${DEFAULT_CONFIG_FILE} ]]; then
_log 'debug' "'${DEFAULT_CONFIG_FILE}' exists, not supplying a default"
_log 'info' "'${DEFAULT_CONFIG_FILE}' exists, not supplying a default ('--force' does not overwrite this file, manual adjustment required)"
else
_log 'info' "Supplying a default configuration ('${DEFAULT_CONFIG_FILE}')"
_log 'info' "Supplying a default configuration (to '${DEFAULT_CONFIG_FILE}')"
cat >"${DEFAULT_CONFIG_FILE}" << EOF
# documentation: https://rspamd.com/doc/modules/dkim_signing.html
@ -225,7 +246,15 @@ domain {
}
EOF
chown _rspamd:_rspamd "${DEFAULT_CONFIG_FILE}"
# We copy here immediately in order to not rely on the changedetector - this way, users
# can immediately use the new keys. The file should not already exist in ${RSPAMD_OVERRIDE_D}
# since it would have been copied already.
cp "${DEFAULT_CONFIG_FILE}" "${RSPAMD_OVERRIDE_D}/dkim_signing.conf"
chown _rspamd:_rspamd "${DEFAULT_CONFIG_FILE}" "${RSPAMD_OVERRIDE_D}/dkim_signing.conf"
_log 'debug' 'Restarting Rspamd as initial DKIM configuration was suppplied'
supervisorctl restart rspamd
fi
}
@ -254,6 +283,7 @@ function _final_steps() {
_obtain_hostname_and_domainname
_require_n_parameters_or_print_usage 0 "${@}"
_parse_arguments "${@}"
_preflight_checks
_create_keys
_check_permissions
_setup_default_signing_conf

View File

@ -54,6 +54,7 @@ smtpd_relay_restrictions = permit_mynetworks permit_sasl_authenticated defer_una
smtpd_recipient_restrictions = permit_sasl_authenticated, permit_mynetworks, reject_unauth_destination, reject_unauth_pipelining, reject_invalid_helo_hostname, reject_non_fqdn_helo_hostname, reject_unknown_recipient_domain
smtpd_client_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, reject_unauth_pipelining
smtpd_sender_restrictions = $dms_smtpd_sender_restrictions
smtpd_discard_ehlo_keywords = silent-discard, dsn
disable_vrfy_command = yes
# Custom defined parameters for DMS:

View File

@ -24,6 +24,7 @@ submission inet n - n - - smtpd
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_sender_restrictions=$mua_sender_restrictions
-o smtpd_discard_ehlo_keywords=
-o milter_macro_daemon_name=ORIGINATING
-o cleanup_service_name=sender-cleanup
@ -37,6 +38,7 @@ submissions inet n - n - - smtpd
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_sender_restrictions=$mua_sender_restrictions
-o smtpd_discard_ehlo_keywords=
-o milter_macro_daemon_name=ORIGINATING
-o cleanup_service_name=sender-cleanup

View File

@ -21,6 +21,10 @@ source /etc/dms-settings
# usage with DMS_HOSTNAME, which should remove the need to call this:
_obtain_hostname_and_domainname
# This is a helper to properly set all Rspamd-related environment variables
# correctly and in one place.
_rspamd_get_envs
# verify checksum file exists; must be prepared by start-mailserver.sh
if [[ ! -f ${CHKSUM_FILE} ]]; then
_exit_with_error "'${CHKSUM_FILE}' is missing" 0
@ -49,6 +53,7 @@ function _check_for_changes() {
# Handle any changes
_ssl_changes
_postfix_dovecot_changes
_rspamd_changes
_log_with_date 'debug' 'Reloading services due to detected changes'
@ -174,6 +179,33 @@ function _ssl_changes() {
# They presently have no special handling other than to trigger a change that will restart Postfix/Dovecot.
}
function _rspamd_changes() {
# RSPAMD_DMS_D='/tmp/docker-mailserver/rspamd'
if [[ ${CHANGED} =~ ${RSPAMD_DMS_D}/.* ]]; then
# "${RSPAMD_DMS_D}/override.d"
if [[ ${CHANGED} =~ ${RSPAMD_DMS_OVERRIDE_D}/.* ]]; then
_log_with_date 'trace' 'Rspamd - Copying configuration overrides'
rm "${RSPAMD_OVERRIDE_D}"/*
cp "${RSPAMD_DMS_OVERRIDE_D}"/* "${RSPAMD_OVERRIDE_D}"
fi
# "${RSPAMD_DMS_D}/custom-commands.conf"
if [[ ${CHANGED} =~ ${RSPAMD_DMS_CUSTOM_COMMANDS_F} ]]; then
_log_with_date 'trace' 'Rspamd - Generating new configuration from custom commands'
_rspamd_handle_user_modules_adjustments
fi
# "${RSPAMD_DMS_D}/dkim"
if [[ ${CHANGED} =~ ${RSPAMD_DMS_DKIM_D} ]]; then
_log_with_date 'trace' 'Rspamd - DKIM files updated'
fi
_log_with_date 'debug' 'Rspamd configuration has changed - restarting service'
supervisorctl restart rspamd
fi
}
while true; do
_check_for_changes
sleep 2

View File

@ -40,6 +40,12 @@ function _monitored_files_checksums() {
"${DMS_DIR}/dovecot-quotas.cf"
"${DMS_DIR}/dovecot-masters.cf"
)
# Check whether Rspamd is used and if so, monitor it's changes as well
if [[ ${ENABLE_RSPAMD} -eq 1 ]] && [[ -d ${RSPAMD_DMS_D} ]]; then
readarray -d '' STAGING_FILES_RSPAMD < <(find "${RSPAMD_DMS_D}" -type f -name "*.sh" -print0)
STAGING_FILES+=("${STAGING_FILES_RSPAMD[@]}")
fi
fi
# SSL certs:

View File

@ -16,6 +16,7 @@ function _import_scripts() {
source "${PATH_TO_SCRIPTS}/network.sh"
source "${PATH_TO_SCRIPTS}/postfix.sh"
source "${PATH_TO_SCRIPTS}/relay.sh"
source "${PATH_TO_SCRIPTS}/rspamd.sh"
source "${PATH_TO_SCRIPTS}/ssl.sh"
source "${PATH_TO_SCRIPTS}/utils.sh"

View File

@ -0,0 +1,105 @@
#! /bin/bash
# shellcheck disable=SC2034 # VAR appears unused.
function _rspamd_get_envs() {
readonly RSPAMD_LOCAL_D='/etc/rspamd/local.d'
readonly RSPAMD_OVERRIDE_D='/etc/rspamd/override.d'
readonly RSPAMD_DMS_D='/tmp/docker-mailserver/rspamd'
readonly RSPAMD_DMS_DKIM_D="${RSPAMD_DMS_D}/dkim"
readonly RSPAMD_DMS_OVERRIDE_D="${RSPAMD_DMS_D}/override.d"
readonly RSPAMD_DMS_CUSTOM_COMMANDS_F="${RSPAMD_DMS_D}/custom-commands.conf"
}
# Parses `RSPAMD_DMS_CUSTOM_COMMANDS_F` and executed the directives given by the file.
# To get a detailed explanation of the commands and how the file works, visit
# https://docker-mailserver.github.io/docker-mailserver/latest/config/security/rspamd/#with-the-help-of-a-custom-file
function _rspamd_handle_user_modules_adjustments() {
# Adds an option with a corresponding value to a module, or, in case the option
# is already present, overwrites it.
#
# @param ${1} = file name in ${RSPAMD_OVERRIDE_D}/
# @param ${2} = module name as it should appear in the log
# @param ${3} = option name in the module
# @param ${4} = value of the option
#
# ## Note
#
# While this function is currently bound to the scope of `_rspamd_handle_user_modules_adjustments`,
# it is written in a versatile way (taking 4 arguments instead of assuming `ARGUMENT2` / `ARGUMENT3`
# are set) so that it may be used elsewhere if needed.
function __add_or_replace() {
local MODULE_FILE=${1:?Module file name must be provided}
local MODULE_LOG_NAME=${2:?Module log name must be provided}
local OPTION=${3:?Option name must be provided}
local VALUE=${4:?Value belonging to an option must be provided}
# remove possible whitespace at the end (e.g., in case ${ARGUMENT3} is empty)
VALUE=${VALUE% }
local FILE="${RSPAMD_OVERRIDE_D}/${MODULE_FILE}"
readonly MODULE_FILE MODULE_LOG_NAME OPTION VALUE FILE
[[ -f ${FILE} ]] || touch "${FILE}"
if grep -q -E "${OPTION}.*=.*" "${FILE}"; then
__rspamd__log 'trace' "Overwriting option '${OPTION}' with value '${VALUE}' for ${MODULE_LOG_NAME}"
sed -i -E "s|([[:space:]]*${OPTION}).*|\1 = ${VALUE};|g" "${FILE}"
else
__rspamd__log 'trace' "Setting option '${OPTION}' for ${MODULE_LOG_NAME} to '${VALUE}'"
echo "${OPTION} = ${VALUE};" >>"${FILE}"
fi
}
# We check for usage of the previous location of the commands file.
# TODO This can be removed after the release of v14.0.0.
local RSPAMD_DMS_CUSTOM_COMMANDS_F_OLD="${RSPAMD_DMS_D}-modules.conf"
readonly RSPAMD_DMS_CUSTOM_COMMANDS_F_OLD
if [[ -f ${RSPAMD_DMS_CUSTOM_COMMANDS_F_OLD} ]]; then
_dms_panic__general "Old custom command file location '${RSPAMD_DMS_CUSTOM_COMMANDS_F_OLD}' is deprecated (use '${RSPAMD_DMS_CUSTOM_COMMANDS_F}' now)" 'Rspamd setup'
fi
if [[ -f "${RSPAMD_DMS_CUSTOM_COMMANDS_F}" ]]; then
__rspamd__log 'debug' "Found file '${RSPAMD_DMS_CUSTOM_COMMANDS_F}' - parsing and applying it"
local COMMAND ARGUMENT1 ARGUMENT2 ARGUMENT3
while read -r COMMAND ARGUMENT1 ARGUMENT2 ARGUMENT3; do
case "${COMMAND}" in
('disable-module')
__rspamd__helper__enable_disable_module "${ARGUMENT1}" 'false' 'override'
;;
('enable-module')
__rspamd__helper__enable_disable_module "${ARGUMENT1}" 'true' 'override'
;;
('set-option-for-module')
__add_or_replace "${ARGUMENT1}.conf" "module '${ARGUMENT1}'" "${ARGUMENT2}" "${ARGUMENT3}"
;;
('set-option-for-controller')
__add_or_replace 'worker-controller.inc' 'controller worker' "${ARGUMENT1}" "${ARGUMENT2} ${ARGUMENT3}"
;;
('set-option-for-proxy')
__add_or_replace 'worker-proxy.inc' 'proxy worker' "${ARGUMENT1}" "${ARGUMENT2} ${ARGUMENT3}"
;;
('set-common-option')
__add_or_replace 'options.inc' 'common options' "${ARGUMENT1}" "${ARGUMENT2} ${ARGUMENT3}"
;;
('add-line')
__rspamd__log 'trace' "Adding complete line to '${ARGUMENT1}'"
echo "${ARGUMENT2}${ARGUMENT3+ ${ARGUMENT3}}" >>"${RSPAMD_OVERRIDE_D}/${ARGUMENT1}"
;;
(*)
__rspamd__log 'warn' "Command '${COMMAND}' is invalid"
continue
;;
esac
done < <(_get_valid_lines_from_file "${RSPAMD_DMS_CUSTOM_COMMANDS_F}")
fi
}

View File

@ -1,12 +1,17 @@
#!/bin/bash
# Function called during global setup to handle the complete setup of Rspamd.
# This file is executed during startup of DMS. Hence, the `index.sh` helper has already
# been sourced, and thus, all helper functions from `rspamd.sh` are available.
# Function called during global setup to handle the complete setup of Rspamd. Functions
# with a single `_` prefix are sourced from the `rspamd.sh` helper.
function _setup_rspamd() {
if _env_var_expect_zero_or_one 'ENABLE_RSPAMD' && [[ ${ENABLE_RSPAMD} -eq 1 ]]; then
_log 'debug' 'Enabling and configuring Rspamd'
__rspamd__log 'trace' '---------- Setup started ----------'
__rspamd__run_early_setup_and_checks # must run first
_rspamd_get_envs # must run first
__rspamd__run_early_setup_and_checks # must run second
__rspamd__setup_logfile
__rspamd__setup_redis
__rspamd__setup_postfix
@ -16,7 +21,7 @@ function _setup_rspamd() {
__rspamd__setup_greylisting
__rspamd__setup_hfilter_group
__rspamd__setup_check_authenticated
__rspamd__handle_user_modules_adjustments # must run last
_rspamd_handle_user_modules_adjustments # must run last
__rspamd__log 'trace' '---------- Setup finished ----------'
else
@ -64,25 +69,11 @@ EOF
# Run miscellaneous early setup tasks and checks, such as creating files needed at runtime
# or checking for other anti-spam/anti-virus software.
function __rspamd__run_early_setup_and_checks() {
# Note: Variables not marked with `local` are
# used in other functions as well.
readonly RSPAMD_LOCAL_D='/etc/rspamd/local.d'
readonly RSPAMD_OVERRIDE_D='/etc/rspamd/override.d'
readonly RSPAMD_DMS_D='/tmp/docker-mailserver/rspamd'
local RSPAMD_DMS_OVERRIDE_D="${RSPAMD_DMS_D}/override.d/"
readonly RSPAMD_DMS_OVERRIDE_D
mkdir -p /var/lib/rspamd/
: >/var/lib/rspamd/stats.ucl
if [[ -d ${RSPAMD_DMS_OVERRIDE_D} ]]; then
__rspamd__log 'debug' "Found directory '${RSPAMD_DMS_OVERRIDE_D}' - linking it to '${RSPAMD_OVERRIDE_D}'"
if rmdir "${RSPAMD_OVERRIDE_D}" 2>/dev/null; then
ln -s "${RSPAMD_DMS_OVERRIDE_D}" "${RSPAMD_OVERRIDE_D}"
else
__rspamd__log 'warn' "Could not remove '${RSPAMD_OVERRIDE_D}' (not empty?; not a directory?; did you restart properly?) - not linking '${RSPAMD_DMS_OVERRIDE_D}'"
fi
cp "${RSPAMD_DMS_OVERRIDE_D}"/* "${RSPAMD_OVERRIDE_D}"
fi
if [[ ${ENABLE_AMAVIS} -eq 1 ]] || [[ ${ENABLE_SPAMASSASSIN} -eq 1 ]]; then
@ -303,98 +294,3 @@ function __rspamd__setup_check_authenticated() {
"${MODULE_FILE}"
fi
}
# Parses `RSPAMD_CUSTOM_COMMANDS_FILE` and executed the directives given by the file.
# To get a detailed explanation of the commands and how the file works, visit
# https://docker-mailserver.github.io/docker-mailserver/edge/config/security/rspamd/#with-the-help-of-a-custom-file
function __rspamd__handle_user_modules_adjustments() {
# Adds an option with a corresponding value to a module, or, in case the option
# is already present, overwrites it.
#
# @param ${1} = file name in ${RSPAMD_OVERRIDE_D}/
# @param ${2} = module name as it should appear in the log
# @param ${3} = option name in the module
# @param ${4} = value of the option
#
# ## Note
#
# While this function is currently bound to the scope of `__rspamd__handle_user_modules_adjustments`,
# it is written in a versatile way (taking 4 arguments instead of assuming `ARGUMENT2` / `ARGUMENT3`
# are set) so that it may be used elsewhere if needed.
function __add_or_replace() {
local MODULE_FILE=${1:?Module file name must be provided}
local MODULE_LOG_NAME=${2:?Module log name must be provided}
local OPTION=${3:?Option name must be provided}
local VALUE=${4:?Value belonging to an option must be provided}
# remove possible whitespace at the end (e.g., in case ${ARGUMENT3} is empty)
VALUE=${VALUE% }
local FILE="${RSPAMD_OVERRIDE_D}/${MODULE_FILE}"
readonly MODULE_FILE MODULE_LOG_NAME OPTION VALUE FILE
[[ -f ${FILE} ]] || touch "${FILE}"
if grep -q -E "${OPTION}.*=.*" "${FILE}"; then
__rspamd__log 'trace' "Overwriting option '${OPTION}' with value '${VALUE}' for ${MODULE_LOG_NAME}"
sed -i -E "s|([[:space:]]*${OPTION}).*|\1 = ${VALUE};|g" "${FILE}"
else
__rspamd__log 'trace' "Setting option '${OPTION}' for ${MODULE_LOG_NAME} to '${VALUE}'"
echo "${OPTION} = ${VALUE};" >>"${FILE}"
fi
}
local RSPAMD_CUSTOM_COMMANDS_FILE="${RSPAMD_DMS_D}/custom-commands.conf"
local RSPAMD_CUSTOM_COMMANDS_FILE_OLD="${RSPAMD_DMS_D}-modules.conf"
readonly RSPAMD_CUSTOM_COMMANDS_FILE RSPAMD_CUSTOM_COMMANDS_FILE_OLD
# We check for usage of the previous location of the commands file.
# This can be removed after the release of v14.0.0.
if [[ -f ${RSPAMD_CUSTOM_COMMANDS_FILE_OLD} ]]; then
__rspamd__log 'warn' "Detected usage of old file location for modules adjustment ('${RSPAMD_CUSTOM_COMMANDS_FILE_OLD}') - please use the new location ('${RSPAMD_CUSTOM_COMMANDS_FILE}')"
__rspamd__log 'warn' "Using old file location now (deprecated) - this will prevent startup in v13.0.0"
RSPAMD_CUSTOM_COMMANDS_FILE=${RSPAMD_CUSTOM_COMMANDS_FILE_OLD}
fi
if [[ -f "${RSPAMD_CUSTOM_COMMANDS_FILE}" ]]; then
__rspamd__log 'debug' "Found file '${RSPAMD_CUSTOM_COMMANDS_FILE}' - parsing and applying it"
local COMMAND ARGUMENT1 ARGUMENT2 ARGUMENT3
while read -r COMMAND ARGUMENT1 ARGUMENT2 ARGUMENT3; do
case "${COMMAND}" in
('disable-module')
__rspamd__helper__enable_disable_module "${ARGUMENT1}" 'false' 'override'
;;
('enable-module')
__rspamd__helper__enable_disable_module "${ARGUMENT1}" 'true' 'override'
;;
('set-option-for-module')
__add_or_replace "${ARGUMENT1}.conf" "module '${ARGUMENT1}'" "${ARGUMENT2}" "${ARGUMENT3}"
;;
('set-option-for-controller')
__add_or_replace 'worker-controller.inc' 'controller worker' "${ARGUMENT1}" "${ARGUMENT2} ${ARGUMENT3}"
;;
('set-option-for-proxy')
__add_or_replace 'worker-proxy.inc' 'proxy worker' "${ARGUMENT1}" "${ARGUMENT2} ${ARGUMENT3}"
;;
('set-common-option')
__add_or_replace 'options.inc' 'common options' "${ARGUMENT1}" "${ARGUMENT2} ${ARGUMENT3}"
;;
('add-line')
__rspamd__log 'trace' "Adding complete line to '${ARGUMENT1}'"
echo "${ARGUMENT2}${ARGUMENT3+ ${ARGUMENT3}}" >>"${RSPAMD_OVERRIDE_D}/${ARGUMENT1}"
;;
(*)
__rspamd__log 'warn' "Command '${COMMAND}' is invalid"
continue
;;
esac
done < <(_get_valid_lines_from_file "${RSPAMD_CUSTOM_COMMANDS_FILE}")
fi
}

View File

@ -0,0 +1 @@
smtpd_discard_ehlo_keywords =

View File

@ -0,0 +1,2 @@
submission/inet/smtpd_discard_ehlo_keywords=silent-discard,dsn
submissions/inet/smtpd_discard_ehlo_keywords=silent-discard,dsn

View File

@ -0,0 +1,14 @@
EHLO mail
AUTH LOGIN dXNlcjFAbG9jYWxob3N0LmxvY2FsZG9tYWlu
bXlwYXNzd29yZA==
MAIL FROM: user1@localhost.localdomain
RCPT TO: user1@localhost.localdomain NOTIFY=success,failure
DATA
From: Existing Local User <user1@localhost.localdomain>
To: Existing Local User <user1@localhost.localdomain>
Date: Sat, 22 May 2010 07:43:25 -0400
Subject: Test Message
This is a test mail.
.
QUIT

View File

@ -0,0 +1,12 @@
HELO mail.external.tld
MAIL FROM: user@external.tld
RCPT TO: user1@localhost.localdomain NOTIFY=success,failure
DATA
From: Docker Mail Server <dockermailserver@external.tld>
To: Existing Local User <user1@localhost.localdomain>
Date: Sat, 22 May 2010 07:43:25 -0400
Subject: Test Message
This is a test mail.
.
QUIT

View File

@ -4,8 +4,8 @@ load "${REPOSITORY_ROOT}/test/helper/setup"
BATS_TEST_NAME_PREFIX='[Rspamd] (DKIM) '
CONTAINER_NAME='dms-test_rspamd-dkim'
DOMAIN_NAME='fixed.com'
SIGNING_CONF_FILE='/etc/rspamd/override.d/dkim_signing.conf'
DOMAIN_NAME='example.test'
SIGNING_CONF_FILE='/tmp/docker-mailserver/rspamd/override.d/dkim_signing.conf'
function setup_file() {
_init_with_defaults
@ -59,7 +59,7 @@ function teardown_file() { _default_teardown ; }
__create_key
assert_success
__log_is_free_of_warnings_and_errors
assert_output --partial "Supplying a default configuration ('${SIGNING_CONF_FILE}')"
assert_output --partial "Supplying a default configuration (to '${SIGNING_CONF_FILE}')"
refute_output --partial "'${SIGNING_CONF_FILE}' exists, not supplying a default"
assert_output --partial "Finished DKIM key creation"
_run_in_container_bash "[[ -f ${SIGNING_CONF_FILE} ]]"
@ -68,8 +68,14 @@ function teardown_file() { _default_teardown ; }
local INITIAL_SHA512_SUM=$(_exec_in_container sha512sum "${SIGNING_CONF_FILE}")
__create_key
assert_failure
assert_output --partial "Not overwriting existing files (use '--force' to overwrite existing files)"
# the same as before, but with the '--force' option
__create_key 'rsa' 'mail' "${DOMAIN_NAME}" '2048' '--force'
__log_is_free_of_warnings_and_errors
refute_output --partial "Supplying a default configuration ('${SIGNING_CONF_FILE}')"
assert_output --partial "Overwriting existing files as the '--force' option was supplied"
assert_output --partial "'${SIGNING_CONF_FILE}' exists, not supplying a default"
assert_output --partial "Finished DKIM key creation"
local SECOND_SHA512_SUM=$(_exec_in_container sha512sum "${SIGNING_CONF_FILE}")
@ -188,11 +194,15 @@ function __create_key() {
local SELECTOR=${2:-mail}
local DOMAIN=${3:-${DOMAIN_NAME}}
local KEYSIZE=${4:-2048}
local FORCE=${5:-}
_run_in_container setup config dkim \
keytype "${KEYTYPE}" \
keysize "${KEYSIZE}" \
selector "${SELECTOR}" \
# Not quoting is intended here as we would othewise provide
# the argument "''" (empty string), which would cause errors
# shellcheck disable=SC2086
_run_in_container setup config dkim ${FORCE} \
keytype "${KEYTYPE}" \
keysize "${KEYSIZE}" \
selector "${SELECTOR}" \
domain "${DOMAIN}"
}

View File

@ -66,12 +66,9 @@ function teardown_file() { _default_teardown ; }
assert_output 'rspamd_milter = inet:localhost:11332'
}
@test "'/etc/rspamd/override.d/' is linked correctly" {
@test "contents of '/etc/rspamd/override.d/' are copied" {
local OVERRIDE_D='/etc/rspamd/override.d'
_run_in_container_bash "[[ -h ${OVERRIDE_D} ]]"
assert_success
_run_in_container_bash "[[ -f ${OVERRIDE_D}/testmodule_complicated.conf ]]"
assert_success
}

View File

@ -0,0 +1,95 @@
load "${REPOSITORY_ROOT}/test/helper/setup"
load "${REPOSITORY_ROOT}/test/helper/common"
BATS_TEST_NAME_PREFIX='[DSN] '
CONTAINER1_NAME='dms-test_dsn_send_always'
CONTAINER2_NAME='dms-test_dsn_send_auth'
CONTAINER3_NAME='dms-test_dsn_send_none'
# A similar line is added to the log when a DSN (Delivery Status Notification) is sent:
#
# postfix/bounce[1023]: C943BA6B46: sender delivery status notification: DBF86A6B4CO
#
LOG_DSN='delivery status notification'
function setup_file() {
local CUSTOM_SETUP_ARGUMENTS=(
# Required only for delivery via nc (_send_email)
--env PERMIT_DOCKER=container
)
export CONTAINER_NAME=${CONTAINER1_NAME}
_init_with_defaults
# Unset `smtpd_discard_ehlo_keywords` to allow DSNs by default on any `smtpd` service:
cp "${TEST_TMP_CONFIG}/dsn/postfix-main.cf" "${TEST_TMP_CONFIG}/postfix-main.cf"
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
_wait_for_service postfix
_wait_for_smtp_port_in_container
export CONTAINER_NAME=${CONTAINER2_NAME}
_init_with_defaults
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
_wait_for_service postfix
_wait_for_smtp_port_in_container
export CONTAINER_NAME=${CONTAINER3_NAME}
_init_with_defaults
# Mirror default main.cf (disable DSN on ports 465 + 587 too):
cp "${TEST_TMP_CONFIG}/dsn/postfix-master.cf" "${TEST_TMP_CONFIG}/postfix-master.cf"
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
_wait_for_service postfix
_wait_for_smtp_port_in_container
}
function teardown_file() {
docker rm -f "${CONTAINER1_NAME}" "${CONTAINER2_NAME}" "${CONTAINER3_NAME}"
}
@test "should always send a DSN when requested" {
export CONTAINER_NAME=${CONTAINER1_NAME}
_send_email 'email-templates/dsn-unauthenticated'
_send_email 'email-templates/dsn-authenticated' '0.0.0.0 465'
_send_email 'email-templates/dsn-authenticated' '0.0.0.0 587'
_wait_for_empty_mail_queue_in_container
_run_in_container grep "${LOG_DSN}" /var/log/mail/mail.log
_should_output_number_of_lines 3
}
# Defaults test case
@test "should only send a DSN when requested from ports 465/587" {
export CONTAINER_NAME=${CONTAINER2_NAME}
_send_email 'email-templates/dsn-unauthenticated'
_wait_for_empty_mail_queue_in_container
# DSN requests can now only be made on ports 465 and 587,
# so grep should not find anything.
#
# Although external requests are discarded, anyone who has requested a DSN
# will still receive it, but it will come from the sending mail server, not this one.
_run_in_container grep "${LOG_DSN}" /var/log/mail/mail.log
assert_failure
# These ports are excluded via master.cf.
_send_email 'email-templates/dsn-authenticated' '0.0.0.0 465'
_send_email 'email-templates/dsn-authenticated' '0.0.0.0 587'
_wait_for_empty_mail_queue_in_container
_run_in_container grep "${LOG_DSN}" /var/log/mail/mail.log
_should_output_number_of_lines 2
}
@test "should never send a DSN" {
export CONTAINER_NAME=${CONTAINER3_NAME}
_send_email 'email-templates/dsn-unauthenticated'
_send_email 'email-templates/dsn-authenticated' '0.0.0.0 465'
_send_email 'email-templates/dsn-authenticated' '0.0.0.0 587'
_wait_for_empty_mail_queue_in_container
# DSN requests are rejected regardless of origin.
# This is usually a bad idea, as you won't get them either.
_run_in_container grep "${LOG_DSN}" /var/log/mail/mail.log
assert_failure
}