The milter headers
module (formerly known as rmilter headers
) has been added in Rspamd 1.5 to provide a relatively simple way to configure adding/removing of headers via Rmilter (the alternative being to use the API). Despite its name, it is not tied to the milter
protocol and also works with supported mailservers that use the HTTP interface such as Haraka and OpenSMTPD.
The milter headers
module offers several routines for adding/removing common headers, which can be selectively enabled and configured according to specific needs. Additionally, users have the flexibility to add their own custom routines to the configuration or add them directly from Lua.
# local.d/milter_headers.conf:
# Options
# Add "extended Rspamd headers" (default false) (enables x-spamd-result, x-rspamd-server & x-rspamd-queue-id routines)
# extended_spam_headers = true;
# List of routines to enable for authenticated users (default empty); see also `skip_authenticated`
# authenticated_headers = ["authentication-results"];
# Set false to enable all used routines for authenticated users (default true)
# skip_authenticated = true;
# List of routines to enable for local IPs (default empty); see also `skip_local`
# local_headers = ["x-spamd-bar"];
# Set false to enable all used routines for local IPs (default true)
# skip_local = true;
# Routines to use- this is the only required setting (may be omitted if using extended_spam_headers)
use = ["x-spamd-bar", "authentication-results"];
# this is where we may configure our selected routines
routines {
# settings for x-spamd-bar routine
x-spamd-bar {
# effectively disables negative spambar
negative = "";
}
# other routines...
}
custom {
# user-defined routines: more on these later
}
Add “extended Rspamd headers” to messages NOT originated from authenticated users or our_networks
(default false
). Enables the following routines: x-spamd-result
, x-rspamd-server
and x-rspamd-queue-id
.
extended_spam_headers = true;
List of routines to be enabled for authenticated users (default empty
). See also skip_authenticated
.
authenticated_headers = ["authentication-results"];
Set false
to keep pre-existing spam flag added by an upstream spam filter (default true
). This will enable the remove-spam-flag
option.
remove_upstream_spam_flag = true;
List of routines to enable for local IPs (default empty
). See also skip_local
.
local_headers = ["x-spamd-bar"];
Set false to always add headers for local IPs (default true
).
skip_local = true;
Do not add extended headers for any messages (except those matching extended_headers_rcpt) (default false
)
skip_all = true;
Set false to always add headers for authenticated users (default true
)
skip_authenticated = true;
List of recipients (default empty
).
When extended_spam_headers
is enabled, also add extended Rspamd headers to messages if EVERY envelope recipient match this list (e.g. a list of domains mail server responsible for).
extended_headers_rcpt = ["user1", "@example1.com", "user2@example2.com"];
extended_headers_rcpt
has higher precedence than skip_local
, skip_authenticated
and skip_all
.
extended_headers_rcpt
paired with skip_all = true
can be used to only add extended headers to a map of specific recipients.
Routines to use- this is the only required setting (may be omitted if using extended_spam_headers
)
use = ["x-spamd-bar", "authentication-results"];
Configuration dealing with removing headers commonly sets a numeric parameter which is typically set to 0
.
0
means all headers with a given name should be removed.
1
means remove the first header with a given name, and so on
-1
means remove the last header with a given name, and so on
From version 3.9.0, this can be set to null
to indicate that foreign headers should not be removed.
Available routines and their settings are as below, default values are as indicated:
Add an authentication-results header.
use = ["authentication-results"];
#authenticated_headers = ["authentication-results"]; # to add this header for authenticated users
routines {
authentication-results {
# Name of header
header = "Authentication-Results";
# Remove existing headers
remove = 1;
# Set this false not to add SMTP usernames in authentication-results
add_smtp_user = true;
# SPF/DKIM/DMARC symbols in case these are redefined
spf_symbols {
pass = "R_SPF_ALLOW";
fail = "R_SPF_FAIL";
softfail = "R_SPF_SOFTFAIL";
neutral = "R_SPF_NEUTRAL";
temperror = "R_SPF_DNSFAIL";
none = "R_SPF_NA";
permerror = "R_SPF_PERMFAIL";
}
dkim_symbols {
pass = "R_DKIM_ALLOW";
fail = "R_DKIM_REJECT";
temperror = "R_DKIM_TEMPFAIL";
none = "R_DKIM_NA";
permerror = "R_DKIM_PERMFAIL";
}
dmarc_symbols {
pass = "DMARC_POLICY_ALLOW";
permerror = "DMARC_BAD_POLICY";
temperror = "DMARC_DNSFAIL";
none = "DMARC_NA";
reject = "DMARC_POLICY_REJECT";
softfail = "DMARC_POLICY_SOFTFAIL";
quarantine = "DMARC_POLICY_QUARANTINE";
}
}
}
For each matched fuzzy hash adds a header containing the hash.
use = ["fuzzy-hashes"];
routines {
fuzzy-hashes {
header = "X-Rspamd-Fuzzy";
remove = 0;
}
}
Removes a header with the specified name (header
MUST be specified):
use = ["remove-header"];
routines {
remove-header {
header = "Remove-This";
remove = 0; # 0 means remove all, 1 means remove the first one , -1 remove the last and so on
}
}
Removes multiple headers (headers
MUST be specified):
use = ["remove-headers"];
routines {
remove-headers {
headers {
"Remove-This" = 0;
"This-Too" = 0;
}
}
}
Removes pre-existing spam flag added by an upstream spam filter.
use = ["remove-spam-flag"];
routines {
remove-spam-flag {
header = "X-Spam";
}
}
Default name of the header to be removed is X-Spam
which can be manipulated using the header
setting.
Adds a predefined header to mail identified as spam.
use = ["spam-header"];
routines {
spam-header {
header = "Deliver-To";
value = "Junk";
remove = 0;
}
}
Default name/value of the added header is Deliver-To
/Junk
which can be manipulated using the header
and value
settings.
Attaches the stat signature to the message.
use = ["stat-signature"];
routines {
stat-signature {
header = 'X-Stat-Signature';
remove = 0;
}
}
Adds a header containing the Rspamd queue id of the message if it is NOT originated from authenticated users or our_networks
.
use = ["x-rspamd-queue-id"];
routines {
x-rspamd-queue-id {
header = 'X-Rspamd-Queue-Id';
remove = 0;
}
}
Adds a header containing the scan results if the message is NOT originated from authenticated users or our_networks
.
use = ["x-spamd-result"];
routines {
x-spamd-result {
header = 'X-Spamd-Result';
remove = 0;
}
}
Adds a header containing the local computer host name of the Rspamd server that checked out the message if it is NOT originated from authenticated users or our_networks
. Since Rspamd 2.4 the host name can be replaced with a user-defined string specified in the hostname
setting.
use = ["x-rspamd-server"];
routines {
x-rspamd-server {
header = 'X-Rspamd-Server';
remove = 0;
#hostname = "foo.com"; -- Local computer host name if unspecified (2.4+)
}
}
Adds a visual indicator of spam/ham level.
use = ["x-spamd-bar"];
routines {
x-spamd-bar {
header = "X-Spamd-Bar";
positive = "+";
negative = "-";
neutral = "/";
remove = 0;
}
}
Another visual indicator of spam level- SpamAssassin style.
use = ["x-spam-level"];
routines {
x-spamd-level {
header = "X-Spam-Level";
char = "*";
remove = 0;
}
}
SpamAssassin-style X-Spam-Status header indicating spam status.
use = ["x-spam-status"];
routines {
x-spam-status {
header = "X-Spam-Status";
remove = 0;
}
}
use = ["x-virus"];
routines {
x-virus {
header = "X-Virus";
remove = 0;
# The following setting is an empty list by default and required to be set
# These are user-defined symbols added by the antivirus module
symbols = ["CLAM_VIRUS", "FPROT_VIRUS"];
}
}
If the Antivirus module detects any viruses in an email, the module adds a header that contains the names of the viruses detected by the configured scanners.
User-defined routines can be defined in configuration in the custom
section, for example:
use = ["my_routine"];
custom {
my_routine = <<EOD
return function(task, common_meta)
-- parameters are task and metadata from previous functions
return nil, -- no error
{['X-Foo'] = 'Bar'}, -- add header: X-Foo: Bar
{['X-Foo'] = 0 }, -- remove foreign X-Foo headers
{} -- metadata to return to other functions
end
EOD;
}
You can reference the key my_routine
in the use
setting, just like you would with other routines.
Here’s a more complex example: If a specific symbol is added, the module will add an additional header:
use = ["my_routine"];
custom {
my_routine = <<EOD
return function(task, common_meta)
-- parameters are task and metadata from previous functions
if task:has_symbol('SYMBOL') then
return nil, -- no error
{['X-Foo'] = 'Bar'}, -- set extra header
{['X-Foo'] = 0}, -- remove foreign X-Foo headers
{} -- metadata to return to other functions
end
return nil, -- no error
{}, -- need to fill the parameter
{['X-Foo'] = 0}, -- remove foreign X-Foo headers
{} -- metadata to return to other functions
end
EOD;
}
To avoid exposing scan results in outbound email, the extended Rspamd headers routines (x-spamd-result
, x-rspamd-server
and x-rspamd-queue-id
) only add headers if the message is NOT originated from authenticated users or our_networks
.
If desired, the extended_headers_rcpt
option can be used to include the extended Rspamd headers in messages sent to specific recipients or domains, such as a list of domains the mail server is responsible for.
Delivery status notification (DSN) reports for successful email deliveries can include the original message headers, including Rspamd headers. The only way to prevent this is to stop offering DSN to foreign servers.
Additionally, disabling DSN can prevent the generation of backscatter.
The following configuration example restricts DSN requests to local subnets and authenticated users only. Note that the smtpd_discard_ehlo_keyword_address_maps
setting is applied to the smtp
service only, and not to smtps
or submission
.
Make sure to modify the example below to match your subnet(s) accordingly.
esmtp_access:
# Allow DSN requests from local subnets only
192.168.0.0/16 silent-discard
10.10.0.0/16 silent-discard
0.0.0.0/0 silent-discard, dsn
::/0 silent-discard, dsn
master.cf:
# ==========================================================================
# service type private unpriv chroot wakeup maxproc command + args
# (yes) (yes) (no) (never) (100)
# ==========================================================================
smtp inet n - n - 1 postscreen
-o smtpd_discard_ehlo_keyword_address_maps=cidr:$config_directory/esmtp_access
or globally main.cf:
smtpd_discard_ehlo_keyword_address_maps =
cidr:$config_directory/esmtp_access
DSN can also be disabled for everyone with a shorter configuration change: main.cf:
# $config_directory/main.cf:
smtpd_discard_ehlo_keywords = silent-discard, dsn