1
0
mirror of https://github.com/tomav/docker-mailserver.git synced 2024-07-02 14:11:12 +02:00
docker-mailserver/docs/content/config/best-practices/dkim_dmarc_spf.md
Brennan Kinney e9f04cf8a7
chore: Change setup config dkim default key size to 2048 (open-dkim) (#3508)
* chore: Adjust default DKIM size (`open-dkim`) from 4096-bit to 2048-bit

4096-bit is excessive in size for DKIM key. 2048-bit is plenty.

* chore: Additional revisions to `open-dkim` command help output

- The examples use `keysize 2048`, but as that's the new default it makes sense to change that.
- Other help text was also revised.
- Last example for domains did not need to demonstrate the other options. Changed example domains to more appropriate values.

* docs: Revise DKIM docs

Primarily for the change in default key size, but does revise some text to better communicate to the user.
- While the referenced RFC advises 512-bit to 2048-bit key size, we now explicitly discourage `512-bit` as it's not secure. `1024-bit` is still likely safe for most, but `2048-bit` is a good default for those not rotating their keys.
- Adjusted the domains example to match the new `setup config dkim domain` domains example.
- Tip for changing default key size changed to "info" with added clarity of lowering security or increasing it (excessively).
- Rspamd section is minor formatting changes, with the exception of clarifying the "main domain" for the mail accounts is assumed as the DMS FQDN with any subdomain (like `mail.`) stripped away. This is not great, but a legacy issue that needs to be addressed in future.
- `docs-rspamd-override-d` ref removed, and usage replaced with equivalent ref `docs-rspamd-config-dropin`, while `docs-rspamd-config-declarative` ref was not in use and also removed.
- Revised the `<selector>.txt` DNS formatting info section to better communicate with the reader. Additionally it had mixed usage of default `mail` and custom `dkim-rsa` selectors (_file content and output_).

* docs: Sync DKIM commands help messages and update DKIM docs for LDAP

- Adopt the help options format style from the `rspamd-dkim` into `open-dkim` command. And convert `./setup.sh` to `setup`. `selector` option has been implemented. for a while now.
- Update `rspamd-dkim` examples help output to align with `open-dkim` command examples.
- Give both DKIM command tools a consistent description. The two tools differ in support for the `domain` option (_implicit domain sourcing for default account provisioner, and support for multiple domains as input_).
- DKIM docs for LDAP domain support revised to better communicate when explicit domain config is necessary.

* tests: Adjust test-cases for `setup config dkim` change

`rspamd_dkim.bats`:
- Update assert for command help output.
- Don't bother creating a DKIM key at 512-bit size.

`setup_cli.bats`:
- Update assert for command help output of the `setup config dkim` (OpenDKIM) command.

* docs: Update DKIM section for large keys to newer RFC

The linked discussion from 2021 does mention this updated RFC over the original. That removes outdated advice about `512-bit` key length support.

The discussion link is still kept to reference a comment for the reader to better understand the security strength of 2048-bit RSA keys and why larger keys are not worthwhile, especially for DKIM.

* docs: Extract out common DKIM generation command from content tabs

Should be fine to be DRY here, not specific to `open-dkim` or `rspamd` generation/support. Previously rspamd lacked support of an equivalent command in DMS.

* docs: DKIM refactoring

- Shifted out the info admonition on key size advice out of the content tabs as it's now generic information.
- Indented the 4096-bit warning into this, which is less of a concern as the default for our DKIM generation tools is consistently 2048-bit now.
- Reworked the LDAP and Rspamd multi-domain advice. To avoid causing a bad diff, these sections haven't been moved/merged yet.

* docs: Revise DKIM docs

Advice for managing domains individually with LDAP and Rspamd extracted out of the content tabs. Default domain behaviour explained with extra info about OpenDKIM + FILE provisioner sourcing extra domains implicitly.
2023-08-29 09:40:02 +12:00

18 KiB

DKIM, DMARC & SPF

Cloudflare has written an article about DKIM, DMARC and SPF that we highly recommend you to read to get acquainted with the topic.

!!! note "Rspamd vs Individual validators"

With v12.0.0, Rspamd was integrated into DMS. It can perform validations for DKIM, DMARC and SPF as part of the `spam-score-calculation` for an email. DMS provides individual alternatives for each validation that can be used instead of deferring to Rspamd:

- DKIM: `opendkim` is used as a milter (like Rspamd)
- DMARC: `opendmarc` is used as a milter (like Rspamd)
- SPF: `policyd-spf` is used in Postfix's `smtpd_recipient_restrictions`

In a future release Rspamd will become the default for these validations, with a deprecation notice issued prior to the removal of the above alternatives.

We encourage everyone to prefer Rspamd via `ENABLE_RSPAMD=1`.

!!! warning "DNS Caches & Propagation"

While modern DNS providers are quick, it may take minutes or even hours for new DNS records to become available / propagate.

DKIM

!!! quote "What is DKIM"

DomainKeys Identified Mail (DKIM) is an email authentication method designed to detect forged sender addresses in email (email spoofing), a technique often used in phishing and email spam.

[Source][wikipedia-dkim]

When DKIM is enabled:

  1. Inbound mail will verify any included DKIM signatures
  2. Outbound mail is signed (when you're sending domain has a configured DKIM key)

DKIM requires a public/private key pair to enable signing (via private key) your outgoing mail, while the receiving end must query DNS to verify (via public key) that the signature is trustworthy.

Generating Keys

You'll need to repeat this process if you add any new domains.

You should have:

!!! example "Creating DKIM Keys"

DKIM keys can be generated with good defaults by running:

```bash
docker exec -it <CONTAINER NAME> setup config dkim
```

If you need to generate your keys with different settings, check the `help` output for supported config options and examples:

```bash
docker exec -it <CONTAINER NAME> setup config dkim help
```

As described by the help output, you may need to use the `domain` option explicitly when you're using LDAP or Rspamd.

??? info "Changing the key size"

The keypair generated for using with DKIM presently defaults to RSA-2048. This is a good size but you can lower the security to `1024-bit`, or increase it to `4096-bit` (_discouraged as that is excessive_).

To generate a key with different size (_for RSA 1024-bit_) run:

```sh
setup config dkim keysize 1024
```

!!! warning "RSA Key Sizes >= 4096 Bit"

    According to [RFC 8301][rfc-8301], keys are preferably between 1024 and 2048 bits. Keys of size 4096-bit or larger may not be compatible to all systems your mail is intended for.

    You [should not need a key length beyond 2048-bit][github-issue-dkimlength]. If 2048-bit does not meet your security needs, you may want to instead consider adopting key rotation or switching from RSA to ECC keys for DKIM.

??? note "You may need to specify mail domains explicitly"

Required when using LDAP and Rspamd.

`setup config dkim` will generate DKIM keys for what is assumed as the primary mail domain (_derived from the FQDN assigned to DMS, minus any subdomain_).

When the DMS FQDN is `mail.example.com` or `example.com`, by default this command will generate DKIM keys for `example.com` as the primary domain for your users mail accounts (eg: `hello@example.com`).

The DKIM generation does not have support to query LDAP for additionanl mail domains it should know about. If the primary mail domain is not sufficient, then you must explicitly specify any extra domains via the `domain` option:

```sh
# ENABLE_OPENDKIM=1 (default):
setup config dkim domain 'example.com,another-example.com'

# ENABLE_RSPAMD=1 + ENABLE_OPENDKIM=0:
setup config dkim domain example.com
setup config dkim domain another-example.com
```

!!! info "OpenDKIM with `ACCOUNT_PROVISIONER=FILE`"

    When DMS uses this configuration, it will by default also detect mail domains (_from accounts added via `setup email add`_), generating additional DKIM keys.

DKIM is currently supported by either OpenDKIM or Rspamd:

=== "OpenDKIM"

OpenDKIM is currently [enabled by default][docs-env-opendkim].

After running `setup config dkim`, your new DKIM key files (_and OpenDKIM config_) have been added to `/tmp/docker-mailserver/opendkim/`.

!!! info "Restart required"

    After restarting DMS, outgoing mail will now be signed with your new DKIM key(s) :tada:

=== "Rspamd"

Requires opt-in via [`ENABLE_RSPAMD=1`][docs-env-rspamd] (_and disable the default OpenDKIM: `ENABLE_OPENDKIM=0`_).

Rspamd provides DKIM support through two separate modules:

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).

??? warning "Using Multiple Domains"

    If you have multiple domains, you need to:

    - Create a key wth `docker exec -it <CONTAINER NAME> setup config dkim domain <DOMAIN>` for each domain DMS should sign outgoing mail for.
    - Provide a custom `dkim_signing.conf` (for which an example is shown below), as the default config only supports one domain.

!!! 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).

    ---

    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 exists, it will not be overwritten.

    When you're already using [the `rspamd/override.d/` directory][docs-rspamd-config-dropin], 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).

    An example of what a default configuration file for DKIM signing looks like can be found by expanding the example below.

