What's new

Custom firewall-start script for OpenVPN (country allow list)

  • SNBForums Code of Conduct

    SNBForums is a community for everyone, no matter what their level of experience.

    Please be tolerant and patient of others, especially newcomers. We are all here to share and learn!

    The rules are simple: Be patient, be nice, be helpful or be gone!

zmaster

New Around Here
Hi guys,

I've been a reader of this forum for quite some time and I have a RT-AC68U for almost 2.5 years now but flashed Merlin's firmware last week. My reason was to flash was OpenVPN 2.4.x. My Synology was a bit behind and I don’t like services being exposed to the internet when they are not on a pretty recent release.

So since I knew the existence of custom scripts I started hardening my OpenVPN setup. I’m a Microsoft consultant by nature so bash, iptables and unix commands are completely new to me. The thing that bothered me is that I saw multiple connection attempts on my OpenVPN port (running on tcp/443) from lots of different countries. Lots of them are port scanners, I know but still.. :).

With the help of this forum and my friend Google I modified firewall-start scripts to only allow connections from The Netherlands since I only use my VPN connection from my own country. I thought of sharing this with everyone on this forum, in case anyone wants to use it. I have not yet copied it to my RT-AC68U but I’ve tested it on a Centos VM. I would like to hear from you guys if I’m working in the right direction and if the stuff I made, makes any sense.

Like the last line contained -z "$(iptables-save | grep AllowedCountries)", I have changed that to -z "$(iptables -S | grep AllowedCountries)". I saw no reason why it should be saved in order to check if the rule was already there.

I also got two questions:
  • The zone files which are downloaded to /jffs/ipset_lists, will they be removed after a reboot? Because I want to work with a fresh zone file every reboot.
  • The Iptables rule for 443 is ALLOW for the AllowedCountry list. Is that a best practise or is it better to DROP everything except for the AllowedCountry list?
firewall-start:
Code:
#!/bin/sh
echo "[firewall-start] Starting custom firewall script to allow OpenVPN from NL"

LINENUMBER=$(iptables -L INPUT --line-numbers | grep "dpt:https" | cut -d" " -f1)
if [ -z "$LINENUMBER" ]
then
        echo "[firewall-start] Couldn't find a firewall rule for OpenVPN. Exiting firewall-start script."
        exit 1
else
        echo "[firewall-start] Found OpenVPN firewall rule at line $LINENUMBER."
fi

# Load ipset modules
ipset -v | grep -i "v4" > /dev/null 2>&1
if [ $? -eq 0 ];
then
   # old ipset
   ipsetv=4
   lsmod | grep "ipt_set" > /dev/null 2>&1 || \
   for module in ip_set ip_set_nethash ip_set_iphash ipt_set
   do
       insmod $module
   done
else
   # new ipset
   ipsetv=6
   lsmod | grep "xt_set" > /dev/null 2>&1 || \
   for module in ip_set ip_set_hash_net ip_set_hash_ip xt_set
   do
       insmod $module
   done
fi

# Preparing folder to cache downloaded files
IPSET_LISTS_DIR=/jffs/ipset_lists
[ -d "$IPSET_LISTS_DIR" ] || mkdir -p $IPSET_LISTS_DIR

# Different routers got different iptables syntax
case $(uname -m) in
  armv7l)
    MATCH_SET='--match-set'
    ;;
  mips)
    MATCH_SET='--set'
    ;;
  x86_64)
    MATCH_SET='--match-set'
    ;;
esac

# Allow incoming traffic from some countries. nl is for Netherlands. See other countries code at http://www.ipdeny.com/ipblocks/
if [ -z $(ipset list -n | grep "AllowedCountries") ]
then
    ipset create AllowedCountries hash:net
    for country in nl
    do
        echo "[firewall-start] Downloading zone file for $country."
        [ -e $IPSET_LISTS_DIR/$country-aggregated.zone ] || wget -q -O $IPSET_LISTS_DIR/$country-aggregated.zone http://ipdeny.com/ipblocks/data/aggregated/$country-aggregated.zone
        for IP in $(cat $IPSET_LISTS_DIR/$country-aggregated.zone)
        do
            ipset add AllowedCountries $IP
        done
    done
