From: Stefan Schantl Date: Wed, 3 Feb 2016 07:34:12 +0000 (+0100) Subject: Add functionality for whitelisting single addresses or network ranges. X-Git-Tag: 2.0~40 X-Git-Url: http://git.ipfire.org/?p=people%2Fstevee%2Fguardian.git;a=commitdiff_plain;h=66e1ad0ac7b647b0063b52b3ca8749a1470d8f48 Add functionality for whitelisting single addresses or network ranges. This commit adds the posibilty for generating a ignore list based on a set of IP addresses or networks (IPv4/IPv6 are supported) which are provided by an so called ignore file. The path and the filename can be configured by using the "IgnoreFile = /path/to/somefile" option inside the config file of guardian. Using this feature is completely optional and only be activated if an ignore file has been specified. Signed-off-by: Stefan Schantl --- diff --git a/modules/Base.pm b/modules/Base.pm index 96ea256..194cb96 100644 --- a/modules/Base.pm +++ b/modules/Base.pm @@ -135,6 +135,60 @@ sub IsValidAddressOrNetwork ($) { return 1; } +# +## Address/Network to binary format caluculator function. +# +## This function is used to convert a given single IP address +## or network into a binary format. +# +## The used Net::IP module is not able to directly detect +## single addresses or network ranges. Only an element which may be +## a single address or a whole network can be assigned, for which a +## lot of different values can be calculated. In case the input has +## been a single address, the module will calculate the same binary +## address (intip) and last address for the network range (last_int) +## because internally it uses a /32 bit prefix for IPv4 and a /128 prefix +## on IPv6 addresses. +# +## So a single address can be detected by just comparing both calculated +## addresses if they are equal. +# +sub IPOrNet2Int($) { + my $address = shift; + + # Assign and validate the given address, or directly return + # nothing (False) and exit the function. + my $ip = new Net::IP ($address) || return; + + # Convert the given address into integer format. + my $first .= $ip->intip(); + + # Calculate last address for the given network. + my $last .= $ip->last_int(); + + # Check whether the first address equals the last address. + # If this is true, a single IP address has been passed. + if ($first eq $last) { + # Return the binary converted single address. + return $first; + } + + # If both addresses are not equal a network has been passed. + # + # Check if the converted first address is less than the calculated last + # address of the network. + elsif ($first < $last) { + # Return the binary converted first and last address of + # the given network. + return $first, $last; + } + + # If we got here, something strange happend, return nothing (False). + else { + return; + } +} + # ## Function for fileposition initialization. # diff --git a/modules/Events.pm b/modules/Events.pm index b60a572..fa3be24 100644 --- a/modules/Events.pm +++ b/modules/Events.pm @@ -21,6 +21,10 @@ my %counthash = (); # when the block for this address can be released. my %blockhash = (); +# Hash to store user-defined IP addresses and/or subnets which should be +# ignored in case any events should be repored for them. +my %ignorehash = (); + # This object will contain the reference to the logger object after calling Init. my $logger; @@ -55,6 +59,12 @@ sub Init (%) { my $module_name = "Guardian::" . $self->{FirewallEngine}; eval "use $module_name; 1" or die "Could not load a module for firewall engine: $self->{FirewallEngine}!"; + # Check if an IgnoreFile has been configured. + if (exists($self->{IgnoreFile})) { + # Call function to handle the ignore mechanism. + &GenerateIgnoreList($self->{IgnoreFile}); + } + # Return the class object. return $self; } @@ -77,13 +87,22 @@ sub CheckAction ($$) { return; } - # Check if the given address is valid. - unless(&Guardian::Base::IsValidAddressOrNetwork($address)) { - # Log error message. + # Convert and validate the given address. + my $bin_address = &Guardian::Base::IPOrNet2Int($address); + + # Abort if the given address could not be converted because it is not valid. + unless ($bin_address) { $logger->Log("err", "Invalid IP address: $address"); return; } + # Check if address should be ignored. + if(&_IsOnIgnoreList($bin_address)) { + # Log message. + $logger->Log("info", "Ignoring event for $address, because it is part of the ignore list."); + return; + } + # Call required handler. my $error = $commands{$command}->($self, $address, $module, $message); @@ -263,4 +282,122 @@ sub CallUnblock ($) { return undef; } +# +## GenerateIgnoreList function. +# +## This function is responsible for generating/updating the +## IgnoreHash which contains all ignored IP addresses and +## networks. +# +sub GenerateIgnoreList($) { + my $file = shift; + + # Check if the given IgnoreFile could be opened. + unless(-e $file) { + $logger->Log("err", "The configured IgnoreFile \($file\) could not be opened. Skipped!"); + return; + } + + # Open the given IgnoreFile. + open (IGNORE, $file); + + # Read-in the file line by line. + while () { + # Skip comments. + next if (/\#/); + + # Skip blank lines. + next if (/^\s*$/); + + # Remove any newlines. + chomp; + + # Check if the line contains a valid single address or network and + # convert it into binary format. Store the result/start and + # end values in a temporary array. + my @values = &Guardian::Base::IPOrNet2Int($_); + + # If the function returned any values, the line contained a valid + # single address or network which successfully has been converted into + # binary format. + if (@values) { + # Assign the array as value to the ignorehash. + $ignorehash{$_} = [@values]; + } else { + # Log invalid entry. + $logger->Log("err", "IgnoreFile contains an invalid address/network: $_"); + + # Skip line. + next; + } + } + + # Close filehandle for the IgnoreFile. + close (IGNORE); +} + +# +## Private function to check if an address is part of the Ignore Hash. +# +## This function requires +# +sub _IsOnIgnoreList ($) { + my $bin_address = shift; + + # Loop through the ignore hash and grab the stored values. + foreach my $key ( keys %ignorehash ) { + # Dereference values array. + my @values = @{$ignorehash{$key}}; + + # Obtain amount of items for the current value array. + my $items = scalar @values; + + # Whether the amount equals one, the given binary address just + # needs to be compared against a single address. + if ($items eq "1") { + my ($ignored_address) = @values; + + # Simple check if the stored and the given binary address + # are the same. + if ($bin_address eq $ignored_address) { + # The given address is part of the ignore list. + $logger->Log("debug", "Address $key found on the ignore list."); + + # Return "1" (True). + return 1; + } + } + + # If the amount equals two, for passed binary address needs to + # be checked if it is part of the ignored network range. + elsif ($items eq "2") { + my ($first_address, $last_address) = @values; + + # Check if the passed binary address is bigger than + # the first address and smaler than the last address + # (between) the stored network range. + if (($bin_address >= $first_address) && ($bin_address <= $last_address)) { + # The address is part of an ignored network. + $logger->Log("debug", "Address is found inside the ignored network $key."); + + # Return "1" (True). + return 1; + } + + # If the amount is not eighter one or two, the current entry of the ignorehash seems + # to be corrupted. Skip and log it. + } else { + # Write log message about this corruped item in the ignore hash. + $logger->Log("err", "Invalid item in the Ignore Hash: $key - @values"); + + # Skip this element of the ignore hash. + next; + } + } + + # If we got here, the given address is not part of the ignore hash. + # Return nothing (False). + return; +} + 1;