mod_audit/mod_audit.lua
author Matthew Wild <mwild1@gmail.com>
Sat, 01 Apr 2023 13:11:53 +0100
changeset 5302 12f7d8b901e0
parent 5255 f3123cbbd894
child 5303 e3a3a6c86a9f
permissions -rw-r--r--
mod_audit: Support for adding location (GeoIP) to audit events This can be more privacy-friendly than logging full IP addresses, and also more informative to a user - IP addresses don't mean much to the average person, however if they see activity from outside their expected country, they can immediately identify suspicious activity. As with IPs, this field is configurable for deployments that would like to disable it. Location is also not logged when the geoip library is not available.
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
4936
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
     1
module:set_global();
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
     2
5119
4a5837591380 mod_audit: remove event hook
Jonas Schäfer <jonas@wielicki.name>
parents: 4938
diff changeset
     3
local audit_log_limit = module:get_option_number("audit_log_limit", 10000);
4a5837591380 mod_audit: remove event hook
Jonas Schäfer <jonas@wielicki.name>
parents: 4938
diff changeset
     4
local cleanup_after = module:get_option_string("audit_log_expires_after", "2w");
4a5837591380 mod_audit: remove event hook
Jonas Schäfer <jonas@wielicki.name>
parents: 4938
diff changeset
     5
5255
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
     6
local attach_ips = module:get_option_boolean("audit_log_ips", true);
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
     7
local attach_ipv4_prefix = module:get_option_number("audit_log_ipv4_prefix", nil);
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
     8
local attach_ipv6_prefix = module:get_option_number("audit_log_ipv6_prefix", nil);
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
     9
5302
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5255
diff changeset
    10
local have_geoip, geoip = pcall(require, "geoip.country");
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5255
diff changeset
    11
local attach_location = have_geoip and module:get_option_boolean("audit_log_location", true);
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5255
diff changeset
    12
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5255
diff changeset
    13
local geoip4_country, geoip6_country;
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5255
diff changeset
    14
if have_geoip and attach_location then
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5255
diff changeset
    15
	geoip4_country = geoip.open(module:get_option_string("geoip_ipv4_country", "/usr/share/GeoIP/GeoIP.dat"));
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5255
diff changeset
    16
	geoip6_country = geoip.open(module:get_option_string("geoip_ipv6_country", "/usr/share/GeoIP/GeoIPv6.dat"));
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5255
diff changeset
    17
end
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5255
diff changeset
    18
4936
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    19
local time_now = os.time;
5255
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
    20
local ip = require "util.ip";
4936
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    21
local st = require "util.stanza";
4938
ae83200fb55f mod_audit: make the extension of the module API less of a hack
Jonas Schäfer <jonas@wielicki.name>
parents: 4937
diff changeset
    22
local moduleapi = require "core.moduleapi";
4936
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    23
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    24
local host_wide_user = "@";
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    25
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    26
local stores = {};
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    27
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    28
local function get_store(self, host)
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    29
	local store = rawget(self, host);
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    30
	if store then
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    31
		return store
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    32
	end
4937
08dea42a302a mod_audit*: fix luacheck warnings
Jonas Schäfer <jonas@wielicki.name>
parents: 4936
diff changeset
    33
	store = module:context(host):open_store("audit", "archive");
4936
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    34
	rawset(self, host, store);
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    35
	return store;
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    36
end
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    37
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    38
setmetatable(stores, { __index = get_store });
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    39
5255
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
    40
local function get_ip_network(ip_addr)
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
    41
	local _ip = ip.new_ip(ip_addr);
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
    42
	local proto = _ip.proto;
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
    43
	local network;
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
    44
	if proto == "IPv4" and attach_ipv4_prefix then
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
    45
		network = ip.truncate(_ip, attach_ipv4_prefix).normal.."/"..attach_ipv4_prefix;
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
    46
	elseif proto == "IPv6" and attach_ipv6_prefix then
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
    47
		network = ip.truncate(_ip, attach_ipv6_prefix).normal.."/"..attach_ipv6_prefix;
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
    48
	end
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
    49
	return network;
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
    50
