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 pull-requests: write
statuses: 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: jobs:
add-contributors: add-contributors:
name: 'Add Contributors' name: 'Add Contributors'
@ -24,64 +18,15 @@ jobs:
- name: 'Checkout' - name: 'Checkout'
uses: actions/checkout@v4 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' - 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: with:
ACCESS_TOKEN: ${{secrets.GITHUB_TOKEN}} readme_path: CONTRIBUTORS.md
BRANCH: ${{ env.BRANCH_NAME }} collaborators: all
COMMIT_MESSAGE: 'docs: update `CONTRIBUTORS.md`' commit_message: 'docs: updated `CONTRIBUTORS.md`'
PATH: /CONTRIBUTORS.md committer_username: github-actions[bot]
CONTRIBUTOR: '## Contributors' committer_email: 41898282+github-actions[bot]@users.noreply.github.com
COLUMN_PER_ROW: 6 pr_title_on_protected: 'docs: update `CONTRIBUTORS.md'
IMG_WIDTH: 100 auto_detect_branch_protection: true
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.

View File

@ -9,6 +9,19 @@ All notable changes to this project will be documented in this file. The format
### Breaking ### Breaking
- The environment variable `ENABLE_LDAP=1` has been changed to `ACCOUNT_PROVISIONER=LDAP`. - 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 ### 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]?" !!! 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" !!! 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 [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 [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} ${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'. 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} ${ORANGE}EXIT STATUS${RESET}
Exit status is 0 if command was successful. If wrong arguments are provided 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. or arguments contain errors, the script will exit early with exit status 1.

View File

