diff --git a/config/backup/include b/config/backup/include index d310b8487..aacfaf64a 100644 --- a/config/backup/include +++ b/config/backup/include @@ -41,6 +41,7 @@ var/ipfire/ethernet/aliases var/ipfire/ethernet/wireless var/ipfire/firewall var/ipfire/fwhosts +var/ipfire/ipblocklist/modified var/ipfire/main/* var/ipfire/ovpn var/ipfire/ovpn/collectd.vpn @@ -54,6 +55,7 @@ var/ipfire/*/settings var/ipfire/time/ var/ipfire/urlfilter var/ipfire/vpn +var/lib/ipblocklist var/lib/suricata var/log/ip-acct/* var/log/rrd/* diff --git a/config/cfgroot/ipblocklist-functions.pl b/config/cfgroot/ipblocklist-functions.pl new file mode 100644 index 000000000..ecabf42e8 --- /dev/null +++ b/config/cfgroot/ipblocklist-functions.pl @@ -0,0 +1,386 @@ +#!/usr/bin/perl -w +############################################################################### +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2022 IPFire 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 2 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 . # +# # +############################################################################### + +package IPblocklist; + +require '/var/ipfire/general-functions.pl'; +require "${General::swroot}/ipblocklist/sources"; + +# The directory where all ipblocklist related files and settings are stored. +our $settings_dir = "/var/ipfire/ipblocklist"; + +# Main settings file. +our $settings_file = "$settings_dir/settings"; + +# The file which keeps the time, when a blocklist last has been modified. +our $modified_file = "$settings_dir/modified"; + +# Location where the blocklists in ipset compatible format are stored. +our $blocklist_dir = "/var/lib/ipblocklist"; + +# File extension of the blocklist files. +my $blocklist_file_extension = ".conf"; + +# Hash which calls the correct parser functions. +my %parsers = ( + 'ip-or-net-list' => \&parse_ip_or_net_list, + 'dshield' => \&parse_dshield +); + +# +## Function to get all available blocklists. +# +sub get_blocklists () { + my @blocklists; + + # Loop through the hash of blocklists. + foreach my $blocklist ( keys %IPblocklist::List::sources ) { + # Add the list to the array. + push(@blocklists, $blocklist); + } + + # Sort and return the array. + return sort(@blocklists); +} + +# +## Tiny function to get the full path and name of a given blocklist. +# +sub get_ipset_db_file($) { + my ($set) = @_; + + # Generate the + my $file = "$blocklist_dir/$set$blocklist_file_extension"; + + # Return the file name. + return $file; +} + +# +## The main download_and_create blocklist function. +## +## Uses LWP to download a given blocklist. The If-Modified-Since header is +## specified in the request so that only updated lists are downloaded (providing +## that the server supports this functionality). +## +## Once downloaded the list gets parsed, converted and stored in an ipset compatible +## format. +## +## Parameters: +## list The name of the blocklist +## +## Returns: +## nothing - On success +## not_modified - In case the servers responds with "Not modified" (304) +## dl_error - If the requested blocklist could not be downloaded. +## empty_list - The downloaded blocklist is empty, or the parser was not able to parse +## it correctly. +# +sub download_and_create_blocklist($) { + my ($list) = @_; + + # Check if the given blockist is known and data available. + unless($IPblocklist::List::sources{$list}) { + # No valid data for this blocklist - exit and return "1". + return 1; + } + + # The allowed maximum download size in bytes. + my $max_dl_bytes = 10_485_760; + + # The amount of download attempts before giving up and + # logging an error. + my $max_dl_attempts = 5; + + # Read proxysettings. + my %proxysettings=(); + &General::readhash("${General::swroot}/proxy/settings", \%proxysettings); + + # Load required perl module to handle the download. + use LWP::UserAgent; + + # Create a user agent for downloading the blacklist + # Limit the download size for safety + my $ua = LWP::UserAgent->new ( + ssl_opts => { + SSL_ca_file => '/etc/ssl/cert.pem', + verify_hostname => 1, + }, + + max_size => $max_dl_bytes, + ); + + # Set timeout to 10 seconds. + $ua->timeout(10); + + # Check if an upstream proxy is configured. + if ($proxysettings{'UPSTREAM_PROXY'}) { + my $proxy_url; + + $proxy_url = "http://"; + + # Check if the proxy requires authentication. + if (($proxysettings{'UPSTREAM_USER'}) && ($proxysettings{'UPSTREAM_PASSWORD'})) { + $proxy_url .= "$proxysettings{'UPSTREAM_USER'}\:$proxysettings{'UPSTREAM_PASSWORD'}\@"; + } + + # Add proxy server address and port. + $proxy_url .= $proxysettings{'UPSTREAM_PROXY'}; + + # Setup proxy settings. + $ua->proxy(['http', 'https'], $proxy_url); + } + + # Gather the details, when a list got modified last time. + my %modified = (); + + # Read-in data if the file exists. + &General::readhash($modified_file, \%modified ) if (-e $modified_file); + + # Get the last modified time for this list. + my $last_modified = gmtime($modified{$list} || 0); + + my $dl_attempt = 1; + my $response; + + # Download and rety on failure loop. + while ($dl_attempt <= $max_dl_attempts) { + # Try to determine if there is a newer blocklist since last time and grab it. + $response = $ua->get($IPblocklist::List::sources{$list}{'url'}, 'If-Modified-Since' => $last_modified ); + + # Check if the download attempt was successfull. + if ($response->is_success) { + # We successfully grabbed the list - no more retries needed, break the loop. + # Further process the script code. + last; + + # Exit, if the server responds with "Not modified (304). + } elsif ($response->code == 304) { + # Exit and return "not modified". + return "not_modified"; + + # Exit and log an erro + } elsif ($dl_attempt eq $max_dl_attempts) { + # Exit and return "dl_error". + return "dl_error"; + } + + # Increase download attempt counter. + $dl_attempt++; + } + + # Update the timestamp for the new or modified list. + if($response->last_modified) { + $modified{$list} = $response->last_modified; + } else { + $modified{$list} = time(); + } + + # Write-back the modified timestamps. + &General::writehash($modified_file, \%modified); + + # Parse and loop through the downloaded list. + my @blocklist = (); + + # Get the responsible parser for the current list. + my $parser = $parsers{$IPblocklist::List::sources{$list}{'parser'}}; + + # Loop through the grabbed raw list. + foreach my $line (split /[\r\n]+/, $response->content) { + # Remove newlines. + chomp $line; + + # Call the parser and obtain the addess or network. + my $address = &$parser($line); + + # Skip the line if it does not contain an address. + next unless ($address and $address =~ m/\d+\.\d+\.\d+\.\d+/); + + # Check if we got a single address. + if ($address =~ m|/32|) { + # Add /32 as prefix. + $address =~ s|/32||; + } + + # Push the address/network to the blocklist array. + push(@blocklist, $address); + } + + # Check if the content could be parsed correctly and the blocklist + # contains at least one item. + unless(@blocklist) { + # No entries - exit and return "empty_list". + return "empty_list"; + } + + # Get amount of entries in the blocklist array. + my $list_entries = scalar(@blocklist); + + # Optain the filename for this blocklist to save. + my $file = &get_ipset_db_file($list); + + # Open the file for writing. + open(FILE, ">", "$file") or die "Could not write to $file. $!\n"; + + # Write file header. + print FILE "#Autogenerated file. Any custom changes will be overwritten!\n\n"; + + # Calculate the hashsize for better list performance. + my $hashsize = &_calculate_hashsize($list_entries); + + # Simply set the limit of list elements to the double of current list elements. + my $maxelem = $list_entries *2; + + # Add "v4" suffix to the list name. + $list = "$list" . "v4"; + + # Write line to create the set. + # + # We safely can use hash:net as type because it supports single addresses and networks. + print FILE "create $list hash:net family inet hashsize $hashsize maxelem $maxelem -exist\n"; + + # Write line to flush the set itself during loading. + print FILE "flush $list\n"; + + # Loop through the array which contains the blocklist. + foreach my $entry (@blocklist) { + # Add the entry to the list. + print FILE "add $list $entry\n"; + } + + # Close the file handle. + close(FILE); + + # Finished. + return; +} + +# +## sub parse_ip_or_net_list( line ) +## +## Parses an input line, looking for lines starting with an IP Address or +### Network specification. +## +## Parameters: +## line The line to parse +## +## Returns: +## Either an IP Address or a null string +# +sub parse_ip_or_net_list( $ ) { + my ($line) = @_; + + # Grab the IP address or network. + $line =~ m|^(\d+\.\d+\.\d+\.\d+(?:/\d+)?)|; + + # Return the grabbed address. + return $1; +} + +# +## sub parse_dshield( line ) +## +## Parses an input line removing comments. +## +## The format is: +## Start Addrs End Addrs Netmask Nb Attacks Network Name Country email +## We're only interested in the start address and netmask. +## +## Parameters: +## line The line to parse +## +## Returns: +## Either and IP Address or a null string +# +sub parse_dshield( $ ) { + my ($line) = @_; + + # Skip coments. + return "" if ($line =~ m/^\s*#/); + + $line =~ s/#.*$//; + + # |Start addrs | |End Addrs | |Mask + $line =~ m|(\d+\.\d+\.\d+\.\d+(?:/\d+)?)\s+\d+\.\d+\.\d+\.\d+(?:/\d+)?\s+(\d+)|; + + # Return nothing if no start address could be grabbed. + return unless ($1); + + # Add /32 as prefix for single addresses and return it. + return "$1/32" unless ($2); + + # Return the obtained network. + return "$1/$2"; +} + +# +## Helper function to proper calculate the hashsize. +# +sub _calculate_hashsize($) { + my ($list_entries) = @_; + + my $hashsize = 1; + $hashsize <<= 1 while ($hashsize < $list_entries); + + # Return the calculated hashsize. + return $hashsize; +} + +# +## sub get_holdoff_rate(list) +## +## This function is used to get the holdoff rate in seconds for a desired provider, +## based on the configured rate limit in minutes (m), hours (h) or days (d) in the +## blacklist sources settings file. +## +# +sub get_holdoff_rate($) { + my ($list) = @_; + + # Grab the configured lookup rate for the given list. + my $rate = $IPblocklist::List::sources{$list}{'rate'}; + + # Split the grabbed rate into value and unit. + my ($value, $unit) = (uc $rate) =~ m/(\d+)([DHM]?)/; + + # Days + if ($unit eq 'D') { + $value *= 60 * 60 * 24; + + # Minutes + } elsif ($unit eq 'M') { + $value *= 60; + + # Everything else - assume hours. + } else { + $value *= 60 * 60; + } + + # Sanity check - limit to range 5 min .. 1 week + + # d h m s + $value = 5 * 60 if ($value < 5 * 60); + $value = 7 * 24 * 60 * 60 if ($value > 7 * 24 * 60 * 60); + + return $value; +} + +1; diff --git a/config/cron/crontab b/config/cron/crontab index c42104626..b727531fc 100644 --- a/config/cron/crontab +++ b/config/cron/crontab @@ -65,6 +65,9 @@ HOME=/ # Perform a surciata rules update every 12 hours. @ 12h [ -f "/var/ipfire/red/active" ] && /usr/local/bin/update-ids-ruleset >/dev/null 2>&1 +# Update Lists for IP-based blocking every 15 minutes. +@ 15 [ -f "/var/ipfire/red/active" ] && /usr/local/bin/update-ipblocklists >/dev/null 2>&1 + # Retry sending spooled mails regularly %hourly * /usr/sbin/dma -q diff --git a/config/firewall/rules.pl b/config/firewall/rules.pl index 432619184..e1d7718a8 100644 --- a/config/firewall/rules.pl +++ b/config/firewall/rules.pl @@ -26,6 +26,7 @@ require '/var/ipfire/general-functions.pl'; require "${General::swroot}/lang.pl"; require "/usr/lib/firewall/firewall-lib.pl"; require "${General::swroot}/location-functions.pl"; +require "${General::swroot}/ipblocklist-functions.pl"; # Set to one to enable debugging mode. my $DEBUG = 0; @@ -73,6 +74,10 @@ my %confignatfw=(); my %locationsettings = ( "LOCATIONBLOCK_ENABLED" => "off" ); +my %blocklistsettings= ( + "ENABLE" => "off", +); + my %ipset_loaded_sets = (); my @ipset_used_sets = (); @@ -82,6 +87,7 @@ my $configoutgoing = "${General::swroot}/firewall/outgoing"; my $locationfile = "${General::swroot}/firewall/locationblock"; my $configgrp = "${General::swroot}/fwhosts/customgroups"; my $netsettings = "${General::swroot}/ethernet/settings"; +my $blocklistfile = "${General::swroot}/ipblocklist/settings"; &General::readhash("${General::swroot}/firewall/settings", \%fwdfwsettings); &General::readhash("${General::swroot}/optionsfw/settings", \%fwoptions); @@ -97,9 +103,18 @@ if (-e "$locationfile") { &General::readhash("$locationfile", \%locationsettings); } +# Check if the ipblocklist settings file exits. +if (-e "$blocklistfile") { + # Read-in settings file. + &General::readhash("$blocklistfile", \%blocklistsettings); +} + # Get all available locations. my @locations = &Location::Functions::get_locations(); +# Get all supported blocklists. +my @blocklists = &IPblocklist::get_blocklists(); + # Name or the RED interface. my $RED_DEV = &General::get_red_interface(); @@ -144,6 +159,9 @@ sub main { # Load rules to block hostile networks. &drop_hostile_networks(); + # Handle ipblocklist. + &ipblocklist(); + # Reload firewall policy. run("/usr/sbin/firewall-policy"); @@ -708,6 +726,64 @@ sub drop_hostile_networks () { run("$IPTABLES -A HOSTILE -o $RED_DEV -m set --match-set $HOSTILE_CCODE dst -j HOSTILE_DROP"); } +sub ipblocklist () { + # Flush the ipblocklist chains. + run("$IPTABLES -F BLOCKLISTIN"); + run("$IPTABLES -F BLOCKLISTOUT"); + + # Check if the blocklist feature is enabled. + if($blocklistsettings{'ENABLE'} eq "on") { + # Loop through the array of private networks. + foreach my $private_network (@PRIVATE_NETWORKS) { + # Create firewall rules to never block private networks. + run("$IPTABLES -A BLOCKLISTIN -p ALL -i $RED_DEV -s $private_network -j RETURN"); + run("$IPTABLES -A BLOCKLISTOUT -p ALL -o $RED_DEV -d $private_network -j RETURN"); + } + } + + # Loop through the array of blocklists. + foreach my $blocklist (@blocklists) { + # Check if the blocklist feature and the current processed blocklist is enabled. + if(($blocklistsettings{'ENABLE'} eq "on") && ($blocklistsettings{$blocklist}) && ($blocklistsettings{$blocklist} eq "on")) { + # Call function to load the blocklist. + &ipset_restore($blocklist); + + # Call function to check if the corresponding iptables drop chain already has been created. + if(&firewall_chain_exists("${blocklist}_DROP")) { + # Create iptables chain. + run("$IPTABLES -N ${blocklist}_DROP"); + } else { + # Flush the chain. + run("$IPTABLES -F ${blocklist}_DROP"); + } + + # Check if logging is enabled. + if($blocklistsettings{'LOGGING'} eq "on") { + # Create logging rule. + run("$IPTABLES -A ${blocklist}_DROP -j LOG -m limit --limit 10/second --log-prefix \"BLKLST_$blocklist\" "); + } + + # Create Drop rule. + run("$IPTABLES -A ${blocklist}_DROP -j DROP"); + + # Add the rules to check against the set + run("$IPTABLES -A BLOCKLISTIN -p ALL -i $RED_DEV -m set --match-set $blocklist src -j ${blocklist}_DROP"); + run("$IPTABLES -A BLOCKLISTOUT -p ALL -o $RED_DEV -m set --match-set $blocklist dst -j ${blocklist}_DROP"); + + # IP blocklist or the blocklist is disabled. + } else { + # Check if the blocklist related iptables drop chain exits. + unless(&firewall_chain_exists("${blocklist}_DROP")) { + # Flush the chain. + run("$IPTABLES -F ${blocklist}_DROP"); + + # Drop the chain. + run("$IPTABLES -X ${blocklist}_DROP"); + } + } + } +} + sub get_protocols { my $hash = shift; my $key = shift; @@ -921,6 +997,14 @@ sub firewall_is_in_subnet { return 0; } +sub firewall_chain_exists ($) { + my ($chain) = @_; + + my $ret = &General::system("iptables", "--wait", "-n", "-L", "$chain"); + + return $ret; +} + sub ipset_get_sets () { my @sets; @@ -986,6 +1070,26 @@ sub ipset_restore ($) { # If the set is not loaded, we have to rename it to proper use it. run("$IPSET rename $loc_set $set"); } + + # Check if the given set name is a blocklist. + } elsif ($set ~~ @blocklists) { + # IPblocklist sets contains v4 as setname extension. + my $set_name = "$set" . "v4"; + + # Get the database file for the given blocklist. + my $db_file = &IPblocklist::get_ipset_db_file($set); + + # Call function to restore/load the set. + &ipset_call_restore($db_file); + + # Check if the set is already loaded (has been used before). + if ($set ~~ @ipset_used_sets) { + # Swap the sets. + run("$IPSET swap $set_name $set"); + } else { + # Rename the set to proper use it. + run("$IPSET rename $set_name $set"); + } } # Store the restored set to the hash to prevent from loading it again. diff --git a/config/ipblocklist/sources b/config/ipblocklist/sources new file mode 100644 index 000000000..4d42ad974 --- /dev/null +++ b/config/ipblocklist/sources @@ -0,0 +1,140 @@ +############################################################################ +# # +# IP Address blocklists for IPFire # +# # +# This file contains a list of blocklist sources that will replace the one # +# internal to the updated if it is found at /var/ipfire/blocklist/sources. # +# The intention is to provide a common source of information for both the # +# updater and WUI. # +# # +# The chains created in the packet filter will be named by the top level # +# key and this will also be used in the log message to identify the reason # +# for the dropped packet. # +# # +# The fields are: # +# # +# name The blocklist's full name # +# url URL of the file containing the list # +# info URL giving information about the source # +# parser The parser function used to extract IP addresses from the # +# downloaded list # +# rate Minimum period between checks for updates. Can be specified in # +# days (d), hours (h) or minutes (m) # +# category Used for documentation on the WUI. Can be one of the following # +# 'application' Potentially unwanted applications # +# 'attacker' Generic source of malicious packets # +# 'c and c' Malware Command and Control source # +# 'composite' Composite of other lists # +# 'invalid' Invalid addresses on the public internet # +# 'scanner' Port scanner that is not initself malicious # +# disable Name of another list to disable if this one is enabled. Used # +# when the other list is a subset of this one. # +# # +# The info and category fields are purely for documentation. # +# # +############################################################################ + +package IPblocklist::List; + +our %sources = ( 'EMERGING_FWRULE' => { 'name' => 'Emerging Threats Blocklist', + 'url' => 'https://rules.emergingthreats.net/fwrules/emerging-Block-IPs.txt', + 'info' => 'https://doc.emergingthreats.net/bin/view/Main/EmergingFirewallRules', + 'parser' => 'ip-or-net-list', + 'rate' => '1h', + 'category' => 'composite', + 'disable' => ['FEODO_RECOMMENDED', 'FEODO_IP', 'FEODO_AGGRESIVE', 'SPAMHAUS_DROP', 'DSHIELD'] }, + 'EMERGING_COMPROMISED' => { 'name' => 'Emerging Threats Compromised IPs', + 'url' => 'https://rules.emergingthreats.net/blockrules/compromised-ips.txt', + 'info' => 'https://doc.emergingthreats.net/bin/view/Main/CompromisedHost', + 'parser' => 'ip-or-net-list', + 'rate' => '1h', + 'category' => 'attacker' }, + 'SPAMHAUS_DROP' => { 'name' => "Spamhaus Don't Route or Peer List", + 'url' => 'https://www.spamhaus.org/drop/drop.txt', + 'info' => 'https://www.spamhaus.org/drop/', + 'parser' => 'ip-or-net-list', + 'rate' => '12h', + 'category' => 'reputation' }, + 'SPAMHAUS_EDROP' => { 'name' => "Spamhaus Extended Don't Route or Peer List", + 'url' => 'https://www.spamhaus.org/drop/edrop.txt', + 'info' => 'https://www.spamhaus.org/drop/', + 'parser' => 'ip-or-net-list', + 'rate' => '1h', + 'category' => 'reputation' }, + 'DSHIELD' => { 'name' => 'Dshield.org Recommended Block List', + 'url' => 'https://www.dshield.org/block.txt', + 'info' => 'https://dshield.org/', + 'parser' => 'dshield', + 'rate' => '1h', + 'category' => 'attacker' }, + 'FEODO_RECOMMENDED'=> {'name' => 'Feodo Trojan IP Blocklist (Recommended)', + 'url' => 'https://feodotracker.abuse.ch/downloads/ipblocklist_recommended.txt', + 'info' => 'https://feodotracker.abuse.ch/blocklist', + 'parser' => 'ip-or-net-list', + 'rate' => '5m', + 'category' => 'c and c' }, + 'FEODO_IP' => { 'name' => 'Feodo Trojan IP Blocklist', + 'url' => 'https://feodotracker.abuse.ch/downloads/ipblocklist.txt', + 'info' => 'https://feodotracker.abuse.ch/blocklist', + 'parser' => 'ip-or-net-list', + 'rate' => '5m', + 'category' => 'c and c', + 'disable' => 'FEODO_RECOMMENDED' }, + 'FEODO_AGGRESIVE' => { 'name' => 'Feodo Trojan IP Blocklist (Aggresive)', + 'url' => 'https://feodotracker.abuse.ch/downloads/ipblocklist_aggressive.txt', + 'info' => 'https://feodotracker.abuse.ch/blocklist', + 'parser' => 'ip-or-net-list', + 'rate' => '5m', + 'category' => 'c and c', + 'disable' => ['FEODO_IP', 'FEODO_RECOMMENDED'] }, + 'CIARMY' => { 'name' => 'The CINS Army List', + 'url' => 'https://cinsscore.com/list/ci-badguys.txt', + 'info' => 'https://cinsscore.com/#list', + 'parser' => 'ip-or-net-list', + 'rate' => '15m', + 'category' => 'reputation' }, + 'TOR_ALL' => { 'name' => 'Known TOR Nodes', + 'url' => 'https://www.dan.me.uk/torlist', + 'info' => 'https://www.dan.me.uk/tornodes', + 'parser' => 'ip-or-net-list', + 'rate' => '1h', + 'category' => 'application', + 'disable' => 'TOR_EXIT' }, + 'TOR_EXIT' => { 'name' => 'Known TOR Exit Nodes', + 'url' => 'https://www.dan.me.uk/torlist/?exit', + 'info' => 'https://www.dan.me.uk/tornodes', + 'parser' => 'ip-or-net-list',, + 'rate' => '1h', + 'category' => 'application' }, + 'ALIENVAULT' => { 'name' => 'AlienVault IP Reputation database', + 'url' => 'https://reputation.alienvault.com/reputation.generic', + 'info' => 'https://www.alienvault.com/resource-center/videos/what-is-ip-domain-reputation', + 'parser' => 'ip-or-net-list', + 'rate' => '1h', + 'category' => 'reputation' }, + 'BOGON' => { 'name' => 'Bogus address list (Martian)', + 'url' => 'https://www.team-cymru.org/Services/Bogons/bogon-bn-agg.txt', + 'info' => 'https://www.team-cymru.com/bogon-reference.html', + 'parser' => 'ip-or-net-list', + 'rate' => '1d', + 'category' => 'invalid' }, + 'BOGON_FULL' => { 'name' => 'Full Bogus Address List', + 'url' => 'https://www.team-cymru.org/Services/Bogons/fullbogons-ipv4.txt', + 'info' => 'https://www.team-cymru.com/bogon-reference.html', + 'parser' => 'ip-or-net-list', + 'rate' => '4h', + 'category' => 'invalid', + 'disable' => 'BOGON' }, + 'SHODAN' => { 'name' => 'ISC Shodan scanner blocklist', + 'url' => 'https://isc.sans.edu/api/threatlist/shodan?tab', + 'info' => 'https://isc.sans.edu', + 'parser' => 'ip-or-net-list', + 'rate' => '1d', + 'category' => 'scanner' }, + 'BLOCKLIST_DE' => { 'name' => 'Blocklist.de all attacks list', + 'url' => 'https://lists.blocklist.de/lists/all.txt', + 'info' => 'https://www.blocklist.de', + 'parser' => 'ip-or-net-list', + 'rate' => '30m', + 'category' => 'attacker' } + ); diff --git a/config/logwatch/ipblocklist b/config/logwatch/ipblocklist new file mode 100755 index 000000000..8d006377e --- /dev/null +++ b/config/logwatch/ipblocklist @@ -0,0 +1,91 @@ +########################################################################### +# ipblocklist script for Logwatch +# Analyzes the IPFire IP Blocklist log +# +######################################################################### + +######################################################## +## Copyright (c) 2008 Lars Skjærlund +## Covered under the included MIT/X-Consortium License: +## http://www.opensource.org/licenses/mit-license.php +## All modifications and contributions by other persons to +## this script are assumed to have been donated to the +## Logwatch project and thus assume the above copyright +## and licensing terms. If you want to make contributions +## under your own copyright or a different license this +## must be explicitly stated in the contribution and the +## Logwatch project reserves the right to not accept such +## contributions. If you have made significant +## contributions to this script and want to claim +## copyright please contact logwatch-devel@lists.sourceforge.net. +######################################################### + +######################################################################### +# Files - all shown with default paths: +# +# /usr/share/logwatch/default.conf/logfiles/messages.conf +# /usr/share/logwatch/dist.conf/services/blocklist.conf +# /usr/share/logwatch/scripts/services/ipblocklist (this file) +# +# ... and of course +# +# /var/log/messages +######################################################################### + +use Logwatch ':dates'; + +my $Detail = $ENV{'LOGWATCH_DETAIL_LEVEL'}; + +my $SearchDate; + +my %Updates; +my %Errors; + +$SearchDate = TimeFilter("%b %e"); + +while (defined(my $ThisLine = )) +{ + next unless ($ThisLine =~ m/^\s*\w+\s+\w+\s+(..:..:..) .* ipblocklist: (.*)/); + + my $text = $2; + + if ($text =~ m/Successfully updated (\w+) blocklist/) + { + $Updates{$1}{updates}++; + } + elsif ($text !~ m/Skipping (\w+) blocklist - Too frequent update attempts!/ and + $text !~ m/Skipping (\w+) blocklist - It has not been modified!/ ) + { + $Errors{$text}++; + } +} + +##################################################################### + +if (keys %Updates) +{ + print "\nThe following block lists were updated:\n"; + foreach my $Lists (sort keys %Updates) + { + print " $Lists: $Updates{$Lists}{updates} Time(s)\n"; + } +} + +if (keys %Errors) +{ + print "\nThe following errors were detected:\n"; + + foreach my $Text (keys %Errors) + { + print " $Text: $Errors{$Text} Time(s)\n"; + } +} + +exit(0); + +# vi: shiftwidth=3 tabstop=3 syntax=perl et +# Local Variables: +# mode: perl +# perl-indent-level: 3 +# indent-tabs-mode: nil +# End: diff --git a/config/logwatch/ipblocklist.conf b/config/logwatch/ipblocklist.conf new file mode 100644 index 000000000..a723da350 --- /dev/null +++ b/config/logwatch/ipblocklist.conf @@ -0,0 +1,34 @@ +######################################################################### +# ids-update script for Logwatch +# Analyzes the IPFire IP Blocklist update log +# +# Version: 1.0.0 +# Initial release +# +######################################################################### + +######################################################################### +# This script is subject to the same copyright as Logwatch itself +######################################################################### + +######################################################################### +# Files - all shown with default paths: +# +# /usr/share/logwatch/default.conf/logfiles/messages.conf +# /usr/share/logwatch/dist.conf/services/blocklist.conf (this file) +# /usr/share/logwatch/scripts/services/blocklist +# +# ... and of course +# +# /var/log/messages +######################################################################### + + +Title = "IP Blocklist" + +# Which logfile group... +LogFile = messages + +*applystddate + +# vi: shiftwidth=3 tabstop=3 et diff --git a/config/menu/50-firewall.menu b/config/menu/50-firewall.menu index aa67d9007..cd2af7cbb 100644 --- a/config/menu/50-firewall.menu +++ b/config/menu/50-firewall.menu @@ -21,6 +21,11 @@ 'title' => "$Lang::tr{'intrusion detection system'}", 'enabled' => 1, }; + $subfirewall->{'50.ipblocklist'} = {'caption' => $Lang::tr{'ipblocklist'}, + 'uri' => '/cgi-bin/ipblocklist.cgi', + 'title' => "$Lang::tr{'ipblocklist'}", + 'enabled' => 1, + }; $subfirewall->{'60.locationblock'} = { 'caption' => $Lang::tr{'locationblock'}, 'uri' => '/cgi-bin/location-block.cgi', diff --git a/config/menu/70-log.menu b/config/menu/70-log.menu index 2fa0e426e..19add1fb1 100644 --- a/config/menu/70-log.menu +++ b/config/menu/70-log.menu @@ -43,6 +43,11 @@ 'title' => "$Lang::tr{'ids logs'}", 'enabled' => 1 }; + $sublogs->{'53.ipblocklist'} = {'caption' => $Lang::tr{'ipblocklist logs'}, + 'uri' => '/cgi-bin/logs.cgi/ipblocklists.dat', + 'title' => "$Lang::tr{'ipblocklist logs'}", + 'enabled' => 1 + }; $sublogs->{'55.ovpnclients'} = { 'caption' => $Lang::tr{'ovpn rw connection log'}, 'uri' => '/cgi-bin/logs.cgi/ovpnclients.dat', diff --git a/config/rootfiles/common/aarch64/stage2 b/config/rootfiles/common/aarch64/stage2 index d528b788c..9911bb0d5 100644 --- a/config/rootfiles/common/aarch64/stage2 +++ b/config/rootfiles/common/aarch64/stage2 @@ -106,6 +106,7 @@ usr/local/bin/settime usr/local/bin/timecheck usr/local/bin/timezone-transition usr/local/bin/update-ids-ruleset +usr/local/bin/update-ipblocklists usr/local/bin/update-lang-cache usr/local/bin/update-location-database #usr/local/include @@ -154,6 +155,7 @@ var/cache/ldconfig var/cache/ldconfig/aux-cache var/empty #var/lib +var/lib/ipblocklist #var/lib/misc #var/local var/lock diff --git a/config/rootfiles/common/armv6l/stage2 b/config/rootfiles/common/armv6l/stage2 index 047591d2b..634b843c2 100644 --- a/config/rootfiles/common/armv6l/stage2 +++ b/config/rootfiles/common/armv6l/stage2 @@ -104,6 +104,7 @@ usr/local/bin/settime usr/local/bin/timecheck usr/local/bin/timezone-transition usr/local/bin/update-ids-ruleset +usr/local/bin/update-ipblocklists usr/local/bin/update-lang-cache usr/local/bin/update-location-database #usr/local/include @@ -152,6 +153,7 @@ var/cache/ldconfig var/cache/ldconfig/aux-cache var/empty #var/lib +var/lib/ipblocklist #var/lib/misc #var/local var/lock diff --git a/config/rootfiles/common/configroot b/config/rootfiles/common/configroot index fef5ffbcf..088e87f5b 100644 --- a/config/rootfiles/common/configroot +++ b/config/rootfiles/common/configroot @@ -81,6 +81,9 @@ var/ipfire/graphs.pl var/ipfire/header.pl var/ipfire/location-functions.pl var/ipfire/ids-functions.pl +var/ipfire/ipblocklist-functions.pl +var/ipfire/ipblocklist +#var/ipfire/ipblocklist/settings var/ipfire/isdn #var/ipfire/isdn/settings var/ipfire/key diff --git a/config/rootfiles/common/ipblocklist-sources b/config/rootfiles/common/ipblocklist-sources new file mode 100644 index 000000000..e1789037b --- /dev/null +++ b/config/rootfiles/common/ipblocklist-sources @@ -0,0 +1 @@ +var/ipfire/ipblocklist/sources diff --git a/config/rootfiles/common/logwatch b/config/rootfiles/common/logwatch index 40d90cd96..91e2d37b3 100644 --- a/config/rootfiles/common/logwatch +++ b/config/rootfiles/common/logwatch @@ -195,6 +195,7 @@ usr/share/logwatch/default.conf/services/zz-sys.conf usr/share/logwatch/dist.conf/logfiles usr/share/logwatch/dist.conf/services usr/share/logwatch/dist.conf/services/dialup.conf +usr/share/logwatch/dist.conf/services/ipblocklist.conf #usr/share/logwatch/lib usr/share/logwatch/lib/Logwatch.pm #usr/share/logwatch/scripts @@ -260,6 +261,7 @@ usr/share/logwatch/scripts/services/http usr/share/logwatch/scripts/services/imapd #usr/share/logwatch/scripts/services/in.qpopper usr/share/logwatch/scripts/services/init +usr/share/logwatch/scripts/services/ipblocklist usr/share/logwatch/scripts/services/ipop3d usr/share/logwatch/scripts/services/iptables usr/share/logwatch/scripts/services/kernel diff --git a/config/rootfiles/common/web-user-interface b/config/rootfiles/common/web-user-interface index cae8dde32..33f0d30a7 100644 --- a/config/rootfiles/common/web-user-interface +++ b/config/rootfiles/common/web-user-interface @@ -27,6 +27,7 @@ srv/web/ipfire/cgi-bin/hardwaregraphs.cgi srv/web/ipfire/cgi-bin/hosts.cgi srv/web/ipfire/cgi-bin/ids.cgi srv/web/ipfire/cgi-bin/index.cgi +srv/web/ipfire/cgi-bin/ipblocklist.cgi srv/web/ipfire/cgi-bin/ipinfo.cgi srv/web/ipfire/cgi-bin/iptables.cgi srv/web/ipfire/cgi-bin/location-block.cgi @@ -38,9 +39,11 @@ srv/web/ipfire/cgi-bin/logs.cgi/firewalllogcountry.dat srv/web/ipfire/cgi-bin/logs.cgi/firewalllogip.dat srv/web/ipfire/cgi-bin/logs.cgi/firewalllogport.dat srv/web/ipfire/cgi-bin/logs.cgi/ids.dat +srv/web/ipfire/cgi-bin/logs.cgi/ipblocklists.dat srv/web/ipfire/cgi-bin/logs.cgi/log.dat srv/web/ipfire/cgi-bin/logs.cgi/ovpnclients.dat srv/web/ipfire/cgi-bin/logs.cgi/proxylog.dat +srv/web/ipfire/cgi-bin/logs.cgi/showrequestfromblocklist.dat srv/web/ipfire/cgi-bin/logs.cgi/showrequestfromcountry.dat srv/web/ipfire/cgi-bin/logs.cgi/showrequestfromip.dat srv/web/ipfire/cgi-bin/logs.cgi/showrequestfromport.dat diff --git a/config/rootfiles/common/x86_64/stage2 b/config/rootfiles/common/x86_64/stage2 index 5fa1fb44f..c54fccc20 100644 --- a/config/rootfiles/common/x86_64/stage2 +++ b/config/rootfiles/common/x86_64/stage2 @@ -106,6 +106,7 @@ usr/local/bin/settime usr/local/bin/timecheck usr/local/bin/timezone-transition usr/local/bin/update-ids-ruleset +usr/local/bin/update-ipblocklists usr/local/bin/update-lang-cache usr/local/bin/update-location-database #usr/local/include @@ -154,6 +155,7 @@ var/cache/ldconfig var/cache/ldconfig/aux-cache var/empty #var/lib +var/lib/ipblocklist #var/lib/misc #var/local var/lock diff --git a/config/rootfiles/oldcore/168/update.sh b/config/rootfiles/oldcore/168/update.sh index 6bb081ff2..418c49399 100644 --- a/config/rootfiles/oldcore/168/update.sh +++ b/config/rootfiles/oldcore/168/update.sh @@ -143,6 +143,22 @@ if grep -q "ENABLED=on" /var/ipfire/vpn/settings; then /etc/init.d/ipsec start fi +# krb5 is now part of the core system, remove Pakfire metadata for it +if [ -e "/opt/pakfire/db/installed/meta-krb5" ] && [ -e "/opt/pakfire/db/meta/meta-krb5" ]; then + rm -vf \ + /opt/pakfire/db/installed/meta-krb5 \ + /opt/pakfire/db/meta/meta-krb5 \ + /opt/pakfire/db/rootfiles/krb5 +fi + +# libtiff is now part of the core system, remove Pakfire metadata for it +if [ -e "/opt/pakfire/db/installed/meta-libtiff" ] && [ -e "/opt/pakfire/db/meta/meta-libtiff" ]; then + rm -vf \ + /opt/pakfire/db/installed/meta-libtiff \ + /opt/pakfire/db/meta/meta-libtiff \ + /opt/pakfire/db/rootfiles/libtiff +fi + # This update needs a reboot... touch /var/run/need_reboot diff --git a/html/cgi-bin/ipblocklist.cgi b/html/cgi-bin/ipblocklist.cgi new file mode 100644 index 000000000..431ff1a37 --- /dev/null +++ b/html/cgi-bin/ipblocklist.cgi @@ -0,0 +1,260 @@ +#!/usr/bin/perl + +############################################################################### +# # +# IPFire.org - A linux based firewall # +# # +# 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 . # +# # +# Copyright (C) 2018 - 2020 The IPFire Team # +# # +############################################################################### + +use strict; + +# enable the following only for debugging purposes +#use warnings; +#use CGI::Carp 'fatalsToBrowser'; + +require '/var/ipfire/general-functions.pl'; +require "${General::swroot}/lang.pl"; +require "${General::swroot}/header.pl"; +require "${General::swroot}/ipblocklist-functions.pl"; + +# Import blockist sources and settings file. +require "${General::swroot}/ipblocklist/sources"; + +############################################################################### +# Configuration variables +############################################################################### + +my $settings = "${General::swroot}/ipblocklist/settings"; +my %cgiparams = ('ACTION' => ''); + +############################################################################### +# Variables +############################################################################### + +my $errormessage = ''; +my $headline = "$Lang::tr{'error message'}"; +my $updating = 0; +my %mainsettings; +my %color; + +# Default settings - normally overwritten by settings file +my %settings = ( + 'DEBUG' => 0, + 'LOGGING' => 'on', + 'ENABLE' => 'off' +); + +# Read all parameters +&Header::getcgihash( \%cgiparams); +&General::readhash( "${General::swroot}/main/settings", \%mainsettings ); +&General::readhash( "/srv/web/ipfire/html/themes/".$mainsettings{'THEME'}."/include/colors.txt", \%color ); + +# Get list of supported blocklists. +my @blocklists = &IPblocklist::get_blocklists(); + +# Show Headers +&Header::showhttpheaders(); + +# Process actions +if ($cgiparams{'ACTION'} eq "$Lang::tr{'save'}") { + # Array to store if blocklists are missing on the system + # and needs to be downloaded first. + my @missing_blocklists = (); + + # Loop through the array of supported blocklists. + foreach my $blocklist (@blocklists) { + # Skip the blocklist if it is not enabled. + next if($cgiparams{$blocklist} ne "on"); + + # Get the file name which keeps the converted blocklist. + my $ipset_db_file = &IPblocklist::get_ipset_db_file($blocklist); + + # Check if the blocklist already has been downloaded. + if(-f "$ipset_db_file") { + # Blocklist already exits, we can skip it. + next; + } else { + # Blocklist not present, store in array to download it. + push(@missing_blocklists, $blocklist); + } + } + + # Check if the red device is not active and blocklists are missing. + if ((not -e "${General::swroot}/red/active") && (@missing_blocklists)) { + # The system is offline, cannot download the missing blocklists. + # Store an error message. + $errormessage = "$Lang::tr{'system is offline'}"; + } else { + # Loop over the array of missing blocklists. + foreach my $missing_blocklist (@missing_blocklists) { + # Call the download and convert function to get the missing blocklist. + my $status = &IPblocklist::download_and_create_blocklist($missing_blocklist); + + # Check if there was an error during download. + if ($status eq "dl_error") { + $errormessage = "$Lang::tr{'ipblocklist could not download blocklist'} - $Lang::tr{'ipblocklist download error'}"; + } elsif ($status eq "empty_list") { + $errormessage = "$Lang::tr{'ipblocklist could not download blocklist'} - $Lang::tr{'ipblocklist empty blocklist received'}"; + } + } + } + + # Check if there was an error. + unless($errormessage) { + # Write configuration hash. + &General::writehash($settings, \%cgiparams); + + # Call function to mark a required reload of the firewall. + &General::firewall_config_changed(); + + # Display notice about a required reload of the firewall. + $headline = "$Lang::tr{'notice'}"; + $errormessage = "$Lang::tr{'fw rules reload notice'}"; + } +} + +# Show site +&Header::openpage($Lang::tr{'ipblocklist'}, 1, ''); +&Header::openbigbox('100%', 'left'); + +# Display error message if there was one. +&error() if ($errormessage); + +# Read-in ipblocklist settings. +&General::readhash( $settings, \%settings ) if (-r $settings); + +# Display configuration section. +&configsite(); + +# End of page +&Header::closebigbox(); +&Header::closepage(); + + +#------------------------------------------------------------------------------ +# sub configsite() +# +# Displays configuration +#------------------------------------------------------------------------------ + +sub configsite { + # Find preselections + my $enable = 'checked'; + + &Header::openbox('100%', 'left', $Lang::tr{'settings'}); + + # Enable checkbox + $enable = ($settings{'ENABLE'} eq 'on') ? ' checked' : ''; + +print< + + + + + +
$Lang::tr{'ipblocklist use ipblocklists'}

+END + + # The following are only displayed if the blacklists are enabled + $enable = ($settings{'LOGGING'} eq 'on') ? ' checked' : ''; + +print < + + + + + +
$Lang::tr{'ipblocklist log'}
+ +

+

$Lang::tr{'ipblocklist blocklist settings'}

+ + + + + + + + +END + + # Iterate through the list of sources + my $lines = 0; + + foreach my $blocklist (@blocklists) { + # Display blocklist name or provide a link to the website if available. + my $website = "$blocklist"; + if ($IPblocklist::List::sources{$blocklist}{info}) { + $website ="$blocklist"; + } + + # Get the full name for the blocklist. + my $name = &CGI::escapeHTML( $IPblocklist::List::sources{$blocklist}{'name'} ); + + # Get category for this blocklist. + my $category = $Lang::tr{"ipblocklist category $IPblocklist::List::sources{$blocklist}{'category'}"}; + + # Determine if the blocklist is enabled. + my $enable = ''; + $enable = 'checked' if ($settings{$blocklist} eq 'on'); + + # Set colour for the table columns. + my $col = ($lines++ % 2) ? "bgcolor='$color{'color20'}'" : "bgcolor='$color{'color22'}'"; + + +print < + + + + + +END + } + +# The save button at the bottom of the table +print < + + + +
$Lang::tr{'ipblocklist id'}$Lang::tr{'ipblocklist name'}$Lang::tr{'ipblocklist category'}$Lang::tr{'ipblocklist enable'}
$website$name$category
+ + + +
+ +END + + &Header::closebox(); +} + +#------------------------------------------------------------------------------ +# sub error() +# +# Shows error messages +#------------------------------------------------------------------------------ + +sub error { + &Header::openbox('100%', 'left', $headline); + print "$errormessage\n"; + print " \n"; + &Header::closebox(); +} diff --git a/html/cgi-bin/logs.cgi/ipblocklists.dat b/html/cgi-bin/logs.cgi/ipblocklists.dat new file mode 100755 index 000000000..2435137b5 --- /dev/null +++ b/html/cgi-bin/logs.cgi/ipblocklists.dat @@ -0,0 +1,366 @@ +#!/usr/bin/perl +# +# SmoothWall CGIs +# +# This code is distributed under the terms of the GPL +# +# JC HERITIER +# page inspired from the initial firewalllog.dat +# +# Modified for IPFire by Christian Schmidt +# and Michael Tremer (www.ipfire.org) + +use strict; + +# enable only the following on debugging purpose +#use warnings; +#use CGI::Carp 'fatalsToBrowser'; + +require '/var/ipfire/general-functions.pl'; +require "${General::swroot}/lang.pl"; +require "${General::swroot}/header.pl"; + +require "${General::swroot}/ipblocklist-functions.pl"; +require "${General::swroot}/ipblocklist/sources"; + +use POSIX(); + +my %cgiparams=(); +my $errormessage = ''; + +my @shortmonths = ( 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', + 'Sep', 'Oct', 'Nov', 'Dec' ); +my @longmonths = ( $Lang::tr{'january'}, $Lang::tr{'february'}, $Lang::tr{'march'}, + $Lang::tr{'april'}, $Lang::tr{'may'}, $Lang::tr{'june'}, $Lang::tr{'july'}, $Lang::tr{'august'}, + $Lang::tr{'september'}, $Lang::tr{'october'}, $Lang::tr{'november'}, + $Lang::tr{'december'} ); + +my @now = localtime(); +my $dow = $now[6]; +my $doy = $now[7]; +my $tdoy = $now[7]; +my $year = $now[5]+1900; + +$cgiparams{'DAY'} = $now[3]; +$cgiparams{'MONTH'} = $now[4]; +$cgiparams{'ACTION'} = ''; + +&Header::getcgihash(\%cgiparams); + +my $start = -1; +if ($ENV{'QUERY_STRING'} && $cgiparams{'ACTION'} ne $Lang::tr{'update'}) +{ + my @temp = split(',',$ENV{'QUERY_STRING'}); + $start = $temp[0]; + $cgiparams{'MONTH'} = $temp[1]; + $cgiparams{'DAY'} = $temp[2]; +} + +if (!($cgiparams{'MONTH'} =~ /^(0|1|2|3|4|5|6|7|8|9|10|11)$/) || + !($cgiparams{'DAY'} =~ /^(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31)$/)) +{ + $cgiparams{'DAY'} = $now[3]; + $cgiparams{'MONTH'} = $now[4]; +} +elsif($cgiparams{'ACTION'} eq '>>') +{ + my @temp_then=(); + my @temp_now = localtime(time); + $temp_now[4] = $cgiparams{'MONTH'}; + $temp_now[3] = $cgiparams{'DAY'}; + @temp_then = localtime(POSIX::mktime(@temp_now) + 86400); + ## Retrieve the same time on the next day - + ## 86400 seconds in a day + $cgiparams{'MONTH'} = $temp_then[4]; + $cgiparams{'DAY'} = $temp_then[3]; +} +elsif($cgiparams{'ACTION'} eq '<<') +{ + my @temp_then=(); + my @temp_now = localtime(time); + $temp_now[4] = $cgiparams{'MONTH'}; + $temp_now[3] = $cgiparams{'DAY'}; + @temp_then = localtime(POSIX::mktime(@temp_now) - 86400); + ## Retrieve the same time on the previous day - + ## 86400 seconds in a day + $cgiparams{'MONTH'} = $temp_then[4]; + $cgiparams{'DAY'} = $temp_then[3]; +} + +if (($cgiparams{'DAY'} ne $now[3]) || ($cgiparams{'MONTH'} ne $now[4])) +{ + my @then = (); + if ( ( $cgiparams{'MONTH'} eq $now[4]) && ($cgiparams{'DAY'} > $now[3]) || + ( $cgiparams{'MONTH'} > $now[4] ) ) { + @then = localtime(POSIX::mktime( 0, 0, 0, $cgiparams{'DAY'}, $cgiparams{'MONTH'}, $year - 1901 )); + } else { + @then = localtime(POSIX::mktime( 0, 0, 0, $cgiparams{'DAY'}, $cgiparams{'MONTH'}, $year - 1900 )); + } + $tdoy = $then[7]; + my $lastleap=($year-1)%4; + if ($tdoy>$doy) { + if ($lastleap == 0 && $tdoy < 60) { + $doy=$tdoy+366; + } else { + $doy=$doy+365; + } + } +} + +my $datediff=0; +my $dowd=0; +my $multifile=0; +if ($tdoy ne $doy) { + $datediff=int(($doy-$tdoy)/7); + $dowd=($doy-$tdoy)%7; + if (($dow-$dowd)<1) { + $datediff=$datediff+1; + } + if (($dow-$dowd)==0) { + $multifile=1; + } +} + +my $monthstr = $shortmonths[$cgiparams{'MONTH'}]; +my $longmonthstr = $longmonths[$cgiparams{'MONTH'}]; +my $day = $cgiparams{'DAY'}; +my $daystr=''; +if ($day <= 9) { + $daystr = " $day"; } +else { + $daystr = $day; +} + +my %lists; +my %directions; +my %sources = (); +my %settings = (); +&General::readhash("${General::swroot}/ipblocklist/settings", \%settings); + +# Get all available blocklists. +my @blocklists = &IPblocklist::get_blocklists(); + +foreach my $blocklist (@blocklists) +{ + $lists{$blocklist} = {} if ($settings{$blocklist} eq 'on'); +} + +my $skip=0; +my $filestr=''; +if ($datediff==0) { + $filestr="/var/log/messages"; +} else { + $filestr="/var/log/messages.$datediff"; + $filestr = "$filestr.gz" if -f "$filestr.gz"; +} + +if (!(open (FILE,($filestr =~ /.gz$/ ? "gzip -dc $filestr |" : $filestr)))) { + $errormessage = "$Lang::tr{'date not in logs'}: $filestr $Lang::tr{'could not be opened'}"; + $skip=1; + # Note: This is in case the log does not exist for that date +} + +my $lines = 0; +my $directions = 0; + +if (!$skip) +{ + while () + { + if (/^${monthstr} ${daystr} ..:..:.. [\w\-]+ kernel:.*BLKLST_(\w+)\s*IN=(\w*)/) + { + my $list = $1; + + if ($2 =~ m/ppp|red/) + { + $lists{$list}{in}++; + $directions{in}++; + } + else + { + $lists{$list}{out}++; + $directions{out}++; + } + + $lines++; + } + + } + close (FILE); +} + +if ($multifile) { + $datediff=$datediff-1; + if ($datediff==0) { + $filestr="/var/log/messages"; + } else { + $filestr="/var/log/messages.$datediff"; + $filestr = "$filestr.gz" if -f "$filestr.gz"; + } + if (!(open (FILE,($filestr =~ /.gz$/ ? "gzip -dc $filestr |" : $filestr)))) { + $errormessage="$Lang::tr{'date not in logs'}: $filestr $Lang::tr{'could not be opened'}"; + $skip=1; + } + if (!$skip) { + while () { + if (/^${monthstr} ${daystr} ..:..:.. [\w\-]+ kernel:.*BLKLST_(\w+)\s*IN=(\w+)/) + { + my $list = $1; + + if ($2 =~ m/ppp|red/) + { + $lists{$list}{in}++; + $directions{in}++; + } + else + { + $lists{$list}{out}++; + $directions{out}++; + } + + $lines++; + } + } + close (FILE); + } +} + +my $MODNAME="fwlogs"; + +&Header::showhttpheaders(); +&Header::openpage($Lang::tr{'ipblocklist logs'}, 1, ''); +&Header::openbigbox('100%', 'left', '', $errormessage); + + +if ($errormessage) { + &Header::openbox('100%', 'left', $Lang::tr{'error messages'}); + print "$errormessage \n"; + &Header::closebox(); +} + +&Header::openbox('100%', 'left', "$Lang::tr{'settings'}"); + +print < + + + + + + + + + + +
$Lang::tr{'month'}:  +  $Lang::tr{'day'}:  +
+ +END +; + +&Header::closebox(); + +&Header::openbox('100%', 'left', $Lang::tr{'firewall log'}); +print "

$Lang::tr{'ipblocklist hits'} $longmonthstr $daystr: $lines

"; + +my %color = (); +my %mainsettings = (); +&General::readhash("${General::swroot}/main/settings", \%mainsettings); +&General::readhash("/srv/web/ipfire/html/themes/ipfire/include/colors.txt", \%color); + +my @lists = sort keys (%lists); + +print < + + +$Lang::tr{'ipblocklist id'} +$Lang::tr{'ipblocklist category'} +$Lang::tr{'ipblocklist input'} +$Lang::tr{'ipblocklist output'} + + +$Lang::tr{'count'} +$Lang::tr{'percentage'} +$Lang::tr{'count'} +$Lang::tr{'percentage'} + +END +; + +$lines = 0; +my $lists = join ',', @lists; + +foreach my $list (@lists) +{ + my $col = ($lines++ % 2) ? "bgcolor='$color{'color20'}'" : "bgcolor='$color{'color22'}'"; + my $category = exists( $IPblocklist::List::sources{$list}) ? $Lang::tr{"ipblocklist category $IPblocklist::List::sources{$list}{'category'}"} : ' '; + + print ""; + + print "
"; + + if (exists($IPblocklist::List::sources{$list}) and $IPblocklist::List::sources{$list}{'info'}) + { + print "$list"; + } + else + { + print "$list"; + } + + print "$category"; + + foreach my $direction ('in', 'out') + { + my $count = $lists{$list}{$direction} || 0; + my $percent = $directions{$direction} > 0 ? $count * 100 / $directions{$direction} : 0; + $percent = sprintf("%.f", $percent); + print "$count"; + print "$percent%"; + } + + print ""; +} +print < +END +; + +&Header::closebox(); +&Header::closebigbox(); +&Header::closepage(); + +sub checkversion { + #Automatic Updates is disabled + return "0","0"; +} diff --git a/html/cgi-bin/logs.cgi/log.dat b/html/cgi-bin/logs.cgi/log.dat index 3364b7bea..20f61d4ea 100644 --- a/html/cgi-bin/logs.cgi/log.dat +++ b/html/cgi-bin/logs.cgi/log.dat @@ -59,6 +59,7 @@ my %sections = ( 'dhcp' => '(dhcpd: )', 'dma' => '(dma: |dma\[.*\]: |postfix/\w*\[\d*\]: )', 'guardian' => '(guardian\[.*\]: )', + 'ipblocklist' => '(ipblocklist: )', 'ipfire' => '(ipfire: )', 'ipsec' => '(ipsec_[\w_]+: |pluto\[.*\]: |charon: |vpnwatch: )', 'kernel' => '(kernel: (?!DROP_))', @@ -89,6 +90,7 @@ my %trsections = ( 'dhcp' => "$Lang::tr{'dhcp server'}", 'dma' => 'Mail', 'guardian' => "$Lang::tr{'guardian'}", + 'ipblocklist' => "$Lang::tr{'ipblocklist'}", 'ipfire' => 'IPFire', 'ipsec' => 'IPSec', 'kernel' => "$Lang::tr{'kernel'}", diff --git a/html/cgi-bin/logs.cgi/showrequestfromblocklist.dat b/html/cgi-bin/logs.cgi/showrequestfromblocklist.dat new file mode 100755 index 000000000..98524614f --- /dev/null +++ b/html/cgi-bin/logs.cgi/showrequestfromblocklist.dat @@ -0,0 +1,414 @@ +#!/usr/bin/perl +# SmoothWall CGIs +# +# This code is distributed under the terms of the GPL +# +# JC HERITIER +# page inspired from the initial firewalllog.dat +# +# Modified for IPFire by Christian Schmidt (www.ipfire.org) + +# enable only the following on debugging purpose +#use warnings; +#use CGI::Carp 'fatalsToBrowser'; + +require '/var/ipfire/general-functions.pl'; +require "${General::swroot}/lang.pl"; +require "${General::swroot}/header.pl"; + +use POSIX(); + +#workaround to suppress a warning when a variable is used only once +my @dummy = ( ${Header::table2colour} ); +undef (@dummy); + +my %cgiparams=(); +my %logsettings=(); +my $errormessage = ''; + +my @shortmonths = ( 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', + 'Sep', 'Oct', 'Nov', 'Dec' ); +my @longmonths = ( $Lang::tr{'january'}, $Lang::tr{'february'}, $Lang::tr{'march'}, + $Lang::tr{'april'}, $Lang::tr{'may'}, $Lang::tr{'june'}, $Lang::tr{'july'}, $Lang::tr{'august'}, + $Lang::tr{'september'}, $Lang::tr{'october'}, $Lang::tr{'november'}, + $Lang::tr{'december'} ); + +my @now = localtime(); +my $dow = $now[6]; +my $doy = $now[7]; +my $tdoy = $now[7]; +my $year = $now[5]+1900; + +$cgiparams{'DAY'} = $now[3]; +$cgiparams{'MONTH'} = $now[4]; +$cgiparams{'ACTION'} = ''; + +&Header::getcgihash(\%cgiparams); + +$logsettings{'LOGVIEW_REVERSE'} = 'off'; +&General::readhash("${General::swroot}/logging/settings", \%logsettings); + +my $start = -1; +my @blocklists; +if ($ENV{'QUERY_STRING'} && $cgiparams{'ACTION'} ne $Lang::tr{'update'}) +{ + my @temp = split(',',$ENV{'QUERY_STRING'}, 5); + $start = shift @temp; + $cgiparams{'MONTH'} = shift @temp; + $cgiparams{'DAY'} = shift @temp; + $cgiparams{'blocklist'} = shift @temp; + $cgiparams{'blocklists'} = shift @temp; +} + +if (!($cgiparams{'MONTH'} =~ /^(0|1|2|3|4|5|6|7|8|9|10|11)$/) || + !($cgiparams{'DAY'} =~ /^(1|2|3|4|5|6|7|8|9|10|11|12|13|14|15|16|17|18|19|20|21|22|23|24|25|26|27|28|29|30|31)$/)) +{ + $cgiparams{'DAY'} = $now[3]; + $cgiparams{'MONTH'} = $now[4]; +} +elsif($cgiparams{'ACTION'} eq '>>') +{ + my @temp_then=(); + my @temp_now = localtime(time); + $temp_now[4] = $cgiparams{'MONTH'}; + $temp_now[3] = $cgiparams{'DAY'}; + @temp_then = localtime(POSIX::mktime(@temp_now) + 86400); + ## Retrieve the same time on the next day - + ## 86400 seconds in a day + $cgiparams{'MONTH'} = $temp_then[4]; + $cgiparams{'DAY'} = $temp_then[3]; +} +elsif($cgiparams{'ACTION'} eq '<<') +{ + my @temp_then=(); + my @temp_now = localtime(time); + $temp_now[4] = $cgiparams{'MONTH'}; + $temp_now[3] = $cgiparams{'DAY'}; + @temp_then = localtime(POSIX::mktime(@temp_now) - 86400); + ## Retrieve the same time on the previous day - + ## 86400 seconds in a day + $cgiparams{'MONTH'} = $temp_then[4]; + $cgiparams{'DAY'} = $temp_then[3]; +} + +if (($cgiparams{'DAY'} ne $now[3]) || ($cgiparams{'MONTH'} ne $now[4])) +{ + my @then = (); + if ( ( $cgiparams{'MONTH'} eq $now[4]) && ($cgiparams{'DAY'} > $now[3]) || + ( $cgiparams{'MONTH'} > $now[4] ) ) { + @then = localtime(POSIX::mktime( 0, 0, 0, $cgiparams{'DAY'}, $cgiparams{'MONTH'}, $year - 1901 )); + } else { + @then = localtime(POSIX::mktime( 0, 0, 0, $cgiparams{'DAY'}, $cgiparams{'MONTH'}, $year - 1900 )); + } + $tdoy = $then[7]; + my $lastleap=($year-1)%4; + if ($tdoy>$doy) { + if ($lastleap == 0 && $tdoy < 60) { + $doy=$tdoy+366; + } else { + $doy=$doy+365; + } + } +} + +if ($cgiparams{'blocklists'}) +{ + @blocklists = split ',', $cgiparams{'blocklists'}; +} + +my $datediff=0; +my $dowd=0; +my $multifile=0; +if ($tdoy ne $doy) { + $datediff=int(($doy-$tdoy)/7); + $dowd=($doy-$tdoy)%7; + if (($dow-$dowd)<1) { + $datediff=$datediff+1; + } + if (($dow-$dowd)==0) { + $multifile=1; + } +} + +my $monthstr = $shortmonths[$cgiparams{'MONTH'}]; +my $longmonthstr = $longmonths[$cgiparams{'MONTH'}]; +my $day = $cgiparams{'DAY'}; +my $daystr=''; +if ($day <= 9) { + $daystr = " $day"; } +else { + $daystr = $day; +} + +my $skip=0; +my $filestr=''; +if ($datediff==0) { + $filestr="/var/log/messages"; +} else { + $filestr="/var/log/messages.$datediff"; + $filestr = "$filestr.gz" if -f "$filestr.gz"; +} + +if (!(open (FILE,($filestr =~ /.gz$/ ? "gzip -dc $filestr |" : $filestr)))) { + $errormessage = "$Lang::tr{'date not in logs'}: $filestr $Lang::tr{'could not be opened'}"; + $skip=1; + # Note: This is in case the log does not exist for that date +} +my $lines = 0; +my @log=(); +my $blocklist = $cgiparams{blocklist}; + +if (!$skip) +{ + while () { + if (/^${monthstr} ${daystr} ..:..:.. [\w\-]+ kernel:.*BLKLST_(\w+)\s?IN=.*/) { + if($1 eq $blocklist){ + $log[$lines] = $_; + $lines++; + } + } + } + close (FILE); +} + +$skip=0; +if ($multifile) { + $datediff=$datediff-1; + if ($datediff==0) { + $filestr="/var/log/messages"; + } else { + $filestr="/var/log/messages.$datediff"; + $filestr = "$filestr.gz" if -f "$filestr.gz"; + } + if (!(open (FILE,($filestr =~ /.gz$/ ? "gzip -dc $filestr |" : $filestr)))) { + $errormessage="$Lang::tr{'date not in logs'}: $filestr $Lang::tr{'could not be opened'}"; + $skip=1; + } + if (!$skip) { + while () { + if (/^${monthstr} ${daystr} ..:..:.. [\w\-]+ kernel:.*BLKLST_(\w+)\s?IN=.*/) { + if($1 eq $blocklist){ + $log[$lines] = $_; + $lines++; + } + } + } + close (FILE); + } +} + +&Header::showhttpheaders(); +&Header::openpage($Lang::tr{'ipblocklist log list'}, 1, ''); +&Header::openbigbox('100%', 'left', '', $errormessage); + +if ($errormessage) { + &Header::openbox('100%', 'left', $Lang::tr{'error messages'}); + print "$errormessage \n"; + &Header::closebox(); +} + +&Header::openbox('100%', 'left', "$Lang::tr{'settings'}:"); + +print < + + + + + + + + + + + + +
$Lang::tr{'month'}:  +  $Lang::tr{'day'}:  +
$Lang::tr{'ipblocklist id'}
+ +END +; + +&Header::closebox(); + +&Header::openbox('100%', 'left', $Lang::tr{'ipblocklist log list'}); +print "

