docker-mailserver/target/bin/rspamd-dkim
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

292 lines
9.3 KiB
Bash
Executable file

#!/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 DKIM (DomainKeys Identified Mail)
${ORANGE}SYNOPSIS${RESET}
setup config dkim [ OPTIONS${RED}...${RESET} ]
${ORANGE}DESCRIPTION${RESET}
Creates DKIM keys and configures them within DMS for Rspamd.
OPTIONS can be used when your requirements are not met by the defaults.
${ORANGE}OPTIONS${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').
-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 to generate keys for.
Default: The FQDN assigned to DMS, excluding any subdomain.
${ORANGE}EXAMPLES${RESET}
${LWHITE}setup config dkim keysize 4096${RESET}
Creates keys with their length increased to a size of 4096-bit.
${LWHITE}setup config dkim keysize 1024 selector 2023-dkim${RESET}
Creates 1024-bit sized keys, and changes the DKIM selector to '2023-dkim'.
${LWHITE}setup config dkim domain example.com${RESET}
Generate the DKIM key for a different domain (example.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() {
FORCE=0
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
;;
( '-f' | '--force' )
FORCE=1
shift 1
continue
;;
( '-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
return 0
}
function _preflight_checks() {
if [[ ${KEYTYPE} == 'ed25519' ]] && [[ ${KEYSIZE} -ne 2048 ]]; then
_exit_with_error "Chosen keytype does not accept the 'keysize' argument"
fi
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() {
if [[ ${KEYTYPE} == 'rsa' ]]; then
local BASE_FILE_NAME="${RSPAMD_DMS_DKIM_D}/${KEYTYPE}-${KEYSIZE}-${SELECTOR}-${DOMAIN}"
KEYTYPE_OPTIONS=('-b' "${KEYSIZE}")
_log 'info' "Creating DKIM keys of type '${KEYTYPE}' and length '${KEYSIZE}' with selector '${SELECTOR}' for domain '${DOMAIN}'"
else
local BASE_FILE_NAME="${RSPAMD_DMS_DKIM_D}/${KEYTYPE}-${SELECTOR}-${DOMAIN}"
KEYTYPE_OPTIONS=('-t' "${KEYTYPE}")
_log 'info' "Creating DKIM keys of type '${KEYTYPE}' with selector '${SELECTOR}' for domain '${DOMAIN}'"
fi
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"
if [[ -f ${PUBLIC_KEY_FILE} ]] || [[ -f ${PUBLIC_KEY_DNS_FILE} ]] || [[ -f ${PRIVATE_KEY_FILE} ]]; then
if [[ ${FORCE} -eq 0 ]]; then
_log 'error' "Not overwriting existing files (use '--force' to overwrite existing files)"
exit 1
else
_log 'info' "Overwriting existing files as the '--force' option was supplied"
rm "${PUBLIC_KEY_FILE}" "${PUBLIC_KEY_DNS_FILE}" "${PRIVATE_KEY_FILE}"
fi
fi
# shellcheck disable=SC2310
if __do_as_rspamd_user rspamadm \
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 "${RSPAMD_DMS_DKIM_D}" >/dev/null; then
_log 'warn' "The Rspamd user ('_rspamd') seems to be unable to list files in the keys directory ('${RSPAMD_DMS_DKIM_D}') - Rspamd may experience permission errors later"
elif ! __do_as_rspamd_user cat "${PRIVATE_KEY_FILE}" >/dev/null; then
_log 'warn' "The Rspamd user ('_rspamd') seems to be unable to read the private key file - Rspamd may experience permission errors later"
else
_log 'debug' 'Permissions on files and directories seem ok'
fi
}
function _setup_default_signing_conf() {
local DEFAULT_CONFIG_FILE="${RSPAMD_DMS_OVERRIDE_D}/dkim_signing.conf"
if [[ -f ${DEFAULT_CONFIG_FILE} ]]; then
_log 'info' "'${DEFAULT_CONFIG_FILE}' exists, not supplying a default ('--force' does not overwrite this file, manual adjustment required)"
else
_log 'info' "Supplying a default configuration (to '${DEFAULT_CONFIG_FILE}')"
cat >"${DEFAULT_CONFIG_FILE}" << EOF
# documentation: https://rspamd.com/doc/modules/dkim_signing.html
enabled = true;
sign_authenticated = true;
sign_local = false;
try_fallback = false;
use_domain = "header";
use_redis = false; # don't change unless Redis also provides the DKIM keys
use_esld = true;
allow_username_mismatch = true;
check_pubkey = true; # you want to use this in the beginning
domain {
${DOMAIN} {
path = "${PRIVATE_KEY_FILE}";
selector = "${SELECTOR}";
}
}
EOF
# 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
}
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 "${@}"
_preflight_checks
_create_keys
_check_permissions
_setup_default_signing_conf
_transform_public_key_file_to_dns_record_contents
_final_steps