scripts: add DKIM helper script for Rspamd (#3286)

Co-authored-by: Casper <casperklein@users.noreply.github.com>
This commit is contained in:
Georg Lauterbach 2023-05-03 08:30:49 +02:00 committed by GitHub
parent 423188176f
commit bba72daedf
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 600 additions and 36 deletions

View file

@ -40,6 +40,10 @@ You should have:
- At least one [email account setup][docs-accounts-add] - At least one [email account setup][docs-accounts-add]
- Attached a [volume for config][docs-volumes-config] to persist the generated files to local storage - Attached a [volume for config][docs-volumes-config] to persist the generated files to local storage
!!! warning "RSA Key Sizes >= 4096 Bit"
Keys of 4096 bits could be denied by some mail servers. According to [RFC 6376][rfc-6376], keys are [preferably between 512 and 2048 bits][github-issue-dkimlength].
DKIM is currently supported by either OpenDKIM or Rspamd: DKIM is currently supported by either OpenDKIM or Rspamd:
=== "OpenDKIM" === "OpenDKIM"
@ -48,7 +52,7 @@ DKIM is currently supported by either OpenDKIM or Rspamd:
The command `docker exec <CONTAINER NAME> setup config dkim help` details supported config options, along with some examples. The command `docker exec <CONTAINER NAME> setup config dkim help` details supported config options, along with some examples.
!!! example "Create a DKIM key" !!! example "Creating a DKIM key"
Generate the DKIM files with: Generate the DKIM files with:
@ -74,6 +78,12 @@ DKIM is currently supported by either OpenDKIM or Rspamd:
setup config dkim keysize 2048 setup config dkim keysize 2048
``` ```
!!! info "Restart required"
After restarting DMS, outgoing mail will now be signed with your new DKIM key(s) :tada:
You'll need to repeat this process if you add any new domains.
=== "Rspamd" === "Rspamd"
Opt-in via [`ENABLE_RSPAMD=1`][docs-env-rspamd] (_and disable the default OpenDKIM: `ENABLE_OPENDKIM=0`_). Opt-in via [`ENABLE_RSPAMD=1`][docs-env-rspamd] (_and disable the default OpenDKIM: `ENABLE_OPENDKIM=0`_).
@ -83,31 +93,33 @@ DKIM is currently supported by either OpenDKIM or Rspamd:
1. [Verifying DKIM signatures from inbound mail][rspamd-docs-dkim-checks] is enabled by default. 1. [Verifying DKIM signatures from inbound mail][rspamd-docs-dkim-checks] is enabled by default.
2. [Signing outbound mail with your DKIM key][rspamd-docs-dkim-signing] needs additional setup (key + dns + config). 2. [Signing outbound mail with your DKIM key][rspamd-docs-dkim-signing] needs additional setup (key + dns + config).
!!! example "Create a DKIM key" !!! example "Creating DKIM Keys"
Presently only OpenDKIM is supported with `setup config dkim`. To generate your DKIM key and DNS files you'll need to specify: You can simply run
- `-s` The DKIM selector (_eg: `mail`, it can be anything you like_) ```bash
- `-d` The sender address domain (_everything after `@` from the email address_) docker exec -ti <CONTAINER NAME> setup config dkim help
```
See `rspamadm dkim_keygen -h` for an overview of the supported options. which provides you with an overview of what the script can do. Just running
```bash
docker exec -ti <CONTAINER NAME> setup config dkim
```
will execute the helper script with default parameters.
!!! info "About the Helper Script"
The script will persist the keys in `/tmp/docker-mailserver/rspamd/dkim/`. Hence, if you are already using the default volume mounts, the keys are persisted in a volume. The script also restarts Rspamd directly, so changes take effect without restarting DMS.
The script provides you with log messages along the way of creating keys. In case you want to read the complete log, use `-v` (verbose) or `-vv` (very verbose).
--- ---
1. Go inside the container with `docker exec -ti <CONTAINER NAME> bash` In case you have not already provided a default DKIM signing configuration, the script will create one and write it to `/etc/rspamd/override.d/dkim_signing.conf`. If this file already exist, it will not be overwritten. When you're already using [the `rspamd/override.d/` directory][docs-rspamd-override-d], the file is created inside your volume and therefore persisted correctly. If you are not using `rspamd/override.d/`, you will need to persist the file yourself (otherwise it is lost on container restart).
2. Add `rspamd/dkim/` folder to your config volume and switch to it: `cd /tmp/docker-mailserver/rspamd/dkim`
3. Run: `rspamadm dkim_keygen -s mail -b 2048 -d example.com -k mail.private > mail.txt` (_change `-d` to your domain-part_)
4. Presently you must ensure Rspamd can read the `<selector>.private` file, run:
-`chgrp _rspamd mail.private`
-`chmod g+r mail.private`
--- An example of what a default configuration file for DKIM signing looks like can be found by expanding the example below.
!!! bug inline end "DMS config volume support is not ready for Rspamd"
Presently you'll need to [explicitly mount `rspamd/modules/override.d/`][docs-rspamd-config-dropin] as an additional volume; do not use [`rspamd-modules.conf`][docs-rspamd-config-declarative] for this purpose.
Create a configuration file for the DKIM signing module at `rspamd/modules/override.d/dkim_signing.conf` and populate it with config as shown in the example below:
??? example "DKIM Signing Module Configuration Examples" ??? example "DKIM Signing Module Configuration Examples"
@ -124,6 +136,7 @@ DKIM is currently supported by either OpenDKIM or Rspamd:
use_domain = "header"; use_domain = "header";
use_redis = false; # don't change unless Redis also provides the DKIM keys use_redis = false; # don't change unless Redis also provides the DKIM keys
use_esld = true; use_esld = true;
check_pubkey = true; # you wan't to use this in the beginning check_pubkey = true; # you wan't to use this in the beginning
domain { domain {
@ -134,7 +147,7 @@ DKIM is currently supported by either OpenDKIM or Rspamd:
} }
``` ```
As shown next, you can: As shown next:
- You can add more domains into the `domain { ... }` section. - You can add more domains into the `domain { ... }` section.
- A domain can also be configured with multiple selectors and keys within a `selectors [ ... ]` array. - A domain can also be configured with multiple selectors and keys within a `selectors [ ... ]` array.
@ -170,27 +183,19 @@ DKIM is currently supported by either OpenDKIM or Rspamd:
} }
``` ```
!!! warning "Support for DKIM keys using Ed25519" ??? warning "Support for DKIM Keys using ED25519"
This modern elliptic curve is supported by Rspamd, but support by third-parties for [verifying Ed25519 DKIM signatures is unreliable][dkim-ed25519-support]. This modern elliptic curve is supported by Rspamd, but support by third-parties for [verifying Ed25519 DKIM signatures is unreliable][dkim-ed25519-support].
If you sign your mail with this key type, you should include RSA as a fallback, like shown in the above example. If you sign your mail with this key type, you should include RSA as a fallback, like shown in the above example.
!!! tip "DKIM Signing config: `check_pubkey = true;`" ??? tip "Let Rspamd Check Your Keys"
This setting will have Rspamd query the DNS record for each DKIM selector, verifying each public key matches the private key configured. When `check_pubkey = true;` is set, Rspamd will query the DNS record for each DKIM selector, verifying each public key matches the private key configured.
If there is a mismatch, a warning will be omitted to the Rspamd log (`/var/log/supervisor/rspamd.log`). If there is a mismatch, a warning will be omitted to the Rspamd log `/var/log/supervisor/rspamd.log`.
!!! info "Restart required" [docs-rspamd-override-d]: ../security/rspamd.md#manually
After restarting DMS, outgoing mail will now be signed with your new DKIM key(s) :tada:
You'll need to repeat this process if you add any new domains.
!!! warning "RSA Key Sizes >= 4096 Bit"
Keys of 4096 bits could denied by some mail servers. According to [RFC 6376][rfc-6376] keys are [preferably between 512 and 2048 bits][github-issue-dkimlength].
### DNS Record { #dkim-dns } ### DNS Record { #dkim-dns }
@ -211,6 +216,8 @@ When mail signed with your DKIM key is sent from your mail server, the receiver
| TTL | Use the default (_otherwise [3600 seconds is appropriate][dns::digicert-ttl]_) | | TTL | Use the default (_otherwise [3600 seconds is appropriate][dns::digicert-ttl]_) |
| Data | File content within `( ... )` (_formatted as advised below_) | | Data | File content within `( ... )` (_formatted as advised below_) |
When using Rspamd, the helper script has already provided you with the contents (the "Data" field) of the DNS record you need to create - you can just copy-paste this text.
=== "DNS Zone file" === "DNS Zone file"
`<selector>.txt` is already formatted as a snippet for adding to your [DNS Zone file][dns::wikipedia-zonefile]. `<selector>.txt` is already formatted as a snippet for adding to your [DNS Zone file][dns::wikipedia-zonefile].

