Solved openvpn and pf at startup

Hi all,

In my /etc/rc.conf I launch several instances of security/openvpn at startup.

For some reason, a vpn session can then be initiated by a client, but no traffic is passed through until I manually reload the PF rule set by running pfctl -vf /etc/pf.conf.

Just a wild guess, but I am thinking that at boot time maybe PF can't yet see the tun interfaces as defined and ignores these lines:

Code:
vpn0_if = "tun0"
vpn1_if = "tun1"
vpn2_if = "tun2"

[...]

pass out quick on $vpn0_if inet keep state
pass out quick on $vpn1_if inet keep state
pass out quick on $vpn2_if inet keep state
I tried putting the openvpn startup commands above the invocation of PF in /etc/rc.conf, but that has no effect.

Any ideas on how to fix this?
 
Just a wild guess, but I am thinking that at boot time maybe PF can't yet see the tun interfaces as defined and ignores these lines:
Your guess is spot on.

The trick is to use the function of OpenVPN to start a script when the connection goes up/down. Use that to reload PF (or load/unload additional rules specific for the VPN).

Don't add commands to any of the rc.conf files. This file is sourced multiple times by the various /etc/rc.d/ scripts and your commands will get executed several times in quick succession. Only put variables in rc.conf, NEVER commands.
 
Thanks, I'll read up on this in the openvpn docs.

The way I run the extra instances of openvpn is like this: first, in /usr/local/etc/rc.d/ I created sylinks openvpn443 and openvpn80 both pointing to the original openvpn starup script.

I then added this to /etc/rc.conf:
Code:
openvpn_enable="YES"
openvpn_configfile="/usr/local/etc/openvpn/server.conf"

openvpn443_enable="YES"
openvpn443_configfile="/usr/local/etc/openvpn/server443.conf"

openvpn80_enable="YES"
openvpn80_configfile="/usr/local/etc/openvpn/server80.conf"
These are just variables, not the actual startup commands, right?
 
Adding this to /etc/rc.conf would seem to do the trick:
Code:
openvpn_flags='--script-security 2 --up "/sbin/pfctl -vf /etc/pf.conf"'
Source
 
The way I run the extra instances of openvpn is like this: first, in /usr/local/etc/rc.d/ I created sylinks openvpn443 and openvpn80 both pointing to the original openvpn startup script.
Good solution. It's also explained in the first few lines of /usr/local/etc/rc.d/openvpn:
Code:
# This script supports running multiple instances of openvpn.
# To run additional instances link this script to something like
# % ln -s openvpn openvpn_foo
# and define additional openvpn_foo_* variables in one of
# /etc/rc.conf, /etc/rc.conf.local or /etc/rc.conf.d/openvpn_foo
These are just variables, not the actual startup commands, right?
Yes, you only set variables, the actual heavy lifting is done by all the rc(8) scripts.
 
Unfortunately, this doesn't work:
Code:
openvpn_flags='--script-security 2 --up "/sbin/pfctl -vf /etc/pf.conf"'
The "--up" command fails with error status 1 and so openvpn shuts down.

This is what I see in /var/log/openvpn.log:

Code:
[...]
Thu Jan 26 19:56:12 2017 TUN/TAP device /dev/tun3 opened
Thu Jan 26 19:56:12 2017 do_ifconfig, tt->did_ifconfig_ipv6_setup=0
Thu Jan 26 19:56:12 2017 /sbin/ifconfig tun3 10.8.3.1 10.8.3.2 mtu 1500 netmask 255.255.255.255 up
Thu Jan 26 19:56:12 2017 /sbin/pfctl -vf /etc/pf.conf tun3 1500 1624 10.8.3.1 10.8.3.2 init
pfctl: unknown command line argument: tun3 ...
usage: pfctl [-AdeghmNnOPqRrvz] [-a anchor] [-D macro=value] [-F modifier]
   [-f file] [-i interface] [-K host | network]
   [-k host | network | label | id] [-o level] [-p device]
   [-s modifier] [-t table -T command [address ...]] [-x level]
Thu Jan 26 19:56:12 2017 WARNING: Failed running command (--up/--down): external program exited with error status: 1
Thu Jan 26 19:56:12 2017 Exiting due to fatal error
From what I can see, openvpn seems to be trying to pass the string "tun3 1500 1624 10.8.3.1 10.8.3.2 init" to pfctl, and the latter doesn't like that.

Any thoughts on how I might work around this?
 
Yes, use a script: --up "/usr/local/etc/openvpn/reload_pf.sh" for example. Then do the reload in /usr/local/etc/openvpn/reload_pf.sh:

Code:
#!/bin/sh

/usr/sbin/service pf reload
 
Sorry for reply to this "old" thread, but i ran into the same issue.

The suggested fix does not work, because all openvpn scripts are started in same time and so this problem occurs:

Code:
Reloading pf rules.
pfctl: DIOCADDRULE: Device busy

or:

Code:
Reloading pf rules.
/etc/pf.conf:174: cannot define table abusive_hosts_ssh: Device busy
/etc/pf.conf:175: cannot define table abusive_hosts_www: Device busy
pfctl: Syntax error in config file: pf rules not loaded
 
Reloading PF is a bit of a workaround. A better solution is to use PF anchors. It took me living with this issue for years to finally fix it today. My server that runs OpenVPN doesn't reboot often, so it just wasn't a big deal. Until recently when I'm using both OpenVPN and WireGuard. It's time to fix it right.

After reading the docs for PF anchors and openvpn man page, I changed my PF rules so they can load at startup. OpenVPN rules are inserted some time thereafter when the tun interface comes up.

Changes
In /etc/pf.conf, add anchors as needed. In my case, I needed only to create a nat anchor:

# grep anchor /etc/pf.conf
nat-anchor "openvpn"


# cat /usr/local/etc/openvpn/add-to-pf.sh
#!/bin/sh
PFCTL=/sbin/pfctl
echo "nat on tun0 from $MyPrivNet to <$MyTableName> -> tun0" | $PFCTL -a openvpn -f -


Obviously, adjust the PF rule(s) for your needs.

In my openvpn.conf file, I added these two lines:
Code:
script-security 2
up /usr/local/etc/openvpn/add-to-pf.sh

And voila. During boot, PF loads normally despite OpenVPN not being up. When OpenVPN connects, it adds the NAT rule and my workstations have access to the OpenVPN resources.
 
When OpenVPN connects, it adds the NAT rule and my workstations have access to the OpenVPN resources.
It's not removed when the connection drops. So if you have a 'flapping' connection it continuously adds those rules. This is a good solution though but you need to add a down script too.
 
I tested that by running the add command multiple times and then checking the anchor. I was only adding a NAT rule and it did not add duplicates. This was on a FreeBSD 13.1 host. If some rules get duplicated, that sounds to me like a reason to also add a down script?
 
Also, if the openvpn connection is down the NAT rule would still be there. This may have some unintended consequences. Probably a simple pfctl -Fa -a openvpn would do to remove any and all rules and states from the anchor.
 
Back
Top