$Lang::tr{'firewall hits'} $longmonthstr $daystr: $lines

"; + +if ($start == -1) { + $start = $lines - ${Header::viewsize}; +} +if ($start >= $lines - ${Header::viewsize}) { $start = $lines - ${Header::viewsize}; }; +if ($start < 0) { $start = 0; } + +my $prev = $start - ${Header::viewsize}; +my $next = $start + ${Header::viewsize}; + +if ($prev < 0) { $prev = 0; } +if ($next >= $lines) { $next = -1 } +if ($start == 0) { $prev = -1; } + +if ($lines != 0) { &oldernewer(); } + +print < + +$Lang::tr{'time'} +$Lang::tr{'iface'} +$Lang::tr{'proto'} +$Lang::tr{'source'} +$Lang::tr{'src port'} +$Lang::tr{'destination'} +$Lang::tr{'dst port'} + +END +; + +my @slice = splice(@log, $start, ${Header::viewsize}); + +if ($logsettings{'LOGVIEW_REVERSE'} eq 'on') { @slice = reverse @slice; } + +$lines = 0; +foreach $_ (@slice) { + $a = $_; + # Check whether valid ipv4 or ipv6 address + if (($_ =~ /BLKLST_(\w+)\s?IN=/)) { + if($1 eq $blocklist) { + my $in = '-'; my $out = '-'; + my $srcaddr = ''; my $dstaddr = ''; + my $protostr = ''; + my $srcport = ''; my $dstport = ''; + + # If ipv6 uses bridge, the use PHYSIN, otherwise use IN + if ($_ =~ /(^.* ..:..:..) [\w\-]+ kernel:.*(IN=.*)(PHYSIN=.*)$/) {} + elsif ($_ =~ /(^.* ..:..:..) [\w\-]+ kernel:.*(IN=.*)$/) {} + my $timestamp = $1; my $packet = $2; + $timestamp =~ /(...) (..) (..:..:..)/; + my $month = $1; my $day = $2; my $time = $3; + + # If ipv6 uses bridge, the use PHYSIN and PHYSOUT, otherwise use IN and OUT + if ($a =~ /PHYSIN=(\w+)/) { $iface = $1; } elsif ($a =~ /IN=(\w+)/) { $iface = $1; } + if ($a =~ /PHYSOUT=(\w+)/) { $out = $1; } elsif ($a =~ /OUT=(\w+)/) { $out = $1; } + # Detect ipv4 and ipv6 addresses + if (($a =~ /SRC\=(([\d]{1,3})(\.([\d]{1,3})){3})/) or ($a =~ /SRC\=(([0-9a-fA-F]{0,4})(\:([0-9a-fA-F]{0,4})){2,7})/)) { $srcaddr = $1; } + if (($a =~ /DST\=(([\d]{1,3})(\.([\d]{1,3})){3})/) or ($a =~ /DST\=(([0-9a-fA-F]{0,4})(\:([0-9a-fA-F]{0,4})){2,7})/)) { $dstaddr = $1; } + if ($a =~ /PROTO\=(\w+)/) { $protostr = $1; } + my $protostrlc = lc($protostr); + if ($a =~ /SPT\=([\d\.]+)/){ $srcport = $1; } + if ($a =~ /DPT\=([\d\.]+)/){ $dstport = $1; } + + if ($lines % 2) { + print "\n"; + } + else { + print "\n"; + } + print <$time + $iface + $protostr + + + +
$srcaddr
+ + $srcport + + + +
$dstaddr
+ + $dstport + +END + ; + $lines++; + } + } +} + +print < +END +; + +&oldernewer(); + + print"
$Lang::tr{
"; + +&Header::closebox(); + +&Header::closebigbox(); + +&Header::closepage(); + +sub oldernewer +{ + print < + +END + ; + + my $blocklists = join ',', @blocklists; + + print ""; + if ($prev != -1) { + print "$Lang::tr{'older'}"; + } + else { + print "$Lang::tr{'older'}"; + } + print "\n"; + + print ""; + if ($next != -1) { + print "$Lang::tr{'newer'}"; + } + else { + print "$Lang::tr{'newer'}"; + } + print "\n"; + + print < + +END + ; +} diff --git a/langs/en/cgi-bin/en.pl b/langs/en/cgi-bin/en.pl index 99698948d..87cc679a5 100644 --- a/langs/en/cgi-bin/en.pl +++ b/langs/en/cgi-bin/en.pl @@ -1585,6 +1585,33 @@ 'ip basic info' => 'Basic IP information', 'ip info' => 'IP information', 'ip info for' => 'IP information for', +'ipblocklist' => 'IP Address Blocklists', +'ipblocklist blocklist settings' => 'Blocklist settings', +'ipblocklist category' => 'Category', +'ipblocklist category application' => 'Application', +'ipblocklist category attacker' => 'Attacker', +'ipblocklist category c and c' => 'Malware C&C', +'ipblocklist category composite' => 'Composite', +'ipblocklist category invalid' => 'Invalid Address', +'ipblocklist category reputation' => 'Reputation', +'ipblocklist category scanner' => 'Scanner', +'ipblocklist could not download blocklist' => 'Could not download blocklist', +'ipblocklist disable mid' => 'because it is included in', +'ipblocklist disable post' => '', +'ipblocklist disable pre' => 'Disabling', +'ipblocklist download error' => 'A download error occurs.', +'ipblocklist empyt blocklist received' => 'An empty blocklist has been received.', +'ipblocklist enable' => 'Enable', +'ipblocklist entries' => 'Entries', +'ipblocklist hits' => 'Total number of blocklist hits for', +'ipblocklist id' => 'Blocklist', +'ipblocklist input' => 'Packets Dropped In', +'ipblocklist log list' => 'Firewall log (blocklist)', +'ipblocklist log' => 'Log dropped packets', +'ipblocklist logs' => 'IP Address Blocklist Logs', +'ipblocklist name' => 'Name', +'ipblocklist output' => 'Packets Dropped Out', +'ipblocklist use ipblocklists' => 'Enable IP Blocklists', 'ipfire has now rebooted' => 'IPFire is rebooting now.', 'ipfire has now shutdown' => 'IPFire is shutting down now.', 'ipfire side' => 'IPFire side:', diff --git a/lfs/configroot b/lfs/configroot index 2f3b93324..7a6334cc6 100644 --- a/lfs/configroot +++ b/lfs/configroot @@ -51,7 +51,7 @@ $(TARGET) : # Create all directories for i in addon-lang auth backup ca captive certs connscheduler crls ddns dhcp dhcpc dns dnsforward \ - ethernet extrahd/bin fwlogs fwhosts firewall isdn key langs logging mac main \ + ethernet extrahd/bin fwlogs fwhosts firewall ipblocklist isdn key langs logging mac main \ menu.d modem optionsfw \ ovpn patches pakfire portfw ppp private proxy/advanced/cre \ proxy/calamaris/bin qos/bin red remote sensors suricata time \ @@ -65,7 +65,7 @@ $(TARGET) : captive/settings captive/agb.txt captive/clients captive/voucher_out certs/index.txt certs/index.txt.attr ddns/config ddns/settings ddns/ipcache dhcp/settings \ dhcp/fixleases dhcp/advoptions dhcp/dhcpd.conf.local dns/settings dns/servers dnsforward/config ethernet/aliases ethernet/settings ethernet/known_nics ethernet/scanned_nics \ ethernet/wireless extrahd/scan extrahd/devices extrahd/partitions extrahd/settings firewall/settings firewall/config firewall/locationblock firewall/input firewall/outgoing \ - fwhosts/customnetworks fwhosts/customhosts fwhosts/customgroups fwhosts/customservicegrp fwhosts/customlocationgrp fwlogs/ipsettings fwlogs/portsettings \ + fwhosts/customnetworks fwhosts/customhosts fwhosts/customgroups fwhosts/customservicegrp fwhosts/customlocationgrp fwlogs/ipsettings fwlogs/portsettings ipblocklist/settings \ isdn/settings mac/settings main/hosts main/routing main/security main/settings optionsfw/settings \ ovpn/ccd.conf ovpn/ccdroute ovpn/ccdroute2 pakfire/settings portfw/config ppp/settings-1 ppp/settings-2 ppp/settings-3 ppp/settings-4 \ ppp/settings-5 ppp/settings proxy/settings proxy/squid.conf proxy/advanced/settings proxy/advanced/cre/enable remote/settings qos/settings qos/classes qos/subclasses qos/level7config qos/portconfig \ diff --git a/lfs/ipblocklist-sources b/lfs/ipblocklist-sources new file mode 100644 index 000000000..30b9e94a4 --- /dev/null +++ b/lfs/ipblocklist-sources @@ -0,0 +1,53 @@ +############################################################################### +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2022 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 . # +# # +############################################################################### + +############################################################################### +# Definitions +############################################################################### + +include Config + +VER = ipfire + +THISAPP = ipblocklist-sources +TARGET = $(DIR_INFO)/$(THISAPP) + +############################################################################### +# Top-level Rules +############################################################################### + +install : $(TARGET) + +check : + +download : + +b2 : + +############################################################################### +# Installation Details +############################################################################### + +$(TARGET) : + @$(PREBUILD) + mkdir -p /var/ipfire/ipblocklist + install -v -m 0644 $(DIR_SRC)/config/ipblocklist/sources /var/ipfire/ipblocklist + + @$(POSTBUILD) diff --git a/lfs/logwatch b/lfs/logwatch index 8631f4541..5cd79843c 100644 --- a/lfs/logwatch +++ b/lfs/logwatch @@ -98,6 +98,9 @@ $(TARGET) : $(patsubst %,$(DIR_DL)/%,$(objects)) cp -f $(DIR_SRC)/config/logwatch/dialup /usr/share/logwatch/scripts/services/dialup cp -f $(DIR_SRC)/config/logwatch/dialup.conf /usr/share/logwatch/dist.conf/services/dialup.conf + cp -f $(DIR_SRC)/config/logwatch/ipblocklist /usr/share/logwatch/scripts/services/ipblocklist + cp -f $(DIR_SRC)/config/logwatch/ipblocklist.conf /usr/share/logwatch/dist.conf/services/ipblocklist.conf + -mkdir -p /var/cache/logwatch chmod -v 777 /var/cache/logwatch -mkdir -p /var/log/logwatch diff --git a/lfs/stage2 b/lfs/stage2 index 3de871fdb..3d6970b82 100644 --- a/lfs/stage2 +++ b/lfs/stage2 @@ -165,4 +165,8 @@ endif -mkdir -pv /opt/pakfire/db/core echo "$(CORE)" > /opt/pakfire/db/core/mine + # IPblocklist. + -mkdir -pv /var/lib/ipblocklist + chown nobody:nobody /var/lib/ipblocklist + @$(POSTBUILD) diff --git a/make.sh b/make.sh index 071c678d3..da3f7f467 100755 --- a/make.sh +++ b/make.sh @@ -1439,6 +1439,7 @@ buildipfire() { lfsmake2 hyperscan lfsmake2 suricata lfsmake2 ids-ruleset-sources + lfsmake2 ipblocklist-sources lfsmake2 squid lfsmake2 squidguard lfsmake2 calamaris diff --git a/src/initscripts/system/firewall b/src/initscripts/system/firewall index 2597dae10..dfa08d58b 100644 --- a/src/initscripts/system/firewall +++ b/src/initscripts/system/firewall @@ -180,6 +180,14 @@ iptables_init() { iptables -A HOSTILE_DROP -m limit --limit 10/second -j LOG --log-prefix "DROP_HOSTILE " iptables -A HOSTILE_DROP -j DROP -m comment --comment "DROP_HOSTILE" + # IP Address Blocklist chains + iptables -N BLOCKLISTIN + iptables -N BLOCKLISTOUT + iptables -A INPUT ! -p icmp -j BLOCKLISTIN + iptables -A FORWARD ! -p icmp -j BLOCKLISTIN + iptables -A FORWARD ! -p icmp -j BLOCKLISTOUT + iptables -A OUTPUT ! -p icmp -j BLOCKLISTOUT + # IPS (Guardian) chains iptables -N GUARDIAN iptables -A INPUT -j GUARDIAN diff --git a/src/scripts/update-ipblocklists b/src/scripts/update-ipblocklists new file mode 100644 index 000000000..9918cac41 --- /dev/null +++ b/src/scripts/update-ipblocklists @@ -0,0 +1,173 @@ +#!/usr/bin/perl +############################################################################### +# # +# IPFire.org - A linux based firewall # +# Copyright (C) 2007-2022 IPFire 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 POSIX; + +# Load perl module to talk to the kernel syslog. +use Sys::Syslog qw(:DEFAULT setlogsock); + +require '/var/ipfire/general-functions.pl'; +require "${General::swroot}/ipblocklist-functions.pl"; +require "${General::swroot}/lang.pl"; + +# Hash to store the settings. +my %settings = (); + +# The user and group name as which this script should be run. +my $run_as = 'nobody'; + +# Get user and group id of the user. +my ( $uid, $gid ) = ( getpwnam $run_as )[ 2, 3 ]; + +# Check if the script currently runs as root. +if ( $> == 0 ) { + # Drop privileges and switch to the specified user and group. + POSIX::setgid( $gid ); + POSIX::setuid( $uid ); +} + +# Establish the connection to the syslog service. +openlog('ipblocklist', 'cons', 'user'); + +# Grab the configured providers. +&General::readhash("${General::swroot}/ipblocklist/settings", \%settings); + +# Check if the blocklist feature is enabled. +unless ($settings{'ENABLE'} eq "on") { + # Exit. + exit 0; +} + +# Check if the red device is active. +unless (-e "${General::swroot}/red/active") { + # Log to syslog. + &_log_to_syslog(" Could not update any blocklist - The system is offline!"); + + # Exit. + exit 1; +} + +# Get all available blocklists. +my @blocklists = &IPblocklist::get_blocklists(); + +# Array to store successfully update blocklists. +# They need to be reloaded. +my @updated_blocklists = (); + +# Gather the details, when a list got modified last time. +my %modified = (); + +# Read-in data if the file exists. +&General::readhash($IPblocklist::modified_file, \%modified ) if (-e $IPblocklist::modified_file); + +# Loop through the array of blocklists. +foreach my $blocklist (@blocklists) { + # Skip if the blocklist is not enabled. + next if($settings{$blocklist} ne "on"); + + # Get current time. + my $time = time(); + + # Get time, when the blocklist has been downloaded last. + my $last_download_time = $modified{$blocklist}; + + # Get the holdoff rate in seconds for the current processed blocklist. + my $rate_time = &IPblocklist::get_holdoff_rate($blocklist); + + # Calculate holdoff time. + my $holdoff_time = $last_download_time + $rate_time; + + # Check if enough time has passed since the last download of the list. + if ($time <= $holdoff_time) { + # To frequent updates, log to syslog. + &_log_to_syslog(" Skipping $blocklist blocklist - Too frequent update attempts!"); + + # Skip this provider. + next; + } + + # Try to download and update the blocklist. + my $return = &IPblocklist::download_and_create_blocklist($blocklist); + + # Check if we got a return code. + if ($return) { + # Handle different return codes. + if ($return eq "not_modified") { + # Log notice to syslog. + &_log_to_syslog(" Skipping $blocklist blocklist - It has not been modified!"); + } elsif ($return eq "dl_error") { + # Log error to the syslog. + &_log_to_syslog(" Could not update $blocklist blocklist - Download error\!"); + } else { + # Log error to syslog. + &_log_to_syslog(" Could not update $blocklist blocklist - Unexpected error\!"); + } + } else { + # Log successfull update. + &_log_to_syslog(" Successfully updated $blocklist blocklist."); + + # Add the list to the array of updated blocklists. + push(@updated_blocklists, $blocklist); + } +} + +# Check if a blocklist has been updated and therefore needs to be reloaded. +if (@updated_blocklists) { + # Loop through the array. + foreach my $updated_blocklist (@updated_blocklists) { + # Get the blocklist file. + my $ipset_db_file = &IPblocklist::get_ipset_db_file($updated_blocklist); + + # Call safe system function to reload/update the blocklist. + &General::system("ipset", "restore", "-f", "$ipset_db_file"); + + # The set name contains a "v4" as suffix. + my $set_name = "$updated_blocklist" . "v4"; + + # Swap the sets to use the new one. + &General::system("ipset", "swap", "$set_name", "$updated_blocklist"); + + # Destroy the old blocklist. + &General::system("ipset", "destroy", "$set_name"); + } +} + +END { + # Close connection to syslog. + closelog(); +} + +# +# Tiny function to sent the error message to the syslog. +# +sub _log_to_syslog($) { + my ($message) = @_; + + # The syslog function works best with an array based input, + # so generate one before passing the message details to syslog. + my @syslog = ("ERR", "$message"); + + # Send the log message. + syslog(@syslog); +} + +1;