Local VPN Router on FreeBSD
Purpose
This guide describes how to build a FreeBSD-based VM that nails up a VPN tunnel to a provider and can be used as a local gateway for other systems.
Adding this system to your network will add another gateway to your network, but instead of routing traffic directly to the public Internet, it will route traffic through the VPN before reaching the public Internet.
Servers on the public Internet will see your systems connecting from the VPN Endpoint's IP address instead of yours.
graph LR
A[System] --> E
B[System]
C{VPN VM} -. encrypted .-> D
D{VPN Endpoint} --> E
E((Public Internet))
B --> C
Requirements
A FreeBSD-based router doesn't need much CPU or RAM to work well.
A single (v)CPU system with a gigabyte of RAM can easily handle 500Mbit/sec of traffic across the VPN.
You'll need a VPN account with a provider that provides OpenVPN configuration files and does not log.
Procedure
Operating System
Install FreeBSD
This guide assumes you have installed and configured FreeBSD and the Packages system.
The FreeBSD Handbook is an excellent resource if you run into problems.
Configure Operating System
rc.conf
All of FreeBSD's system configuration options are set in the /etc/rc.conf
file.
These were probably configured during the OS installation. It doesn't hurt to verify that they are set properly.
Enable these options for your system in the configuration file:
Option | Description |
---|---|
clear_tmp_enable="YES" |
clear the contents of /tmp on boot |
syslogd_flags="-ss" |
do not log messages from remote machines, and do not listen on network sockets |
sendmail_enable="NONE" |
do not start the sendmail daemon |
sshd_enable="YES" |
start the sshd daemon on boot |
ntpdate_enable="YES" |
run ntpdate to sync time on boot |
ntpd_enable="YES" |
start ntpd on boot to keep time in sync |
dumpdev="NO" |
disable saving crash dumps to disk |
When complete, your /etc/rc.conf
file should contain these entries:
clear_tmp_enable="YES"
syslogd_flags="-ss"
sendmail_enable="NONE"
sshd_enable="YES"
ntpdate_enable="YES"
ntpd_enable="YES"
dumpdev="NO"
sysctl.conf
The /etc/sysctl.conf
file is used to control kernel tunables.
These were probably configured during the OS installation. It doesn't hurt to verify that they are set properly.
Enable these options for your system in the configuration file:
Option | Description |
---|---|
security.bsd.see_other_uids=0 |
users only see their own processes |
security.bsd.see_other_gids=0 |
groups only see their own processes |
security.bsd.see_jail_proc=0 |
don't allow users to see inside jailed processes |
security.bsd.unprivileged_read_msgbuf=0 |
unprivileged processes can't read the kernel message buffer |
security.bsd.unprivileged_proc_debug=0 |
unprivileged processes may not use process debugging |
kern.randompid=1 |
calculate PIDs by the modulus of an integer |
kern.elf32.aslr.enable=1 |
enable ASLR for 32bit systems |
kern.elf32.aslr.pie_enable=1 |
enable PIE for 32bit systems |
kern.elf32.aslr.honor_sbrk=0 |
enable SBRK for 32bit systems |
kern.elf64.aslr.enable=1 |
enable ASLR for 64bit systems |
kern.elf64.aslr.pie_enable=1 |
enable PIE for 64bit systems |
kern.elf64.aslr.honor_sbrk=0 |
enable SBRK for 64bit systems |
When complete, your /etc/sysctl.conf
file should contain these entries:
security.bsd.see_other_uids=0
security.bsd.see_other_gids=0
security.bsd.see_jail_proc=0
security.bsd.unprivileged_read_msgbuf=0
security.bsd.unprivileged_proc_debug=0
kern.randompid=1
kern.elf32.aslr.enable=1
kern.elf32.aslr.pie_enable=1
kern.elf32.aslr.honor_sbrk=0
kern.elf64.aslr.enable=1
kern.elf64.aslr.pie_enable=1
kern.elf64.aslr.honor_sbrk=0
Enable VPN
Your VPN provider should provide you with OpenVPN configuration files for your client.
Install the OpenVPN client:
Copy the OpenVPN client configuration file to the /usr/local/etc/openvpn/
directory. In this example, the configuration filename is openvpn.ovpn
.
Edit the /etc/rc.conf
file and enable the OpenVPN client:
Start the VPN client:
You should now have a tun0
interface:
root@vpn:~ # ifconfig tun0
tun0: flags=8043<UP,BROADCAST,RUNNING,MULTICAST> metric 0 mtu 1452
options=80000<LINKSTATE>
inet 10.4.112.148 netmask 0xffffff00 broadcast 10.4.112.255
groups: tun
nd6 options=29<PERFORMNUD,IFDISABLED,AUTO_LINKLOCAL>
Opened by PID 27105
Examining the routing table should show two default routes (one for default
(your real gateway) and another for 0.0.0.0/1
(your VPN gateway)):
root@vpn:~ # netstat -rn | head -n 6
Routing tables
Internet:
Destination Gateway Flags Netif Expire
0.0.0.0/1 10.4.112.1 UGS tun0
default 192.168.200.1 UGS vtnet0
Enable Firewall
FreeBSD's implementation of the pf firewall is easy to configure, offers good security features, and is included in the base system and kernel.
Enable the pf firewall by adding these files to the /etc/rc.conf
file:
The pf firewall ruleset is in /etc/pf.conf
. Edit your config file to look like this:
ext_if="tun0" # your vpn interface name
int_if="vtnet0" # your ethernet interface name
set loginterface $ext_if # set interface for logging
set optimization aggressive # aggressively expire connections
set block-policy return # set default for 'block' action
set skip on lo0 # don't log anything about loopback
scrub on $ext_if all # scrub all packets on VPN
nat on $ext_if from $int_if:network to any -> ($ext_if) # NAT on VPN
pass out quick on $int_if inet all keep state # keep state on connections
block in on $ext_if all # block all ingress on VPN
pass out on $ext_if all # allow all out on VPN
Start the pf and pflog services:
Check the status of the pf firewall with /etc/rc.d/pf status
(this one has been running for a while):
root@vpn:~ # /etc/rc.d/pf status
Status: Enabled for 96 days 16:08:49 Debug: Urgent
Interface Stats for tun0 IPv4 IPv6
Bytes In 4636817128872 0
Bytes Out 181189715493 0
Packets In
Passed 3553031402 0
Blocked 3869284 0
Packets Out
Passed 2431266 0
Blocked 0 0
State Table Total Rate
current entries 47
searches 12591959181 1507.6/s
inserts 11646577 1.4/s
removals 11646530 1.4/s
Counters
match 235225998 28.2/s
bad-offset 0 0.0/s
fragment 522 0.0/s
short 2 0.0/s
normalize 0 0.0/s
memory 0 0.0/s
bad-timestamp 0 0.0/s
congestion 0 0.0/s
ip-option 173 0.0/s
proto-cksum 0 0.0/s
state-mismatch 735 0.0/s
state-insert 0 0.0/s
state-limit 0 0.0/s
src-limit 0 0.0/s
synproxy 0 0.0/s
map-failed 0 0.0/s
Configure Clients
You can now configure other systems on the network to use the FreeBSD VPN system as a default gateway.
Use the VPN system's IP non-VPN IP address (the one you ssh into) as the default gateway on a different system. All routed traffic for this system will now be routed across the VPN before reaching the public Internet.
Test this by querying for your public IP address from the commandline of a system routing traffic through the VPN. It should be a different IP address than what you see from a local system not routing traffic through the VPN.
Testing Client Routing
A system routing traffic through the VPN gateway should have a different public IP address than a system routing traffic through the network's default gateway.
This can easily be tested by using curl
and ifconfig.co.
Public IP
Run curl ifconfig.co
from a machine routing traffic through your regular gateway/router to get the public IP the traffic from your home network egresses from:
Public IP Over VPN
Run curl ifconfig.co
from a machine routing traffic through the VPN system to get the public IP address its traffic egresses from:
This should be a different IP address than you got from a non-VPN routed system.