??? example "DKIM Signing Module Configuration Examples"

    A simple configuration could look like this:

    ```cf
    # 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 want to use this in the beginning

    domain {
        example.com {
            path = "/tmp/docker-mailserver/rspamd/dkim/mail.private";
            selector = "mail";
        }
    }
    ```

    As shown next:

    - You can add more domains into the `domain { ... }` section (in the following example: `example.com` and `example.org`).
    - A domain can also be configured with multiple selectors and keys within a `selectors [ ... ]` array (in the following example, this is done for `example.org`).

    ```cf
    # ...

    domain {
        example.com {
            path = /tmp/docker-mailserver/rspamd/example.com/ed25519.private";
            selector = "dkim-ed25519";
        }
        example.org {
            selectors [
                {
                    path = "/tmp/docker-mailserver/rspamd/dkim/example.org/rsa.private";
                    selector = "dkim-rsa";
                },
                {
                    path = "/tmp/docker-mailserver/rspamd/dkim/example.org/ed25519.private";
                    selector = "dkim-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].

    If you sign your mail with this key type, you should include RSA as a fallback, like shown in the above example.

??? tip "Let Rspamd Check Your Keys"

    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 emitted to the Rspamd log `/var/log/supervisor/rspamd.log`.

DNS Record

When mail signed with your DKIM key is sent from your mail server, the receiver needs to check a DNS TXT record to verify the DKIM signature is trustworthy.

!!! example "Configuring DNS - DKIM record"

When you generated your key in the previous step, the DNS data was saved into a file `<selector>.txt` (default: `mail.txt`). Use this content to update your [DNS via Web Interface][dns::example-webui] or directly edit your [DNS Zone file][dns::wikipedia-zonefile]:

=== "Web Interface"

    Create a new record:

    | Field | Value                                                                          |
    | ----- | ------------------------------------------------------------------------------ |
    | Type  | `TXT`                                                                          |
    | Name  | `<selector>._domainkey` (_default: `mail._domainkey`_)                         |
    | TTL   | Use the default (_otherwise [3600 seconds is appropriate][dns::digicert-ttl]_) |
    | 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"

    `<selector>.txt` is already formatted as a snippet for adding to your [DNS Zone file][dns::wikipedia-zonefile].

    Just copy/paste the file contents into your existing DNS zone. The `TXT` value has been split into separate strings every 255 characters for compatibility.

??? info "<selector>.txt - Formatting the TXT record value correctly"

This file was generated for use within a [DNS zone file][dns::wikipedia-zonefile]. The file name uses the DKIM selector it was generated with (default DKIM selector is `mail`, which creates `mail.txt`_).

For your DNS setup, DKIM support needs to create a `TXT` record to store the public key for mail clients to use. `TXT` records with values that are longer than 255 characters need to be split into multiple parts. This is why the generated `<selector>.txt` file (_containing your public key for use with DKIM_) has multiple value parts wrapped within double-quotes between `(` and `)`.

A DNS web-interface may handle this separation internally instead, and [could expect the value provided all as a single line][dns::webui-dkim] instead of split. When that is required, you'll need to manually format the value as described below.

Your generated DNS record file (`<selector>.txt`) should look similar to this:

```txt
mail._domainkey IN TXT ( "v=DKIM1; k=rsa; "
"p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQMMqhb1S52Rg7VFS3EC6JQIMxNDdiBmOKZvY5fiVtD3Z+yd9ZV+V8e4IARVoMXWcJWSR6xkloitzfrRtJRwOYvmrcgugOalkmM0V4Gy/2aXeamuiBuUc4esDQEI3egmtAsHcVY1XCoYfs+9VqoHEq3vdr3UQ8zP/l+FP5UfcaJFCK/ZllqcO2P1GjIDVSHLdPpRHbMP/tU1a9mNZ"
"5QMZBJ/JuJK/s+2bp8gpxKn8rh1akSQjlynlV9NI+7J3CC7CUf3bGvoXIrb37C/lpJehS39KNtcGdaRufKauSfqx/7SxA0zyZC+r13f7ASbMaQFzm+/RRusTqozY/p/MsWx8QIDAQAB"
) ;
```

Take the content between `( ... )`, and combine all the quote wrapped content and remove the double-quotes including the white-space between them. That is your `TXT` record value, the above example would become this:

```txt
v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQMMqhb1S52Rg7VFS3EC6JQIMxNDdiBmOKZvY5fiVtD3Z+yd9ZV+V8e4IARVoMXWcJWSR6xkloitzfrRtJRwOYvmrcgugOalkmM0V4Gy/2aXeamuiBuUc4esDQEI3egmtAsHcVY1XCoYfs+9VqoHEq3vdr3UQ8zP/l+FP5UfcaJFCK/ZllqcO2P1GjIDVSHLdPpRHbMP/tU1a9mNZ5QMZBJ/JuJK/s+2bp8gpxKn8rh1akSQjlynlV9NI+7J3CC7CUf3bGvoXIrb37C/lpJehS39KNtcGdaRufKauSfqx/7SxA0zyZC+r13f7ASbMaQFzm+/RRusTqozY/p/MsWx8QIDAQAB
```

To test that your new DKIM record is correct, query it with the `dig` command. The `TXT` value response should be a single line split into multiple parts wrapped in double-quotes:

```console
$ dig +short TXT mail._domainkey.example.com
"v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqQMMqhb1S52Rg7VFS3EC6JQIMxNDdiBmOKZvY5fiVtD3Z+yd9ZV+V8e4IARVoMXWcJWSR6xkloitzfrRtJRwOYvmrcgugOalkmM0V4Gy/2aXeamuiBuUc4esDQEI3egmtAsHcVY1XCoYfs+9VqoHEq3vdr3UQ8zP/l+FP5UfcaJFCK/ZllqcO2P1GjIDVSHLdPpRHbMP/tU1a9mNZ5QMZBJ/JuJK/s+2bp8gpxKn8rh1akSQjlynlV9NI+7J3CC7CUf3bGvoXIrb37C/lpJehS39" "KNtcGdaRufKauSfqx/7SxA0zyZC+r13f7ASbMaQFzm+/RRusTqozY/p/MsWx8QIDAQAB"
```

Troubleshooting

MxToolbox has a DKIM Verifier that you can use to check your DKIM DNS record(s).

When using Rspamd, we recommend you turn on check_pubkey = true; in dkim_signing.conf. Rspamd will then check whether your private key matches your public key, and you can check possible mismatches by looking at /var/log/supervisor/rspamd.log.

DMARC

With DMS, DMARC is pre-configured out of the box. You may disable extra and excessive DMARC checks when using Rspamd via ENABLE_OPENDMARC=0.

The only thing you need to do in order to enable DMARC on a "DNS-level" is to add new TXT. In contrast to DKIM, DMARC DNS entries do not require any keys, but merely setting the configuration values. You can either handcraft the entry by yourself or use one of available generators (like this one).

Typically something like this should be good to start with:

_dmarc.example.com. IN TXT "v=DMARC1; p=none; sp=none; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; rua=mailto:dmarc.report@example.com; ruf=mailto:dmarc.report@example.com"

Or a bit more strict policies (mind p=quarantine and sp=quarantine):

_dmarc.example.com. IN TXT "v=DMARC1; p=quarantine; sp=quarantine; fo=0; adkim=r; aspf=r; pct=100; rf=afrf; ri=86400; rua=mailto:dmarc.report@example.com; ruf=mailto:dmarc.report@example.com"

The DMARC status may not be displayed instantly due to delays in DNS (caches). Dmarcian has a few tools you can use to verify your DNS records.

SPF

!!! quote "What is SPF"

Sender Policy Framework (SPF) is a simple email-validation system designed to detect email spoofing by providing a mechanism to allow receiving mail exchangers to check that incoming mail from a domain comes from a host authorized by that domain's administrators.

[Source][wikipedia-spf]

!!! note "Disabling policyd-spf?"

As of now, `policyd-spf` cannot be disabled. This is WIP.

Adding an SPF Record

To add a SPF record in your DNS, insert the following line in your DNS zone:

example.com. IN TXT "v=spf1 mx ~all"

This enables the Softfail mode for SPF. You could first add this SPF record with a very low TTL. SoftFail is a good setting for getting started and testing, as it lets all email through, with spams tagged as such in the mailbox.

After verification, you might want to change your SPF record to v=spf1 mx -all so as to enforce the HardFail policy. See http://www.open-spf.org/SPF_Record_Syntax for more details about SPF policies.

In any case, increment the SPF record's TTL to its final value.

Backup MX & Secondary MX for policyd-spf

For whitelisting an IP Address from the SPF test, you can create a config file (see policyd-spf.conf) and mount that file into /etc/postfix-policyd-spf-python/policyd-spf.conf.

Example: Create and edit a policyd-spf.conf file at docker-data/dms/config/postfix-policyd-spf.conf:

debugLevel = 1
#0(only errors)-4(complete data received)

skip_addresses = 127.0.0.0/8,::ffff:127.0.0.0/104,::1

# Preferably use IP-Addresses for whitelist lookups:
Whitelist = 192.168.0.0/31,192.168.1.0/30
# Domain_Whitelist = mx1.not-example.com,mx2.not-example.com

Then add this line to compose.yaml:

volumes:
  - ./docker-data/dms/config/postfix-policyd-spf.conf:/etc/postfix-policyd-spf-python/policyd-spf.conf