[Rspamd-Users] Could someone please review my first steps?

lutz.niederer at gmx.net lutz.niederer at gmx.net
Sat Aug 20 14:34:52 UTC 2022


I would really appreciate and be very thankful if someone could review my first small customizations.

We will have 2 mail servers, one in the dmz and one behind (with submission, imap etc.)  rspamd will run in the dmz and the postfix there is working as a relay into our net that is in a subnet of  In the dmz the mail is received from the world on port 25, goes through some postfix checks (like postscreen, recipients etc) and then through rspamd milter.  After that mail leaves the relay into our net into another postfix.  On the final server we don't have rspamd running.
Sending mail into the world goes the other way around, but the smtp server in the dmz listens on a specific port where we expect rspamd to add our dkim etc stuff, leave off icap, antivirus, spf and rbl checks and then let postfix send it out into the world.

To just do the things that are needed on the way into the world I have set the following (we use one key for all domains):

enabled = true;
try_fallback = false;
allow_hdrfrom_mismatch_sign_networks = true;
allow_hdrfrom_mismatch_local = true;
sign_networks = [ "" ];
key_table = "/etc/rspamd/mydata/dkim_key_table";
signing_table = "/etc/rspamd/mydata/dkim_signing_table";

my_network {
  id = "my_network";
  priority = high;
  ip = ["", ""];
  authenticated = yes;
  apply {
    groups_disabled = ["rbl", "spf", "icap", "antivirus"];
    actions {
      reject = 10000.0;
  symbols [ "MY_NETWORK", "DIR_OUTBOUND" ]

Is this a way it should work?

When getting mail from the internet we use two virus scanners (via icap & clamav), rbls (spamhaus, abusix), dcc, razor & pyzor.
If mail gets over the spam threshold of 15 we decided to reject the mail.  First question:  Is that a really good idea?
Mail that has a virus is not let in and gets refused (if both scanners say yes).
Because many things are done on our final server inside we need to pass some information from the relay to inside (via headers).
First about virus scanning:
- if both virus scanners detect a mail with virus the mail gets rejected at the dmz.
- if only one detects a virus the mail gets a specific header and another header that notes the scanning engine.
- if one scanner fails then the mail is handled as if one said "virus". (see before)
- if both scanners fail, the mail will be soft rejected.
In case one or more scanners fails there should be a notification sent out to admin via mail as this should not happen and be fixed asap.  I have no clue how.
The mail coming from outside should always have a header with the final score*10 added.  The other way around not, there should be nothing.
And mail should always have a header where we can find the direction (incoming or outgoing).

One question regarding authenticated users / milter auth_authen / sasl.  On a relay we don't have any user connecting to the smtp server when sending mail internally.  What does postfix send as auth_authen to rspamd instead?  Is rspamd's authenticated users of any use in that case?

Could someone please be so kind and tell me how far away I am with the settings below?  I have marked some things I don't know with "???".  Is there even a small chance that this would work or am I completely wrong?
I did not yet try this.  I don't even know if this is syntactically correct.  Please don't laugh at me if this is bullsh*t.  This is a migration where the old system is still running.  If everything "should" work, we will start with one domain.  (Everything will be set up new.  dns, mail, imap,...)

Thank you very much!

in antivirus.conf "action" (clamav) is unset
in external_services.conf "action" (icap) is unset

  expression = "ICAP_VIRUS and CLAM_VIRUS";
  message = "Rejected due to virus infection."
  action "reject";
???  I would need an admin mail here, too.  Should I go the way over milter_headers?

  expression = "ICAP_FAIL and CLAM_FAIL";
  message = "Temporarily rejected due to internal vscan failure."
  action "soft reject";

  expression = "((ICAP_VIRUS or CLAM_VIRUS) and not (ICAP_VIRUS and CLAM_VIRUS)) or ICAP_FAIL or CLAM_FAIL";
  score = 5.0;

# not really needed as we do IS_VIRUS and reject
#routines {
#  x-virus {
#    header = "X-Virus";
#	remove = 0;
#	symbols = ["CLAM_VIRUS", "ICAP_VIRUS"];
#  }

custom {
  my_routine = <<EODATA
    return function(task, common_meta)

    local settings_id = task:get_settings_id()
    if settings_id then
      local lua_settings = require "lua_settings"
      settings_id = lua_settings.settings_by_id(settings_id)
      if settings_id then
        settings_id = settings_id.name or 'dir_inbound'
	  settings_id = '';

    if settings_id == "dir_outbound" then
	  task:modify_header('X-Direction', remove = { 0 }, add = { 'outbound', 0 });
	  local result = task:get_metric_result()
	  if result then
	    local score = result.score
	    score = score * 10
	    task:modify_header('X-Final-Score', remove = { 0 }, add = { score, 0 });
	  task:modify_header('X-Direction', remove = { 0 }, add = { 'inbound', 0 });

    if (task:has_symbol('ICAP_FAIL')) then
		task:modify_header('X-Virus-Failed', remove = { 0 }, add = { 'ICAP', 0 });
???	    send mail to admin (text:"ICAP_FAIL blah")

    if (task:has_symbol('CLAM_FAIL')) then
		task:modify_header('X-Virus-Failed', remove = { 0 }, add = { 'CLAM', 0 });
???		send mail to admin (text:"CLAM_FAIL blah")

??? add header with full and final score multiplied by 10

    if (task:has_symbol('MAYBE_VIRUS') and task:has_symbol('ICAP_VIRUS')) then
      return nil,
      {['X-Virus'] = 'MAYBE'},
      {['X-Virus'] = 0},
      {['X-Virus-Engine'] = 'ICAP'},
      {['X-Virus-Engine'] = 0},

    if (task:has_symbol('MAYBE_VIRUS') and task:has_symbol('CLAM_VIRUS')) then
      return nil,
      {['X-Virus'] = 'MAYBE'},
      {['X-Virus'] = 0},
      {['X-Virus-Engine'] = 'CLAM'},
      {['X-Virus-Engine'] = 0},

    return nil,
    {['X-Virus'] = 0},
    {['X-Virus-Engine'] = 0},
    {['X-Virus-Failed'] = 0},


More information about the Users mailing list