mod_audit_auth: Allow suppressing repeated failure/success log entries from the same IP for a time
authorMatthew Wild <mwild1@gmail.com>
Mon, 13 May 2024 18:30:18 +0100
changeset 5910 cc30c4b5f006
parent 5909 02657e8693bc
child 5911 d194d1012fd3
mod_audit_auth: Allow suppressing repeated failure/success log entries from the same IP for a time This can be triggered by e.g. a distributed brute force attack, or from Monal.
mod_audit_auth/mod_audit_auth.lua
--- a/mod_audit_auth/mod_audit_auth.lua	Sun May 12 17:01:20 2024 +0200
+++ b/mod_audit_auth/mod_audit_auth.lua	Mon May 13 18:30:18 2024 +0100
@@ -1,25 +1,55 @@
-local jid = require"util.jid";
+local cache = require "util.cache";
+local jid = require "util.jid";
 local st = require "util.stanza";
 
 module:depends("audit");
 -- luacheck: read globals module.audit
 
 local only_passwords = module:get_option_boolean("audit_auth_passwords_only", true);
+local cache_size = module:get_option_number("audit_auth_cache_size", 128);
+local repeat_failure_timeout = module:get_option_number("audit_auth_repeat_failure_timeout");
+local repeat_success_timeout = module:get_option_number("audit_auth_repeat_success_timeout");
 
+local failure_cache = cache.new(cache_size);
 module:hook("authentication-failure", function(event)
 	local session = event.session;
-	module:audit(jid.join(session.sasl_handler.username, module.host), "authentication-failure", {
-		session = session,
+
+	local username = session.sasl_handler.username;
+	if repeat_failure_timeout then
+		local cache_key = ("%s\0%s"):format(username, session.ip);
+		local last_failure = failure_cache:get(cache_key);
+		local now = os.time();
+		if last_failure and (now - last_failure) > repeat_failure_timeout then
+			return;
+		end
+		failure_cache:set(cache_key, now);
+	end
+
+	module:audit(jid.join(username, module.host), "authentication-failure", {
+		session = session;
 	});
 end)
 
+local success_cache = cache.new(cache_size);
 module:hook("authentication-success", function(event)
 	local session = event.session;
 	if only_passwords and session.sasl_handler.fast then
 		return;
 	end
-	module:audit(jid.join(session.sasl_handler.username, module.host), "authentication-success", {
-		session = session,
+
+	local username = session.sasl_handler.username;
+	if repeat_success_timeout then
+		local cache_key = ("%s\0%s"):format(username, session.ip);
+		local last_success = success_cache:get(cache_key);
+		local now = os.time();
+		if last_success and (now - last_success) > repeat_success_timeout then
+			return;
+		end
+		success_cache:set(cache_key, now);
+	end
+
+	module:audit(jid.join(username, module.host), "authentication-success", {
+		session = session;
 	});
 end)