maillog-summary.bsh script
So I needed/var
file on plesk, so this is my bash script, running on ubuntu via cron
bash
#!/ bin/ bash
logfile="/ var/ log/ maillog"
# Extract date range
earliest=$(awk 'NR==1 {print $1, $2, $3}' "$logfile")
latest=$(awk 'END {print $1, $2, $3}' "$logfile")
echo "# maillog dates from $earliest - $latest"
echo
echo "# Time Period Summary of Failed Attempts #"
echo
# Process logs for Plesk's smtpd
and extract unique IPs and login IDs with counts
awk '
function parse_timestamp(month, day, time) {
split(time, t, ":");
months["Jan"]=1; months["Feb"]=2; months["Mar"]=3; months["Apr"]=4;
months["May"]=5; months["Jun"]=6; months["Jul"]=7; months["Aug"]=8;
months["Sep"]=9; months["Oct"]=10; months["Nov"]=11; months["Dec"]=12;
return mktime("2025 " months[month] " " day " " t[1] " " t[2] " 0");
}
function format_interval(timestamp, minutes) {
Default to 10 minutes if no minutes argument is provided
if (minutes == "") {
minutes = 10;
}
split(strftime("%Y-%m-%d %H:%M", timestamp), time_parts, ":");
min_block = int(time_parts[2] / minutes) * minutes;
return time_parts[1] ":" (min_block < 10 ? "0" min_block : min_block);
}
function sort_and_print(arr, label, min) {
n = asorti(arr, sorted_arr, "@ val_num_desc");
if (n > 0) {
seen=0;
for (i = 1; i <= n; i++) {
if (arr[sorted_arr[i]] >= min) {
if ( seen==0 ) print " " label ":";
seen=1;
print " " arr[sorted_arr[i]] " attempts : " sorted_arr[i];
}
}
print "";
return 1;
}
return 0;
}
function reset_block() {
ips=0;
logins=0;
# ip_attempts[ip]=count
# login_attempts[login]=count
for (i in ip_attempts) {
if (ip_attempts[i] >= min_count) ips++;
}
for (i in login_attempts) {
if (login_attempts[i] >= min_count) logins++;
}
if(current_interval != "" && (ips >= 1 || logins >= 1)) {
print "";
print "";
print "Summary for " current_interval;
print "";
ip_reported = sort_and_print(ip_attempts, "Unique suspect IP Addresses ", min_count_ip);
login_reported = sort_and_print(login_attempts, "Unique failed Login IDs ", min_count_login);
ip_reported = login_reported = 0;
seen_matches = 0;
if (length(seen_entries) > 0) {
for (i in seen_entries) {
# Loop through ip_attempts to check for the existence of seen_entries[i]
found_in_ip = 0;
for (ip in ip_attempts) {
if (ip_attempts[ip] >= min_count_ip && index(seen_entries[i], ip) > 0) {
found_in_ip = 1;
break; # Exit the loop if a match is found
}
}
# Loop through login_attempts to check for the existence of seen_entries[i]
found_in_login = 0;
for (login in login_attempts) {
if (login_attempts[login]>=min_count_login && index(seen_entries[i], login) > 0) {
found_in_login = 1;
break; # Exit the loop if a match is found
}
}
# If found in either ip_attempts or login_attempts
if (found_in_ip || found_in_login) {
if (seen_matches == 0) {
print " Referenced Entries:";
}
print " > " seen_entries[i];
seen_matches = 1;
}
}
print "";
}
}
delete ip_attempts;
delete login_attempts;
delete seen_entries;
for (i in seen_entries) {
delete seen_entries[i];
}
}
BEGIN {
current_interval = "";
min_count_ip = 2;
min_count_login = 1;
gap_minutes = 30;
delete ip_attempts;
delete login_attempts;
delete seen_entries;
}
{
timestamp = parse_timestamp($1, $2, $3);
timestamp_str = $1 " " $2 " " $3;
interval = format_interval(timestamp, gap_minutes);
if ( current_interval == "" ) current_interval = interval;
if (interval != current_interval) {
reset_block();
current_interval = interval;
start_datestr = interval ":00";
}
# Extract IPs from "warning: unknown"
if ($0 ~ / warning: unknown[([0-9]+.[0-9]+.[0-9]+.[0-9]+)]/ ) {
match($0, / warning: unknown[([0-9]+.[0-9]+.[0-9]+.[0-9]+)]/ , ip_match);
if (ip_match[1] != "") ip_attempts[ip_match[1]]++;
if (ip_match[1] != "") seen_entries[NR] = $0;
}
# Extract login IDs from "No such user"
if ($0 ~ / failed mail authentication attempt for user/ ) {
match($0, / authentication attempt for user x27([^x27]+)x27/ , login_match);
if (login_match[1] != "") login_attempts[login_match[1]]++;
if (login_match[1] != "") seen_entries[NR] = $0;
}
}
END {
reset_block();
print "";
print "End of log summary.";
}' "$logfile"
The output for it will be a summary of the number of failed logins or IPs that have occured during a given period within the log.
stdout
# maillog dates from Jan 25 10:41:52 - Jan 25 16:49:52
# Time Period Summary of Failed Attempts #
Summary for 2025-01-25 10:30
Unique suspect IP Addresses :
2 attempts : 87.120.121.102
2 attempts : 45.151.99.245
2 attempts : 194.0.234.135
2 attempts : 193.32.162.89
Unique failed Login IDs :
2 attempts : test@ abc.com
1 attempts : test3@ zzz.com
1 attempts : test3@ fff.com
1 attempts : soporte@ xyz.com
Referenced Entries:0 1
> Jan 25 10:42:19 xxx plesk_saslauthd[156912]: failed mail authentication attempt for user 'test@ xyz.com' (password len=10)
> Jan 25 10:42:19 xxx postfix/ smtpd[143596]: warning: unknown[87.120.121.102]: SASL LOGIN authentication failed: authentication failure
> Jan 25 10:43:22 xxx plesk_saslauthd[170949]: failed mail authentication attempt for user 'iva' (password len=4)
> Jan 25 10:43:37 xxx plesk_saslauthd[170949]: failed mail authentication attempt for user 'nataliag' (password len=9)
> Jan 25 10:44:23 xxx plesk_saslauthd[184481]: failed mail authentication attempt for user 'news@ xyz.com' (password len=13)
> Jan 25 10:44:23 xxx postfix/ smtpd[143596]: warning: unknown[45.151.99.245]: SASL LOGIN authentication failed: authentication failure
> Jan 25 10:44:32 xxx plesk_saslauthd[184481]: failed mail authentication attempt for user 'mail@ xyz.com' (password len=13)
> Jan 25 10:44:32 xxx postfix/ smtpd[143596]: warning: unknown[194.0.234.135]: SASL LOGIN authentication failed: authentication failure
> Jan 25 10:50:05 xxx plesk_saslauthd[262997]: failed mail authentication attempt for user 'rachel' (password len=7)
Summary for 2025-01-25 11:00
Unique suspect IP Addresses :
2 attempts : 193.32.162.23
Unique failed Login IDs :
1 attempts : user
And so on.
Configuration
Adjust the size of the time periods being chunked out of the logfile, then change gap_minutes
in the script.To change the number of repeats in the log for an IP or a login-name during the period, then change min_count_ip
andmin_count_login
Crontab
To put this into crontab to email yourself, use the command line:
echo -e "Subject:.....nFrom:noreply
(Requires the script to be saved as maillog-summary.bsh, and the noreply
email address to exist).
Environment
NOTE - this script will not protect you from login attempts - you should consider installing fail2ban
on plesk to provide that.