PF PF and OpenVPN

I have my FreeBSD server set up to connect to an OpenVPN connection. When I establish the connection, the machine sends all internet-bound traffic over the VPN. I would like to by default send no traffic out the VPN and be able to send only certain connections out the VPN, for example by originating port or by application. Am I able to do this with pf? Could someone give an example rule?
 
Post your OpenVPN client configuration and the network interface configuration. I think what you want to do is to remove the pull option from the configuration and then set up routes in the configuration file one by one. I can't give you anything more specific until I've seen your configuration
 
Code:
client

dev tun

proto udp

remote openvpn.mullvad.net 1300
cipher AES-256-CBC

# Tunnel IPv6 traffic as well as IPv4
tun-ipv6

# Keep trying indefinitely to resolve the
# host name of the OpenVPN server.  Very useful
# on machines which are not permanently connected
# to the internet such as laptops.
resolv-retry infinite

# Most clients don't need to bind to
# a specific local port number.
nobind

# Try to preserve some state across restarts.
persist-key
persist-tun

# Enable compression on the VPN link.
comp-lzo

# Set log file verbosity.
verb 3

remote-cert-tls server

ping-restart 60

# Allow calling of built-in executables and user-defined scripts.
script-security 2

# Parses DHCP options from openvpn to update resolv.conf
#up /etc/openvpn/update-resolv-conf
#down /etc/openvpn/update-resolv-conf

ping 10

ca ca.crt
cert mullvad.crt
key mullvad.key

crl-verify crl.pem

# Limit range of possible TLS cipher-suites
tls-cipher TLS-DHE-RSA-WITH-AES-256-CBC-SHA:TLS-DHE-RSA-WITH-CAMELLIA-256-CBC-SHA:TLS-DHE-RSA-WITH-3DES-EDE-CBC-SHA:TLS-DHE-RSA-WITH-AES-128-CBC-SHA:TLS-DHE-RSA-WITH-SEED-CBC-SHA:TLS-DHE-RSA-WITH-CAMELLIA-128-CBC-SHA
 
Ok, you should add this to your configuration to prevent the routes being forced to you by the OpenVPN server:

Code:
route-nopull

After that you can specify the desired routes in the configuration with for example:

Code:
route 192.168.42.0 255.255.255.0

That would make all traffic from your system to network 192.168.42.0/255.255.255.0 to be directed to the VPN tunnel.

Edit: After reading your original question that's not going to be enough. You'll need what is called "policy routing". This can be done with PF but only for traffic that comes in to the system via another interface, usually the LAN interface.
 
So basically the
Code:
route-nopull
bit prevents all traffic going out the OpenVPN (tun0) interface?

Then I should be able to figure out how to use pf to nat desired traffic out the OpenVPN (tun0) interface...
 
  • Thanks
Reactions: Oko
And, is there no way to route packets originating from the local machine with PF?
 
This will work for traffic originating from the LAN natwork, let's say you want to redirect HTTP over the VPN:

Code:
pass in quick on $int_if inet proto tcp from any to any port http route-to ($vpn_if, $vpn_gw)

The vpn_if would be tun0 in your case and vpn_gw would be the remote end address of the VPN tunnel. This rule won't work unless the VPN tunnel is up and running so it's a good idea to use an anchor in the main PF ruleset. This should be placed within the filter rules in a suitable place so that the rules in the anchor take precedence over other rules handling the same traffic.

Code:
anchor "openvpn"

This anchor could be then filled with rules using the up/down scripts of openvpn(8) that set up and tear down the OpenVPN rules on demand.

You'll first need to pass some variables to the scripts from your OpenVPN configuration, the OPENVPN_ prefix in the script variables comes from the use of setenv-safe:

Code:
script-security 2
...
up /usr/local/etc/openvpn/up.sh
down /usr/local/etc/openvpn/down.sh

setenv-safe anchor openvpn
setenv-safe lanif em0 # change to what you really have
...

This is a sketch of what the up.sh could look like:

Code:
#!/bin/sh

LANIF="${OPENVPN_lanif}"

ANCHOR="${OPENVPN_anchor}"

/sbin/pfctl -a "${ANCHOR}" -F rules
/sbin/pfctl -a "${ANCHOR}" -F nat
/sbin/pfctl -a "${ANCHOR}" -f - <<EOT
pass in quick on $LANIF inet proto tcp from any to any port http route-to ($dev, $ifconfig_remote)
EOT

Note that $dev and $ifconfig_remote are automatically provided by OpenVPN to the script.

The down.sh script could be just:

Code:
#!/bin/sh

ANCHOR="${OPENVPN_anchor}"

/sbin/pfctl -a "${ANCHOR}" -F rules
/sbin/pfctl -a "${ANCHOR}" -F nat
 
kpa, I don't have a downstream LAN interface, I have an upstream LAN interface. I have a standalone server with a single interface, attached to the LAN. I want to send *some* of the packets originating from the server itself over the VPN tunnel, and all the rest straight out to my gateway. Is this possible?
 
Well, I have no idea if you can do that with the FreeBSD's implementation of PF. In theory you could write the rule as:

Code:
pass out quick on $ext_if inet proto tcp from any to any port http route-to ($vpn_if, $vpn_gw)

I'm just not sure if the re-routing of traffic that is already routed actually works. It might work on OpenBSD's PF based on what I've read but I've never tried that.
 
Yes sorry, I tend to forget about setfib(1) because I'm so familiar with PF and I've used policy routing in real configurations. The downside to setfib(1) is of course that it's all or nothing for the application that is directed to use the alternate routing table, you can't pick and choose which connections should use the alternate routing table or not.
 
I guess this thread is off-topic now, because I don't need PF at all now...

I was able to use setfib(1) like so:

/boot/loader.conf
Code:
#
#    Two routing tables
#
net.fibs=2

/etc/rc.conf
Code:
#
#    Transmission
#
transmission_enable="YES"
[...snip...]
transmission_fib="1"

I tried setting this in /etc/rc.conf:
Code:
#
# Set second routing table default gateway
#
fib1_defaultrouter="10.114.0.1"

...but it didn't work, so at this time I still need to set the default route for table 1 manually after boot. I guess this is because the OpenVPN (tun0) interface is not available when the system first starts because OpenVPN is still initializing?

# setfib 1 route add default 10.114.0.1

Also note I have this in my OpenVPN config:
Code:
# Don't send all traffic out the OpenVPN interface.
route-nopull

Anyway, now transmission uses the tunnel and everything else does not.
 
Back
Top