snoop: redact connect-notice IP + geo/ASN to non-netadmin opers

Per-viewer rendering via new snotice_c_gated: only opers whose type is in
snoop_sensitive_opertype (default netadmin) see the raw IP and geo/ASN;
lower opers see a 🔒 restricted redaction. Logs keep the full line.
This commit is contained in:
Jean Chevronnet 2026-08-29 00:32:11 +00:00
parent 0af2ab7d21
commit d73f68f278
No known key found for this signature in database
GPG key ID: 439666D63A9477E4
4 changed files with 97 additions and 22 deletions

View file

@ -1130,6 +1130,38 @@ impl Server {
crate::modules::log_json::tee(self, msg);
}
/// Like [`Self::snotice_c`], but redacted per viewer: each `+c` oper for whom
/// `allow(&user)` is false receives `redacted` instead of `full`. The server log and
/// the chan/syslog/json tees always record the full line, so nothing is lost for audit.
pub fn snotice_c_gated(
&self,
cat: char,
full: &str,
redacted: &str,
allow: impl Fn(&crate::users::User) -> bool,
) {
self.log_push(full);
let recips: Vec<(Uid, bool)> = self
.users
.iter()
.filter(|(_, u)| u.flags.oper && u.flags.snomask_cats.contains(cat))
.map(|(&uid, u)| (uid, allow(u)))
.collect();
let uids: Vec<Uid> = recips.iter().map(|(u, _)| *u).collect();
let jfull = self.json_log_value(full, &uids);
let jred = self.json_log_value(redacted, &uids);
for (o, revealed) in recips {
if revealed {
self.deliver_server_notice(o, full, &jfull);
} else {
self.deliver_server_notice(o, redacted, &jred);
}
}
crate::modules::chanlog::tee(self, cat, full);
crate::modules::syslog::tee(self, full);
crate::modules::log_json::tee(self, full);
}
/// Broadcast a `*** msg` server NOTICE to *every* registered local user — for
/// server-wide announcements everyone should see (e.g. a config reload). Goes
/// through the same tagged path as `snotice`, so cap-holders get the server-time