Logwatch
- 🐧 Linux Command Line
Ansible Playbook
Core
Install Logwatch
dnf install -y logwatch
Use default configuration
cp -f /usr/share/logwatch/default.conf/logwatch.conf /etc/logwatch/conf/logwatch.conf
Configure email address for dalily report
/etc/logwatch/conf/logwatch.conf
MailTo = support@rectitude.dev
MailFrom = server@noreply.rectitude.dev
Range = yesterday
Detail = Med
Useful commands
# View report for yesterday
logwatch --range yesterday
# View report for today and send to email
logwatch --range yesterday --mailto support@rectitude.dev
# View report for today with medium detail and send to email
logwatch --range today --detail med --mailto support@rectitude.dev
# Built-in service configuration
cd /usr/share/logwatch/default.conf/
Schedule a daily logwatch report
Run crontab -e
00 04 * * * /usr/sbin/logwatch --range yesterday --mailto support@rectitude.dev
Custom Service Configuration
Modsecurity
- Service
- Log File
- Script
/etc/logwatch/conf/services/modsecurity.conf
Title = "ModSecurity Alerts"
LogFile = modsecurity
/etc/logwatch/conf/logfiles/modsecurity.conf
LogFile = /var/log/modsec_audit.log
LogFile = /var/log/modsec_audit.log.1
Archive = /var/log/modsec_audit.*
Archive = /var/log/modsec_audit.*.gz
*ExpandRepeats
/etc/logwatch/scripts/services/modsecurity
use POSIX qw(strftime);
use strict;
use warnings;
my $Detail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0;
my $DateRange = $ENV{'LOGWATCH_DATE_RANGE'} || 'all';
my $AccessDeniedCount = 0;
my %BlockedIPs;
my $RequestBlock = "";
my $InBlock = 0;
# Get today's and yesterday's date in the log format
my $today = strftime('%d/%b/%Y', localtime);
my $yesterday = strftime('%d/%b/%Y', localtime(time - 24 * 60 * 60));
while (defined(my $ThisLine = <STDIN>)) {
chomp($ThisLine);
# Detect start and end of a request block
if ($ThisLine =~ /^---.*---A--$/) {
$InBlock = 1;
$RequestBlock = ""; # Reset request block
} elsif ($ThisLine =~ /^---.*---Z--$/) {
$InBlock = 0;
# Process the complete block
if ($RequestBlock =~ /ModSecurity: Access denied/) {
# Extract the timestamp from the request block
if ($RequestBlock =~ /^\[(\d{2}\/\w{3}\/\d{4}):\d{2}:\d{2}:\d{2} \+\d{4}\]/m) {
my $LogDate = $1;
# Apply date filtering
if ($DateRange eq 'today' && $LogDate ne $today) {
next;
} elsif ($DateRange eq 'yesterday' && $LogDate ne $yesterday) {
next;
}
}
$AccessDeniedCount++;
if ($RequestBlock =~ /^\[.*\]\s+\S+\s+(\S+)\s+/m) {
my $BlockedIP = $1;
$BlockedIPs{$BlockedIP}++;
}
}
} elsif ($InBlock) {
$RequestBlock .= "$ThisLine\n";
}
}
# Output the statistics
if ($AccessDeniedCount > 0) {
print "\nStatistics:\n";
print " ModSecurity: Access denied: $AccessDeniedCount Time(s)\n";
if (keys %BlockedIPs) {
print "\nBlocked IPs:\n";
foreach my $ip (keys %BlockedIPs) {
print " $ip: $BlockedIPs{$ip} Time(s)\n";
}
}
}
exit(0);
Inotify File Monitor
- Service
- Log File
- Script
/etc/logwatch/conf/services/inotify-file-monitor.conf
Title = "Inotify Monitor Alerts"
LogFile = inotify-file-monitor
/etc/logwatch/conf/logfiles/inotify-file-monitor.conf
LogFile = /var/log/inotify_file_monitor.log
LogFile = /var/log/inotify_file_monitor.log.1
Archive = /var/log/inotify_file_monitor.*
Archive = /var/log/inotify_file_monitor.*.gz
*ApplyEuroDate
*ExpandRepeats
/etc/logwatch/scripts/services/inotify-file-monitor
my $Detail = $ENV{'LOGWATCH_DETAIL_LEVEL'} || 0;
my $TotalRecords = 0;
my @RecordList;
while (defined(my $ThisLine = <STDIN>)) {
chomp($ThisLine);
# Match the log format and extract the data
if ($ThisLine =~ /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) \[(\w+)\] (.+)$/) {
my $Timestamp = $1;
my $Action = $2;
my $Path = $3;
$TotalRecords++;
push @RecordList, "$Timestamp [$Action] - $Path";
}
}
# Output the total record count
print "\nInotify Monitor Statistics:\n";
print " Total Records: $TotalRecords\n";
# Output the list of records
if (@RecordList) {
print "\nRecorded Entries:\n";
foreach my $record (@RecordList) {
print " $record\n";
}
}
exit(0);
Linux Malware Detect
- Service
- Log File
- Script
/etc/logwatch/conf/services/maldetect.conf
Title = "Linux Malware Detect (LMD) "
LogFile = maldetect
/etc/logwatch/conf/logfiles/maldetect.conf
LogFile = /usr/local/maldetect/logs/event_log
LogFile = /usr/local/maldetect/logs/event_log.1
Archive = /usr/local/maldetect/logs/event_log.*
Archive = /usr/local/maldetect/logs/event_log.*.gz
*ApplyStdDate="%b %e %Y %H:%M:%S"
*ExpandRepeats
/etc/logwatch/scripts/services/maldetect
while (defined(my $ThisLine = <STDIN>)) {
chomp($ThisLine);
# Match lines with {hit}, {quar}, or {clean}
if ($ThisLine =~ /\{(hit|quar|clean)\}/) {
print "$ThisLine\n";
}
}
exit(0);
Rootkit Hunter
- Service
- Log File
- Script
/etc/logwatch/conf/services/rkhunter.conf
Title = "Rootkit Hunter"
LogFile = rkhunter
/etc/logwatch/conf/logfiles/rkhunter.conf
LogFile = /var/log/rkhunter/rkhunter.log
LogFile = /var/log/rkhunter/rkhunter.log.1
Archive = /var/log/rkhunter/rkhunter.*
Archive = /var/log/rkhunter/rkhunter.*.gz
*ExpandRepeats
/etc/logwatch/scripts/services/rkhunter
use strict;
use warnings;
my @last_lines;
my $max_lines = 17;
while (my $line = <STDIN>) {
push @last_lines, $line;
shift @last_lines if @last_lines > $max_lines;
}
print @last_lines;
exit(0);
Monit
- Service
- Log File
- Script
/etc/logwatch/conf/services/monit.conf
Title = "Monit"
LogFile = monit
/etc/logwatch/conf/logfiles/monit.conf
LogFile = /var/log/monit.log
LogFile = /var/log/monit.log.1
Archive = /var/log/monit.*
Archive = /var/log/monit.*.gz
*ExpandRepeats
/etc/logwatch/scripts/services/monit
use POSIX qw(strftime);
use strict;
use warnings;
my $DateRange = $ENV{'LOGWATCH_DATE_RANGE'} || 'all';
# Get today's and yesterday's date in the same format as the log timestamps
my $today = strftime('%Y-%m-%d', localtime);
my $yesterday = strftime('%Y-%m-%d', localtime(time - 24 * 60 * 60));
while (defined(my $ThisLine = <STDIN>)) {
chomp($ThisLine);
# Extract date from log line
if ($ThisLine =~ /^\[(\d{4}-\d{2}-\d{2})T\d{2}:\d{2}:\d{2}[+-]\d{4}\]/) {
my $LogDate = $1;
# Apply date filtering
if ($DateRange eq 'today' && $LogDate ne $today) {
next;
} elsif ($DateRange eq 'yesterday' && $LogDate ne $yesterday) {
next;
}
}
# Print matching lines
print "$ThisLine\n";
}
exit(0);
WWW Backup
- Service
- Log File
- Script
/etc/logwatch/conf/services/wwwbackup.conf
Title = "WWW Backup"
LogFile = wwwbackup
/etc/logwatch/conf/logfiles/wwwbackup.conf
LogFile = /var/log/wwwbackup.log
LogFile = /var/log/wwwbackup.log.1
Archive = /var/log/wwwbackup.*
Archive = /var/log/wwwbackup.*.gz
*ApplyEuroDate
*ExpandRepeats
/etc/logwatch/scripts/services/wwwbackup
use strict;
use warnings;
while (my $line = <STDIN>) {
print $line;
}
exit(0);
Mariadb Slowlog
- Service
- Log File
- Script
/etc/logwatch/conf/services/mariadb-slowlog.conf
Title = "Mariadb Slowlog"
LogFile = mariadb-slowlog
/etc/logwatch/conf/logfiles/mariadb-slowlog.conf
LogFile = /var/log/mariadb/slow.log
LogFile = /var/log/mariadb/slow.log.1
Archive = /var/log/mariadb/slow.*
Archive = /var/log/mariadb/slow.*.gz
/etc/logwatch/scripts/services/mariadb-slowlog
use POSIX qw(strftime);
use strict;
use warnings;
my $DateRange = $ENV{'LOGWATCH_DATE_RANGE'} || 'all';
# Get today's and yesterday's date in the same format as the log timestamps
my $today = strftime('%y%m%d', localtime);
my $yesterday = strftime('%y%m%d', localtime(time - 24 * 60 * 60));
my $InBlock = 0;
my @BlockLines;
my $CurrentBlockDate = "";
while (defined(my $ThisLine = <STDIN>)) {
chomp($ThisLine);
# Detect start of a block
if ($ThisLine =~ /^# Time: (\d{6}) \d{2}:\d{2}:\d{2}/) {
$CurrentBlockDate = $1;
$InBlock = 1;
@BlockLines = (); # Reset block storage
}
if ($InBlock) {
push @BlockLines, $ThisLine;
# If we have collected 7 lines, process the block
if (scalar @BlockLines == 7) {
# Apply date filtering
if ($DateRange eq 'all' ||
($DateRange eq 'today' && $CurrentBlockDate eq $today) ||
($DateRange eq 'yesterday' && $CurrentBlockDate eq $yesterday)) {
print join("\n", @BlockLines) . "\n";
}
$InBlock = 0; # Reset flag
}
}
}
exit(0);
Mariadb Errorlog
- Service
- Log File
- Script
/etc/logwatch/conf/services/mariadb-errorlog.conf
Title = "Mariadb Errorlog"
LogFile = mariadb-errorlog
/etc/logwatch/conf/logfiles/mariadb-errorlog.conf
LogFile = /var/log/mariadb/error.log
LogFile = /var/log/mariadb/error.log.1
Archive = /var/log/mariadb/error.*
Archive = /var/log/mariadb/error.*.gz
*ApplyEuroDate
/etc/logwatch/scripts/services/mariadb-errorlog
use strict;
use warnings;
while (my $ThisLine = <STDIN>) {
chomp($ThisLine);
# Match lines containing [ERROR]
if ($ThisLine =~ /\[ERROR\]/) {
print "$ThisLine\n";
}
}
exit(0);
logwatch.yml
- name: Install and configure logwatch
hosts: all
remote_user: root
vars:
mail_to: support@rectitude.dev
mail_from: server@noreply.rectitude.dev
cron_hour: "04"
cron_minute: "00"
tasks:
- name: Install logwatch using dnf
dnf:
name: logwatch
state: present
register: logwatch_install
- name: Create temporary directory for cloning repository
tempfile:
state: directory
suffix: bamboo-fort
register: temp_dir
- name: Clone specific files from GitHub repository
git:
repo: https://github.com/rectitude-open/bamboo-fort.git
dest: "{{ temp_dir.path }}"
version: v1
sparse_checkout:
- conf/logwatch
- name: Copy logwatch configuration files to /etc/logwatch
copy:
src: "{{ temp_dir.path }}/conf/logwatch/"
dest: /etc/logwatch/
owner: root
group: root
mode: "0644"
remote_src: true
- name: Copy default logwatch.conf to /etc/logwatch/conf
copy:
src: /usr/share/logwatch/default.conf/logwatch.conf
dest: /etc/logwatch/conf/logwatch.conf
force: true
remote_src: true
mode: "0644"
- name: Update logwatch.conf with required settings
vars:
settings:
- {key: "MailTo", value: "{{ mail_to }}"}
- {key: "MailFrom", value: "{{ mail_from }}"}
- {key: "Range", value: "trueterday"}
- {key: "Detail", value: "Med"}
lineinfile:
path: /etc/logwatch/conf/logwatch.conf
regexp: "^{{ item.key }} ="
line: "{{ item.key }} = {{ item.value }}"
create: true
mode: "0644"
loop: "{{ settings }}"
- name: Ensure cron job for logwatch is present
cron:
name: "logwatch daily report"
minute: "{{ cron_minute }}"
hour: "{{ cron_hour }}"
job: "/usr/sbin/logwatch --range yesterday --mailto {{ mail_to }}"
state: present