Software /
code /
prosody-modules
Comparison
mod_audit/mod_audit.lua @ 5856:75dee6127829 draft
Merge upstream
author | Trần H. Trung <xmpp:trần.h.trung@trung.fun> |
---|---|
date | Tue, 06 Feb 2024 18:32:01 +0700 |
parent | 5786:6c0570a8b866 |
comparison
equal
deleted
inserted
replaced
5664:52db2da66680 | 5856:75dee6127829 |
---|---|
1 module:set_global(); | 1 module:set_global(); |
2 | 2 |
3 local time_now = os.time; | 3 local time_now = os.time; |
4 local parse_duration = require "util.human.io".parse_duration; | |
5 local ip = require "util.ip"; | 4 local ip = require "util.ip"; |
6 local st = require "util.stanza"; | 5 local st = require "util.stanza"; |
7 local moduleapi = require "core.moduleapi"; | 6 local moduleapi = require "core.moduleapi"; |
8 | 7 |
9 local host_wide_user = "@"; | 8 local host_wide_user = "@"; |
10 | 9 |
11 local cleanup_after = module:get_option_string("audit_log_expires_after", "28d"); | 10 local cleanup_after = module:get_option_period("audit_log_expires_after", "28d"); |
12 if cleanup_after == "never" then | |
13 cleanup_after = nil; | |
14 else | |
15 cleanup_after = parse_duration(cleanup_after); | |
16 end | |
17 | 11 |
18 local attach_ips = module:get_option_boolean("audit_log_ips", true); | 12 local attach_ips = module:get_option_boolean("audit_log_ips", true); |
19 local attach_ipv4_prefix = module:get_option_number("audit_log_ipv4_prefix", nil); | 13 local attach_ipv4_prefix = module:get_option_number("audit_log_ipv4_prefix", nil); |
20 local attach_ipv6_prefix = module:get_option_number("audit_log_ipv6_prefix", nil); | 14 local attach_ipv6_prefix = module:get_option_number("audit_log_ipv6_prefix", nil); |
21 | 15 |
59 module:context(host):log("debug", "Pruned expired audit log entries"); | 53 module:context(host):log("debug", "Pruned expired audit log entries"); |
60 return true; | 54 return true; |
61 end | 55 end |
62 | 56 |
63 local function get_ip_network(ip_addr) | 57 local function get_ip_network(ip_addr) |
64 local _ip = ip.new_ip(ip_addr); | 58 local proto = ip_addr.proto; |
65 local proto = _ip.proto; | |
66 local network; | 59 local network; |
67 if proto == "IPv4" and attach_ipv4_prefix then | 60 if proto == "IPv4" and attach_ipv4_prefix then |
68 network = ip.truncate(_ip, attach_ipv4_prefix).normal.."/"..attach_ipv4_prefix; | 61 network = ip.truncate(ip_addr, attach_ipv4_prefix).normal.."/"..attach_ipv4_prefix; |
69 elseif proto == "IPv6" and attach_ipv6_prefix then | 62 elseif proto == "IPv6" and attach_ipv6_prefix then |
70 network = ip.truncate(_ip, attach_ipv6_prefix).normal.."/"..attach_ipv6_prefix; | 63 network = ip.truncate(ip_addr, attach_ipv6_prefix).normal.."/"..attach_ipv6_prefix; |
71 end | 64 end |
72 return network; | 65 return network; |
73 end | 66 end |
74 | 67 |
75 local function session_extra(session) | 68 local function session_extra(session) |
81 end | 74 end |
82 if session.type then | 75 if session.type then |
83 attr.type = session.type; | 76 attr.type = session.type; |
84 end | 77 end |
85 local stanza = st.stanza("session", attr); | 78 local stanza = st.stanza("session", attr); |
86 if attach_ips and session.ip then | 79 local remote_ip = session.ip and ip.new_ip(session.ip); |
87 local remote_ip, network = session.ip; | 80 if attach_ips and remote_ip then |
81 local network; | |
88 if attach_ipv4_prefix or attach_ipv6_prefix then | 82 if attach_ipv4_prefix or attach_ipv6_prefix then |
89 network = get_ip_network(remote_ip); | 83 network = get_ip_network(remote_ip); |
90 end | 84 end |
91 stanza:text_tag("remote-ip", network or remote_ip); | 85 stanza:text_tag("remote-ip", network or remote_ip.normal); |
92 end | 86 end |
93 if attach_location and session.ip then | 87 if attach_location and remote_ip then |
94 local remote_ip = ip.new(session.ip); | 88 local geoip_info = remote_ip.proto == "IPv6" and geoip6_country:query_by_addr6(remote_ip.normal) or geoip4_country:query_by_addr(remote_ip.normal); |
95 local geoip_country = ip.proto == "IPv6" and geoip6_country or geoip4_country; | 89 stanza:text_tag("location", geoip_info.name, { |
96 stanza:tag("location", { | 90 country = geoip_info.code; |
97 country = geoip_country:query_by_addr(remote_ip.normal); | 91 continent = geoip_info.continent; |
98 }):up(); | 92 }):up(); |
99 end | 93 end |
100 if session.client_id then | 94 if session.client_id then |
101 stanza:text_tag("client", session.client_id); | 95 stanza:text_tag("client", session.client_id); |
102 end | 96 end |
138 local id, err = store:append(nil, nil, stanza, extra and extra.timestamp or time_now(), user_key); | 132 local id, err = store:append(nil, nil, stanza, extra and extra.timestamp or time_now(), user_key); |
139 if not id then | 133 if not id then |
140 if err == "quota-limit" then | 134 if err == "quota-limit" then |
141 local limit = store.caps and store.caps.quota or 1000; | 135 local limit = store.caps and store.caps.quota or 1000; |
142 local truncate_to = math.floor(limit * 0.99); | 136 local truncate_to = math.floor(limit * 0.99); |
143 if type(cleanup_after) == "number" then | 137 if cleanup_after ~= math.huge then |
144 module:log("debug", "Audit log has reached quota - forcing prune"); | 138 module:log("debug", "Audit log has reached quota - forcing prune"); |
145 if prune_audit_log(host) then | 139 if prune_audit_log(host) then |
146 -- Retry append | 140 -- Retry append |
147 id, err = store:append(nil, nil, stanza, extra and extra.timestamp or time_now(), user_key); | 141 id, err = store:append(nil, nil, stanza, extra and extra.timestamp or time_now(), user_key); |
148 end | 142 end |
175 local jid = require "util.jid"; | 169 local jid = require "util.jid"; |
176 local arg = require "util.argparse".parse(arg_, { | 170 local arg = require "util.argparse".parse(arg_, { |
177 value_params = { "limit" }; | 171 value_params = { "limit" }; |
178 }); | 172 }); |
179 | 173 |
180 for k, v in pairs(arg) do print("U", k, v) end | 174 module:log("debug", "arg = %q", arg); |
181 local query_user, host = jid.prepped_split(arg[1]); | 175 local query_jid = jid.prep(arg[1]); |
176 local host = jid.host(query_jid); | |
182 | 177 |
183 if arg.prune then | 178 if arg.prune then |
184 local sm = require "core.storagemanager"; | 179 local sm = require "core.storagemanager"; |
185 if host then | 180 if host then |
186 sm.initialize_host(host); | 181 sm.initialize_host(host); |
205 require "core.storagemanager".initialize_host(host); | 200 require "core.storagemanager".initialize_host(host); |
206 local store = stores[host]; | 201 local store = stores[host]; |
207 local c = 0; | 202 local c = 0; |
208 | 203 |
209 if arg.global then | 204 if arg.global then |
210 if query_user then | 205 if jid.node(query_jid) then |
211 print("WW: Specifying a user account is incompatible with --global. Showing only global events."); | 206 print("WW: Specifying a user account is incompatible with --global. Showing only global events."); |
212 end | 207 end |
213 query_user = "@"; | 208 query_jid = "@"; |
209 elseif host == query_jid then | |
210 query_jid = nil; | |
214 end | 211 end |
215 | 212 |
216 local results, err = store:find(nil, { | 213 local results, err = store:find(nil, { |
217 with = query_user; | 214 with = query_jid; |
218 limit = arg.limit and tonumber(arg.limit) or nil; | 215 limit = arg.limit and tonumber(arg.limit) or nil; |
219 reverse = true; | 216 reverse = true; |
220 }) | 217 }) |
221 if not results then | 218 if not results then |
222 print("EE: Failed to query audit log: "..tostring(err)); | 219 print("EE: Failed to query audit log: "..tostring(err)); |
223 return 1; | 220 return 1; |
224 end | 221 end |
225 | 222 |
226 local colspec = { | 223 local colspec = { |
227 { title = "Date", key = "when", width = 19, mapper = function (when) return os.date("%Y-%m-%d %R:%S", when); end }; | 224 { title = "Date", key = "when", width = 19, mapper = function (when) return os.date("%Y-%m-%d %R:%S", math.floor(when)); end }; |
228 { title = "Source", key = "source", width = "2p" }; | 225 { title = "Source", key = "source", width = "2p" }; |
229 { title = "Event", key = "event_type", width = "2p" }; | 226 { title = "Event", key = "event_type", width = "2p" }; |
230 }; | 227 }; |
231 | 228 |
232 if arg.show_user ~= false and (not arg.global and not query_user) or arg.show_user then | 229 if arg.show_user ~= false and (not arg.global and not query_jid) or arg.show_user then |
233 table.insert(colspec, { | 230 table.insert(colspec, { |
234 title = "User", key = "username", width = "2p", | 231 title = "User", key = "username", width = "2p", |
235 mapper = function (user) | 232 mapper = function (user) |
236 if user == "@" then return ""; end | 233 if user == "@" then return ""; end |
237 if user:sub(-#host-1, -1) == ("@"..host) then | 234 if user:sub(-#host-1, -1) == ("@"..host) then |
268 print(row({ | 265 print(row({ |
269 when = when; | 266 when = when; |
270 source = entry.attr.source; | 267 source = entry.attr.source; |
271 event_type = entry.attr.type:gsub("%-", " "); | 268 event_type = entry.attr.type:gsub("%-", " "); |
272 username = user; | 269 username = user; |
273 ip = entry:get_child_text("remote-ip"); | 270 ip = entry:find("{xmpp:prosody.im/audit}session/remote-ip#"); |
274 location = entry:find("location@country"); | 271 country = entry:find("{xmpp:prosody.im/audit}session/location@country"); |
275 note = entry:get_child_text("note"); | 272 note = entry:get_child_text("note"); |
276 })); | 273 })); |
277 end | 274 end |
278 end | 275 end |
279 print(string.rep("-", width)); | 276 print(string.rep("-", width)); |