]> git.ipfire.org Git - ipfire-2.x.git/blobdiff - config/firewall/rules.pl
core186: ship header.pl
[ipfire-2.x.git] / config / firewall / rules.pl
index 57f4809b4e22cfe621cba27c0c05a9dae46ada37..a47c260a152647ab9c740578d4e3c0f27beb0d0a 100644 (file)
@@ -2,7 +2,7 @@
 ###############################################################################
 #                                                                             #
 # IPFire.org - A linux based firewall                                         #
-# Copyright (C) 2007-2020  IPFire Team  <info@ipfire.org>                     #
+# Copyright (C) 2007-2024  IPFire Team  <info@ipfire.org>                     #
 #                                                                             #
 # 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        #
@@ -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;
@@ -54,6 +55,7 @@ my @PRIVATE_NETWORKS = (
        "172.16.0.0/12",
        "192.168.0.0/16",
        "100.64.0.0/10",
+       "224.0.0.0/4",
 );
 
 # MARK masks
@@ -73,6 +75,10 @@ my %confignatfw=();
 my %locationsettings = (
        "LOCATIONBLOCK_ENABLED" => "off"
 );
+my %blocklistsettings= (
+       "ENABLE" => "off",
+);
+
 my %ipset_loaded_sets = ();
 my @ipset_used_sets = ();
 
@@ -82,6 +88,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 +104,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();
 
@@ -122,7 +138,7 @@ undef (@dummy);
 
 sub main {
        # Get currently used ipset sets.
-       &ipset_get_sets();
+       @ipset_used_sets = &ipset_get_sets();
 
        # Flush all chains.
        &flush();
@@ -144,6 +160,9 @@ sub main {
        # Load rules to block hostile networks.
        &drop_hostile_networks();
 
+       # Handle ipblocklist.
+       &ipblocklist();
+
        # Reload firewall policy.
        run("/usr/sbin/firewall-policy");
 
@@ -383,6 +402,9 @@ sub buildrules {
                                        $source = "";
                                }
 
+                               # Make sure that $source is properly defined
+                               next unless (defined $source);
+
                                my $source_intf = @$src[1];
 
                                foreach my $dst (@destinations) {
@@ -411,8 +433,9 @@ sub buildrules {
                                        if ($source =~ /mac/) {
                                                push(@source_options, $source);
                                        } elsif ($source =~ /-m set/) {
-                                               # Grab location code from hash.
-                                               my $loc_src = $$hash{$key}[4];
+                                               # Split given arguments into single chunks to
+                                               # obtain the set name.
+                                               my ($a, $b, $c, $loc_src, $e) = split(/ /, $source);
 
                                                # Call function to load the networks list for this country.
                                                &ipset_restore($loc_src);
@@ -425,8 +448,9 @@ sub buildrules {
                                        # Prepare destination options.
                                        my @destination_options = ();
                                        if ($destination =~ /-m set/) {
-                                               # Grab location code from hash.
-                                               my $loc_dst = $$hash{$key}[6];
+                                               # Split given arguments into single chunks to
+                                               # obtain the set name.
+                                               my ($a, $b, $c, $loc_dst, $e) = split(/ /, $destination);
 
                                                # Call function to load the networks list for this country.
                                                &ipset_restore($loc_dst);
@@ -701,15 +725,67 @@ sub drop_hostile_networks () {
        # Call function to load the network list of hostile networks.
        &ipset_restore($HOSTILE_CCODE);
 
-       # Setup rules to pass traffic which does not belong to a hostile network.
-       run("$IPTABLES -A HOSTILE -i $RED_DEV -m set ! --match-set $HOSTILE_CCODE src -j RETURN");
-       run("$IPTABLES -A HOSTILE -o $RED_DEV -m set ! --match-set $HOSTILE_CCODE dst -j RETURN");
+       # Check traffic in incoming/outgoing direction and drop if it matches
+       run("$IPTABLES -A HOSTILE -i $RED_DEV -m set --match-set $HOSTILE_CCODE src -j HOSTILE_DROP_IN");
+       run("$IPTABLES -A HOSTILE -o $RED_DEV -m set --match-set $HOSTILE_CCODE dst -j HOSTILE_DROP_OUT");
+}
 
-       # Setup logging.
-       run("$IPTABLES -A HOSTILE -m limit --limit 10/second -j LOG  --log-prefix \"DROP_HOSTILE \"");
+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");
+               }
+       }
 
-       # Drop traffic from/to hostile network.
-        run("$IPTABLES -A HOSTILE -j DROP -m comment --comment \"DROP_HOSTILE\"");
+       # 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'}) && ($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 {
@@ -925,7 +1001,17 @@ 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;
+
        # Get all currently used ipset lists and store them in an array.
        my @output = `$IPSET -n list`;
 
@@ -935,14 +1021,17 @@ sub ipset_get_sets () {
                chomp($set);
 
                # Add the set the array of used sets.
-               push(@ipset_used_sets, $set);
+               push(@sets, $set);
        }
 
        # Display used sets in debug mode.
        if($DEBUG) {
                print "Used ipset sets:\n";
-               print "@ipset_used_sets\n\n";
+               print "@sets\n\n";
        }
+
+       # Return the array of sets.
+       return @sets;
 }
 
 sub ipset_restore ($) {
@@ -985,6 +1074,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.
@@ -1002,6 +1111,9 @@ sub ipset_call_restore ($) {
 }
 
 sub ipset_cleanup () {
+       # Reload the array of used sets.
+       @ipset_used_sets = &ipset_get_sets();
+
        # Loop through the array of used sets.
        foreach my $set (@ipset_used_sets) {
                # Check if this set is still in use.