Limit OpenVPN access to just 1 country and log the dropped packets

I know many guys thinks this is ultra-paranoid, but I wanted to limit access to my openvpn port (on the ipfire server) only to my country… Yes, I could just use the options in the “Location Block” page in the WebUI, but there is a flaw (IMHO) when you use the location blocker: actually you cannot log dropped packets from countries disallowed with the location blocker. This is because the Location Block feature actually works just as a filter and it doesn’t generate logs. This is not very good to me… For statistic reasons (and curiosity!), I want to see all the blocked attempts in the log.

Unfortunately, if I create an input firewall rule in the WEB UI to allow openvpn access just from my country, followed by another rule to drop all the other openvpn requests, these 2 rules would be placed in the INPUTFW table, i.e. “after” the OVPNINPUT table, so they would never be reached.

So, I managed to get the desired result by editing the /etc/sysconfig/firewall.local script. This is my script:

#!/bin/sh
# Used for private firewall rules

# See how we were called.

# *** some useful variables: ***
OVPN_PORT=`/bin/cat /var/ipfire/ovpn/server.conf 2> /dev/null | /bin/grep port | /usr/bin/awk '{print $NF}' | /usr/bin/tr -d '\012'`
RED_PUBLIC_IP="***INSERT HERE YOUR PUBLIC IP***"
ACCEPTED_COUNTRY="***INSERT HERE YOUR COUNTRY CODE***"
# ******************************

case "$1" in
  start)
        ## add your 'start' rules here
	
	    iptables -A CUSTOMINPUT -p udp -d "${RED_PUBLIC_IP}" --dport "${OVPN_PORT}" -m geoip --source-country "${ACCEPTED_COUNTRY}" -j ACCEPT
	    iptables -A CUSTOMINPUT -p udp -d "${RED_PUBLIC_IP}" --dport "${OVPN_PORT}" -m limit --limit 10/second -j LOG --log-prefix "DROP_OpenVPN "
	    iptables -A CUSTOMINPUT -p udp -d "${RED_PUBLIC_IP}" --dport "${OVPN_PORT}" -j DROP
        ;;
  stop)
        ## add your 'stop' rules here
	
	    iptables -D CUSTOMINPUT -p udp -d "${RED_PUBLIC_IP}" --dport "${OVPN_PORT}" -m geoip --source-country "${ACCEPTED_COUNTRY}" -j ACCEPT
	    iptables -D CUSTOMINPUT -p udp -d "${RED_PUBLIC_IP}" --dport "${OVPN_PORT}" -m limit --limit 10/second -j LOG --log-prefix "DROP_OpenVPN "
	    iptables -D CUSTOMINPUT -p udp -d "${RED_PUBLIC_IP}" --dport "${OVPN_PORT}" -j DROP
        ;;
  reload)
        $0 stop
        $0 start
        ## add your 'reload' rules here
        ;;
  *)
        echo "Usage: $0 {start|stop|reload}"
        ;;
esac

With this script the openvpn access attempts from other countries are blocked and “logged” in the WebUI, and I don’t need to activate the global location block filter (which anyway would not log dropped packets).
I just wanted to share this script with you, hoping that a future ipfire version would allow for an option like the following in the “Location Block” page:

Log Location Blocked packets [Yes/No]

:wink:

1 Like

Hi,

indeed, selectively allowing traffic from certain sources or even CIDR blocks for services IPFire is offering itself (mostly OpenVPN and IPsec) is currently not really supported the way you like it:

The location filter works, but does not allow any differentiation between services (say one needs OpenVPN access from certain countries, while IPsec access from certain other countries).

I think the location filters’ purpose is frequently misunderstood: It is something like a noice canceller for IPFire’s log, especially designed for systems running on cheap/poor flash storage, where you do not want to write every background noise hit onto disk.

To keep it short: Personally, I would like to see a more sophisticated approach to this problem land in IPFire, too. At the moment, you unfortunately have to deal with firewall.local - unless you donate us a pile of money for implementing this. :wink:

Thanks, and best regards,
Peter Müller

1 Like

What I don’t understand is why all those tables for services (like OVPNINPUT, IPSECINPUT, TOR_INPUT, etc.) are placed “before” the INPUTFW table… If the INPUTFW table was put on top of these, we could just add a rule in the WebUI to do exactly what we want (block/allow different countries for each service, log these rules, etc.) without having to deal manually with firewall.local.

To better clarify what I mean, let’s consider the actual INPUT chain (I omitted the not relevant parts):

BADTCP → CUSTOMINPUT → … → GEOIPBLOCK → IPSECINPUT → GUIINPUT → WIRELESSINPUT → OVPNINPUT → TOR_INPUT → INPUTFW → …

Notice that the GEOIPBLOCK is on top of all the other services so it acts as a global filter. I understand that this is the intended behavior, but why the INPUTFW table (which is used for custom input rules the user can create in the WebUI) is placed “after” the various services (IPSEC, GUI, WIRELESS, OVPN, TOR)? In this way custom input rules (set in the WebUI) affecting these services would never work.

Consider this alternative chain:

BADTCP → CUSTOMINPUT → … → GEOIPBLOCKINPUTFW → IPSECINPUT → GUIINPUT → WIRELESSINPUT → OVPNINPUT → TOR_INPUT → …

I just put the INPUTFW table immediately after the GEOIPBLOCK, without changing anything else. In this way we would have the flexibility (and comfort) to create rules affecting these services too, directly in the WebUI (menu Firewall → Firewall Rules), without having to edit manually the firewall.local script, with obvious advantages for the firewall administrator.

So, what do you think about it?

This way would allow breaking IPsec, OpenVPN and all the other services with a user-defined rule which is not what is intended. The chains are also ordered by how likely it is to hit them with things like port-forwardings being least likely to be hit.

I appreciate your comments around this, but there has been so much time spent on making the firewall engine as flexible, fast and of course secure. You have already posted how to solve this with the CUSTOM* chains which I would say is the way to go if you actually must block access to the VPN services.

1 Like

Yes, of course with that chain you could break the aforementioned services if you set a bad firewall rule… But that could happen with a bad firewall.local script too, so I don’t see the difference. I think that an IPFire administrator should know what he is doing when he creates a firewall rule, regardless of where he creates it (in the WebUI or inside the firewall.local script)… It’s just that IMHO the WebUI is much easier to manage for the administrator, and since the rules in the WebUI are created with the help of a wizard, the firewall admin is less likely to make stupid mistakes. And if you want to log the rule you have just to check a box, while in the firewall.local script I had to create manually the rule to generate the log and the one to drop the packets… I’m a newbie of IPFire and I’m not too versed in iptables commands so it was not easy to me to make those rules in the script… At least, the good thing is that I’ve learned new things. :grinning_face_with_smiling_eyes:

Thanks for your comment!

You would hope so, but people make mistakes…

And that’s not a responsability of IPFire, unless you want to implement an IA system that auto-corrects human mistakes in firewall rules… :laughing: