From: Stefan Schantl Date: Thu, 12 Nov 2015 22:07:44 +0000 (+0100) Subject: Initial checkin. X-Git-Tag: 2.0~83 X-Git-Url: http://git.ipfire.org/?p=people%2Fstevee%2Fguardian.git;a=commitdiff_plain;h=88d9af2ca2c96f98ee03de01fc0fcd0761f5b39d;ds=sidebyside Initial checkin. Signed-off-by: Stefan Schantl --- 88d9af2ca2c96f98ee03de01fc0fcd0761f5b39d diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..56ebe17 --- /dev/null +++ b/Makefile @@ -0,0 +1,14 @@ +NAME = Guardian + +DESTDIR = +PERL_VER := $(shell eval "$$(perl -V:version)"; echo $${version};) +PERL_SITELIB_DIR := $(shell eval "$$(perl -V:installsitelib)"; echo $${installsitelib};) + +PERL_DIR = $(DESTDIR)$(PERL_SITELIB_DIR)/$(NAME)/ + +all: + +install: + -mkdir -pv $(PERL_DIR) + cp -rvf modules/* $(PERL_DIR) + install -v -m 755 guardian $(DESTDIR)/usr/sbin diff --git a/guardian b/guardian new file mode 100644 index 0000000..62cb9cf --- /dev/null +++ b/guardian @@ -0,0 +1,182 @@ +#!/usr/bin/perl +############################################################################### +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2015 IPFire Development Team # +# # +# This program is free software: you can redistribute it and/or modify # +# it under the terms of the GNU General Public License as published by # +# the Free Software Foundation, either version 3 of the License, or # +# (at your option) any later version. # +# # +# This program is distributed in the hope that it will be useful, # +# but WITHOUT ANY WARRANTY; without even the implied warranty of # +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # +# GNU General Public License for more details. # +# # +# You should have received a copy of the GNU General Public License # +# along with this program. If not, see . # +# # +############################################################################### + +use strict; +use threads; +use threads::shared; +use Thread::Queue; +use Linux::Inotify2; + +require Guardian::Parser; + +# Array to store the monitored logfiles. +my @monitored_files = ( + "/var/log/snort/alert", +); + +# Create the main queue. It is used to store and process all events which are +# reported and enqueued by the worker threads. +my $queue :shared = new Thread::Queue or die "Could not create new, empty queue. $!\n";; + +# Call Init function to initzialize guardian. +&Init(); + +# Infinite main loop, which processes all queued events. +while(1) { + # Get the amount of elements in our queue. + # "undef" will be returned if it is empty. + my $current_events = $queue->pending(); + + # If there is at least one element enqued + if($current_events > 0) { + # Grab the data of the top enqueued event. + my $event = $queue->peek(); + + print "Got event: $event\n"; + + # Drop processed event from queue. + $queue->dequeue(); + } + + # XXX + # Temporary workaround to reduce the load of the main process. + sleep(1); +} + +# +## Init function. +# +## This function contains code which has to be executed while guardian +## is starting. +# +sub Init () { + # Loop through the array of which files should be monitored and + # create a worker thread for each single one. + foreach my $monitored_file (@monitored_files) { + # Check if the file exists and is readable. + if (-r "$monitored_file") { + # Create worker thread for the file. + threads->create(\&Worker,$monitored_file); + } + } +} + +# +## Worker function. +# +## This function is responsible for monitoring modifications of the given logfile, +## read them and pass them to the message parser. +# +## To get file modifications the inotify subsystem of the linux kernel is used. +# +## In order to prevent from permanently read and keep files opened, or dealing +## with huge logfiles, at initialization time of the worker process, the file will +## be opened once and the cursor position of the end of file (EOF) get stored. When +## reading any newly added lines from the file, we directly can jump to the last +## known position and get these lines. Afterwards, we store the current curser position +## again, so we can do it in this way over and over again. +# +## All read lines get stored in an array, which will be passed to the Parser. +# +## If any response (action) from the parser get recieved, it will be put into the +## shared event queue. +# +sub Worker ($) { + my $file = @_[0]; + + # Get the fileposition. + my $fileposition = &Init_fileposition("$file"); + + # Create inotify watcher. + my $watcher = new Linux::Inotify2 or die "Could not use inotify. $!\n"; + + # Monitor the specified file. + $watcher->watch("$file", IN_MODIFY) or die "Could not monitor $file. $!\n"; + + # Get all notifications. + while ($watcher->read) { + my @message = (); + + # Open the file. + open (FILE, $file) or die "Could not open $file. $!\n"; + + # Seek to the last known position. + seek (FILE, $fileposition, 0); + + # Get the log message. + while (my $line = ) { + # Remove any newlines. + chomp $line; + + # Add all lines to the message array. + push (@message, $line); + } + + # Update fileposition. + $fileposition = tell(FILE); + + # Close file. + close(FILE); + + # Send filename and message to the parser, + # which will return if an action has to be performed. + my @action = &Guardian::Parser::Parser("$file", @message); + + # Send the action to the main process and put it into + # the queue. + if (@action) { + # Lock the queue. + lock($queue); + + # Put the required action into the queue. + $queue->enqueue(@action); + } + } +} + +# +## Function for fileposition initialization. +# +## This function is used to get the cursor position of the end of file (EOF) of +## a specified file. +# +## In order to prevent from permanently read and keep files opened, or dealing +## with huge logfiles, at initialization time of the worker processes, the file will +## be opened once and the cursor position of the end of file (EOF) get stored. +# +sub Init_fileposition ($) { + my $file = $_[0]; + + # Open the file. + open(FILE, $file) or die "Could not open $file. $!\n"; + + # Just seek to the end of the file (EOF). + seek(FILE, 0, 2); + + # Get and store the position. + my $position = tell(FILE), + + # Close the file again. + close(FILE); + + # Return the position. + return $position; +} diff --git a/modules/Parser.pm b/modules/Parser.pm new file mode 100644 index 0000000..86e7aa1 --- /dev/null +++ b/modules/Parser.pm @@ -0,0 +1,51 @@ +package Guardian::Parser; +use strict; +use warnings; + +use Exporter qw(import); + +our @EXPORT_OK = qw(Parser); + +# This hash contains all supported logfiles and which function +# has to be called to parse them in the right way. +my %logfile_parsers = ( + "/var/log/snort/alert" => \&message_parser_snort, +); + +# +## The main parsing function. +# +## It is used to determine which sub-parser has to be used to +## parse the given message in the right way and to return if +## any action should be performed. +# +sub Parser ($$) { + my ($file, @message) = @_; + + # If no responsible message parser could be found, just return nothing. + unless (exists($logfile_parsers{$file})) { + return; + } + + # Call responsible logfile parser. + my $action = $logfile_parsers{$file}->(@message); + + # Return which action should be performed. + return $action; +} + +# +## The Snort message parser. +# +## This subfunction is responsible for parsing sort alerts and determine if +## an action should be performed. +# +sub message_parser_snort($) { + my @message = @_; + + # XXX + # Currently this parser just returns a simple message. + return "snort_parser_return\n"; +} + +1;