@ -27,6 +27,7 @@ ${ORANGE}DESCRIPTION${RESET}
${ORANGE}OPTIONS${RESET} ${ORANGE}OPTIONS${RESET}
${BLUE}Generic Program Information${RESET} ${BLUE}Generic Program Information${RESET}
-f | --force Overwrite existing files if there are any
-v Enable verbose logging (setting the log level to 'debug'). -v Enable verbose logging (setting the log level to 'debug').
-vv Enable very verbose logging (setting the log level to 'trace'). -vv Enable very verbose logging (setting the log level to 'trace').
help Print the usage information. help Print the usage information.
@ -69,6 +70,7 @@ function __do_as_rspamd_user() {
} }
function _parse_arguments() { function _parse_arguments() {
FORCE=0
KEYTYPE='rsa' KEYTYPE='rsa'
KEYSIZE='2048' KEYSIZE='2048'
SELECTOR='mail' SELECTOR='mail'
@ -112,6 +114,12 @@ function _parse_arguments() {
exit 0 exit 0
;; ;;
( '-f' | '--force' )
FORCE=1
shift 1
continue
;;
( '-vv' ) ( '-vv' )
# shellcheck disable=SC2034 # shellcheck disable=SC2034
LOG_LEVEL='trace' LOG_LEVEL='trace'
@ -132,30 +140,36 @@ function _parse_arguments() {
__usage __usage
_exit_with_error "Unknown option(s) '${1}' ${2:+"and '${2}'"}" _exit_with_error "Unknown option(s) '${1}' ${2:+"and '${2}'"}"
;; ;;
esac esac
shift 2 shift 2
done done
return 0
}
function _preflight_checks() {
if [[ ${KEYTYPE} == 'ed25519' ]] && [[ ${KEYSIZE} -ne 2048 ]]; then if [[ ${KEYTYPE} == 'ed25519' ]] && [[ ${KEYSIZE} -ne 2048 ]]; then
_exit_with_error "Chosen keytype does not accept the 'keysize' argument" _exit_with_error "Chosen keytype does not accept the 'keysize' argument"
fi 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() { 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 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}") KEYTYPE_OPTIONS=('-b' "${KEYSIZE}")
_log 'info' "Creating DKIM keys of type '${KEYTYPE}' and length '${KEYSIZE}' with selector '${SELECTOR}' for domain '${DOMAIN}'" _log 'info' "Creating DKIM keys of type '${KEYTYPE}' and length '${KEYSIZE}' with selector '${SELECTOR}' for domain '${DOMAIN}'"
else 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}") KEYTYPE_OPTIONS=('-t' "${KEYTYPE}")
_log 'info' "Creating DKIM keys of type '${KEYTYPE}' with selector '${SELECTOR}' for domain '${DOMAIN}'" _log 'info' "Creating DKIM keys of type '${KEYTYPE}' with selector '${SELECTOR}' for domain '${DOMAIN}'"
fi fi
@ -164,8 +178,15 @@ function _create_keys() {
PUBLIC_KEY_DNS_FILE="${BASE_FILE_NAME}.public.dns.txt" PUBLIC_KEY_DNS_FILE="${BASE_FILE_NAME}.public.dns.txt"
PRIVATE_KEY_FILE="${BASE_FILE_NAME}.private.txt" PRIVATE_KEY_FILE="${BASE_FILE_NAME}.private.txt"
mkdir -p "${BASE_DIR}" if [[ -f ${PUBLIC_KEY_FILE} ]] || [[ -f ${PUBLIC_KEY_DNS_FILE} ]] || [[ -f ${PRIVATE_KEY_FILE} ]]; then
chown _rspamd:_rspamd "${BASE_DIR}" 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 # shellcheck disable=SC2310
if __do_as_rspamd_user rspamadm \ if __do_as_rspamd_user rspamadm \
@ -186,8 +207,8 @@ function _create_keys() {
function _check_permissions() { function _check_permissions() {
# shellcheck disable=SC2310 # shellcheck disable=SC2310
if ! __do_as_rspamd_user ls "${BASE_DIR}" >/dev/null; then 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 ('${BASE_DIR}') - Rspamd may experience permission errors later" _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 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" _log 'warn' "The Rspamd user ('_rspamd') seems to be unable to read the private key file - Rspamd may experience permission errors later"
else else
@ -196,11 +217,11 @@ function _check_permissions() {
} }
function _setup_default_signing_conf() { 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 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 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 cat >"${DEFAULT_CONFIG_FILE}" << EOF
# documentation: https://rspamd.com/doc/modules/dkim_signing.html # documentation: https://rspamd.com/doc/modules/dkim_signing.html
@ -225,7 +246,15 @@ domain {
} }
EOF 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 fi
} }
@ -254,6 +283,7 @@ function _final_steps() {
_obtain_hostname_and_domainname _obtain_hostname_and_domainname
_require_n_parameters_or_print_usage 0 "${@}" _require_n_parameters_or_print_usage 0 "${@}"
_parse_arguments "${@}" _parse_arguments "${@}"
_preflight_checks
_create_keys _create_keys
_check_permissions _check_permissions
_setup_default_signing_conf _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_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_client_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_unauth_destination, reject_unauth_pipelining
smtpd_sender_restrictions = $dms_smtpd_sender_restrictions smtpd_sender_restrictions = $dms_smtpd_sender_restrictions
smtpd_discard_ehlo_keywords = silent-discard, dsn
disable_vrfy_command = yes disable_vrfy_command = yes
# Custom defined parameters for DMS: # 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_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_sender_restrictions=$mua_sender_restrictions -o smtpd_sender_restrictions=$mua_sender_restrictions
-o smtpd_discard_ehlo_keywords=
-o milter_macro_daemon_name=ORIGINATING -o milter_macro_daemon_name=ORIGINATING
-o cleanup_service_name=sender-cleanup -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_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_relay_restrictions=permit_sasl_authenticated,reject -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
-o smtpd_sender_restrictions=$mua_sender_restrictions -o smtpd_sender_restrictions=$mua_sender_restrictions
-o smtpd_discard_ehlo_keywords=
-o milter_macro_daemon_name=ORIGINATING -o milter_macro_daemon_name=ORIGINATING
-o cleanup_service_name=sender-cleanup -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: # usage with DMS_HOSTNAME, which should remove the need to call this:
_obtain_hostname_and_domainname _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 # verify checksum file exists; must be prepared by start-mailserver.sh
if [[ ! -f ${CHKSUM_FILE} ]]; then if [[ ! -f ${CHKSUM_FILE} ]]; then
_exit_with_error "'${CHKSUM_FILE}' is missing" 0 _exit_with_error "'${CHKSUM_FILE}' is missing" 0
@ -49,6 +53,7 @@ function _check_for_changes() {
# Handle any changes # Handle any changes
_ssl_changes _ssl_changes
_postfix_dovecot_changes _postfix_dovecot_changes
_rspamd_changes
_log_with_date 'debug' 'Reloading services due to detected 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. # 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 while true; do
_check_for_changes _check_for_changes
sleep 2 sleep 2

View File

@ -40,6 +40,12 @@ function _monitored_files_checksums() {
"${DMS_DIR}/dovecot-quotas.cf" "${DMS_DIR}/dovecot-quotas.cf"
"${DMS_DIR}/dovecot-masters.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 fi
# SSL certs: # SSL certs:

View File

@ -16,6 +16,7 @@ function _import_scripts() {
source "${PATH_TO_SCRIPTS}/network.sh" source "${PATH_TO_SCRIPTS}/network.sh"
source "${PATH_TO_SCRIPTS}/postfix.sh" source "${PATH_TO_SCRIPTS}/postfix.sh"
source "${PATH_TO_SCRIPTS}/relay.sh" source "${PATH_TO_SCRIPTS}/relay.sh"
source "${PATH_TO_SCRIPTS}/rspamd.sh"
source "${PATH_TO_SCRIPTS}/ssl.sh" source "${PATH_TO_SCRIPTS}/ssl.sh"
source "${PATH_TO_SCRIPTS}/utils.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 #!/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() { function _setup_rspamd() {
if _env_var_expect_zero_or_one 'ENABLE_RSPAMD' && [[ ${ENABLE_RSPAMD} -eq 1 ]]; then if _env_var_expect_zero_or_one 'ENABLE_RSPAMD' && [[ ${ENABLE_RSPAMD} -eq 1 ]]; then
_log 'debug' 'Enabling and configuring Rspamd' _log 'debug' 'Enabling and configuring Rspamd'
__rspamd__log 'trace' '---------- Setup started ----------' __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_logfile
__rspamd__setup_redis __rspamd__setup_redis
__rspamd__setup_postfix __rspamd__setup_postfix
@ -16,7 +21,7 @@ function _setup_rspamd() {
__rspamd__setup_greylisting __rspamd__setup_greylisting
__rspamd__setup_hfilter_group __rspamd__setup_hfilter_group
__rspamd__setup_check_authenticated __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 ----------' __rspamd__log 'trace' '---------- Setup finished ----------'
else else
@ -64,25 +69,11 @@ EOF
# Run miscellaneous early setup tasks and checks, such as creating files needed at runtime # Run miscellaneous early setup tasks and checks, such as creating files needed at runtime
# or checking for other anti-spam/anti-virus software. # or checking for other anti-spam/anti-virus software.
function __rspamd__run_early_setup_and_checks() { 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/ mkdir -p /var/lib/rspamd/
: >/var/lib/rspamd/stats.ucl : >/var/lib/rspamd/stats.ucl
if [[ -d ${RSPAMD_DMS_OVERRIDE_D} ]]; then if [[ -d ${RSPAMD_DMS_OVERRIDE_D} ]]; then
__rspamd__log 'debug' "Found directory '${RSPAMD_DMS_OVERRIDE_D}' - linking it to '${RSPAMD_OVERRIDE_D}'" cp "${RSPAMD_DMS_OVERRIDE_D}"/* "${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
fi fi
if [[ ${ENABLE_AMAVIS} -eq 1 ]] || [[ ${ENABLE_SPAMASSASSIN} -eq 1 ]]; then if [[ ${ENABLE_AMAVIS} -eq 1 ]] || [[ ${ENABLE_SPAMASSASSIN} -eq 1 ]]; then
@ -303,98 +294,3 @@ function __rspamd__setup_check_authenticated() {
"${MODULE_FILE}" "${MODULE_FILE}"
fi 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) ' BATS_TEST_NAME_PREFIX='[Rspamd] (DKIM) '
CONTAINER_NAME='dms-test_rspamd-dkim' CONTAINER_NAME='dms-test_rspamd-dkim'
DOMAIN_NAME='fixed.com' DOMAIN_NAME='example.test'
SIGNING_CONF_FILE='/etc/rspamd/override.d/dkim_signing.conf' SIGNING_CONF_FILE='/tmp/docker-mailserver/rspamd/override.d/dkim_signing.conf'
function setup_file() { function setup_file() {
_init_with_defaults _init_with_defaults
@ -59,7 +59,7 @@ function teardown_file() { _default_teardown ; }
__create_key __create_key
assert_success assert_success
__log_is_free_of_warnings_and_errors __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" refute_output --partial "'${SIGNING_CONF_FILE}' exists, not supplying a default"
assert_output --partial "Finished DKIM key creation" assert_output --partial "Finished DKIM key creation"
_run_in_container_bash "[[ -f ${SIGNING_CONF_FILE} ]]" _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}") local INITIAL_SHA512_SUM=$(_exec_in_container sha512sum "${SIGNING_CONF_FILE}")
__create_key __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 __log_is_free_of_warnings_and_errors
refute_output --partial "Supplying a default configuration ('${SIGNING_CONF_FILE}')" 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 "'${SIGNING_CONF_FILE}' exists, not supplying a default"
assert_output --partial "Finished DKIM key creation" assert_output --partial "Finished DKIM key creation"
local SECOND_SHA512_SUM=$(_exec_in_container sha512sum "${SIGNING_CONF_FILE}") local SECOND_SHA512_SUM=$(_exec_in_container sha512sum "${SIGNING_CONF_FILE}")
@ -188,8 +194,12 @@ function __create_key() {
local SELECTOR=${2:-mail} local SELECTOR=${2:-mail}
local DOMAIN=${3:-${DOMAIN_NAME}} local DOMAIN=${3:-${DOMAIN_NAME}}
local KEYSIZE=${4:-2048} local KEYSIZE=${4:-2048}
local FORCE=${5:-}
_run_in_container setup config dkim \ # 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}" \ keytype "${KEYTYPE}" \
keysize "${KEYSIZE}" \ keysize "${KEYSIZE}" \
selector "${SELECTOR}" \ selector "${SELECTOR}" \

View File

@ -66,12 +66,9 @@ function teardown_file() { _default_teardown ; }
assert_output 'rspamd_milter = inet:localhost:11332' 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' 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 ]]" _run_in_container_bash "[[ -f ${OVERRIDE_D}/testmodule_complicated.conf ]]"
assert_success 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
}