rspamd: allow configuring dmarc reporting
Enabling collects DMARC results in Redis and sends out aggregated reports (RUA) on a daily basis.
This commit is contained in:
parent
3f0b7a1b5c
commit
fe36e7ae0d
5 changed files with 212 additions and 2 deletions
57
default.nix
57
default.nix
|
@ -627,6 +627,63 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
dmarcReporting = {
|
||||
enable = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
description = ''
|
||||
Whether to send out aggregated, daily DMARC reports in response to incoming
|
||||
mail, when the sender domain defines a DMARC policy including the RUA tag.
|
||||
|
||||
This is helpful for the mail ecosystem, because it allows third parties to
|
||||
get notified about SPF/DKIM violations originating from their sender domains.
|
||||
|
||||
See https://rspamd.com/doc/modules/dmarc.html#reporting
|
||||
'';
|
||||
};
|
||||
|
||||
localpart = mkOption {
|
||||
type = types.str;
|
||||
default = "dmarc-noreply";
|
||||
example = "dmarc-report";
|
||||
description = ''
|
||||
The local part of the email address used for outgoing DMARC reports.
|
||||
'';
|
||||
};
|
||||
|
||||
domain = mkOption {
|
||||
type = types.enum (cfg.domains);
|
||||
example = "example.com";
|
||||
description = ''
|
||||
The domain from which outgoing DMARC reports are served.
|
||||
'';
|
||||
};
|
||||
|
||||
email = mkOption {
|
||||
type = types.str;
|
||||
default = with cfg.dmarcReporting; "${localpart}@${domain}";
|
||||
example = "dmarc-noreply@example.com";
|
||||
readOnly = true;
|
||||
};
|
||||
|
||||
organizationName = mkOption {
|
||||
type = types.str;
|
||||
example = "ACME Corp.";
|
||||
description = ''
|
||||
The name of your organization used in the <literal>org_name</literal> attribute in
|
||||
DMARC reports.
|
||||
'';
|
||||
};
|
||||
|
||||
fromName = mkOption {
|
||||
type = types.str;
|
||||
default = cfg.dmarcReporting.organizationName;
|
||||
description = ''
|
||||
The sender name for DMARC reports. Defaults to the organization name.
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
debug = mkOption {
|
||||
type = types.bool;
|
||||
default = false;
|
||||
|
|
|
@ -19,6 +19,72 @@ to enable this unless you're hacking on nixos-mailserver.
|
|||
- Default: ``False``
|
||||
|
||||
|
||||
mailserver.dmarcReporting.domain
|
||||
--------------------------------
|
||||
|
||||
The domain from which outgoing DMARC reports are served.
|
||||
|
||||
|
||||
- Type: ``value "example.com" (singular enum)``
|
||||
|
||||
|
||||
|
||||
mailserver.dmarcReporting.email
|
||||
-------------------------------
|
||||
|
||||
None
|
||||
|
||||
- Type: ``string``
|
||||
- Default: ``dmarc-noreply@example.com``
|
||||
|
||||
|
||||
mailserver.dmarcReporting.enable
|
||||
--------------------------------
|
||||
|
||||
Whether to send out aggregated, daily DMARC reports in response to incoming
|
||||
mail, when the sender domain defines a DMARC policy including the RUA tag.
|
||||
|
||||
This is helpful for the mail ecosystem, because it allows third parties to
|
||||
get notified about SPF/DKIM violations originating from their sender domains.
|
||||
|
||||
See https://rspamd.com/doc/modules/dmarc.html#reporting
|
||||
|
||||
|
||||
- Type: ``boolean``
|
||||
- Default: ``False``
|
||||
|
||||
|
||||
mailserver.dmarcReporting.fromName
|
||||
----------------------------------
|
||||
|
||||
The sender name for DMARC reports. Defaults to the organization name.
|
||||
|
||||
|
||||
- Type: ``string``
|
||||
- Default: ``Example Corp``
|
||||
|
||||
|
||||
mailserver.dmarcReporting.localpart
|
||||
-----------------------------------
|
||||
|
||||
The local part of the email address used for outgoing DMARC reports.
|
||||
|
||||
|
||||
- Type: ``string``
|
||||
- Default: ``dmarc-noreply``
|
||||
|
||||
|
||||
mailserver.dmarcReporting.organizationName
|
||||
------------------------------------------
|
||||
|
||||
The name of your organization used in the <literal>org_name</literal> attribute in
|
||||
DMARC reports.
|
||||
|
||||
|
||||
- Type: ``string``
|
||||
|
||||
|
||||
|
||||
mailserver.domains
|
||||
------------------
|
||||
|
||||
|
@ -489,7 +555,7 @@ For which domains should this account act as a catch all?
|
|||
Note: Does not allow sending from all addresses of these domains.
|
||||
|
||||
|
||||
- Type: ``list of impossible (empty enum)s``
|
||||
- Type: ``list of value "example.com" (singular enum)s``
|
||||
- Default: ``[]``
|
||||
|
||||
|
||||
|
|
11
flake.nix
11
flake.nix
|
@ -59,7 +59,16 @@
|
|||
# don't care about this package but it is part of the
|
||||
# NixOS module evaluation)
|
||||
nixpkgs.config.allowBroken = true;
|
||||
mailserver.fqdn = "mx.example.com";
|
||||
mailserver = {
|
||||
fqdn = "mx.example.com";
|
||||
domains = [
|
||||
"example.com"
|
||||
];
|
||||
dmarcReporting = {
|
||||
organizationName = "Example Corp";
|
||||
domain = "example.com";
|
||||
};
|
||||
};
|
||||
}
|
||||
];
|
||||
|
||||
|
|
|
@ -56,6 +56,17 @@ in
|
|||
# Disable outbound email signing, we use opendkim for this
|
||||
enabled = false;
|
||||
''; };
|
||||
"dmarc.conf" = { text = ''
|
||||
${lib.optionalString cfg.dmarcReporting.enable ''
|
||||
reporting {
|
||||
enabled = true;
|
||||
email = "${cfg.dmarcReporting.email}";
|
||||
domain = "${cfg.dmarcReporting.domain}";
|
||||
org_name = "${cfg.dmarcReporting.organizationName}";
|
||||
from_name = "${cfg.dmarcReporting.fromName}";
|
||||
msgid_from = "dmarc-rua";
|
||||
}''}
|
||||
''; };
|
||||
};
|
||||
|
||||
overrides = {
|
||||
|
@ -108,6 +119,64 @@ in
|
|||
after = [ "redis-rspamd.service" ] ++ (lib.optional cfg.virusScanning "clamav-daemon.service");
|
||||
};
|
||||
|
||||
systemd.services.rspamd-dmarc-reporter = lib.optionalAttrs (cfg.dmarcReporting.enable) {
|
||||
# Explicitly select yesterday's date to work around broken
|
||||
# default behaviour when called without a date.
|
||||
# https://github.com/rspamd/rspamd/issues/4062
|
||||
script = ''
|
||||
${pkgs.rspamd}/bin/rspamadm dmarc_report $(date -d "yesterday" "+%Y%m%d")
|
||||
'';
|
||||
serviceConfig = {
|
||||
User = "${config.services.rspamd.user}";
|
||||
Group = "${config.services.rspamd.group}";
|
||||
|
||||
AmbientCapabilities = [];
|
||||
CapabilityBoundingSet = "";
|
||||
DevicePolicy = "closed";
|
||||
IPAddressAllow = "localhost";
|
||||
LockPersonality = true;
|
||||
NoNewPrivileges = true;
|
||||
PrivateDevices = true;
|
||||
PrivateMounts = true;
|
||||
PrivateTmp = true;
|
||||
PrivateUsers = true;
|
||||
ProtectClock = true;
|
||||
ProtectControlGroups = true;
|
||||
ProtectHome = true;
|
||||
ProtectHostname = true;
|
||||
ProtectKernelLogs = true;
|
||||
ProtectKernelModules = true;
|
||||
ProtectKernelTunables = true;
|
||||
ProtectProc = "invisible";
|
||||
ProcSubset = "pid";
|
||||
ProtectSystem = "strict";
|
||||
RemoveIPC = true;
|
||||
RestrictAddressFamilies = [ "AF_INET" "AF_INET6" ];
|
||||
RestrictNamespaces = true;
|
||||
RestrictRealtime = true;
|
||||
RestrictSUIDSGID = true;
|
||||
SystemCallArchitectures = "native";
|
||||
SystemCallFilter = [
|
||||
"@system-service"
|
||||
"~@privileged"
|
||||
];
|
||||
UMask = "0077";
|
||||
};
|
||||
};
|
||||
|
||||
systemd.timers.rspamd-dmarc-reporter = lib.optionalAttrs (cfg.dmarcReporting.enable) {
|
||||
description = "Daily delivery of aggregated DMARC reports";
|
||||
wantedBy = [
|
||||
"timers.target"
|
||||
];
|
||||
timerConfig = {
|
||||
OnCalendar = "daily";
|
||||
Persistent = true;
|
||||
RandomizedDelaySec = 86400;
|
||||
FixedRandomDelay = true;
|
||||
};
|
||||
};
|
||||
|
||||
systemd.services.postfix = {
|
||||
after = [ rspamdSocket ];
|
||||
requires = [ rspamdSocket ];
|
||||
|
|
|
@ -43,6 +43,11 @@ pkgs.nixosTest {
|
|||
domains = [ "example.com" "example2.com" ];
|
||||
rewriteMessageId = true;
|
||||
dkimKeyBits = 1535;
|
||||
dmarcReporting = {
|
||||
enable = true;
|
||||
domain = "example.com";
|
||||
organizationName = "ACME Corp";
|
||||
};
|
||||
|
||||
loginAccounts = {
|
||||
"user1@example.com" = {
|
||||
|
@ -494,6 +499,10 @@ pkgs.nixosTest {
|
|||
# check that Junk is not indexed
|
||||
server.fail("journalctl -u dovecot2 | grep 'indexer-worker' | grep -i 'JUNK' >&2")
|
||||
|
||||
with subtest("dmarc reporting"):
|
||||
server.systemctl("start rspamd-dmarc-reporter.service")
|
||||
server.wait_until_succeeds("journalctl -eu rspamd-dmarc-reporter.service -o cat | grep -q 'No reports for '")
|
||||
|
||||
with subtest("no warnings or errors"):
|
||||
server.fail("journalctl -u postfix | grep -i error >&2")
|
||||
server.fail("journalctl -u postfix | grep -i warning >&2")
|
||||
|
|
Loading…
Reference in a new issue