refactor: setup CLI `open-dkim`

This commit is contained in:
Brennan Kinney 2025-02-17 16:25:54 +13:00 committed by GitHub
parent 0294294755
commit b67dad5bf9
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 142 additions and 86 deletions

View File

@ -1,4 +1,4 @@
#!/bin/bash #!/usr/bin/env bash
# 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
@ -12,9 +12,17 @@ if [[ -f /etc/dms-settings ]] && [[ $(_get_dms_env_value 'ENABLE_RSPAMD') -eq 1
fi fi
fi fi
KEYSIZE=2048 function _main() {
SELECTOR=mail # Default parameters (updated by `_parse_arguments()`):
DOMAINS= local KEYSIZE=2048
local SELECTOR=mail
local DMS_DOMAINS=
_require_n_parameters_or_print_usage 0 "${@}"
_parse_arguments "${@}"
_generate_dkim_keys
}
function __usage() { function __usage() {
printf '%s' "${PURPLE}OPEN-DKIM${RED}(${YELLOW}8${RED}) printf '%s' "${PURPLE}OPEN-DKIM${RED}(${YELLOW}8${RED})
@ -57,122 +65,170 @@ ${ORANGE}EXAMPLES${RESET}
${ORANGE}EXIT STATUS${RESET} ${ORANGE}EXIT STATUS${RESET}
Exit status is 0 if command was successful. If wrong arguments are provided or arguments contain Exit status is 0 if command was successful. If wrong arguments are provided or arguments contain
errors, the script will exit early with exit status 2. errors, the script will exit early with a non-zero exit status.
" "
} }
_require_n_parameters_or_print_usage 0 "${@}" function _parse_arguments() {
# Parse the command args through iteration:
while [[ ${#} -gt 0 ]]; do
case "${1}" in
while [[ ${#} -gt 0 ]]; do ( 'keysize' )
case "${1}" in if [[ -n ${2+set} ]]; then
( 'keysize' ) KEYSIZE="${2}"
if [[ -n ${2+set} ]]; then _log 'debug' "Keysize set to '${KEYSIZE}'"
KEYSIZE="${2}" else
shift _exit_with_error "No keysize provided after 'keysize' argument"
shift fi
else ;;
_exit_with_error "No keysize provided after 'keysize' argument"
fi
;;
( 'selector' ) ( 'selector' )
if [[ -n ${2+set} ]]; then if [[ -n ${2+set} ]]; then
# shellcheck disable=SC2034 SELECTOR="${2}"
SELECTOR="${2}" _log 'debug' "Selector set to '${SELECTOR}'"
shift else
shift _exit_with_error "No selector provided after 'selector' argument"
else fi
_exit_with_error "No selector provided after 'selector' argument" ;;
fi
;;
( 'domain' ) ( 'domain' )
if [[ -n ${2+set} ]]; then if [[ -n ${2+set} ]]; then
DOMAINS="${2}" DMS_DOMAINS="${2}"
shift _log 'debug' "Domain(s) set to '${DMS_DOMAINS}'"
shift else
else _exit_with_error "No domain(s) provided after 'domain' argument"
_exit_with_error "No domain(s) provided after 'domain' argument" fi
fi ;;
;;
( * ) ( 'help' )
__usage __usage
_exit_with_error "Unknown options '${1}' ${2:+and \'${2}\'}" exit 0
;; ;;
esac ( * )
done __usage
_exit_with_error "Unknown option(s) '${1}' ${2:+"and '${2}'"}"
;;
esac
# Discard these two args (option + value) now that they've been processed:
shift 2
done
}
function _generate_dkim_keys() {
_generate_domains_config
if [[ ! -s ${DATABASE_VHOST} ]]; then
_log 'warn' 'No entries found, no keys to make'
exit 0
fi
# Initialize OpenDKIM configs if necessary:
_create_opendkim_configs
# Generate a keypair per domain and add reference to OpenDKIM configs:
local ENTRY_KEY KEY_TABLE_ENTRY SIGNING_TABLE_ENTRY
while read -r DKIM_DOMAIN; do
_create_dkim_key "${DKIM_DOMAIN}"
# Create / Update OpenDKIM configs with new DKIM key:
ENTRY_KEY="${SELECTOR}._domainkey.${DKIM_DOMAIN}"
KEY_TABLE_ENTRY="${ENTRY_KEY} ${DKIM_DOMAIN}:${SELECTOR}:/etc/opendkim/keys/${DKIM_DOMAIN}/${SELECTOR}.private"
SIGNING_TABLE_ENTRY="*@${DKIM_DOMAIN} ${ENTRY_KEY}"
# If no existing entry, add one:
if ! grep -q "${KEY_TABLE_ENTRY}" "${KEY_TABLE_FILE}"; then
echo "${KEY_TABLE_ENTRY}" >> "${KEY_TABLE_FILE}"
fi
if ! grep -q "${SIGNING_TABLE_ENTRY}" "${SIGNING_TABLE_FILE}"; then
echo "${SIGNING_TABLE_ENTRY}" >> "${SIGNING_TABLE_FILE}"
fi
done < <(_get_valid_lines_from_file "${DATABASE_VHOST}")
# No longer needed, remove:
rm "${DATABASE_VHOST}"
# Ensure ownership is consistent for all content belonging to the base directory,
# During container startup, an internal copy will be made via `_setup_opendkim()`
# with ownership we expect, while this chown is for the benefit of the users ownership.
chown -R "$(stat -c '%U:%G' "${OPENDKIM_BASE_DIR}")" "${OPENDKIM_BASE_DIR}"
}
# Prepare a file with one domain per line (iterated via while loop as DKIM_DOMAIN):
# Depends on methods from `scripts/helpers/postfix.sh`:
DATABASE_VHOST='/tmp/vhost.dkim' DATABASE_VHOST='/tmp/vhost.dkim'
# Prepare a file with one domain per line:
function _generate_domains_config() { function _generate_domains_config() {
local TMP_VHOST='/tmp/vhost.dkim.tmp' local TMP_VHOST='/tmp/vhost.dkim.tmp'
# Generate the default vhost (equivalent to /etc/postfix/vhost), # Generate the default vhost (equivalent to /etc/postfix/vhost),
# unless CLI arg DOMAINS provided an alternative list to use instead: # unless CLI arg DMS_DOMAINS provided an alternative list to use instead:
if [[ -z ${DOMAINS} ]]; then if [[ -z ${DMS_DOMAINS} ]]; then
_obtain_hostname_and_domainname _obtain_hostname_and_domainname
# uses TMP_VHOST: # uses TMP_VHOST:
_vhost_collect_postfix_domains _vhost_collect_postfix_domains
else else
tr ',' '\n' <<< "${DOMAINS}" >"${TMP_VHOST}" tr ',' '\n' <<< "${DMS_DOMAINS}" >"${TMP_VHOST}"
fi fi
# uses DATABASE_VHOST + TMP_VHOST: # Uses DATABASE_VHOST + TMP_VHOST:
_create_vhost _create_vhost
} }
_generate_domains_config # `opendkim-genkey` generates two files at the configured `--directory`:
if [[ ! -s ${DATABASE_VHOST} ]]; then # - <selector>.private (Private key, PEM encoded)
_log 'warn' 'No entries found, no keys to make' # - <selector>.txt (Public key, formatted as a TXT record for a RFC 1035 DNS Zone file)
exit 0 function _create_dkim_key() {
fi DKIM_DOMAIN=${1?Expected to be provided a domain}
while read -r DKIM_DOMAIN; do OPENDKIM_DOMAINKEY_DIR="${OPENDKIM_BASE_DIR}/keys/${DKIM_DOMAIN}"
mkdir -p "/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}" mkdir -p "${OPENDKIM_DOMAINKEY_DIR}"
if [[ ! -f "/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}/${SELECTOR}.private" ]]; then DKIM_KEY_FILE="${OPENDKIM_DOMAINKEY_DIR}/${SELECTOR}.private"
_log 'info' "Creating DKIM private key '/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}/${SELECTOR}.private'" if [[ ! -f "${DKIM_KEY_FILE}" ]]; then
_log 'info' "Creating DKIM private key '${DKIM_KEY_FILE}'"
# NOTE:
# --domain only affects a comment in the generated DNS Zone file
# --subdomains is the default,
# --nosubdomains would add `t=s` to the DNS TXT record generated
# http://www.opendkim.org/opendkim-genkey.8.html
opendkim-genkey \ opendkim-genkey \
--bits="${KEYSIZE}" \ --bits="${KEYSIZE}" \
--subdomains \ --subdomains \
--domain="${DKIM_DOMAIN}" \ --domain="${DKIM_DOMAIN}" \
--selector="${SELECTOR}" \ --selector="${SELECTOR}" \
--directory="/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}" --directory="${OPENDKIM_DOMAINKEY_DIR}"
fi fi
}
# fix permissions to use the same user:group as /tmp/docker-mailserver/opendkim/keys OPENDKIM_BASE_DIR='/tmp/docker-mailserver/opendkim'
chown -R "$(stat -c '%U:%G' /tmp/docker-mailserver/opendkim/keys)" "/tmp/docker-mailserver/opendkim/keys/${DKIM_DOMAIN}" KEY_TABLE_FILE="${OPENDKIM_BASE_DIR}/KeyTable"
SIGNING_TABLE_FILE="${OPENDKIM_BASE_DIR}/SigningTable"
TRUSTED_HOSTS_FILE="${OPENDKIM_BASE_DIR}/TrustedHosts"
# Create configs if missing:
function _create_opendkim_configs() {
_log 'debug' 'Creating any missing OpenDKIM configs'
# write to KeyTable if necessary mkdir -p "${OPENDKIM_BASE_DIR}"
KEYTABLEENTRY="${SELECTOR}._domainkey.${DKIM_DOMAIN} ${DKIM_DOMAIN}:${SELECTOR}:/etc/opendkim/keys/${DKIM_DOMAIN}/${SELECTOR}.private" local OPENDKIM_CONFIGS=(
if [[ ! -f "/tmp/docker-mailserver/opendkim/KeyTable" ]]; then "${KEY_TABLE_FILE}"
_log 'debug' 'Creating DKIM KeyTable' "${SIGNING_TABLE_FILE}"
echo "${KEYTABLEENTRY}" >/tmp/docker-mailserver/opendkim/KeyTable "${TRUSTED_HOSTS_FILE}"
else )
if ! grep -q "${KEYTABLEENTRY}" "/tmp/docker-mailserver/opendkim/KeyTable"; then
echo "${KEYTABLEENTRY}" >>/tmp/docker-mailserver/opendkim/KeyTable # Only create if the file doesn't exist (avoids modifying mtime):
fi for FILE in ${OPENDKIM_CONFIGS[@]}; do
[[ ! -f "${FILE}" ]] && touch "${FILE}"
done
# If file exists but is empty, add default hosts to trust:
if [[ ! -s "${TRUSTED_HOSTS_FILE}" ]]; then
_log 'debug' 'Adding default trust to OpenDKIM TrustedHosts config'
echo "127.0.0.1" > "${TRUSTED_HOSTS_FILE}"
echo "localhost" >> "${TRUSTED_HOSTS_FILE}"
fi fi
}
# write to SigningTable if necessary _main "${@}"
SIGNINGTABLEENTRY="*@${DKIM_DOMAIN} ${SELECTOR}._domainkey.${DKIM_DOMAIN}"
if [[ ! -f /tmp/docker-mailserver/opendkim/SigningTable ]]; then
_log 'debug' 'Creating DKIM SigningTable'
echo "*@${DKIM_DOMAIN} ${SELECTOR}._domainkey.${DKIM_DOMAIN}" >/tmp/docker-mailserver/opendkim/SigningTable
else
if ! grep -q "${SIGNINGTABLEENTRY}" /tmp/docker-mailserver/opendkim/SigningTable; then
echo "${SIGNINGTABLEENTRY}" >>/tmp/docker-mailserver/opendkim/SigningTable
fi
fi
done < <(_get_valid_lines_from_file "${DATABASE_VHOST}")
# create TrustedHosts if missing
if [[ -d /tmp/docker-mailserver/opendkim ]] && [[ ! -f /tmp/docker-mailserver/opendkim/TrustedHosts ]]; then
_log 'debug' 'Creating DKIM TrustedHosts'
echo "127.0.0.1" >/tmp/docker-mailserver/opendkim/TrustedHosts
echo "localhost" >>/tmp/docker-mailserver/opendkim/TrustedHosts
fi