2

I have a network gateway system using iptables to allow/deny traffic to/from an internal network. I have to manually add and remove rules in iptables depending on the requirements of the entities in the internal network, including NATing their traffic. Trusted entities on the internal network can request rules to be added and removed as required. The end result is a bunch of rules in the nat and filter tables, including POSTROUTING rules with SNAT targets, setting the source IP to that of the gateway system, and rules in other tables with the gateway external ip address as dst.

I want to be able to set the IP of the gateway system on the fly. This would of course have disruptive consequences to TCP sessions etc., but let's keep that out of the picture. The system handles these issues with other mechanisms.

The problem is that an unknown number of rules in the firewall tables still refer to this old source IP address.

Is there a way to easily change all these rules to use the new IP address instead of the old one? Of course I can also accept some fancy script using sed/awk or similar if you are more of a bash wizard than me...

3
  • Probably you should just write a script which flush the tables and add all the rules again with desired IP(s).
    – Tom Yan
    Commented Feb 3, 2023 at 10:08
  • @TomYan This works for the "remove old rules" part, but requires me to signal all the entities in the network and then they need to retransmit their required rules. Or I can make smarter software that remembers all rules separately and does some magic to set them again with a different IP. But this is besides the question :) Thanks though. Commented Feb 3, 2023 at 10:28
  • You need to supply more information about your network setup, "entities" etc. This doesn't seem like a simple sed/awk question.
    – harrymc
    Commented Feb 3, 2023 at 10:58

2 Answers 2

2

If the traffic being SNATed always uses the outbound interface's IP address, replace -j SNAT with -j MASQUERADE. This will use the same conntrack-based SNAT but will select the replacement source address automatically.

You can mass-edit iptables rules using iptables-save and iptables-restore:

  • For example, to edit the ruleset interactively (especially if your editor has a good search-and-replace function):

    iptables-save > /tmp/rules
    vim /tmp/rules
    iptables-restore < /tmp/rules
    

    Using 'vipe' from moreutils:

    iptables-save | vipe | iptables-restore
    
  • To mass-replace one exact IP address with another (keeping in mind that . is a wildcard character in regexes, as well as using \b to ensure that "12.34" will not accidentally match "112.34"):

    iptables-save | sed 's/\b12\.34\.56\.78\b/12.34.56.99/g' | iptables-restore
    

But in my opinion, any editing of iptables rules (including script-based "iptables -A" setup) is a bad approach. Instead, keep an iptables.rules file (in the iptables-save format) as the "source" and always load the ruleset from that file. Then, you can start using some form of macro/templating language to define the IP address once and refer to it everywhere – similar to how it's done when deploying configuration files through Salt/Ansible (if anybody still uses those).

  • For example (don't actually do this; it'll spiral into something unmanageable too quickly):

    cat /etc/iptables.rules | sed 's/%CUST_IP%/12.34.56.78/g' | iptables-restore
    
  • I would also suggest taking a look at Ferm, which implements an alternative syntax for iptables rules – a bit more readable in general, but also has macros built in so you can @def $cust_ip once and refer to it everywhere.

    domain (ip ip6) {
        @def dns_host4 = 10.10.0.53;
        @def ntp_host4 = 10.10.0.123;
    
        table filter {
            chain FORWARD {
                daddr ($dns_host4 $ntp_host4) accept;
            }
        }
    
        table nat {
            chain PREROUTING {
                proto (tcp udp) dport 53 DNAT to-destination $dns_host4;
                proto udp dport 123 DNAT to-destination $ntp_host4;
            }
        }
    }
    

    This works similarly to the iptables-restore – instead of making changes live, you edit /etc/ferm.conf and run ferm to reload the whole ruleset when done. If an IP address changes, just edit the @def macro in ferm.conf and reload.

    (Also, it's good to get used to this if you eventually end up switching to nftables, as it works pretty much the same as Ferm – of course the syntax is a bit different, but it also has $macros and you edit /etc/nftables.conf and reload the whole thing at once.)

3

I would suggest iptables in combination with ipset.

In iptables you would have your rule but then add --match-set setname src

iptables -I PREROUTING -s whatever ... --match-set setname src
iptables -I PREROUTING -s whatever ... --match-set setname dst

Then to disable the rule you would remove the ip address from the set named "setname" To enable it you just add an ip address to setname.

Then iptables remain the same and your just adding and subtracting entries from setname

Each rule would have a different name as needed.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .