#!/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" [[ -f ${PUBLIC_KEY_FILE} ]] && rm "${PUBLIC_KEY_FILE}" [[ -f ${PUBLIC_KEY_DNS_FILE} ]] && rm "${PUBLIC_KEY_DNS_FILE}" [[ -f ${PRIVATE_KEY_FILE} ]] && rm "${PRIVATE_KEY_FILE}" fi fi __create_rspamd_err_log # shellcheck disable=SC2310 if __do_as_rspamd_user rspamadm \ dkim_keygen \ -s "${SELECTOR}" \ -d "${DOMAIN}" \ "${KEYTYPE_OPTIONS[@]}" \ -k "${PRIVATE_KEY_FILE}" \ >"${PUBLIC_KEY_FILE}" \ && ! __filter_rspamd_err_log 'Permission denied' # we also need to check the log for error messages 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 __print_rspamd_err_log _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