fi

if [ -z "$(iptables -S | grep AllowedCountries)" ]
then
    echo "[firewall-start] Creating restricted firewall rule."
    iptables -R INPUT $LINENUMBER -p tcp -m tcp --dport 443 -m set $MATCH_SET AllowedCountries src -j ACCEPT
else
    echo "[firewall-start] Already found a rule with an AllowedCountries list. Skipping creation of firewall rule."
fi
 
Like the last line contained -z "$(iptables-save | grep AllowedCountries)", I have changed that to -z "$(iptables -S | grep AllowedCountries)". I saw no reason why it should be saved in order to check if the rule was already there.
iptables -S is just a synonym for iptables-save in the version of iptables used on the ARM routers. It's just a specific output format and doesn't actually save anything unless you redirect the output. iptables-save is used for backward compatibility with the MIPS based routers.
I also got two questions:
The zone files which are downloaded to /jffs/ipset_lists, will they be removed after a reboot? Because I want to work with a fresh zone file every reboot.
jffs is non-volatile storage, so it is saved across reboots. It's checked to exist with the -e test. If you want to re-download at every boot, remove that test.
The Iptables rule for 443 is ALLOW for the AllowedCountry list. Is that a best practise or is it better to DROP everything except for the AllowedCountry list?
The way the iptables rules are structured by default for the router, you want to just use ACCEPT for the packets you really want to let through.
 
Thanks for the tips John! Good to know that some commands are needed for backward compatibility reasons. I’ll keep them for now in my config.

In the meantime I put the script on my 68u but it seems the firewall rules for OpenVPN are applied at a later stadium. So the script breaks at the first if. I’ll have to figure out when the ports for OpenVPN are applied.

Is it an idea to put in a sleep for a few seconds? Other thing I was thinking off is maybe working but custom OpenVPN scripts when the interface goes up.
 
You could call the firewall script after the vpn is up - (openvpn custom script) .
 
Any good reasons why are u running on tcp/443? Why not UDP/443 since it is best to run openvpn on UDP, fallback to TCP/443 only when your ISP is throttling or UDP is not allowed client or server internet connection.
You can also use --learn-address option or --client-config-directory (ccd) or the --up and --down option to add or remove firewall rules to accept the connection. You can just block all incoming except for the openvpn port and then allow client IP to pass whatever traffic. There is no need to drop all countries and allow certain country only as this involves more filtering then rejecting invalid traffic and since its also your own personal vpn, running on some uncommon port is also good. Try not to burden the router CPU since its not really a hexacores or quadcores of enterprise router.
 
You could call the firewall script after the vpn is up - (openvpn custom script) .
I have tried this and I must say it worked like a charm. I'm using my script with a few tweaks and it modifies the firewall just as I want :). Together with a --down custom script which removes the custom firewall rule and ipset. Thanks for the tip!
Any good reasons why are u running on tcp/443? Why not UDP/443 since it is best to run openvpn on UDP, fallback to TCP/443 only when your ISP is throttling or UDP is not allowed client or server internet connection.
You can also use --learn-address option or --client-config-directory (ccd) or the --up and --down option to add or remove firewall rules to accept the connection. You can just block all incoming except for the openvpn port and then allow client IP to pass whatever traffic. There is no need to drop all countries and allow certain country only as this involves more filtering then rejecting invalid traffic and since its also your own personal vpn, running on some uncommon port is also good. Try not to burden the router CPU since its not really a hexacores or quadcores of enterprise router.
I choose tcp/443 because that's the only port the proxy at my works allows. But you got a point. I'll look into the learn-address and ccd option!
But that's one of the reasons I choose to allow just 1 set of country subnets instead of blocking multiple countries. I think it will also be the only customisation I will do for this router. It's stable as it is, I don't want to break the awesome peace of hardware :D.
 

Latest threads

Sign Up For SNBForums Daily Digest

Get an update of what's new every day delivered to your mailbox. Sign up here!
Top