Dovecot and Postfix
Dovecot and Postfix may log interesting things.
Those are quite old beasts, and they can be configured in a multitude of ways, so please check that your actual logs match. Some wording may differ, and some private IPs could be logged in place of the real client IPs. If so, adjust your configuration!
Invalid commands
Misbehaving clients are often malicious clients.
Dovecot logs lines like this:
imap-login: Disconnected: Too many invalid commands (no auth attempts): rip=44.44.44.44, lip=55.55.55.55
imap-login: Info: Login aborted: Too many invalid commands (no auth attempts in 0 secs) (no_auth_attempts): user=<>, rip=44.44.44.44, lip=55.55.55.55, secured, session=...
As a regex:
imap-login: .* Too many invalid commands .* rip=<ip>
Postfix logs misbehaving hosts like this:
postfix/smtpd[131]: improper command pipelining after CONNECT from unknown[44.44.44.44]: ...
postfix/smtpd[131]: warning: non-SMTP command from unknown[44.44.44.44]: ...
As a regex:
^postfix/\S+: improper command pipelining after \S+ from \S+\[<ip>\]: ^postfix/\S+: warning: non-SMTP command from \S+\[<ip>\]:
Authentication failures
Postfix logs authentication failure like this:
postfix/smtpd[1551]: warning: unknown[44.44.44.44]: SASL LOGIN authentication failed: authentication failure, sasl_username=...
postfix/smtpd[6429]: warning: unknown[44.44.44.44]: SASL PLAIN authentication failed: authentication failure, sasl_username=...
As a regex:
^postfix/\S+: warning: \S+\[<ip>\]: SASL \S+ authentication failed: authentication failure
⚠️ Postfix's logs are dangerous
You may see that postfix logs what has been sent by the untrusted, misbehaving client at the end of the log line.
So an attacker could use that to try mimicking postfix's logs and make you ban any IP.
That's why we anchor the regex to the beginning of the line with ^ and don't use .* patterns, that could expand the trusted part of the log line to the untrusted one.
If your logs are prepended with some timestamps or other information, please incorporate them at the beginning of the regex, do not remove the ^ and don't use loose patterns like .*.
You can use for example .{30,50}, if you know that the prefix before the interesting part of the line is 30 to 50 characters.
Please adjust accordingly and test that those regexes actually match your logs with reaction test-filter!
If you are in doubt, don't hesitate to reach out.
Example config
Depending on your setup, you may have separate logs for the 2 services, merged logs, just one of them...
{
streams: {
mail: {
// Classic mail.log on some Linux distributions
cmd: ['tail', '-n0', '-F', '/var/log/mail.log'],
// With services declared in one docker compose.yml file
// cmd: ['docker', 'compose', '-f', '/path/to/docker-compose.yml', 'logs', '-n0', '-f'],
filters: {
badMessage: {
regex: [
@'imap-login: .* Too many invalid commands .* rip=<ip>',
@'^postfix/\S+: improper command pipelining after \S+ from \S+\[<ip>\]:',
@'^postfix/\S+: warning: non-SMTP command from \S+\[<ip>\]:',
],
retry: 2,
retryperiod: '6h',
actions: banFor('48h'),
},
failedLogin: {
regex: [
@'^postfix/\S+: warning:\s+\S+\[<ip>\]: SASL \S+ authentication failed: authentication failure',
],
retry: 2,
retryperiod: '1h',
actions: banFor('48h'),
},
},
},
},
}
This example makes the assumption that a function named banFor is defined. See the ipset example.
It also makes the assumption that there is a pattern named ip.