]>
git.ipfire.org Git - people/stevee/guardian.git/blob - modules/Events.pm
c451914da5b34aa63a9d877887cede3e829ccdd5
1 package Guardian
::Events
;
5 use Exporter
qw(import);
7 our @EXPORT_OK = qw(Init CheckAction GenerateIgnoreList Update);
9 # Hash which stores all supported commands from the queue.
12 'block' => \
&CallBlock
,
13 'unblock' => \
&CallUnblock
,
14 'flush' => \
&CallFlush
,
15 'reload' => \
&main
::Reload
,
16 'reload-ignore-list' => \
&main
::ReloadIgnoreList
,
17 'logrotate' => \
&main
::Logrotate
,
20 # Hash to store addresses and their current count.
23 # Hash to store all currentyl blocked addresses and a timestamp
24 # when the block for this address can be released.
27 # Hash to store user-defined IP addresses and/or subnets which should be
28 # ignored in case any events should be repored for them.
31 # Array to store localhost related IP addresses.
32 # They are always white-listed to prevent guardian from blocking
34 my @localhost_addresses = ("127.0.0.1", "::1");
36 # This object will contain the reference to the logger object after calling Init.
40 ## The "Init" (Block) function.
42 ## This function is responsible to initialize Block as a class based object.
43 ## It has to be called once before any blocking event can be processed.
45 ## The following arguments must be passed, during initialization:
46 ## "BlockCount" and "BlockTime" which both have to be natural numbers.
49 my ( $class, %args ) = @_;
52 # Fail, if some critical arguments are missing.
53 unless ((exists($self->{BlockCount
})) && (exists($self->{BlockTime
}))) {
54 die "Could not initialize Block: Too less arguments are given.\n";
57 # Use bless to make "$self" to an object of class "$class".
60 # Assign logger object.
61 $logger = $self->{Logger
};
63 # Log used firewall engine.
64 $logger->Log("debug", "Using firewall engine: $self->{FirewallEngine}");
66 # Try to load the specified firewall engine or die.
67 my $module_name = "Guardian::" . $self->{FirewallEngine
};
68 eval "use $module_name; 1" or die "Could not load a module for firewall engine: $self->{FirewallEngine}!";
70 # Check if an IgnoreFile has been configured.
71 if (exists($self->{IgnoreFile
})) {
72 # Call function to handle the ignore mechanism.
73 &GenerateIgnoreList
($self->{IgnoreFile
});
75 # Whitelist local addresses.
76 %ignorehash = &_whitelist_localhost
();
79 # Return the class object.
84 ## The "Update" Block settings function.
86 ## This object based function is called to update various class settings.
91 # Dereference the given hash-ref and store
92 # the values into a new temporary hash.
93 my %settings = %{ $_[0] };
95 # Skip settings update if some essential settings are missing.
96 unless ((exists($settings{BlockCount
})) && (exists($settings{BlockTime
}))) {
97 $logger->Log("err", "Values for BlockCount or BlockTime are missing, keeping previously configured settings.");
99 # Return unmodified class object.
104 $self->{BlockCount
} = $settings{BlockCount
};
105 $self->{BlockTime
} = $settings{BlockTime
};
107 # Return modified class object.
112 ## The main "CheckAction" function.
114 ## This function is used to handle each recived event from the main event queue of guardian.
116 ## It will check if the given command is valid and will pass it to the responsible function.
118 sub CheckAction
($$) {
120 my @event = split(/ /, $_[0], 4);
121 my ($command, $address, $module, $message) = @event;
123 # Check if we got an invalid command.
124 unless(exists($commands{$command})) {
125 $logger->Log("err", "The CheckAction function has been called with an unsupported command ($command)!");
129 # Check if the given event contains an address.
131 # Convert and validate the given address.
132 my $bin_address = &Guardian
::Base
::IPOrNet2Int
($address);
134 # Abort if the given address could not be converted because it is not valid.
135 unless ($bin_address) {
136 $logger->Log("err", "Invalid IP address: $address");
140 # Check if address should be ignored.
141 if(&_IsOnIgnoreList
($bin_address)) {
143 $logger->Log("info", "Ignoring event for $address, because it is part of the ignore list.");
148 # Call required handler.
149 my $error = $commands{$command}->($self, $address, $module, $message);
151 # If we got any return code, something went wrong and should be logged.
153 $logger->Log("err", "Got error: $error");
159 ## The main "Counter" function.
161 ## This function is used to handle each count message + address, which has been sent by the main event
164 ## It stores the address and the current count into the counthash and increase the count each time when
165 ## the same address should be counted again. When the current count reaches the configured BlockCount,
166 ## the responsible function will be called to block the address.
170 my ($address, $module, $message) = @_;
173 $logger->Log("debug", "$module reported $message for address: $address");
175 # Increase existing count or start counting for new source addresses.
176 if (exists($counthash{$address})) {
177 # Skip already blocked addresses.
178 if (exists($blockhash{$address})) {
182 # Increase count of the existing entry.
183 $counthash{$address} = $counthash{$address} + 1;
185 # Log altered count of the address.
186 $logger->Log("debug", "Source $address now has count $counthash{$address}/$self->{BlockCount}...");
188 # Log that counting for the address has been started.
189 $logger->Log("debug", "Start counting for $address...");
192 $counthash{$address} = 1;
195 # Check if the address has reached the configured count limit.
196 if ($counthash{$address} >= $self->{BlockCount
}) {
197 # Write out log message.
198 $logger->Log("info", "Blocking $address for $self->{BlockTime} seconds...");
200 # Call block subroutine to block the address.
201 my $error = &CallBlock
($self, $address, $module, $message);
203 # Return the message if an error occurs.
207 # Everything worked well, return nothing.
212 ## The RemoveBlocks function.
214 ## This function periodly will be called and is responsible for releasing the block if the Blocktime
215 ## on an address has expired.
217 ## To do this, the code will loop through all entries of the blockhash and check
218 ## if the estimiated BlockTime of each address has reached and so the block can be released.
220 sub RemoveBlocks
() {
223 # Get the current time.
226 # Loop through the blockhash.
227 foreach my $address (keys %blockhash) {
228 # Check if the blocktime for the address has expired.
229 if ($blockhash{$address} <= $time) {
230 # Call unblock subroutine.
231 my $error = &CallUnblock
($self, $address, "BlockTime", "has expired for $address");
233 # Log error messages if returned.
235 $logger->Log("err", "$error");
240 # Everything okay, return nothing.
245 ## The CallBlock function.
247 ## This function is called, if the BlockCount for an address is reached or a direct
248 ## request for blocking an address has been recieved.
252 my ($address, $module, $message) = @_;
254 # Log the call for blocking an address.
255 $logger->Log("info", "$module - $message");
257 # Check if an entry for this address exists
258 # in the blockhash. If not, the address has
259 # not been blocked yet, call the responisible
260 # function to do this now.
261 unless (exists($blockhash{$address})) {
262 # Obtain the configured FirewallAction.
263 my $action = $self->{FirewallAction
};
265 # Block the given address.
266 my $error = &DoBlock
($address, $action);
268 # If we got back an error message something went wrong.
270 # Exit function and return the used FirewallEngine and the error message.
271 return "$self->{FirewallEngine} - $error";
273 # Address has been successfully blocked, print a log message.
274 $logger->Log("debug", "Address $address successfully has been blocked...");
278 # Generate time when the block will expire.
279 my $expire = time() + $self->{BlockTime
};
281 # Store the blocked address and the expire time
283 $blockhash{$address} = $expire;
285 # Return nothing "undef" if everything is okay.
290 ## CallUnblock function.
292 ## This function is responsible for unblocking and deleting a given
293 ## address from the blockhash.
295 sub CallUnblock
($) {
297 my ($address, $module, $message) = @_;
299 # Log the call for unblocking an address.
300 $logger->Log("info", "$module - $message");
302 # Return an error if no entry for the given address
303 # is present in the blockhash.
304 unless (exists($blockhash{$address})) {
305 return "Address $address was not blocked!";
308 # Unblock the address.
309 my $error = &DoUnblock
($address);
311 # If an error message is returned, something went wrong.
313 # Exit function and return the error message.
316 # Address successfully has been unblocked.
317 $logger->Log("debug", "Address $address successfully has been unblocked...");
320 # Drop address from blockhash.
321 delete ($blockhash{$address});
323 # Drop address from counthash if the address has been unblocked
324 # by the user. This happens when the called module is "Socket".
325 if ($module eq "Socket") {
326 delete ($counthash{$address});
329 # Everything worked well, return nothing.
334 ## CallFlush function.
336 ## This function is responsible for calling the used firewall
337 ## engine to do a flush of the used firewall chain. This will
338 ## clean the entire firewall chain.
343 # Log the call for flushing.
344 $logger->Log("info", "Flush has been called...");
347 my $error = &DoFlush
();
349 # If an error message is returned, something went wrong.
351 # Exit function and return the error message.
354 # Flush successfully has been performed.
355 $logger->Log("debug", "Flush successfully has been performed...");
361 # Everything worked well, return nothing.
366 ## GenerateIgnoreList function.
368 ## This function is responsible for generating/updating the
369 ## IgnoreHash which contains all ignored IP addresses and
372 sub GenerateIgnoreList
($) {
376 # Reset current ignore hash and add
377 # localhost related IP addresses.
378 %ignorehash = &_whitelist_localhost
();
380 # Check if the given IgnoreFile could be opened.
382 $logger->Log("err", "The configured IgnoreFile \($file\) could not be opened. Skipped!");
386 # Open the given IgnoreFile.
387 open (IGNORE
, $file);
389 # Read-in the file line by line.
397 # Remove any newlines.
400 # Check for an include instruction.
401 if ($_ =~ /^Include_File = (.*)/) {
402 my $include_file = $1;
404 # Check if the parsed include file exists and is read-able.
405 if (-e
$include_file) {
406 # Add file to the array of files wich will be included.
407 push(@include_files, $include_file);
409 # Write out log message.
410 $logger->Log("debug", "Addresses from $include_file will be included...");
413 $logger->Log("err", "$include_file will not be included. File does not exist!");
416 # Check if the line contains a valid single address or network and
417 # convert it into binary format. Store the result/start and
418 # end values in a temporary array.
419 my @values = &Guardian
::Base
::IPOrNet2Int
($_);
421 # If the function returned any values, the line contained a valid
422 # single address or network which successfully has been converted into
425 # Assign the array as value to the ignorehash.
426 $ignorehash{$_} = [@values];
429 $logger->Log("err", "IgnoreFile contains an invalid address/network: $_");
437 # Close filehandle for the IgnoreFile.
440 # Check if any files should be included.
441 if (@include_files) {
442 # Loop through the array of files which should be included.
443 foreach my $file (@include_files) {
445 open(INCLUDE
, $file);
447 # Read-in file line by line.
452 # Skip any blank lines.
455 # Chomp any newlines.
458 # Check if the line contains a valid single address or network and
459 # convert it into binary format. Store the result/start and
460 # end values in a temporary array.
461 my @values = &Guardian
::Base
::IPOrNet2Int
($_);
463 # If the function returned any values, the line contained a valid
464 # single address or network which successfully has been converted into
467 # Assign the array as value to the ignorehash.
468 $ignorehash{$_} = [@values];
471 $logger->Log("err", "$file contains an invalid address/network: $_");
483 # Get amount of current elements in hash.
484 my $amount = scalar(keys(%ignorehash));
486 # Write out log message.
487 $logger->Log("debug", "Ignore list currently contains $amount entries:");
489 # Sort the ignore hash.
490 my @sorted_addresses = &Guardian
::Base
::SortAddressHash
(\
%ignorehash);
492 # Loop through the entire array.
493 foreach my $address (@sorted_addresses) {
494 # Log the ignored address.
495 $logger->Log("debug", "\- $address");
498 # Finished return nothing.
503 ## Private function to check if an address is part of the Ignore Hash.
505 ## This function checks if a passed IP address in binary format (!),
506 ## directly or as part of an ignored network is stored in the ignore hash.
508 sub _IsOnIgnoreList
($) {
509 my $bin_address = shift;
511 # Loop through the ignore hash and grab the stored values.
512 foreach my $key ( keys %ignorehash ) {
513 # Dereference values array.
514 my @values = @
{$ignorehash{$key}};
516 # Obtain amount of items for the current value array.
517 my $items = scalar @values;
519 # Whether the amount equals one, the given binary address just
520 # needs to be compared against a single address.
522 my ($ignored_address) = @values;
524 # Simple check if the stored and the given binary address
526 if ($bin_address eq $ignored_address) {
527 # The given address is part of the ignore list.
528 $logger->Log("debug", "Address $key found on the ignore list.");
535 # If the amount equals two, for passed binary address needs to
536 # be checked if it is part of the ignored network range.
537 elsif ($items eq "2") {
538 my ($first_address, $last_address) = @values;
540 # Check if the passed binary address is bigger than
541 # the first address and smaler than the last address
542 # (between) the stored network range.
543 if (($bin_address >= $first_address) && ($bin_address <= $last_address)) {
544 # The address is part of an ignored network.
545 $logger->Log("debug", "Address is found inside the ignored network $key.");
551 # If the amount is not eighter one or two, the current entry of the ignorehash seems
552 # to be corrupted. Skip and log it.
554 # Write log message about this corruped item in the ignore hash.
555 $logger->Log("err", "Invalid item in the Ignore Hash: $key - @values");
557 # Skip this element of the ignore hash.
562 # If we got here, the given address is not part of the ignore hash.
563 # Return nothing (False).
568 ## The _whitelist_localhost function.
570 ## This tiny private function simple generates and returns a hash which contains
571 ## the clear and binary converted addresses for all array-stored
572 ## (@localhost_addresses) in an ignorelist compatible format.
574 sub _whitelist_localhost
() {
577 # Loop through the array of localhost related addresses.
578 foreach my $address (@localhost_addresses) {
579 # Validate and convert the addresss into binary format.
580 my @values = &Guardian
::Base
::IPOrNet2Int
($address);
582 # Check if any values are returned.
584 # Store the converted binary values in the temporary hash.
585 $temphash{$address} = [@values];
589 # Return the temporary hash.