Firewall update script

I have had for sometime a script on my Linux devices and on my mother's Windows PC at her place a script that will open specific ports, namely for ssh and a vpn on the firewalls for specific address. Essentially the functionality is every 10 minutes or so it looks up a couple of dynamic dns address and opens a port on the fire wall for those devices or if the port is already open updates the address if it varies from that already there. If I am out and about or my ISP updates then I need only wait a max of 10 minutes or so and I can access again.

I've just swapped my ISP router out for a Pi4B with an OpenWRT snapshot, everything seems to be working got the DNS hijack to force queries to the PiHole etc. Now what I'd like to do if it is possible is to replicate the scripts that are updating UFW on my Linux devices to the new router in place of the direct port forwards I copied over form the ISP router. However, I am really not sure how to do this.

Essentially the UFW update I use is based on the one from here [https://rubysash.com/operating-system/linux/bash-script-update-ufw-rule-for-dynamic-host/]

Okay I did a bit of investigation and it looks like the rules I need to change are the pre_routing ones.
i.e.

Chain zone_wan_prerouting (1 references)
target     prot opt source               destination
prerouting_wan_rule  all  --  0.0.0.0/0            0.0.0.0/0            /* !fw3: Custom wan prerouting rule chain */
DNAT       tcp  --  0.0.0.0/0            0.0.0.0/0            tcp dpts:15251:15255 /* !fw3: PI (Servers) */ to:192.168.70.250:15251-15255
DNAT       udp  --  0.0.0.0/0            0.0.0.0/0            udp dpts:15251:15255 /* !fw3: PI (Servers) */ to:192.168.70.250:15251-15255

changes to something like made up addresses
mobile 123.123.123.123
laptop 345.345.345.345

Chain zone_wan_prerouting (1 references)
target     prot opt source               destination
prerouting_wan_rule  all  --  0.0.0.0/0            0.0.0.0/0            /* !fw3: Custom wan prerouting rule chain */
DNAT       tcp  --  123.123.123.123      0.0.0.0/0            tcp dpts:15251:15255 /* !fw3: PI (Servers) */ to:192.168.70.250:15251-15255
DNAT       udp  --  123.123.123.123      0.0.0.0/0            udp dpts:15251:15255 /* !fw3: PI (Servers) */ to:192.168.70.250:15251-15255
DNAT       tcp  --  345.345.345.345      0.0.0.0/0            tcp dpts:15251:15255 /* !fw3: PI (Servers) */ to:192.168.70.250:15251-15255
DNAT       udp  --  345.345.345.345      0.0.0.0/0            udp dpts:15251:15255 /* !fw3: PI (Servers) */ to:192.168.70.250:15251-15255

Is that correct? What would be the syntax to add/modify these from cli/script?
Is anyone is aware of an exiting script to do this or do I need to use the ufw one as a base?

Looks like you are making it more complicated than necessary.
Since you already have a VPN, there's no need to expose other ports.
Just open the VPN port on the WAN and assign the VPN network to your LAN zone.

While I do mostly use it I also run some servers for family too, principally FTP, I'd also like to lock down the VPN just to my devices. I made a bit of a start on the logic, just getting the addresses, wondered about using UCI to modify/delete the rules if addresses have changed.

#!/bin/sh
#set -x
# Some variables

LAPTOP=`dig +short laptop.freeddns.org`
MOBILE=`dig +short mobile.freeddns.org`
MUM=`dig +short mum.freeddns.org`
LISS=`dig +short liss.freeddns.org`
#
# Read saved addresses
laptop=`cat LAPTOP.sav`
mobile=`cat MOBILE.sav`
mum=`cat MUM.sav`
liss=`cat LISS.sav`

# Now do stuff

if [ $LAPTOP = $laptop ]; then echo "Matched"; else echo "Mismatched"; fi
if [ $MOBILE = $mobile ]; then echo "Matched"; else echo "Mismatched"; fi
if [ $MUM = $mum ]; then echo "Matched"; else echo "Mismatched"; fi
if [ $LISS = $liss ]; then echo "Matched"; else echo "Mismatched"; fi

# If mismatch need to deleted old rule, add new one, update saved address otherwise exit.

I assume, that you have the package bind-dig installed and working.
This is an example for the laptop. You'll have to multiply the rules for the other devices.

Create the initial rule(s) in /etc/firewall.user

#Get the laptop public IP address
LAPTOP=$(dig +short laptop.freeddns.org)

#If the IP address cannot be found, use some dummy IP address to create the rule 
if [ -z "$LAPTOP" ]; then
LAPTOP="123.123.123.123"
fi

#Save the current laptop IP address
echo "$LAPTOP" > /tmp/LAPTOP.sav

#Create the DNAT rules
iptables -t nat -A prerouting_wan_rule -m comment --comment "LAPTOP-TCP" -m tcp -p tcp -s "$LAPTOP"/32 -m multiport --dports 15251:15255 -j DNAT --to-destination 192.168.70.250:15251-15255
iptables -t nat -A prerouting_wan_rule -m comment --comment "LAPTOP-UDP" -m udp -p udp -s "$LAPTOP"/32 -m multiport --dports 15251:15255 -j DNAT --to-destination 192.168.70.250:15251-15255

Here is an example of the update script:

#!/bin/sh

#Get the laptop real IP address
LAPTOP=$(dig +short laptop.freeddns.org)

#Get the last known laptop IP address
laptop=$(cat /tmp/LAPTOP.sav)

#Update the rules if the laptop has a new ip address
if [ ! -z "$LAPTOP" ] && [ "$laptop" != "$LAPTOP" ]; then
#Get the rule numbers
rntcplaptop=$(iptables -t nat -L --line-numbers | grep LAPTOP-TCP | awk '{ print $1 }')
rnudplaptop=$(iptables -t nat -L --line-numbers | grep LAPTOP-UDP | awk '{ print $1 }')
#Replace the rules
iptables -t nat -R prerouting_wan_rule "$rntcplaptop" -m comment --comment "LAPTOP-TCP" -m tcp -p tcp -s "$LAPTOP"/32 -m multiport --dports 15251:15255 -j DNAT --to-destination 192.168.70.250:15251-15255
iptables -t nat -R prerouting_wan_rule "$rnudplaptop" -m comment --comment "LAPTOP-UDP" -m udp -p udp -s "$LAPTOP"/32 -m multiport --dports 15251:15255 -j DNAT --to-destination 192.168.70.250:15251-15255
#Update the laptop IP address
echo "$LAPTOP" > /tmp/LAPTOP.sav
fi

Create a cron job

*/10 * * * * /root/update.scr 1>/dev/null 2>&1

Check your custom rules, running the following command:

iptables -t nat -nvL prerouting_wan_rule

Thanks. That gives me the outline to get a working script. In case it is of use to anyone in the future I put in this one line that verifies the IP address and if not uses the previous saved. It only checks for a valid formatted IPv4.

if [[ $LAPTOP =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then echo "Good Address"; else LAPTOP=$laptop; echo "Bad Address, using saved"; fi

The only query I am scratching my head over is the Wireguard VPN, how from iptables restrict the source IP in a similar way. Anyway shall keep reading.
1 Like

This topic was automatically closed 10 days after the last reply. New replies are no longer allowed.