Compare commits

..

4 Commits

Author SHA1 Message Date
Georg Lauterbach 82939961ea
Update sender_header_filter.pcre 2025-03-24 09:59:20 +01:00
Georg Lauterbach 2ad8d2e098
chore: fix CHANGELOG
Signed-off-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2025-03-23 23:05:06 +01:00
Georg Lauterbach 8ace34e45e
chore: update `CHANGELOG.md`
Signed-off-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2025-03-23 23:03:28 +01:00
Georg Lauterbach cee6e116f5
fix: GPG signing
by adjusting `sender_header_filter.pcre` and related tests

Signed-off-by: Georg Lauterbach <44545919+georglauterbach@users.noreply.github.com>
2025-03-23 22:58:45 +01:00
12 changed files with 112 additions and 203 deletions

View File

@ -2,23 +2,14 @@
All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
## [Unreleased](https://github.com/docker-mailserver/docker-mailserver/compare/v15.0.2...HEAD)
## [Unreleased](https://github.com/docker-mailserver/docker-mailserver/compare/v15.0.1...HEAD)
> **Note**: Changes and additions listed here are contained in the `:edge` image tag. These changes may not be as stable as released changes.
### Updates
- **Documentation:**
- Added a compatibility note for a Dovecot + Solr 9.8 breaking change ([#4433](https://github.com/docker-mailserver/docker-mailserver/pull/4433))
- **Internal:**
- Refactored `setup config dkim` (`open-dkim`) ([#4375](https://github.com/docker-mailserver/docker-mailserver/pull/4375))
## [v15.0.2](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v15.0.2)
### Fixes
- **Postfix**
- Avoid modifying the message body when filtering sender headers. This regression was introduced from [#4120](https://github.com/docker-mailserver/docker-mailserver/pull/4120) as part of DMS v15.0.0 ([#4429](https://github.com/docker-mailserver/docker-mailserver/pull/4429))
- ensure message content is not modified by header filter ([#4427](https://github.com/docker-mailserver/docker-mailserver/pull/4427))
## [v15.0.1](https://github.com/docker-mailserver/docker-mailserver/releases/tag/v15.0.1)
@ -29,6 +20,8 @@ All notable changes to this project will be documented in this file. The format
### Fixes
- **Docs**
- Gender-neutral language
- **Postfix:**
- `setup email restrict` generated configs now only prepend to `dms_smtpd_sender_restrictions` ([#4379](https://github.com/docker-mailserver/docker-mailserver/pull/4379))
- **Rspamd:**

View File

@ -146,25 +146,6 @@ docker compose exec mailserver doveadm fts rescan -A
Usually within 15 minutes or so, you should be able to search your mail using the Dovecot FTS feature! :tada:
### Compatibility
Since Solr 9.8.0 was released (Jan 2025), a breaking change [deprecates support for `<lib>` directives][solr::9.8::lib-directive] which is presently used by the Dovecot supplied Solr config (`solr-config-9.xml`) to automatically load additional jars required.
To enable support for `<lib>` directives, add the following ENV to your `solr` container:
```yaml
services:
solr:
environment:
SOLR_CONFIG_LIB_ENABLED: true
```
!!! warning "Solr 10"
From the Solr 10 release onwards, this opt-in ENV will no longer be available.
If Dovecot has not updated their example Solr config ([upstream PR][dovecot::pr::solr-config-lib]), you will need to manually modify the Solr XML config to remove the `<lib>` directives and replace the suggested ENV `SOLR_CONFIG_LIB_ENABLED=true` with `SOLR_MODULES=analysis-extras`.
[docs::user-patches]: ../../config/advanced/override-defaults/user-patches.md
[docs::dovecot::full-text-search]: ../../config/advanced/full-text-search.md
[gh-dms::feature-request::dovecot-solr-package]: https://github.com/docker-mailserver/docker-mailserver/issues/4052
@ -173,6 +154,3 @@ services:
[dockerfile-solr-uidgid]: https://github.com/apache/solr-docker/blob/9cd850b72309de05169544395c83a85b329d6b86/9.6/Dockerfile#L89-L92
[github-solr]: https://github.com/apache/solr
[github-dovecot::core-docs]: https://github.com/dovecot/core/tree/main/doc
[solr::9.8::lib-directive]: https://issues.apache.org/jira/browse/SOLR-16781
[dovecot::pr::solr-config-lib]: https://github.com/dovecot/core/pull/238

View File

@ -92,7 +92,7 @@ TLS_LEVEL=
# 1 => Mail spoofing denied. Each user may only send with their own or their alias addresses. Addresses with extension delimiters(http://www.postfix.org/postconf.5.html#recipient_delimiter) are not able to send messages.
SPOOF_PROTECTION=
# Enables the Sender Rewriting Scheme. SRS is needed if your mail server acts as forwarder. See [postsrsd](https://github.com/roehling/postsrsd/blob/main/README.rst) for further explanation.
# Enables the Sender Rewriting Scheme. SRS is needed if your mail server acts as forwarder. See [postsrsd](https://github.com/roehling/postsrsd/blob/master/README.md#sender-rewriting-scheme-crash-course) for further explanation.
# - **0** => Disabled
# - 1 => Enabled
ENABLE_SRS=0

View File

@ -12,17 +12,9 @@ if [[ -f /etc/dms-settings ]] && [[ $(_get_dms_env_value 'ENABLE_RSPAMD') -eq 1
fi
fi
function _main() {
# Default parameters (updated by `_parse_arguments()`):
local KEYSIZE=2048
local SELECTOR=mail
local DMS_DOMAINS=
_require_n_parameters_or_print_usage 0 "${@}"
_parse_arguments "${@}"
_generate_dkim_keys
}
KEYSIZE=2048
SELECTOR=mail
DOMAINS=
function __usage() {
printf '%s' "${PURPLE}OPEN-DKIM${RED}(${YELLOW}8${RED})
@ -65,171 +57,122 @@ ${ORANGE}EXAMPLES${RESET}
${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 a non-zero exit status.
errors, the script will exit early with exit status 2.
"
}
function _parse_arguments() {
# Parse the command args through iteration:
while [[ ${#} -gt 0 ]]; do
case "${1}" in
_require_n_parameters_or_print_usage 0 "${@}"
( 'keysize' )
if [[ -n ${2:-} ]]; then
KEYSIZE="${2}"
_log 'trace' "Keysize set to '${KEYSIZE}'"
else
_exit_with_error "No keysize provided after 'keysize' argument"
fi
;;
while [[ ${#} -gt 0 ]]; do
case "${1}" in
( 'keysize' )
if [[ -n ${2+set} ]]; then
KEYSIZE="${2}"
shift
shift
else
_exit_with_error "No keysize provided after 'keysize' argument"
fi
;;
( 'selector' )
if [[ -n ${2:-} ]]; then
SELECTOR="${2}"
_log 'trace' "Selector set to '${SELECTOR}'"
else
_exit_with_error "No selector provided after 'selector' argument"
fi
;;
( 'selector' )
if [[ -n ${2+set} ]]; then
# shellcheck disable=SC2034
SELECTOR="${2}"
shift
shift
else
_exit_with_error "No selector provided after 'selector' argument"
fi
;;
( 'domain' )
if [[ -n ${2:-} ]]; then
DMS_DOMAINS="${2}"
_log 'trace' "Domain(s) set to '${DMS_DOMAINS}'"
else
_exit_with_error "No domain(s) provided after 'domain' argument"
fi
;;
( 'domain' )
if [[ -n ${2+set} ]]; then
DOMAINS="${2}"
shift
shift
else
_exit_with_error "No domain(s) provided after 'domain' argument"
fi
;;
( 'help' )
__usage
exit 0
;;
( * )
__usage
_exit_with_error "Unknown options '${1}' ${2:+and \'${2}\'}"
;;
( * )
__usage
_exit_with_error "Unknown option(s) ${*}"
;;
esac
esac
done
# Discard these two args (option + value) now that they've been processed:
shift 2
done
}
function _generate_dkim_keys() {
_generate_domains_config
if [[ ! -s ${DATABASE_VHOST} ]]; then
_log 'warn' 'No entries found, no keys to make'
exit 0
fi
# Initialize OpenDKIM configs if necessary:
_create_opendkim_configs
# Generate a keypair per domain and add reference to OpenDKIM configs:
local ENTRY_KEY KEY_TABLE_ENTRY SIGNING_TABLE_ENTRY
while read -r DKIM_DOMAIN; do
_create_dkim_key "${DKIM_DOMAIN}"
# Create / Update OpenDKIM configs with new DKIM key:
ENTRY_KEY="${SELECTOR}._domainkey.${DKIM_DOMAIN}"
KEY_TABLE_ENTRY="${ENTRY_KEY} ${DKIM_DOMAIN}:${SELECTOR}:/etc/opendkim/keys/${DKIM_DOMAIN}/${SELECTOR}.private"
SIGNING_TABLE_ENTRY="*@${DKIM_DOMAIN} ${ENTRY_KEY}"
# If no existing entry, add one:
if ! grep -q "${KEY_TABLE_ENTRY}" "${KEY_TABLE_FILE}"; then
echo "${KEY_TABLE_ENTRY}" >> "${KEY_TABLE_FILE}"
fi
if ! grep -q "${SIGNING_TABLE_ENTRY}" "${SIGNING_TABLE_FILE}"; then
echo "${SIGNING_TABLE_ENTRY}" >> "${SIGNING_TABLE_FILE}"
fi
done < <(_get_valid_lines_from_file "${DATABASE_VHOST}")
# No longer needed, remove:
rm "${DATABASE_VHOST}"
# Ensure ownership is consistent for all content belonging to the base directory,
# During container startup, an internal copy will be made via `_setup_opendkim()`
# with ownership we expect, while this chown is for the benefit of the users ownership.
chown -R "$(stat -c '%U:%G' "${OPENDKIM_BASE_DIR}")" "${OPENDKIM_BASE_DIR}"
}
# Prepare a file with one domain per line (iterated via while loop as DKIM_DOMAIN):
# Depends on methods from `scripts/helpers/postfix.sh`:
DATABASE_VHOST='/tmp/vhost.dkim'
# Prepare a file with one domain per line:
function _generate_domains_config() {
local TMP_VHOST='/tmp/vhost.dkim.tmp'
# Generate the default vhost (equivalent to /etc/postfix/vhost),
# unless CLI arg DMS_DOMAINS provided an alternative list to use instead:
if [[ -z ${DMS_DOMAINS:-} ]]; then
# unless CLI arg DOMAINS provided an alternative list to use instead:
if [[ -z ${DOMAINS} ]]; then
_obtain_hostname_and_domainname
# uses TMP_VHOST:
_vhost_collect_postfix_domains
else
tr ',' '\n' <<< "${DMS_DOMAINS}" >"${TMP_VHOST}"
tr ',' '\n' <<< "${DOMAINS}" >"${TMP_VHOST}"
fi
# Uses DATABASE_VHOST + TMP_VHOST:
# uses DATABASE_VHOST + TMP_VHOST:
_create_vhost
}
# `opendkim-genkey` generates two files at the configured `--directory`:
# - <selector>.private (Private key, PEM encoded)
# - <selector>.txt (Public key, formatted as a TXT record for a RFC 1035 DNS Zone file)
function _create_dkim_key() {
DKIM_DOMAIN=${1?Expected to be provided a domain}
_generate_domains_config
if [[ ! -s ${DATABASE_VHOST} ]]; then
_log 'warn' 'No entries found, no keys to make'
exit 0
fi
OPENDKIM_DOMAINKEY_DIR="${OPENDKIM_BASE_DIR}/keys/${DKIM_DOMAIN}"
mkdir -p "${OPENDKIM_DOMAINKEY_DIR}"
while read -r DKIM_DOMAIN; do
mkdir -p "/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}"
DKIM_KEY_FILE="${OPENDKIM_DOMAINKEY_DIR}/${SELECTOR}.private"
if [[ ! -f "${DKIM_KEY_FILE}" ]]; then
_log 'info' "Creating DKIM private key '${DKIM_KEY_FILE}'"
if [[ ! -f "/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}/${SELECTOR}.private" ]]; then
_log 'info' "Creating DKIM private key '/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}/${SELECTOR}.private'"
# NOTE:
# --domain only affects a comment in the generated DNS Zone file
# --subdomains is the default,
# --nosubdomains would add `t=s` to the DNS TXT record generated
# http://www.opendkim.org/opendkim-genkey.8.html
opendkim-genkey \
--bits="${KEYSIZE}" \
--subdomains \
--domain="${DKIM_DOMAIN}" \
--selector="${SELECTOR}" \
--directory="${OPENDKIM_DOMAINKEY_DIR}"
--directory="/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}"
fi
}
OPENDKIM_BASE_DIR='/tmp/docker-mailserver/opendkim'
KEY_TABLE_FILE="${OPENDKIM_BASE_DIR}/KeyTable"
SIGNING_TABLE_FILE="${OPENDKIM_BASE_DIR}/SigningTable"
TRUSTED_HOSTS_FILE="${OPENDKIM_BASE_DIR}/TrustedHosts"
# Create configs if missing:
function _create_opendkim_configs() {
mkdir -p "${OPENDKIM_BASE_DIR}"
local OPENDKIM_CONFIGS=(
"${KEY_TABLE_FILE}"
"${SIGNING_TABLE_FILE}"
"${TRUSTED_HOSTS_FILE}"
)
# fix permissions to use the same user:group as /tmp/docker-mailserver/opendkim/keys
chown -R "$(stat -c '%U:%G' /tmp/docker-mailserver/opendkim/keys)" "/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}"
# Only create if the file doesn't exist (avoids modifying mtime):
for FILE in "${OPENDKIM_CONFIGS[@]}"; do
if [[ ! -f "${FILE}" ]]; then
_log 'debug' "Creating OpenDKIM config '${FILE}'"
touch "${FILE}"
# write to KeyTable if necessary
KEYTABLEENTRY="${SELECTOR}._domainkey.${DKIM_DOMAIN} ${DKIM_DOMAIN}:${SELECTOR}:/etc/opendkim/keys/${DKIM_DOMAIN}/${SELECTOR}.private"
if [[ ! -f "/tmp/docker-mailserver/opendkim/KeyTable" ]]; then
_log 'debug' 'Creating DKIM KeyTable'
echo "${KEYTABLEENTRY}" >/tmp/docker-mailserver/opendkim/KeyTable
else
if ! grep -q "${KEYTABLEENTRY}" "/tmp/docker-mailserver/opendkim/KeyTable"; then
echo "${KEYTABLEENTRY}" >>/tmp/docker-mailserver/opendkim/KeyTable
fi
done
# If file exists but is empty, add default hosts to trust:
if [[ ! -s "${TRUSTED_HOSTS_FILE}" ]]; then
_log 'debug' 'Adding default trust to OpenDKIM TrustedHosts config'
echo "127.0.0.1" > "${TRUSTED_HOSTS_FILE}"
echo "localhost" >> "${TRUSTED_HOSTS_FILE}"
fi
}
_main "${@}"
# write to SigningTable if necessary
SIGNINGTABLEENTRY="*@${DKIM_DOMAIN} ${SELECTOR}._domainkey.${DKIM_DOMAIN}"
if [[ ! -f /tmp/docker-mailserver/opendkim/SigningTable ]]; then
_log 'debug' 'Creating DKIM SigningTable'
echo "*@${DKIM_DOMAIN} ${SELECTOR}._domainkey.${DKIM_DOMAIN}" >/tmp/docker-mailserver/opendkim/SigningTable
else
if ! grep -q "${SIGNINGTABLEENTRY}" /tmp/docker-mailserver/opendkim/SigningTable; then
echo "${SIGNINGTABLEENTRY}" >>/tmp/docker-mailserver/opendkim/SigningTable
fi
fi
done < <(_get_valid_lines_from_file "${DATABASE_VHOST}")
# create TrustedHosts if missing
if [[ -d /tmp/docker-mailserver/opendkim ]] && [[ ! -f /tmp/docker-mailserver/opendkim/TrustedHosts ]]; then
_log 'debug' 'Creating DKIM TrustedHosts'
echo "127.0.0.1" >/tmp/docker-mailserver/opendkim/TrustedHosts
echo "localhost" >>/tmp/docker-mailserver/opendkim/TrustedHosts
fi

View File

@ -1,12 +1,11 @@
/^\s*Received:.*with ESMTPSA/ IGNORE
/^\s*Received: from.*127.0.0.1/ IGNORE
/^\s*Received:.*amavisd-new/ IGNORE
/^\s*X-Originating-IP:/ IGNORE
/^\s*X-Mailer:/ IGNORE
/^\s*Mime-Version: 1.0.*/ REPLACE MIME-Version: 1.0
/^\s*Received:.*with ESMTPSA/ IGNORE
/^\s*User-Agent/ IGNORE
/^\s*X-Enigmail/ IGNORE
/^\s*X-Mailer/ IGNORE
/^\s*X-MS-Reactions/ IGNORE
/^\s*X-Originating-IP/ IGNORE
/^\s*Received: from.*127.0.0.1/ IGNORE
/^\s*X-MS-Reactions:/ IGNORE
/^\s*Message-Id:/i PREPEND X-MS-Reactions: disallow
/^\s*Message-ID:/i PREPEND X-MS-Reactions: disallow
/^\s*Mime-Version: 1.0.*/i REPLACE MIME-Version: 1.0

View File

@ -1,7 +1,7 @@
#!/bin/bash
function _exit_with_error() {
if [[ -n ${1:-} ]]; then
if [[ -n ${1+set} ]]; then
_log 'error' "${1}"
else
_log 'error' "Call to '_exit_with_error' is missing a message to log"

View File

@ -43,12 +43,12 @@ RESET=$(echo -ne '\e[0m')
# message is logged. Likewise when the second argument
# is missing. Both failures will return with exit code '1'.
function _log() {
if [[ -z ${1:-} ]]; then
if [[ -z ${1+set} ]]; then
_log 'error' "Call to '_log' is missing a valid log level"
return 1
fi
if [[ -z ${2:-} ]]; then
if [[ -z ${2+set} ]]; then
_log 'error' "Call to '_log' is missing a message to log"
return 1
fi
@ -116,7 +116,7 @@ function _log() {
# variables file. If this does not yield a value either,
# use the default log level.
function _get_log_level_or_default() {
if [[ -n ${LOG_LEVEL:-} ]]; then
if [[ -n ${LOG_LEVEL+set} ]]; then
echo "${LOG_LEVEL}"
elif [[ -e /etc/dms-settings ]] && grep -q -E "^LOG_LEVEL='[a-z]+'" /etc/dms-settings; then
grep '^LOG_LEVEL=' /etc/dms-settings | cut -d "'" -f 2

View File

@ -131,9 +131,9 @@ function _reload_postfix() {
# 1. No first and second argument is supplied
# 2. The second argument is a path to a file that does not exist
function _replace_by_env_in_file() {
if [[ -z ${1:-} ]]; then
if [[ -z ${1+set} ]]; then
_dms_panic__invalid_value 'first argument unset' 'utils.sh:_replace_by_env_in_file'
elif [[ -z ${2:-} ]]; then
elif [[ -z ${2+set} ]]; then
_dms_panic__invalid_value 'second argument unset' 'utils.sh:_replace_by_env_in_file'
elif [[ ! -f ${2} ]]; then
_dms_panic__invalid_value "file '${2}' does not exist" 'utils.sh:_replace_by_env_in_file'

View File

@ -23,11 +23,7 @@ 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/
local DKIM_DOMAINS
DKIM_DOMAINS=$(find /etc/opendkim/keys/ -maxdepth 1 -type f -printf '%f ')
_log 'trace' "DKIM keys added for: ${DKIM_DOMAINS}"
_log 'trace' "DKIM keys added for: $(find /etc/opendkim/keys/ -maxdepth 1 -type f -printf '%f ')"
chown -R opendkim:opendkim /etc/opendkim/
chmod -R 0700 /etc/opendkim/keys/
else

View File

@ -56,7 +56,7 @@ function __handle_container_name() {
if [[ -n ${1:-} ]] && [[ ${1:-} =~ ^dms-test_ ]]; then
printf '%s' "${1}"
return 0
elif [[ -n ${CONTAINER_NAME:-} ]]; then
elif [[ -n ${CONTAINER_NAME+set} ]]; then
printf '%s' "${CONTAINER_NAME}"
return 0
else

View File

@ -17,7 +17,7 @@
# This function is internal and should not be used in tests.
function __initialize_variables() {
function __check_if_set() {
if [[ -z ${!1:-} ]]; then
if [[ ${!1+set} != 'set' ]]; then
echo "ERROR: (helper/setup.sh) '${1:?No variable name given to __check_if_set}' is not set" >&2
exit 1
fi

View File

@ -62,7 +62,7 @@ function teardown() { _default_teardown ; }
__init_container_without_waiting
__should_generate_dkim_key 7
__should_generate_dkim_key 6
__assert_outputs_common_dkim_logs
__should_have_tables_trustedhosts_for_domain
@ -78,7 +78,7 @@ function teardown() { _default_teardown ; }
# Only mount single config file (postfix-virtual.cf):
__init_container_without_waiting "${PWD}/test/config/postfix-virtual.cf:/tmp/docker-mailserver/postfix-virtual.cf:ro"
__should_generate_dkim_key 6
__should_generate_dkim_key 5
__assert_outputs_common_dkim_logs
__should_have_tables_trustedhosts_for_domain
@ -95,7 +95,7 @@ function teardown() { _default_teardown ; }
# Only mount single config file (postfix-accounts.cf):
__init_container_without_waiting "${PWD}/test/config/postfix-accounts.cf:/tmp/docker-mailserver/postfix-accounts.cf:ro"
__should_generate_dkim_key 6
__should_generate_dkim_key 5
__assert_outputs_common_dkim_logs
__should_have_tables_trustedhosts_for_domain
@ -113,7 +113,7 @@ function teardown() { _default_teardown ; }
__init_container_without_waiting '/tmp/docker-mailserver'
# generate first key (with a custom selector)
__should_generate_dkim_key 5 '1024' 'domain1.tld' 'mailer'
__should_generate_dkim_key 4 '1024' 'domain1.tld' 'mailer'
__assert_outputs_common_dkim_logs
# generate two additional keys different to the previous one
__should_generate_dkim_key 2 '1024' 'domain2.tld,domain3.tld'
@ -183,15 +183,15 @@ function __assert_logged_dkim_creation() {
function __assert_outputs_common_dkim_logs() {
refute_output --partial 'No entries found, no keys to make'
assert_output --partial "Creating OpenDKIM config '/tmp/docker-mailserver/opendkim/KeyTable'"
assert_output --partial "Creating OpenDKIM config '/tmp/docker-mailserver/opendkim/SigningTable'"
assert_output --partial "Creating OpenDKIM config '/tmp/docker-mailserver/opendkim/TrustedHosts'"
assert_output --partial 'Creating DKIM KeyTable'
assert_output --partial 'Creating DKIM SigningTable'
assert_output --partial 'Creating DKIM TrustedHosts'
}
function __should_support_creating_key_of_size() {
local EXPECTED_KEYSIZE=${1:-}
__should_generate_dkim_key 7 "${EXPECTED_KEYSIZE}"
__should_generate_dkim_key 6 "${EXPECTED_KEYSIZE}"
__assert_outputs_common_dkim_logs
__assert_logged_dkim_creation 'localdomain2.com'
__assert_logged_dkim_creation 'localhost.localdomain'