end
4936
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    51
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    52
local function session_extra(session)
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    53
	local attr = {
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    54
		xmlns = "xmpp:prosody.im/audit",
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    55
	};
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    56
	if session.id then
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    57
		attr.id = session.id;
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    58
	end
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    59
	if session.type then
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    60
		attr.type = session.type;
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    61
	end
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    62
	local stanza = st.stanza("session", attr);
5255
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
    63
	if attach_ips and session.ip then
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
    64
		local remote_ip, network = session.ip;
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
    65
		if attach_ipv4_prefix or attach_ipv6_prefix then
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
    66
			network = get_ip_network(remote_ip);
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
    67
		end
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5254
diff changeset
    68
		stanza:text_tag("remote-ip", network or remote_ip);
4936
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    69
	end
5302
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5255
diff changeset
    70
	if attach_location and session.ip then
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5255
diff changeset
    71
		local remote_ip = ip.new(session.ip);
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5255
diff changeset
    72
		local geoip_country = ip.proto == "IPv6" and geoip6_country or geoip4_country;
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5255
diff changeset
    73
		stanza:tag("location", {
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5255
diff changeset
    74
			country = geoip_country:query_by_addr(remote_ip.normal);
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5255
diff changeset
    75
		}):up();
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5255
diff changeset
    76
	end
5254
d9577083c5f5 mod_audit: Include client id in audit log entries (if known)
Matthew Wild <mwild1@gmail.com>
parents: 5119
diff changeset
    77
	if session.client_id then
d9577083c5f5 mod_audit: Include client id in audit log entries (if known)
Matthew Wild <mwild1@gmail.com>
parents: 5119
diff changeset
    78
		stanza:text_tag("client", session.client_id);
d9577083c5f5 mod_audit: Include client id in audit log entries (if known)
Matthew Wild <mwild1@gmail.com>
parents: 5119
diff changeset
    79
	end
4936
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    80
	return stanza
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    81
end
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    82
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    83
local function audit(host, user, source, event_type, extra)
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    84
	if not host or host == "*" then
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    85
		error("cannot log audit events for global");
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    86
	end
4937
08dea42a302a mod_audit*: fix luacheck warnings
Jonas Schäfer <jonas@wielicki.name>
parents: 4936
diff changeset
    87
	local user_key = user or host_wide_user;
4936
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    88
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    89
	local attr = {
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    90
		["source"] = source,
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    91
		["type"] = event_type,
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    92
	};
4937
08dea42a302a mod_audit*: fix luacheck warnings
Jonas Schäfer <jonas@wielicki.name>
parents: 4936
diff changeset
    93
	if user_key ~= host_wide_user then
08dea42a302a mod_audit*: fix luacheck warnings
Jonas Schäfer <jonas@wielicki.name>
parents: 4936
diff changeset
    94
		attr.user = user_key;
4936
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    95
	end
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    96
	local stanza = st.stanza("audit-event", attr);
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    97
	if extra ~= nil then
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    98
		if extra.session then
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
    99
			local child = session_extra(extra.session);
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   100
			if child then
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   101
				stanza:add_child(child);
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   102
			end
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   103
		end
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   104
		if extra.custom then
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   105
			for _, child in extra.custom do
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   106
				if not st.is_stanza(child) then
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   107
					error("all extra.custom items must be stanzas")
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   108
				end
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   109
				stanza:add_child(child);
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   110
			end
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   111
		end
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   112
	end
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   113
4937
08dea42a302a mod_audit*: fix luacheck warnings
Jonas Schäfer <jonas@wielicki.name>
parents: 4936
diff changeset
   114
	local id, err = stores[host]:append(nil, nil, stanza, time_now(), user_key);
4936
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   115
	if err then
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   116
		module:log("error", "failed to persist audit event: %s", err);
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   117
		return
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   118
	else
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   119
		module:log("debug", "persisted audit event %s as %s", stanza:top_tag(), id);
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   120
	end
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   121
end
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   122
4938
ae83200fb55f mod_audit: make the extension of the module API less of a hack
Jonas Schäfer <jonas@wielicki.name>
parents: 4937
diff changeset
   123
function moduleapi.audit(module, user, event_type, extra)
ae83200fb55f mod_audit: make the extension of the module API less of a hack
Jonas Schäfer <jonas@wielicki.name>
parents: 4937
diff changeset
   124
	audit(module.host, user, "mod_" .. module:get_name(), event_type, extra);
4936
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
   125
end