[Netfilter][Dovecot] Count failed master logins, log the client address of successful logins

This commit is contained in:
FreddleSpl0it committed 2026-11-02 14:55:42 +01:00
1 parent e96fb24a67
commit a4b60ae26f
4 files changed
+34 -13

No files matched your search

@@ -8,6 +8,7 @@ class Regexes:
# Anchored to the line start: text clients control in other lines (login names, SMTP commands) must not match.
# Failed IMAP, POP3, Sieve and SMTP logins are reported by mailcowauth.php (rule 3), not by Dovecot or Postfix.
# Only the master passdb is checked by Dovecot itself (rule 8); the last address is the one Dovecot added.
DEFAULTS = {
1: r'^mailcow UI: Invalid password for .+ by ([0-9a-f\.:]+)$',
2: r'^Rspamd UI: Invalid password by ([0-9a-f\.:]+)$',
@@ -16,6 +17,7 @@ class Regexes:
5: r'^NOQUEUE: reject: RCPT from \[([0-9a-f\.:]+)].+Protocol error.+',
6: r'^(?:\[\d+\]: )?SOGo\w* Login from \'(?:[0-9a-f\.:]+, )*([0-9a-f\.:]+)\' for user .+ might not have worked',
7: r'([0-9a-f\.:]+) \"GET \/SOGo\/.* HTTP.+\" 403 .+',
8: r'^auth: passwd-file\(.*,([0-9a-f\.:]+),master(?:,<[^<>]*>)?\): (?:Password mismatch|unknown user)',
}
# Former default rules (with any rule id) => rule id of their replacement, None: the rule is removed.
# Rules the admin changed are kept.
@@ -34,9 +36,9 @@ class Regexes:
r'-login: Aborted login.+ \(auth failed .+\): user=.+, rip=([0-9a-f\.:]+), lip.+': None,
r'-login: Aborted login \(tried to use disallowed .+\): user=.+, rip=([0-9a-f\.:]+), lip.+': None,
r'-login: Aborted login.+ \(tried to use disallowed .+\): user=.+, rip=([0-9a-f\.:]+), lip.+': None,
r'auth: static\([^,]+,([0-9a-f\.:]+),<[^>]+>\): Password mismatch \(SHA1 of given password: [a-f0-9]+\)': None,
r'auth: \w+\([^,]+,([0-9a-f\.:]+),<[^>]+>\): Password mismatch \(SHA1 of given password: [a-f0-9]+\)': None,
r'\w+\([^,]+,([0-9a-f\.:]+),<[^>]+>\): Password mismatch \(SHA1 of given password: [a-f0-9]+\)': None,
r'auth: static\([^,]+,([0-9a-f\.:]+),<[^>]+>\): Password mismatch \(SHA1 of given password: [a-f0-9]+\)': 8,
r'auth: \w+\([^,]+,([0-9a-f\.:]+),<[^>]+>\): Password mismatch \(SHA1 of given password: [a-f0-9]+\)': 8,
r'\w+\([^,]+,([0-9a-f\.:]+),<[^>]+>\): Password mismatch \(SHA1 of given password: [a-f0-9]+\)': 8,
r'\w+\([^,]+,([0-9a-f\.:]+),<[^>]+>\): unknown user \(SHA1 of given password: [a-f0-9]+\)': None,
}
# Lines of the web UI are not sanitized like syslog lines: a newline in a login name must not hide the address
+7 -7
View File
@@ -68,7 +68,7 @@ require_once 'functions.acl.inc.php';
$isSOGoRequest = $post['real_rip'] == getenv('IPV4_NETWORK') . '.248';
// real_rip is the address that connected to Dovecot (for proxied connections the proxy), client_ip the client
// real_rip is the address that connected to Dovecot (for proxied connections the proxy), client_ip the client (for logs)
$client_ip = filter_var($post['client_ip'] ?? '', FILTER_VALIDATE_IP) ? $post['client_ip'] : $post['real_rip'];
$result = false;
if ($isSOGoRequest) {
@@ -76,7 +76,7 @@ if ($isSOGoRequest) {
$sogo_sso_pass = file_get_contents("/etc/sogo-sso/sogo-sso.pass");
if ($sogo_sso_pass === $post['password']){
error_log('MAILCOWAUTH: SOGo SSO auth for user ' . $post['username']);
set_sasl_log($post['username'], $post['real_rip'], "SOGO");
set_sasl_log($post['username'], $client_ip, "SOGO");
$result = true;
}
}
@@ -95,8 +95,8 @@ if ($result === false){
'remote_addr' => $post['real_rip']
));
if ($result) {
error_log('MAILCOWAUTH: App auth for user ' . $post['username'] . " with service " . $service . " from IP " . $post['real_rip']);
set_sasl_log($post['username'], $post['real_rip'], $service);
error_log('MAILCOWAUTH: App auth for user ' . $post['username'] . " with service " . $service . " from IP " . $client_ip);
set_sasl_log($post['username'], $client_ip, $service);
}
}
if ($result === false){
@@ -107,15 +107,15 @@ if ($result === false){
$result = user_login($post['username'], $post['password'], array('is_internal' => true, 'service' => $post['service']));
} catch (\Mailcow\Auth\IdpUnavailable $e) {
// temporary failure for Dovecot, no failed login
error_log("MAILCOWAUTH: Identity provider unavailable for user " . $post['username'] . " with service " . $post['service'] . " from IP " . $post['real_rip'] . ": " . $e->getMessage());
error_log("MAILCOWAUTH: Identity provider unavailable for user " . $post['username'] . " with service " . $post['service'] . " from IP " . $client_ip . ": " . $e->getMessage());
http_response_code(503); // Service Unavailable
echo json_encode($return);
session_destroy();
exit;
}
if ($result) {
error_log('MAILCOWAUTH: User auth for user ' . $post['username'] . " with service " . $post['service'] . " from IP " . $post['real_rip']);
set_sasl_log($post['username'], $post['real_rip'], $post['service']);
error_log('MAILCOWAUTH: User auth for user ' . $post['username'] . " with service " . $post['service'] . " from IP " . $client_ip);
set_sasl_log($post['username'], $client_ip, $post['service']);
}
}
+1 -1
View File
@@ -291,7 +291,7 @@ $RSPAMD_MAPS = array(
// true = option requires a non-empty value
// false = pure flag, must NOT have a value
// string = PCRE (no delimiters) the whole non-empty value must match
// Anything not listed here is rejected when a job is saved (the runner passes stored options as they are).
// Anything not listed here is rejected when a job is saved; the runner passes stored options as they are, except the blocked ones below.
// Override in vars.local.inc.php per option, e.g. $IMAPSYNC_OPTIONS['someoption'] = true; or unset($IMAPSYNC_OPTIONS['debug']);
// The old format $IMAPSYNC_OPTIONS['whitelist'] (list of names) is no longer supported and ignored (logged).
// NEVER add (RCE): 'delete2foldersonly', 'delete2foldersbutnot', 'regexflag', 'regexmess', 'pipemess', 'regextrans2', 'maxlinelengthcmd', 'oauthrefreshcmd1', 'oauthrefreshcmd2'