Multiple interfaces sharing a filter config: a script



  • Hello, everyone!

    We manage a pfSense box running 1.2.3 for stability reasons, and had run into a problem where we needed the rules for two interfaces to be exactly the same at all times. I've since heard that there's a feature in pfSense 2.x that will allow one set of rules to apply to multiple interfaces, but upgrading is not an option; therefore, I wrote this script with some guidance from the IRC channel. Hopefully it will be of use to someone.

    In the configuration section, $iface1 is the "master" interface that you want to propagate to the other interface; $iface2 is the interface to compare and POTENTIALLY OVERWRITE. They use the internal names pfSense uses: you can usually figure out the internal names of the interfaces you want to use by looking at their firewall rules pages and taking the "if=" parameter from the URL. For example, in the URL https://your.pfsense.firewall.com/firewall_rules.php?if=opt1 , 'opt1' is your interface name.

    Be sure to take a full backup of your configuration before running this script. Of course, I am not responsible for any damage done to your firewall as a result of running this script; it is provided free of charge with no warranty and all of that.

    
    // Configuration section
    $iface1 = "wan";
    $iface2 = "opt1";
    // End configuration section
    
    require_once("globals.inc");
    require_once("functions.inc");
    require_once("config.inc");
    require_once("util.inc");
    // ^ preamble
    
    $lock_fh = fopen("/tmp/propagator.lock", "w+");
    $blocked_lock = FALSE;
    $test_lock = flock($lock_fh, LOCK_EX|LOCK_NB, $blocked_lock);
    if (!$test_lock && $blocked_lock) exit(0);
    if (!$test_lock) exit(1);
    
    $iface1_rules = array();
    $iface2_rules = array();
    
    $sorted_out_rules = array();
    
    foreach ($config['filter']['rule'] as $index => $rule) {
    	if ($rule['interface'] != $iface2) {
    		if (!isset($sorted_out_rules[$rule['interface']])) $sorted_out_rules[$rule['interface']] = array();
    		$sorted_out_rules[$rule['interface']][] = $rule; 
    	}
    
    	if ($rule['interface'] == $iface1) {
    		unset($rule['interface']);
    		$iface1_rules[] = $rule;
    		continue;
    	}
    
    	if ($rule['interface'] == $iface2) {
    		unset($rule['interface']);
    		$iface2_rules[] = $rule;
    		continue;
    	}
    }
    
    $filter_applied = (file_exists($g['varrun_path'] . "/filter.conf.dirty") ? 0 : 1);
    
    if ($iface1_rules != $iface2_rules) {
    	/* Our iface2 is different from our iface1 :( */
    	$new_rules = array(); 
    	foreach ($sorted_out_rules as $iface => $rule_array) {
    		$new_rules = array_merge($new_rules, $rule_array);
    	}
    
    	foreach ($sorted_out_rules[$iface1] as $rule) {
    		$rule['interface'] = $iface2;
    		$new_rules[] = $rule;
    	}
    
    	$config['filter']['rule'] = $new_rules;
    
    	write_config("Syncing out-of-date firewall rules in {$iface2}...");
    	if ($filter_applied) filter_configure();
    }
    
    flock($lock_fh, LOCK_UN|LOCK_NB);
    fclose($lock_fh);
    unlink("/tmp/propagator.lock");
    
    ?>
    

    You should be able to use it in a cron as well; it should prevent itself from running twice at the same time.

    Enjoy! :)


Log in to reply