mirror of
https://github.com/docker-mailserver/docker-mailserver.git
synced 2024-01-19 02:48:50 +00:00
scripts: add DKIM helper script for Rspamd (#3286)
Co-authored-by: Casper <casperklein@users.noreply.github.com>
This commit is contained in:
parent
423188176f
commit
bba72daedf
|
@ -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].
|
||||||
|
|
|
@ -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
279
target/bin/rspamd-dkim
Executable 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
|
|
@ -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
|
||||||
|
|
||||||
|
|
272
test/tests/parallel/set1/spam_virus/rspamd_dkim.bats
Normal file
272
test/tests/parallel/set1/spam_virus/rspamd_dkim.bats
Normal 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}"
|
||||||
|
}
|
Loading…
Reference in a new issue