annotate mod_audit/mod_audit.lua @ 5298:12f7d8b901e0

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.
author Matthew Wild <mwild1@gmail.com>
date Sat, 01 Apr 2023 13:11:53 +0100
parents f3123cbbd894
children e3a3a6c86a9f
Ignore whitespace changes - Everywhere: Within whitespace: At end of lines:
rev   line source
4933
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
5115
4a5837591380 mod_audit: remove event hook
Jonas Schäfer <jonas@wielicki.name>
parents: 4935
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: 4935
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: 4935
diff changeset
5
5251
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5250
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: 5250
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: 5250
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: 5250
diff changeset
9
5298
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5251
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: 5251
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: 5251
diff changeset
12
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5251
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: 5251
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: 5251
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: 5251
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: 5251
diff changeset
17 end
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5251
diff changeset
18
4933
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
19 local time_now = os.time;
5251
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5250
diff changeset
20 local ip = require "util.ip";
4933
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
21 local st = require "util.stanza";
4935
ae83200fb55f mod_audit: make the extension of the module API less of a hack
Jonas Schäfer <jonas@wielicki.name>
parents: 4934
diff changeset
22 local moduleapi = require "core.moduleapi";
4933
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
4934
08dea42a302a mod_audit*: fix luacheck warnings
Jonas Schäfer <jonas@wielicki.name>
parents: 4933
diff changeset
33 store = module:context(host):open_store("audit", "archive");
4933
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
5251
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5250
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: 5250
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: 5250
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: 5250
diff changeset
43 local network;
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5250
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: 5250
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: 5250
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: 5250
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: 5250
diff changeset
48 end
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5250
diff changeset
49 return network;
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5250
diff changeset
50 end
4933
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);
5251
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5250
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: 5250
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: 5250
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: 5250
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: 5250
diff changeset
67 end
f3123cbbd894 mod_audit: Allow disabling IP logging, or limiting it to a prefix
Matthew Wild <mwild1@gmail.com>
parents: 5250
diff changeset
68 stanza:text_tag("remote-ip", network or remote_ip);
4933
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
69 end
5298
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5251
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: 5251
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: 5251
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: 5251
diff changeset
73 stanza:tag("location", {
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5251
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: 5251
diff changeset
75 }):up();
12f7d8b901e0 mod_audit: Support for adding location (GeoIP) to audit events
Matthew Wild <mwild1@gmail.com>
parents: 5251
diff changeset
76 end
5250
d9577083c5f5 mod_audit: Include client id in audit log entries (if known)
Matthew Wild <mwild1@gmail.com>
parents: 5115
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: 5115
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: 5115
diff changeset
79 end
4933
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
4934
08dea42a302a mod_audit*: fix luacheck warnings
Jonas Schäfer <jonas@wielicki.name>
parents: 4933
diff changeset
87 local user_key = user or host_wide_user;
4933
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 };
4934
08dea42a302a mod_audit*: fix luacheck warnings
Jonas Schäfer <jonas@wielicki.name>
parents: 4933
diff changeset
93 if user_key ~= host_wide_user then
08dea42a302a mod_audit*: fix luacheck warnings
Jonas Schäfer <jonas@wielicki.name>
parents: 4933
diff changeset
94 attr.user = user_key;
4933
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
4934
08dea42a302a mod_audit*: fix luacheck warnings
Jonas Schäfer <jonas@wielicki.name>
parents: 4933
diff changeset
114 local id, err = stores[host]:append(nil, nil, stanza, time_now(), user_key);
4933
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
4935
ae83200fb55f mod_audit: make the extension of the module API less of a hack
Jonas Schäfer <jonas@wielicki.name>
parents: 4934
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: 4934
diff changeset
124 audit(module.host, user, "mod_" .. module:get_name(), event_type, extra);
4933
530d116b7f68 mod_audit*: modules for audit logging in prosody
Jonas Schäfer <jonas@wielicki.name>
parents:
diff changeset
125 end