1
0
mirror of https://github.com/tomav/docker-mailserver.git synced 2024-07-01 05:31:36 +02:00
docker-mailserver/target/bin/rspamd-dkim
Georg Lauterbach 5f2fb72c9c
Rspamd: add check for DKIM private key files' permissions (#3627)
* added check for Rspamd DKIM on startup

The newly added function `__rspamd__check_dkim_permissions` performs a
check on DKIM private key files. This is useful to prevent issues
like #3621 in the future. The function is deliberately kept simple and
may not catch every single misconfiguration in terms of permissions and
ownership, but it should be quite accurate.

Please note that the Rspamd setup does NOT change at all, and the checks
will not abort the setup in case they fail. A simple warning is emmited.

* add more documentation to Rspamd functions

* Apply suggestions from code review

* improve `__do_as_rspamd_user`

* rework check similar to review suggestion

see https://github.com/docker-mailserver/docker-mailserver/pull/3627#discussion_r1388697547

---------

Co-authored-by: Brennan Kinney <5098581+polarathene@users.noreply.github.com>
2023-11-13 12:34:46 +01:00

285 lines
9.1 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 _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