====== Enhanced Samba Logging ======
Samba logging appears to show you everything but what you want to know. There is a solution however, in the vfs full audit module.
One client wanted to monitor activity by individuals at their location, watching for unusual activity. They agreed that simply comparing activity on a weekly basis would be sufficient.
Add in /etc/samba/smb.conf. This can be global, or on a particular share
# use the vfs full audit module
# see https://moiristo.wordpress.com/2009/08/10/samba-logging-user-activity/
# https://www.samba.org/samba/docs/man/manpages-3/vfs_full_audit.8.html
vfs objects = full_audit
# Username | machine name | name of service
vfs_full_audit:prefix = %U|%m|%S
## log the following functions
## create a directory, rename something, delete (unlink) a file, rm a directroy
## read/write a file, open a file
full_audit:success = mkdir rename unlink rmdir pwrite open rmdir pread
# rename open !strict_unlock !pread !get_alloc_size !readdir !telldir !lstat !closedir !connectpath !opendir
## Do not log anything on failure
full_audit:failure = none
## use syslog local7 for the logs
## you must create this in syslog.conf by adding a line
## local7.* /var/log/samba/log.audit
## and also set logrotate
full_audit:facility = local7
## all set to NOTICE
full_audit:priority = NOTICE
append to /etc/rsyslog.conf
# set to log samba vfs audit module
local7.* /var/log/samba/audit.log
Append to /etc/logrotate.d/samba
/var/log/samba/audit.log {
daily
missingok
rotate 7
postrotate
invoke-rc.d rsyslog rotate > /dev/null
/etc/init.d/samba reload > /dev/null
endscript
compress
notifempty
}
/etc/init.d/rsyslog restart
/etc/init.d/samba restart
The following script (which is really too large for a KB article) can be run to summarize the log file. I put it in /opt/scripts, and store the results in /opt/smbaudit/. I then run the script via cron at 8am every morning (on audit.log.1, which is the previous days work after logrotate does its thing).
Two things about the code:
1. I have never gotten the hang of the correct syntax for going through a hash of hashes
2. I wrote it in a couple of hours, so it is definitely **not** the best code I have ever written
#! /usr/bin/env perl
# copyright 2017, R. W. Rodolico
# use it as you want, make money from it, sell it for all I care, Rename it, remove my name, whatever
#
# You could write the same
# thing in a few hours so this is true open source; I don't want anything except to have the right to
# use it myself.
# BTW, I have no responsibility if it destroys your server, your marriage, or anything else. Use at your own
# risk.
# script to process samba audit log, storing summary of information
# for future processing.
# will read $summaryFile into memory, if it exists, then scan
# $auditFile, adding new entries.
# will then write results back to $summaryFile (making a backup first)
# resulting file is a tab delimited text file, where each line begins
# with three standard fields; username, timestamp and IP.
# the remaining fields are actions based on %headers below (other actions
# are ignored).
#
# NOTE: timestamp is floor'd to the nearest day, ie int(timestamp/86400)*86400
# as we want to summarize a days activity.
use strict;
use warnings;
use Parse::Syslog;
# apt-get install libparse-syslog-perl
# path to Samba audit file
my $auditFile = '/var/log/samba/audit.log';
# path to our historical summary file
my $summaryFile = '/opt/smbaudit/samba_audit.summary';
# hash to store all our activity
my %activity;
# hash containing the action headers we care about
my %headers = ( 'mkdir' => 1, 'pread' => 1, 'pwrite' => 1,'rmdir' => 1,'unlink' => 1 );
# number of seconds in a day, 86400. BREAKS on Daylight Savings Time
my $secondsInDay = 60*60*24;
# function loads the summary file into %activity
# also modified %headers, based on the headers it finds in the summary file
sub loadSummary {
open SUMMARY,"<$summaryFile" or return;
print STDERR "Loading summary file\n";
# file exists, so read in the first line, which is the headers
my $line = ;
chomp $line;
my @headers = split( "\t", $line );
# replace our preset ones with whatever headers we have here
%headers = map{ $_ => 1 } @headers;
# the following are not actions, so remove them from the headers
delete @headers{qw(user day ip)};
# read each line and create the activity.
# note that user, timestamp and IP are required to be in the first
# three columns
while ( $line = ) {
chomp $line;
my @data = split( "\t", $line );
for ( my $i = 3; $i < @headers; $i++ ) {
$activity{$data[0]}{$data[1]}{$data[2]}{$headers[$i]} = $data[$i];
}
}
close SUMMARY;
}
# get our summary file into the access hash
&loadSummary();
# use Parse::Syslog to read in each line, mainly to get the timestamp
my $parser = Parse::Syslog->new( $auditFile );
while ( my $sl = $parser->next ) {
# remove the time from it; just date
my $timestamp = int( $sl->{'timestamp'} / $secondsInDay ) * $secondsInDay;
# text contains the information we care about
my @temp = split( '\|', $sl->{'text'} );
# and we only care about the first three of them, which are user, ip
# and the action they took
my ( $user, $ip, $action ) = @temp[0..2];
# update %activity if this is an action we track
$activity{$user}{$timestamp}{$ip}{$action}++ if $headers{$action};
}
# make a backup of the summary file, if it exists
unlink "$summaryFile~" if -e "$summaryFile~";
rename $summaryFile, "$summaryFile~" if -e $summaryFile;
# and overwrite it
open SUMMARY, ">$summaryFile" or die "Could not save summary file $summaryFile: $!\n";
my @line; # the line we'll build for output
# header line
print SUMMARY "user\tday\tip\t" . join( "\t", sort keys %headers ) . "\n";
# process each user
foreach my $user ( sort keys %activity ) {
push @line, $user;
my $timestamp = $activity{$user};
# process the date
foreach my $time ( sort keys %$timestamp ) {
push @line, $time;
my $ips = $$timestamp{$time};
# and the time
foreach my $ip ( sort keys %$ips ) {
push @line, $ip;
my $actions = $$ips{$ip};
# Finally, get the actions (all of them)
foreach my $action ( sort keys %headers ) {
push @line, $$actions{$action} ? $$actions{$action} : 0;
}
# finished with all the actions, so dump the line
print SUMMARY join( "\t", @line ) . "\n";
# and delete all the actions for a new IP
delete @line[2..$#line];
} # ip
# delete the IP also for a new timestamp
delete @line[1..$#line];
} # timestamp
# completely reset @line for a new user
@line = ();
} # user
close SUMMARY;
1;