View file

@ -3,6 +3,12 @@
# shellcheck source=../scripts/helpers/index.sh # shellcheck source=../scripts/helpers/index.sh
source /usr/local/bin/helpers/index.sh source /usr/local/bin/helpers/index.sh
if [[ -f /etc/dms-settings ]] && [[ $(_get_dms_env_value 'ENABLE_RSPAMD') -eq 1 ]]
then
/usr/local/bin/rspamd-dkim "${@}"
exit
fi
KEYSIZE=4096 KEYSIZE=4096
SELECTOR=mail SELECTOR=mail
DOMAINS= DOMAINS=

279
target/bin/rspamd-dkim Executable file
View file

@ -0,0 +1,279 @@
#!/bin/bash
# shellcheck source=../scripts/helpers/index.sh
source /usr/local/bin/helpers/index.sh
_trap_err_signal
set -u -eE -o pipefail
shopt -s inherit_errexit
# shellcheck source=/dev/null
source /etc/dms-settings
function __usage
{
_log 'trace' 'Showing usage message now'
echo -e "${PURPLE}RSPAMD-DKIM${RED}(${YELLOW}8${RED})
${ORANGE}NAME${RESET}
rspamd-dkim - Configure DomainKeys Identified Mail (DKIM) via Rspamd
${ORANGE}SYNOPSIS${RESET}
setup config dkim [ OPTIONS${RED}...${RESET} ]
${ORANGE}DESCRIPTION${RESET}
This script aids in creating DKIM signing keys. The keys are created and managed by Rspamd.
OPTIONS can be used to configure a more complex setup.
${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.
${BLUE}Configuration adjustments${RESET}
keytype Set the type of key you want to use
Possible values: rsa, ed25519
Default: rsa
keysize Set the size of the keys to be generated
Possible values: 1024, 2048 and 4096
Default: 2048
Only applies when using keytype=rsa
selector Set a manual selector for the key
Default: mail
domain Provide the domain for which keys are to be generated
Default: primary domain name of DMS
${ORANGE}EXAMPLES${RESET}
${LWHITE}setup config dkim keysize 2048${RESET}
Creates keys of length 2048 bit in a default setup where domains are obtained from
your accounts.
${LWHITE}setup config dkim keysize 512 selector 2023-dkim${RESET}
Creates keys of length 512 bit in a default setup where domains are obtained from
your accounts. The DKIM selector used is '2023-dkim'.
${LWHITE}setup config dkim keysize 1024 selector 2023-dkim domain whoami.com${RESET}
Creates keys of length 1024 bit in a default setup where domains are obtained from your accounts.
The DKIM selector used is '2023-dkim'. The domain for which DKIM keys are generated is whoami.com.
${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.
"
}
function __do_as_rspamd_user
{
local COMMAND=${1:?Command required when using __do_as_rspamd_user}
_log 'trace' "Running '${*}' as user '_rspamd' now"
shift 1
su -l '_rspamd' -s "$(command -v "${COMMAND}")" -- "${@}"
}
function _parse_arguments
{
KEYTYPE='rsa'
KEYSIZE='2048'
SELECTOR='mail'
DOMAIN=${DOMAINNAME}
_log 'trace' "Options given to this script: '${*}'"
while [[ ${#} -gt 0 ]]
do
case "${1}" in
( 'keytype' )
[[ -n ${2:-} ]] || _exit_with_error "No keytype provided after 'keytype' argument"
if [[ ${2} == 'rsa' ]] || [[ ${2} == 'ed25519' ]]
then
KEYTYPE=${2}
_log 'debug' "Keytype set to '${KEYTYPE}'"
else
_exit_with_error "Unknown keytype '${2}'"
fi
;;
( 'keysize' )
[[ -n ${2:-} ]] || _exit_with_error "No keysize provided after 'keysize' argument"
KEYSIZE=${2}
_log 'debug' "Keysize set to '${KEYSIZE}'"
;;
( 'selector' )
[[ -n ${2:-} ]] || _exit_with_error "No selector provided after 'selector' argument"
SELECTOR=${2}
_log 'debug' "Selector set to '${SELECTOR}'"
;;
( 'domain' )
[[ -n ${2:-} ]] || _exit_with_error "No domain provided after 'domain' argument"
DOMAIN=${2}
_log 'debug' "Domain set to '${DOMAIN}'"
;;
( 'help' )
__usage
exit 0
;;
( '-vv' )
# shellcheck disable=SC2034
LOG_LEVEL='trace'
shift 1
_log 'trace' 'Enabled trace-logging'
continue
;;
( '-v' )
# shellcheck disable=SC2034
LOG_LEVEL='debug'
shift 1
_log 'debug' 'Enabled debug-logging'
continue
;;
( * )
__usage
_exit_with_error "Unknown option(s) '${1}' ${2:+"and '${2}'"}"
;;
esac
shift 2
done
if [[ ${KEYTYPE} == 'ed25519' ]] && [[ ${KEYSIZE} -ne 2048 ]]
then
_exit_with_error "Chosen keytype does not accept the 'keysize' argument"
fi
return 0
}
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}"
KEYTYPE_OPTIONS=('-b' "${KEYSIZE}")
_log 'info' "Creating DKIM keys of type '${KEYTYPE}' and lenght '${KEYSIZE}' with selector '${SELECTOR}' for domain '${DOMAIN}'"
else
local BASE_FILE_NAME="${BASE_DIR}/${KEYTYPE}-${SELECTOR}-${DOMAIN}"
KEYTYPE_OPTIONS=('-t' "${KEYTYPE}")
_log 'info' "Creating DKIM keys of type '${KEYTYPE}' with selector '${SELECTOR}' for domain '${DOMAIN}'"
fi
PUBLIC_KEY_FILE="${BASE_FILE_NAME}.public.txt"
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}"
# shellcheck disable=SC2310
if __do_as_rspamd_user rspamadm \
dkim_keygen \
-s "${SELECTOR}" \
-d "${DOMAIN}" \
"${KEYTYPE_OPTIONS[@]}" \
-k "${PRIVATE_KEY_FILE}" \
>"${PUBLIC_KEY_FILE}"
then
_log 'info' 'Successfully created DKIM keys'
_log 'debug' "Public key written to '${PUBLIC_KEY_FILE}'"
_log 'debug' "Private key written to '${PRIVATE_KEY_FILE}'"
else
_exit_with_error 'Creating keys failed'
fi
}
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"
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
_log 'debug' 'Permissions on files and directories seem ok'
fi
}
function _setup_default_signing_conf
{
local DEFAULT_CONFIG_FILE='/etc/rspamd/override.d/dkim_signing.conf'
if [[ -f ${DEFAULT_CONFIG_FILE} ]]
then
_log 'debug' "'${DEFAULT_CONFIG_FILE}' exists, not supplying a default"
else
_log 'info' "Supplying a default configuration ('${DEFAULT_CONFIG_FILE}')"
cat >"${DEFAULT_CONFIG_FILE}" << EOF
# documentation: https://rspamd.com/doc/modules/dkim_signing.html
enabled = true;
sign_authenticated = true;
sign_local = true;
use_domain = "header";
use_redis = false; # don't change unless Redis also provides the DKIM keys
use_esld = true;
check_pubkey = true; # you wan't to use this in the beginning
domain {
${DOMAIN} {
path = "${PRIVATE_KEY_FILE}";
selector = "${SELECTOR}";
}
}
EOF
chown _rspamd:_rspamd "${DEFAULT_CONFIG_FILE}"
fi
}
function _transform_public_key_file_to_dns_record_contents
{
_log 'trace' 'Transforming DNS zone format to DNS record content now'
: >"${PUBLIC_KEY_DNS_FILE}"
grep -o '".*"' "${PUBLIC_KEY_FILE}" | tr -d '"\n' >>"${PUBLIC_KEY_DNS_FILE}"
echo '' >>"${PUBLIC_KEY_DNS_FILE}"
if ! _log_level_is '(warn|error)'
then
_log 'info' "Here is the content of the TXT DNS record ${SELECTOR}._domainkey.${DOMAIN} that you need to create:\n"
cat "${PUBLIC_KEY_DNS_FILE}"
printf '\n'
fi
}
function _final_steps
{
# We need to restart Rspamd so the changes take effect immediately.
if ! supervisorctl restart rspamd
then
_log 'warn' 'Could not restart Rspamd via Supervisord'
fi
_log 'trace' 'Finished DKIM key creation'
}
_obtain_hostname_and_domainname
_require_n_parameters_or_print_usage 0 "${@}"
_parse_arguments "${@}"
_create_keys
_check_permissions
_setup_default_signing_conf
_transform_public_key_file_to_dns_record_contents
_final_steps

View file

@ -82,7 +82,7 @@ function __rspamd__run_early_setup_and_checks
then then
ln -s "${RSPAMD_DMS_OVERRIDE_D}" "${RSPAMD_OVERRIDE_D}" ln -s "${RSPAMD_DMS_OVERRIDE_D}" "${RSPAMD_OVERRIDE_D}"
else else
__rspamd__log 'warn' "Could not remove '${RSPAMD_OVERRIDE_D}' - not linking '${RSPAMD_DMS_OVERRIDE_D}'" __rspamd__log 'warn' "Could not remove '${RSPAMD_OVERRIDE_D}' (not empty?) - not linking '${RSPAMD_DMS_OVERRIDE_D}'"
fi fi
fi fi

View file

@ -0,0 +1,272 @@
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='fixed.com'
SIGNING_CONF_FILE='/etc/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 DomainKeys Identified Mail (DKIM) via Rspamd'
}
@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 ('${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
__log_is_free_of_warnings_and_errors
refute_output --partial "Supplying a default configuration ('${SIGNING_CONF_FILE}')"
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 512 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}
_run_in_container setup config dkim \
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}"
}