Rspamd logging und policy banks

In einem voran gegangenen Artikel habe ich generell über Logging von Rspamd in Verbindung mit rsyslog geschrieben. In diesem Artikel zeige ich exemplarisch, wie man in den sogenannten „user settings“ Policies erstellen kann und eigene Symbole in die Log-Ausgabe integriert. Ferner verwende ich ein eigenes Log-Template in Rspamd, in dem ich Lua eingebaut habe.

Hier ein Beispiel meiner aktuellen rsyslog-Meldungen für Submission:

Nov 24 13:05:39 mx rspamd[13209]: <5f9508>; proxy; rspamd_task_write_log: qid: <3yjvz711jZzMnk8>, policy_bank: POLICY_SUBMISSION, message-id: <9B5974F9-C49F-4DC0-8FCF-7D4530B6A4AE@example.test>, ip: 2a05:bec0:28:1:134:255:226:248, smtp_from: <user@example.test>, mime_from: user@example.test, action: "no action" [-7.70/7.50] [BAYES_HAM(-3.00){100.00%;},MV_CASE(0.50){},MIME_GOOD(-0.20){multipart/signed;text/plain;},ARC_NA(0.00){},ASN(0.00){asn:197071, ipnet:2a05:bec0::/29, country:DE;},FROM_EQ_ENVFROM(0.00){},FROM_HAS_DN(0.00){},HAS_ATTACHMENT(0.00){},MID_RHS_MATCH_FROM(0.00){},PREVIOUSLY_DELIVERED(0.00){chrroessner@gmail.com;},RCPT_COUNT_ONE(0.00){1;},RCVD_COUNT_TWO(0.00){2;},RCVD_NO_TLS_LAST(0.00){},RCVD_VIA_SMTP_AUTH(0.00){},TO_DN_NONE(0.00){},TO_MATCH_ENVRCPT_ALL(0.00){}]), len: 6331, time: 96.005ms real, 12.620ms virtual, dns req: 0, smtp_rcpts: <***@****>, mime_rcpts: <****@****>

Im Log taucht eine Information zu policy_bank auf.

Ausgangspunkt für mich war, dass ich ein verlässliches Merkmal im Log benötige, um in rsyslog Meldungen basierend auf dieser Information unterschiedlich zu behandeln.

User Settings in Rspamd können prinzipiell auf zei verschiedene Arten realisiert werden. Entweder man definiert setting {} innerhalb der Datei rspamd.conf.local oder settings_redis. In meinem Beispiel nutze ich Variante 1 und nutze zusätzlich eine Datei-Map:

settings = "file://$LOCAL_CONFDIR/maps.d/settings.map";

Wie man erkennen kann, liegt eine Datei namens settings.map in einem Unterverzeichnis maps.d. Dieses muss der Administrator selbst erstellen. Der Pfad ist dabei grundsätzlich frei wählbar.

Ohne zu sehr auf die Einzelheiten dieser Datei eingehen zu wollen, zeige ich beispielhaft, wie die Idee von Policy-Banks funktioniert.

settings {
    whitelist {
        symbols = [ "POLICY_WHITELIST" ];
        priority = low;
        rcpt = "postmaster";
        rcpt = "abuse";
        whitelist = yes;
    }

    SUBMISSION {
        symbols = [ "POLICY_SUBMISSION" ];
        priority = high;
        request_header = {
            "MTA-Tag" = "^mail\.example\.test$";
        }
        apply "default" {
            groups_disabled = [
                "rbl",
                "surbl",
                "spf",
                "dkim",
                "dmarc",
            ]
            symbols_disabled = [
                "DCC_BULK"
            ]
        }
    }
}

Dieses Beispiel zeigt zwei Policy-Banks. Jede dieser Regeln setzt ein eigenes Symbol beginnend mit „POLICY_“. Diese Symbole wurden selbst definiert und werden nur durch diese User-Settings gesetzt.

Hier die Definition der Symbole in der Datei /etc/rspamd/rspamd.local.lua:

-- rspamd.local.lua

-- Dummy symbols for user settings
rspamd_config.POLICY_WHITELIST = {
        callback = function(task)
                return false
        end,
        group = 'policies'
}
rspamd_config.POLICY_SUBMISSION = {
        callback = function(task)
                return false
        end,
        group = 'policies'
}

Diese Regeln benötigen noch einen Score. Und hier zeigt sich auch schon ein Vorteil: Admins können auf diese Weise verschiedenen Policy-Banks vorab Punkte schenken.

In der local.d/metrics.conf befindet sich folgende Definition:

group policies {
    symbol "POLICY_WHITELIST" {
        score 0.0;
        description = "Postmaster and abuse whitelisted policy bank";
    }
    symbol "POLICY_SUBMISSION" {
        score = -5.0;
        description = "Submission policy bank";
    }
}

Im letzten Schritt zeige ich, wie ich das Logging nun angepasst habe. Dazu definiere ich ein eigenes Logging-Template in der Datei local.d/logging.inc:

log_format =<<EOD
$if_qid{qid: <$>,}
$lua{
return function(task)
	function string.starts(str, start)
		return string.sub(str, 1, string.len(start)) == start
	end

	local syminf = task:get_symbols_all()
	for _, sym in ipairs(syminf) do
		if string.starts(sym.name, "POLICY_")  then
			return 'policy_bank: ' .. sym.name .. ','
		end
	end
	return 'policy_bank: NA,'
end
}
message-id: <$mid>,$if_ip{ ip: $,}$if_user{ user: $,}
$if_smtp_from{smtp_from: <$>, }$if_mime_from{mime_from: $,}
action: "$action" [$scores] [$lua{
return function(task)
	function string.starts(str, start)
		return string.sub(str, 1, string.len(start)) == start
	end

	local l = {}
	local syminf = task:get_symbols_all()
	for _, sym in ipairs(syminf) do
		if not string.starts(sym.name, "POLICY_") then
			local txt
			if sym.options then
				local topt = table.concat(sym.options, ', ')
				txt = sym.name .. '(' .. string.format('%.2f', sym.score) .. ')' .. '[' .. topt .. ']'
			else
				txt = sym.name .. '(' .. string.format('%.2f', sym.score) .. ')'
			end

			table.insert(l, txt)
		end
	end

	return table.concat(l,',')
end
}]),
len: $len, time: $time_real real, $time_virtual virtual, dns req: $dns_req,
$if_smtp_rcpts{smtp_rcpts: <$>}$if_mime_rcpts{, mime_rcpts: <$>}
EOD

Viel Spaß

[ratings]