docker-mailserver/test/tests/parallel/set1/spam_virus/rspamd_dkim.bats
Georg Lauterbach f674232f71
misc: final Rspamd adjustments for v13 (#3599)
* outsource Rspamd ENVs into explicit helper

This will allow us to uniformly source the helper and get the values
from everywhere consistently. This is more than desirable since we will
be using these values not only for the Rspamd setup, but also for DKIM
management and during change-detection.

* integrate Rspamd into changedetection

We outsource one more function to reside in the helper script for Rspamd
so that we can call this function from the Rspamd setup and from the
changedetection functionality too.

* realize deprecation of old commands file for Rspamd

THIS IS A BREAKING CHANGE!

This change realizes the log message: "Using old file location now
(deprecated) - this will prevent startup in v13.0.0" Startup will now
fail.

* added '--force' option to Rspamd DKIM script

* use new helper to get ENVs for Rspamd in DKIM script

* remove the need for linking directories

This was unnecessary, as explained in
https://github.com/docker-mailserver/docker-mailserver/pull/3597#discussion_r1369413599

* Apply suggestions from code review

review by @polarathene

* apply more review feedback from @polarathene

- <https://github.com/docker-mailserver/docker-mailserver/pull/3599#discussion_r1370885519>
- <https://github.com/docker-mailserver/docker-mailserver/pull/3599#discussion_r1370904201>

* update documentation

---------

Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2023-10-30 10:20:37 +01:00

278 lines
10 KiB
Bash

load "${REPOSITORY_ROOT}/test/helper/common"
load "${REPOSITORY_ROOT}/test/helper/setup"
BATS_TEST_NAME_PREFIX='[Rspamd] (DKIM) '
CONTAINER_NAME='dms-test_rspamd-dkim'
DOMAIN_NAME='example.test'
SIGNING_CONF_FILE='/tmp/docker-mailserver/rspamd/override.d/dkim_signing.conf'
function setup_file() {
_init_with_defaults
# Comment for maintainers about `PERMIT_DOCKER=host`:
# https://github.com/docker-mailserver/docker-mailserver/pull/2815/files#r991087509
local CUSTOM_SETUP_ARGUMENTS=(
--env ENABLE_RSPAMD=1
--env ENABLE_OPENDKIM=0
--env ENABLE_OPENDMARC=0
--env ENABLE_POLICYD_SPF=0
--env LOG_LEVEL=trace
--env OVERRIDE_HOSTNAME="mail.${DOMAIN_NAME}"
)
_common_container_setup 'CUSTOM_SETUP_ARGUMENTS'
_wait_for_service rspamd-redis
_wait_for_service rspamd
}
# We want each test to start with a clean state.
function teardown() {
__remove_signing_config_file
_run_in_container rm -rf /tmp/docker-mailserver/rspamd/dkim
assert_success
}
function teardown_file() { _default_teardown ; }
@test 'log level is applied correctly' {
_run_in_container setup config dkim -vv help
__log_is_free_of_warnings_and_errors
assert_output --partial 'Enabled trace-logging'
_run_in_container setup config dkim -v help
__log_is_free_of_warnings_and_errors
assert_output --partial 'Enabled debug-logging'
}
@test 'help message is properly shown' {
_run_in_container setup config dkim help
__log_is_free_of_warnings_and_errors
assert_output --partial 'Showing usage message now'
assert_output --partial 'rspamd-dkim - Configure DKIM (DomainKeys Identified Mail)'
}
@test 'default signing config is created if it does not exist and not overwritten' {
# Required pre-condition: no default configuration is present
__remove_signing_config_file
__create_key
assert_success
__log_is_free_of_warnings_and_errors
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} ]]"
assert_success
_exec_in_container_bash "echo 'blabla' >${SIGNING_CONF_FILE}"
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}")
assert_equal "${INITIAL_SHA512_SUM}" "${SECOND_SHA512_SUM}"
}
@test 'default directories and files are created' {
__create_key
assert_success
_count_files_in_directory_in_container /tmp/docker-mailserver/rspamd/dkim/ 3
_run_in_container_bash "[[ -f ${SIGNING_CONF_FILE} ]]"
assert_success
__check_path_in_signing_config "/tmp/docker-mailserver/rspamd/dkim/rsa-2048-mail-${DOMAIN_NAME}.private.txt"
__check_selector_in_signing_config 'mail'
}
@test "argument 'domain' is applied correctly" {
for DOMAIN in 'blabla.org' 'someother.com' 'random.de'; do
_run_in_container setup config dkim domain "${DOMAIN}"
assert_success
assert_line --partial "Domain set to '${DOMAIN}'"
local BASE_FILE_NAME="/tmp/docker-mailserver/rspamd/dkim/rsa-2048-mail-${DOMAIN}"
__check_key_files_are_present "${BASE_FILE_NAME}"
__check_path_in_signing_config "${BASE_FILE_NAME}.private.txt"
__remove_signing_config_file
done
}
@test "argument 'keytype' is applied correctly" {
_run_in_container setup config dkim keytype foobar
assert_failure
assert_line --partial "Unknown keytype 'foobar'"
for KEYTYPE in 'rsa' 'ed25519'; do
_run_in_container setup config dkim keytype "${KEYTYPE}"
assert_success
assert_line --partial "Keytype set to '${KEYTYPE}'"
local BASE_FILE_NAME="/tmp/docker-mailserver/rspamd/dkim/ed25519-mail-${DOMAIN_NAME}"
[[ ${KEYTYPE} == 'rsa' ]] && BASE_FILE_NAME="/tmp/docker-mailserver/rspamd/dkim/rsa-2048-mail-${DOMAIN_NAME}"
__check_key_files_are_present "${BASE_FILE_NAME}"
_run_in_container grep ".*k=${KEYTYPE};.*" "${BASE_FILE_NAME}.public.txt"
assert_success
_run_in_container grep ".*k=${KEYTYPE};.*" "${BASE_FILE_NAME}.public.dns.txt"
assert_success
__check_path_in_signing_config "${BASE_FILE_NAME}.private.txt"
__remove_signing_config_file
done
}
@test "argument 'selector' is applied correctly" {
for SELECTOR in 'foo' 'bar' 'baz'; do
__create_key 'rsa' "${SELECTOR}"
assert_success
assert_line --partial "Selector set to '${SELECTOR}'"
local BASE_FILE_NAME="/tmp/docker-mailserver/rspamd/dkim/rsa-2048-${SELECTOR}-${DOMAIN_NAME}"
__check_key_files_are_present "${BASE_FILE_NAME}"
_run_in_container grep "^${SELECTOR}\._domainkey.*" "${BASE_FILE_NAME}.public.txt"
assert_success
__check_rsa_keys 2048 "${SELECTOR}-${DOMAIN_NAME}"
__check_path_in_signing_config "${BASE_FILE_NAME}.private.txt"
__check_selector_in_signing_config "${SELECTOR}"
__remove_signing_config_file
done
}
@test "argument 'keysize' is applied correctly for RSA keys" {
for KEYSIZE in 1024 2048 4096; do
__create_key 'rsa' 'mail' "${DOMAIN_NAME}" "${KEYSIZE}"
assert_success
__log_is_free_of_warnings_and_errors
assert_line --partial "Keysize set to '${KEYSIZE}'"
__check_rsa_keys "${KEYSIZE}" "mail-${DOMAIN_NAME}"
__remove_signing_config_file
done
}
@test "when 'keytype=ed25519' is set, setting custom 'keysize' is rejected" {
__create_key 'ed25519' 'mail' "${DOMAIN_NAME}" 4096
assert_failure
assert_line --partial "Chosen keytype does not accept the 'keysize' argument"
}
@test "setting all arguments to a custom value works" {
local KEYTYPE='ed25519'
local SELECTOR='someselector'
local DOMAIN='dms.org'
__create_key "${KEYTYPE}" "${SELECTOR}" "${DOMAIN}"
assert_success
__log_is_free_of_warnings_and_errors
assert_line --partial "Keytype set to '${KEYTYPE}'"
assert_line --partial "Selector set to '${SELECTOR}'"
assert_line --partial "Domain set to '${DOMAIN}'"
local BASE_FILE_NAME="/tmp/docker-mailserver/rspamd/dkim/${KEYTYPE}-${SELECTOR}-${DOMAIN}"
__check_path_in_signing_config "${BASE_FILE_NAME}.private.txt"
__check_selector_in_signing_config 'someselector'
}
# Create DKIM keys.
#
# @param ${1} = keytype (default: rsa)
# @param ${2} = selector (default: mail)
# @param ${3} = domain (default: ${DOMAIN})
# @param ${4} = keysize (default: 2048)
function __create_key() {
local KEYTYPE=${1:-rsa}
local SELECTOR=${2:-mail}
local DOMAIN=${3:-${DOMAIN_NAME}}
local KEYSIZE=${4:-2048}
local FORCE=${5:-}
# 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}"
}
# Check whether an RSA key is created successfully and correctly
# for a specific key size.
#
# @param ${1} = key size
# @param ${2} = name of the selector and domain name (as one string)
function __check_rsa_keys() {
local KEYSIZE=${1:?Keysize must be supplied to __check_rsa_keys}
local SELECTOR_AND_DOMAIN=${2:?Selector and domain name must be supplied to __check_rsa_keys}
local BASE_FILE_NAME="/tmp/docker-mailserver/rspamd/dkim/rsa-${KEYSIZE}-${SELECTOR_AND_DOMAIN}"
__check_key_files_are_present "${BASE_FILE_NAME}"
__check_path_in_signing_config "${BASE_FILE_NAME}.private.txt"
# Check the private key matches the specification
_run_in_container_bash "openssl rsa -in '${BASE_FILE_NAME}.private.txt' -noout -text"
assert_success
assert_line --index 0 "RSA Private-Key: (${KEYSIZE} bit, 2 primes)"
# Check the public key matches the specification
#
# We utilize the file for the DNS record contents which is already created
# by the Rspamd DKIM helper script. This makes parsing easier here.
local PUBKEY PUBKEY_INFO
PUBKEY=$(_exec_in_container_bash "grep -o 'p=.*' ${BASE_FILE_NAME}.public.dns.txt")
_run_in_container_bash "openssl enc -base64 -d <<< ${PUBKEY#p=} | openssl pkey -inform DER -pubin -noout -text"
assert_success
assert_line --index 0 "RSA Public-Key: (${KEYSIZE} bit)"
}
# Verify that all DKIM key files are present.
#
# @param ${1} = base file name that all DKIM key files have
function __check_key_files_are_present() {
local BASE_FILE_NAME="${1:?Base file name must be supplied to __check_key_files_are_present}"
for FILE in ${BASE_FILE_NAME}.{public.txt,public.dns.txt,private.txt}; do
_run_in_container_bash "[[ -f ${FILE} ]]"
assert_success
done
}
# Check whether `path = .*` is set correctly in the signing configuration file.
#
# @param ${1} = file name that `path` should be set to
function __check_path_in_signing_config() {
local BASE_FILE_NAME=${1:?Base file name must be supplied to __check_path_in_signing_config}
_run_in_container grep "[[:space:]]*path = \"${BASE_FILE_NAME}\";" "${SIGNING_CONF_FILE}"
assert_success
}
# Check whether `selector = .*` is set correctly in the signing configuration file.
#
# @param ${1} = name that `selector` should be set to
function __check_selector_in_signing_config() {
local SELECTOR=${1:?Selector name must be supplied to __check_selector_in_signing_config}
_run_in_container grep "[[:space:]]*selector = \"${SELECTOR}\";" "${SIGNING_CONF_FILE}"
assert_success
}
# Check whether the script output is free of warnings and errors.
function __log_is_free_of_warnings_and_errors() {
assert_success
refute_output --partial '[ WARN ]'
refute_output --partial '[ ERROR ]'
}
# Remove the signing configuration file inside the container.
function __remove_signing_config_file() {
_exec_in_container rm -f "${SIGNING_CONF_FILE}"
}