From: Stefan Schantl Date: Tue, 11 Sep 2018 08:21:00 +0000 (+0200) Subject: ids.cgi: Introduce whitelisting of IP-addresses X-Git-Tag: suricata-beta3~9 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=b7e29743944953c973e3f858c10ab627949f898d;p=people%2Fstevee%2Fipfire-2.x.git ids.cgi: Introduce whitelisting of IP-addresses If an IP-address has been added to the whitelist, any traffic from this host will not longer inspected by suricata. Signed-off-by: Stefan Schantl --- diff --git a/html/cgi-bin/ids.cgi b/html/cgi-bin/ids.cgi index 641a88da88..55f45c5c83 100644 --- a/html/cgi-bin/ids.cgi +++ b/html/cgi-bin/ids.cgi @@ -38,6 +38,7 @@ my %rulesetsources = (); my %cgiparams=(); my %checked=(); my %selected=(); +my %ignored=(); # Read-in main settings, for language, theme and colors. &General::readhash("${General::swroot}/main/settings", \%mainsettings); @@ -62,6 +63,12 @@ my $disabled_sids_file = "$IDS::settingsdir/oinkmaster-disabled-sids.conf"; # File which contains wheater the rules should be changed. my $modify_sids_file = "$IDS::settingsdir/oinkmaster-modify-sids.conf"; +# File which stores the configured settings for whitelisted addresses. +my $ignoredfile = "$IDS::settingsdir/ignored"; + +# File which contains the rules to whitelist addresses on suricata. +my $whitelistfile = "$IDS::rulespath/whitelist.rules"; + my $errormessage; # Create files if they does not exist yet. @@ -69,12 +76,157 @@ unless (-f "$enabled_sids_file") { &IDS::create_empty_file($enabled_sids_file); unless (-f "$disabled_sids_file") { &IDS::create_empty_file($disabled_sids_file); } unless (-f "$modify_sids_file") { &IDS::create_empty_file($modify_sids_file); } unless (-f "$idsusedrulefilesfile") { &IDS::create_empty_file($idsusedrulefilesfile); } +unless (-f "$ignoredfile") { &IDS::create_empty_file($ignoredfile); } +unless (-f "$whitelistfile" ) { &IDS::create_empty_file($whitelistfile); } &Header::showhttpheaders(); #Get GUI values &Header::getcgihash(\%cgiparams); +## Add/edit an entry to the ignore file. +# +if (($cgiparams{'WHITELIST'} eq $Lang::tr{'add'}) || ($cgiparams{'WHITELIST'} eq $Lang::tr{'update'})) { + + # Check if any input has been performed. + if ($cgiparams{'IGNORE_ENTRY_ADDRESS'} ne '') { + + # Check if the given input is no valid IP-address or IP-address with subnet, display an error message. + if ((!&General::validip($cgiparams{'IGNORE_ENTRY_ADDRESS'})) && (!&General::validipandmask($cgiparams{'IGNORE_ENTRY_ADDRESS'}))) { + $errormessage = "$Lang::tr{'guardian invalid address or subnet'}"; + } + } else { + $errormessage = "$Lang::tr{'guardian empty input'}"; + } + + # Go further if there was no error. + if ($errormessage eq '') { + my %ignored = (); + my $id; + my $status; + + # Assign hash values. + my $new_entry_address = $cgiparams{'IGNORE_ENTRY_ADDRESS'}; + my $new_entry_remark = $cgiparams{'IGNORE_ENTRY_REMARK'}; + + # Read-in ignoredfile. + &General::readhasharray($ignoredfile, \%ignored); + + # Check if we should edit an existing entry and got an ID. + if (($cgiparams{'WHITELIST'} eq $Lang::tr{'update'}) && ($cgiparams{'ID'})) { + # Assin the provided id. + $id = $cgiparams{'ID'}; + + # Undef the given ID. + undef($cgiparams{'ID'}); + + # Grab the configured status of the corresponding entry. + $status = $ignored{$id}[2]; + } else { + # Each newly added entry automatically should be enabled. + $status = "enabled"; + + # Generate the ID for the new entry. + # + # Sort the keys by their ID and store them in an array. + my @keys = sort { $a <=> $b } keys %ignored; + + # Reverse the key array. + my @reversed = reverse(@keys); + + # Obtain the last used id. + my $last_id = @reversed[0]; + + # Increase the last id by one and use it as id for the new entry. + $id = ++$last_id; + } + + # Add/Modify the entry to/in the ignored hash. + $ignored{$id} = ["$new_entry_address", "$new_entry_remark", "$status"]; + + # Write the changed ignored hash to the ignored file. + &General::writehasharray($ignoredfile, \%ignored); + + # Regenerate the ignore file. + &GenerateIgnoreFile(); + } + + # Check if the IDS is running. + if(&IDS::ids_is_running()) { + # Call suricatactrl to perform a reload. + &IDS::call_suricatactrl("reload"); + } + +## Toggle Enabled/Disabled for an existing entry on the ignore list. +# + +} elsif ($cgiparams{'WHITELIST'} eq $Lang::tr{'toggle enable disable'}) { + my %ignored = (); + + # Only go further, if an ID has been passed. + if ($cgiparams{'ID'}) { + # Assign the given ID. + my $id = $cgiparams{'ID'}; + + # Undef the given ID. + undef($cgiparams{'ID'}); + + # Read-in ignoredfile. + &General::readhasharray($ignoredfile, \%ignored); + + # Grab the configured status of the corresponding entry. + my $status = $ignored{$id}[2]; + + # Switch the status. + if ($status eq "disabled") { + $status = "enabled"; + } else { + $status = "disabled"; + } + + # Modify the status of the existing entry. + $ignored{$id} = ["$ignored{$id}[0]", "$ignored{$id}[1]", "$status"]; + + # Write the changed ignored hash to the ignored file. + &General::writehasharray($ignoredfile, \%ignored); + + # Regenerate the ignore file. + &GenerateIgnoreFile(); + + # Check if the IDS is running. + if(&IDS::ids_is_running()) { + # Call suricatactrl to perform a reload. + &IDS::call_suricatactrl("reload"); + } + } + +## Remove entry from ignore list. +# +} elsif ($cgiparams{'WHITELIST'} eq $Lang::tr{'remove'}) { + my %ignored = (); + + # Read-in ignoredfile. + &General::readhasharray($ignoredfile, \%ignored); + + # Drop entry from the hash. + delete($ignored{$cgiparams{'ID'}}); + + # Undef the given ID. + undef($cgiparams{'ID'}); + + # Write the changed ignored hash to the ignored file. + &General::writehasharray($ignoredfile, \%ignored); + + # Regenerate the ignore file. + &GenerateIgnoreFile(); + + # Check if the IDS is running. + if(&IDS::ids_is_running()) { + # Call suricatactrl to perform a reload. + &IDS::call_suricatactrl("reload"); + } +} + # Check if any error has been stored. if (-e $IDS::storederrorfile) { # Open file to read in the stored error message. @@ -110,6 +262,9 @@ opendir(DIR, $IDS::rulespath) or die $!; # Ignore files which are not read-able. next unless (-R "$IDS::rulespath/$file"); + # Skip whitelist rules file. + next if( $file eq "whitelist.rules"); + # Call subfunction to read-in rulefile and add rules to # the idsrules hash. &readrulesfile("$file"); @@ -261,7 +416,10 @@ if ($cgiparams{'RULESET'} eq $Lang::tr{'update'}) { # Check if the enabled_rulefiles array contains any entries. if (@enabled_rulefiles) { - # Loop through the array of rulefiles which should be loaded and write the to the file. + # Allways load the whitelist. + print FILE " - whitelist.rules\n"; + + # Loop through the array of rulefiles which should be loaded and write them to the file. foreach my $file (@enabled_rulefiles) { print FILE " - $file\n"; } @@ -415,6 +573,9 @@ unless(exists($idssettings{'RUN_MODE'})) { $idssettings{'RUN_MODE'} = 'IPS'; } +# Read-in ignored hosts. +&General::readhasharray("$IDS::settingsdir/ignored", \%ignored); + $checked{'ENABLE_IDS'}{'off'} = ''; $checked{'ENABLE_IDS'}{'on'} = ''; $checked{'ENABLE_IDS'}{$idssettings{'ENABLE_IDS'}} = "checked='checked'"; @@ -625,6 +786,138 @@ END &Header::closebox(); +# +# Whitelist / Ignorelist +# +&Header::openbox('100%', 'center', $Lang::tr{'guardian ignored hosts'}); + +print < + + $Lang::tr{'ip address'} + $Lang::tr{'remark'} + + +END + # Check if some hosts have been added to be ignored. + if (keys (%ignored)) { + my $col = ""; + + # Loop through all entries of the hash. + while( (my $key) = each %ignored) { + # Assign data array positions to some nice variable names. + my $address = $ignored{$key}[0]; + my $remark = $ignored{$key}[1]; + my $status = $ignored{$key}[2]; + + # Check if the key (id) number is even or not. + if ($cgiparams{'ID'} eq $key) { + $col="bgcolor='${Header::colouryellow}'"; + } elsif ($key % 2) { + $col="bgcolor='$color{'color22'}'"; + } else { + $col="bgcolor='$color{'color20'}'"; + } + + # Choose icon for the checkbox. + my $gif; + my $gdesc; + + # Check if the status is enabled and select the correct image and description. + if ($status eq 'enabled' ) { + $gif = 'on.gif'; + $gdesc = $Lang::tr{'click to disable'}; + } else { + $gif = 'off.gif'; + $gdesc = $Lang::tr{'click to enable'}; + } + +print < + $address + $remark + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + + +
+ + +END + } + } else { + # Print notice that currently no hosts are ignored. + print "\n"; + print "$Lang::tr{'guardian no entries'}\n"; + print "\n"; + } + + print "\n"; + + # Section to add new elements or edit existing ones. +print < +
+
+ +
+ +END + + # Assign correct headline and button text. + my $buttontext; + my $entry_address; + my $entry_remark; + + # Check if an ID (key) has been given, in this case an existing entry should be edited. + if ($cgiparams{'ID'} ne '') { + $buttontext = $Lang::tr{'update'}; + print "\n"; + + # Grab address and remark for the given key. + $entry_address = $ignored{$cgiparams{'ID'}}[0]; + $entry_remark = $ignored{$cgiparams{'ID'}}[1]; + } else { + $buttontext = $Lang::tr{'add'}; + print "\n"; + } + +print < + + + + + + + + + + +
$Lang::tr{'update'}
$Lang::tr{'dnsforward add a new entry'}
$Lang::tr{'ip address'}: $Lang::tr{'remark'}:
+
+END + +&Header::closebox(); + # Only show the section for configuring the ruleset if one is present. if (%idsrules) { &Header::openbox('100%', 'LEFT', $Lang::tr{'intrusion detection system rules'}); @@ -823,7 +1116,7 @@ sub readrulesfile ($) { } } } - } + } } # @@ -946,6 +1239,54 @@ sub generate_home_net_file() { } +# +## Function to generate the rules file with whitelisted addresses. +# +sub GenerateIgnoreFile() { + my %ignored = (); + + # SID range 1000000-1999999 Reserved for Local Use + # Put your custom rules in this range to avoid conflicts + my $sid = 1500000; + + # Read-in ignoredfile. + &General::readhasharray($ignoredfile, \%ignored); + + # Open ignorefile for writing. + open(FILE, ">$whitelistfile") or die "Could not write to $whitelistfile. $!\n"; + + # Config file header. + print FILE "# Autogenerated file.\n"; + print FILE "# All user modifications will be overwritten.\n\n"; + + # Add all user defined addresses to the whitelist. + # + # Check if the hash contains any elements. + if (keys (%ignored)) { + # Loop through the entire hash and write the host/network + # and remark to the ignore file. + while ( (my $key) = each %ignored) { + my $address = $ignored{$key}[0]; + my $remark = $ignored{$key}[1]; + my $status = $ignored{$key}[2]; + + # Check if the status of the entry is "enabled". + if ($status eq "enabled") { + # Check if the address/network is valid. + if ((&General::validip($address)) || (&General::validipandmask($address))) { + # Write rule line to the file to pass any traffic from this IP + print FILE "pass ip $address any -> any any (msg:\"pass all traffic from/to $address\"\; sid:$sid\;)\n"; + + # Increment sid. + $sid++; + } + } + } + } + + close(FILE); +} + # ## Function to read-in the given enabled or disables sids file. #