I am continuing to set up my home server on FreeBSD 14.3, which is intended to serve as a NAS.
In the previous post, FreeBSD: introduction to Packet Filter (PF) firewall, we got acquainted with firewalls; the next step is to configure a VPN for access.
The main idea is to (finally!) connect my “office” and my apartment, and later, perhaps, also connect the server where rtfm.co.ua is currently running so that blog files and database backups can be stored directly on the ZFS mirror pool of the home server.
Contents
WireGuard vs OpenVPN
When it came to choosing which specific VPN server to use, I initially thought about OpenVPN – since I’ve worked with it for years, and there are even some blog posts about it on RTFM.
However, after giving it some thought, I decided that for a home VPN, solutions like OpenVPN or Pritunl would be a bit of overkill, and I could give WireGuard a try.
The systems are very different, but in short:
- WireGuard has a much smaller codebase – for example, the Linux implementation is about 4,000 lines in the kernel, while OpenVPN is about 100,000 lines in user space
- WireGuard works as a kernel module – packet processing and cryptography are performed directly in kernel space, whereas OpenVPN is a user space service that operates through a TCP or UDP socket and interacts with the kernel via the standard kernel network stack
- The same applies to encryption, as WireGuard has built-in cryptography that is part of the protocol itself and runs in kernel space, while OpenVPN uses the standard SSL/TLS stack (OpenSSL, LibreSSL, etc.) in user space, which adds complexity and CPU/RAM overhead
- WireGuard’s operational model is peer-to-peer – meaning the protocol has no built-in “server” or “client” roles, only Peers with keys and allowed IPs, whereas OpenVPN is built around a classic client-server architecture
As a result, WireGuard can be perceived not as a separate service, but as an encrypted network interface, while OpenVPN remains a classic application-based VPN service.
Even the official WireGuard whitepaper is titled “Next Generation Kernel Network Tunnel“.
Network Architecture
So, here is what I have:
- “office”: a separate local network 192.168.0.0/24, with a TP-LINK Archer AX12 router at the entry
- this network contains a work laptop with Arch Linux and a Lenovo ThinkCentre with FreeBSD
- the FreeBSD machine will host the NAS, NFS, and WireGuard itself
- although the Archer AX12 has its own built-in OpenVPN and WireGuard – I want to do it myself, manually, for more control
- home: a 192.168.100.0/24 network with the exact same Archer AX12 router
- the only client there is a home laptop with Arch Linux
And here is what I want to achieve:
- FreeBSD will act as the WireGuard VPN server
- The Archer AX12 router will have NAT port-forwarding to connect to WireGuard on FreeBSD
- VPN network – 10.8.0.1/24
- Packet Filter firewall on FreeBSD to control traffic
- Both laptops should have access to each other and to the future NAS on FreeBSD
Here is how it looks schematically:
Running WireGuard on FreeBSD
In FreeBSD (just like in Linux), WireGuard consists of a kernel module + userspace tools: the main “working” part is loaded as a kernel module, and a separate package is installed to interact with it.
Install wireguard-tools:
root@setevoy-nas:/home/setevoy # pkg install wireguard-tools
Load the module:
root@setevoy-nas:/home/setevoy # kldload if_wg
Verify:
root@setevoy-nas:/home/setevoy # kldstat | grep wg 8 1 0xffffffff82a47000 2f5c0 if_wg.ko
Enable WireGuard in /etc/rc.conf:
root@setevoy-nas:/home/setevoy # sysrc wireguard_enable=YES wireguard_enable: -> YES root@setevoy-nas:/home/setevoy # sysrc wireguard_interfaces=wg0 wireguard_interfaces: -> wg0
Don’t start it yet – let’s move on to network configuration.
Network configuration
Next, the system needs to be configured to route packets between the physical interface and the WireGuard interface, and the firewall config needs an update.
IP forwarding configuration
Enable IP forwarding from the wg0 interface (which doesn’t exist yet, it will appear when WireGuard starts) to the LAN interface, em0.
Update the autostart in /etc/rc.conf:
root@setevoy-nas:/usr/local/etc/wireguard # sysrc gateway_enable="YES" gateway_enable: NO -> YES
To enable forwarding immediately without a reboot – turn it on using sysctl:
root@setevoy-nas:/usr/local/etc/wireguard # sysctl net.inet.ip.forwarding=1 net.inet.ip.forwarding: 0 -> 1
Verify:
root@setevoy-nas:/usr/local/etc/wireguard # sysctl net.inet.ip.forwarding net.inet.ip.forwarding: 1
The next step is configuring Packet Filter.
Packet Filter Configuration
So, here is what we have:
- VPN network: 10.8.0.0/24
- Office network where FreeBSD/VPN is located: 192.168.0.0/24
- FreeBSD LAN IP: 192.168.0.2
- Routing internet through VPN is not required – only traffic between the home and office networks
The current pf config is minimalist, from the previous post:
allowed_tcp_ports = "{ 22 }"
allowed_clients = "{ 192.168.0.0/24, 192.168.1.0/24 }"
set skip on lo
block all
# allow ssh only from specific hosts
pass in proto tcp from $allowed_clients to any port $allowed_tcp_ports keep state
# allow all outgoing traffic
pass out all keep state
What needs to be added:
- allow inbound UDP connections to the WireGuard port (51820) for the handshake
- allow traffic from the VPN network 10.8.0.0/24 to the FreeBSD host itself (ping, SSH)
- allow transit traffic from the VPN network 10.8.0.0/24 to the local office and home networks (192.168.0.0/24 and 192.168.100.0/24)
- allow ICMP and SSH from the VPN network and the home network to the FreeBSD host
- allow outbound traffic from FreeBSD
I’ve added macros to the config, but while writing and testing – I specify all ports and addresses explicitly in the config for better readability.
Now /etc/pf.conf will look like this:
##################
### Interfaces ###
##################
# lan_if = "em0"
# wg_if = "wg0"
################
### Networks ###
################
# lan_net = "192.168.0.0/24"
# home_net = "192.168.100.0/24"
# wg_net = "10.8.0.0/24"
# vpn_nets = "{ 10.8.0.0/24, 192.168.100.0/24 }"
################
### Services ###
################
# ssh_ports = "{ 22 }"
# wg_port = "51820"
######################
### Basic settings ###
######################
# do not filter loopback traffic
set skip on lo
######################
### Default policy ###
######################
# block everything by default
block all
#######################
### Inbound traffic ###
#######################
### SSH
# allow SSH from Office LAN (192.168.0.0/24) to FreeBSD host
pass in log on em0 proto tcp from 192.168.0.0/24 to (em0) port 22 keep state
# allow SSH from Home network (192.168.100.0/24) to FreeBSD host
pass in log on em0 proto tcp from 192.168.100.0/24 to (em0) port 22 keep state
# allow SSH from VPN clients to FreeBSD host
pass in on wg0 proto tcp from 10.8.0.0/24 to (wg0) port 22 keep state
### VPN
# allow WireGuard handshake (UDP/51820) on LAN interface
pass in on em0 proto udp to (em0) port 51820 keep state
# allow VPN clients (10.8.0.0/24) to access FreeBSD host itself
# this allows ping, ssh, etc. to the wg0 address
pass in on wg0 from 10.8.0.0/24 to (wg0) keep state
# allow VPN clients to access Office LAN (192.168.0.0/24)
pass in on wg0 from 10.8.0.0/24 to 192.168.0.0/24 keep state
# allow VPN clients to access Home network (192.168.100.0/24)
pass in on wg0 from 10.8.0.0/24 to 192.168.100.0/24 keep state
# allow ICMP (ping) from VPN clients to FreeBSD host
pass in on wg0 proto icmp from 10.8.0.0/24 to (wg0) keep state
# allow ICMP (ping) from Home network to FreeBSD host
pass in on em0 proto icmp from 192.168.100.0/24 to (em0) keep state
############################
### outbound traffic ###
############################
# allow all outbound traffic from FreeBSD
pass out keep state
Verify the syntax:
root@setevoy-nas:/home/setevoy # pfctl -vnf /etc/pf.conf
set skip on { lo }
block drop all
pass in log on em0 inet proto tcp from 192.168.0.0/24 to (em0) port = ssh flags S/SA keep state
pass in log on em0 inet proto tcp from 192.168.100.0/24 to (em0) port = ssh flags S/SA keep state
pass in on wg0 inet from 10.8.0.0/24 to (wg0) flags S/SA keep state
pass in on wg0 inet proto icmp from 10.8.0.0/24 to (wg0) keep state
pass in on wg0 inet from 10.8.0.0/24 to 192.168.0.0/24 flags S/SA keep state
pass in on wg0 inet from 10.8.0.0/24 to 192.168.100.0/24 flags S/SA keep state
pass in on em0 inet proto icmp from 192.168.100.0/24 to (em0) keep state
pass in on em0 proto udp from any to (em0) port = 51820 keep state
pass out all flags S/SA keep state
Reload the rules:
root@setevoy-nas:/home/setevoy # service pf reload Reloading pf rules.
Now we can prepare to start WireGuard.
WireGuard Configuration
Everything is very simple here – create the keys, write the config file.
Creating Keys
Communication and cryptography in WireGuard are built on a standard asymmetric key scheme:
- The private key is stored on the “server”
- The public key is specified on the client
- During the handshake, the client verifies it is connecting to the correct server whose public key it knows
- Afterward, data encryption is performed using these keys
See Key Exchange and Data Packets.
I’m putting the word “server” in quotes because, as mentioned earlier, WireGuard is P2P, not client-server.
After installing wireguard-tools, the /usr/local/etc/wireguard directory is created – navigate there and create the private and public keys using wg genkey:
root@setevoy-nas:/home/setevoy # cd /usr/local/etc/wireguard root@setevoy-nas:/usr/local/etc/wireguard # wg genkey | tee server.key | wg pubkey > server.pub
Change the permissions for the private key:
root@setevoy-nas:/usr/local/etc/wireguard # chmod 600 server.key
Verify:
root@setevoy-nas:/usr/local/etc/wireguard # ll total 12 -rw------- 1 root wheel 45 Dec 17 15:58 server.key -rw-r--r-- 1 root wheel 45 Dec 17 15:58 server.pub
Basic WireGuard config
You can create several different configurations in /usr/local/etc/wireguard/, each on its own port and/or IP and with its own key, to have multiple distinct VPN connections, managing them by filename – wg0, wg1, etc.
There are even config generators available – https://www.wireguardconfig.com.
Syntax documentation – Wireguard Configuration File Format.
Retrieve the private key:
root@setevoy-nas:/usr/local/etc/wireguard # cat server.key cLS***GQ=
Create the file /usr/local/etc/wireguard/wg0.conf – just the “server” for now:
[Interface] Address = 10.8.0.1/24 ListenPort = 51820 PrivateKey = cLS***sGQ=
The Interface block defines the parameters for the WireGuard interface wg0 – its IP address, UDP port, and the private key used for traffic encryption.
You can also specify which DNS to use, whether to update the routing tables on clients (default is true), and script execution with PreUp, PostUp, PreDown, and PostDown.
Start WireGuard itself:
root@setevoy-nas:/home/setevoy # wg-quick up wg0 [#] ifconfig wg create name wg0 [#] wg setconf wg0 /dev/stdin [#] ifconfig wg0 inet 10.8.0.1/24 alias [#] ifconfig wg0 mtu 1420 [#] ifconfig wg0 up [+] Backgrounding route monitor
Verify the interface:
root@setevoy-nas:/home/setevoy # ifconfig wg0
wg0: flags=10080c1<UP,RUNNING,NOARP,MULTICAST,LOWER_UP> metric 0 mtu 1420
options=80000<LINKSTATE>
inet 10.8.0.1 netmask 0xffffff00
groups: wg
nd6 options=109<PERFORMNUD,IFDISABLED,NO_DAD>
And the WireGuard status:
root@setevoy-nas:/home/setevoy # wg show interface: wg0 public key: xLWA/FgF3LBswHD5Z1uZZMOiCbtSvDaUOOFjH4IF6W8= private key: (hidden) listening port: 51820
Since we don’t have any clients yet – let’s move on to setting them up.
TP-Link Dynamic DNS and NAT port-forwarding
To connect from home to the FreeBSD host with WireGuard – add port forwarding on the office router:
- protocol: UDP
- external port on the router: 51830 (to mask it slightly from bots)
- forward to: 192.168.0.2 (the FreeBSD host)
- forward to port: 51830 (WireGuard on
em0on FreeBSD)
On the TP-Link Archer AX12, it looks like this:
If the internet IP in the office is dynamic – the Archer AX12 has a Dynamic DNS setting:
Although mine is static, I set up DDNS out of interest using https://www.noip.com.
Running WireGuard on Arch Linux
On Linux, the process is identical – the modules are in the kernel, so we just need to install the package with the tools.
Check the modules:
root@setevoy-home:/home/setevoy # lsmod | grep wireguard wireguard 122880 0 curve25519_x86_64 36864 1 wireguard libcurve25519_generic 45056 2 curve25519_x86_64,wireguard ip6_udp_tunnel 16384 1 wireguard udp_tunnel 32768 1 wireguard
Install the package:
root@setevoy-home:/home/setevoy # pacman -S wireguard-tools
Navigate to /etc/wireguard/ and create the keys:
root@setevoy-home:/home/setevoy # cd /etc/wireguard/ root@setevoy-home:/etc/wireguard # wg genkey | tee client1.key | wg pubkey > client1.pub
Change the permissions for the private key:
root@setevoy-home:/etc/wireguard # chmod 600 client1.key
Now we can add Peers – clients.
To do this, we need to add keys on the client and the server:
- on the server:
- In
Interface–PrivateKey: this is/usr/local/etc/wireguard/server.keyon the FreeBSD host - In
Peer–PublicKey: this is/etc/wireguard/client1.pubon the Arch Linux laptop
- In
- on the client:
- In
Interface–PrivateKey: this is/etc/wireguard/client1.key - In
Peer–PublicKey: this is/usr/local/etc/wireguard/server.pub
- In
Define the config **on the client**, file /etc/wireguard/wg0.conf:
[Interface] PrivateKey = 0Cu***UWU= Address = 10.8.0.3/24 [Peer] PublicKey = xLWA/FgF3LBswHD5Z1uZZMOiCbtSvDaUOOFjH4IF6W8= Endpoint = setevoy-***.ddns.me:51830 AllowedIPs = 10.8.0.1/32, 192.168.0.0/24 PersistentKeepalive = 25
In AllowedIPs, we specify the networks that will be accessible and added to the routing table (“Acts as a routing table and access control list“).
Start it on the client:
[root@setevoy-wg-test setevoy]# wg-quick up wg0 [#] ip link add dev wg0 type wireguard [#] wg setconf wg0 /dev/fd/63 [#] ip -4 address add 10.8.0.3/24 dev wg0 [#] ip link set mtu 1420 up dev wg0 [#] ip -4 route add 10.8.0.3/32 dev wg0 [#] ip -4 route add 192.168.0.0/24 dev wg0
Here:
ip -4 address add: theInterface - Addressset forwg0ip -4 route add 10.8.0.3/32and192.168.0.0/24: new routes added via thewg0interface for the VPN and office local networks
Verify:
root@setevoy-home:/etc/wireguard # ip r s 10.8.0.0/24 10.8.0.0/24 dev wg0 proto kernel scope link src 10.8.0.3 root@setevoy-home:/etc/wireguard # ip r s 192.168.0.0/24 192.168.0.0/24 dev wg0 scope link
Add the Peer **on the server**, the /usr/local/etc/wireguard/wg0.conf file will now look like this:
[Interface] Address = 10.8.0.1/24 ListenPort = 51820 PrivateKey = cLS***sGQ= [Peer] PublicKey = d7yqxOky4qOI/NTl/qbUnijfICwmbe/e/ulSVuQKLhk= AllowedIPs = 10.8.0.3/32, 192.168.100.0/24
Restart:
root@setevoy-nas:/usr/local/etc/wireguard # wg-quick down wg0 [#] ifconfig wg0 destroy root@setevoy-nas:/usr/local/etc/wireguard # wg-quick up wg0 [#] ifconfig wg create name wg0 [#] wg setconf wg0 /dev/stdin [#] ifconfig wg0 inet 10.8.0.1/24 alias [#] ifconfig wg0 mtu 1420 [#] ifconfig wg0 up [#] route -q -n add -inet 10.8.0.2/32 -interface wg0 [+] Backgrounding route monitor
Check the status on the client:
root@setevoy-home:/etc/wireguard # wg show interface: wg0 public key: d7yqxOky4qOI/NTl/qbUnijfICwmbe/e/ulSVuQKLhk= private key: (hidden) listening port: 36864 peer: xLWA/FgF3LBswHD5Z1uZZMOiCbtSvDaUOOFjH4IF6W8= endpoint: 178.***.***.184:51830 allowed ips: 10.8.0.1/32, 192.168.0.0/24 latest handshake: 1 minute, 44 seconds ago transfer: 4.35 KiB received, 5.84 KiB sent persistent keepalive: every 25 seconds
The most important thing to look for is “latest handshake” – it means the client has connected to the server.
Check on the server:
root@setevoy-nas:/home/setevoy # wg show interface: wg0 public key: xLWA/FgF3LBswHD5Z1uZZMOiCbtSvDaUOOFjH4IF6W8= private key: (hidden) listening port: 51820 peer: d7yqxOky4qOI/NTl/qbUnijfICwmbe/e/ulSVuQKLhk= endpoint: 178.***.***.236:56432 allowed ips: 192.168.100.0/24, 10.8.0.3/32 latest handshake: 15 seconds ago transfer: 1.69 KiB received, 3.87 KiB sent
Verify SSH from the client to the server:
root@setevoy-home:/etc/wireguard # ssh [email protected] ([email protected]) Password for setevoy@setevoy-nas: ... FreeBSD 14.3-RELEASE (GENERIC) releng/14.3-n271432-8c9ce319fef7 Welcome to FreeBSD! ... setevoy@setevoy-nas:~ $
Or:
[setevoy@setevoy-home ~]$ ssh 192.168.0.2 ([email protected]) Password for setevoy@setevoy-nas:
Enable the wg0 profile on autostart:
[setevoy@setevoy-home ~]$ sudo systemctl enable wg-quick@wg0 Created symlink '/etc/systemd/system/multi-user.target.wants/[email protected]' → '/usr/lib/systemd/system/[email protected]'.
At this point, almost everything is ready – access is established, and everything is working.
However, I also want to have direct access from the home laptop to the work laptop and vice versa, as the work laptop doesn’t have a VPN – it doesn’t need one because FreeBSD/NAS is in the same local network.
Cross-LAN access configuration
So what needs to be done is to set up direct access between the laptops in the home network (192.168.100.0/24) and the office network (192.168.0.0/24), because currently access between the work laptop and the home laptop doesn’t work.
The situation is currently as follows:
- Office laptop IP: 192.168.0.165
- Home laptop IP: 192.168.100.205
- No WireGuard on the work laptop
- No connection from the office to the home laptop
- No connection from home to the work laptop
- Connection from home to FreeBSD exists
Routing Table Setup
While setting this up – comment out block all in /etc/pf.conf; we’ll return to it later.
The result of what we’re about to do will look like this: the key is the routes. I’ve specifically made this as a diagram to make the following steps easier to understand:
Check the routes from the home laptop to FreeBSD:
root@setevoy-home:/etc/wireguard # ip route get 192.168.0.2 192.168.0.2 dev wg0 src 10.8.0.3 uid 0
And to the work laptop:
root@setevoy-home:/etc/wireguard # ip route get 192.168.0.165 192.168.0.165 dev wg0 src 10.8.0.3 uid 0
Traffic goes through wg0, and the Source Address for the packet is set as 10.8.0.3.
However, on the work laptop, the route to the home laptop goes through 192.168.0.1:
[setevoy@setevoy-work ~] $ ip route get 192.168.100.205 192.168.100.205 via 192.168.0.1 dev wlan0 src 192.168.0.165 uid 1000
Here, 192.168.0.1 is the default gateway, the office router, which knows nothing about the home network 192.168.100.0/24.
So first – add a route to the home network via the FreeBSD host:
[setevoy@setevoy-work ~] $ sudo ip route add 192.168.100.0/24 via 192.168.0.2
Check again:
[setevoy@setevoy-work ~] $ ip route get 192.168.100.205 192.168.100.205 via 192.168.0.2 dev wlan0 src 192.168.0.165 uid 1000
Now there is contact from the office to home:
[setevoy@setevoy-work ~] $ ping 192.168.100.205 -c 1 PING 192.168.100.205 (192.168.100.205) 56(84) bytes of data. 64 bytes from 192.168.100.205: icmp_seq=1 ttl=63 time=62.0 ms --- 192.168.100.205 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms
But from home, it still doesn’t work, because from home we send:
- from the home laptop with IP 192.168.100.205
- through FreeBSD with IP 192.168.0.2
- to the work laptop with IP 192.168.0.165
- through FreeBSD with IP 192.168.0.2
But from the home laptop, the Source IP is set as 10.8.0.3:
root@setevoy-home:/etc/wireguard # ip route get 192.168.0.165 192.168.0.165 dev wg0 src 10.8.0.3 uid 0
Because the route to 192.168.0.0/24 is specified via the VPN interface wg0:
root@setevoy-home:/etc/wireguard # ip r s 192.168.0.0/24 192.168.0.0/24 dev wg0 scope link
And wg0 has the IP 10.8.0.3:
root@setevoy-home:/etc/wireguard # ip a s wg0
20: wg0: <POINTOPOINT,NOARP,UP,LOWER_UP> mtu 1420 qdisc noqueue state UNKNOWN group default qlen 1000
link/none
inet 10.8.0.3/24 scope global wg0
The work laptop knows nothing about the 10.8.0.0/24 network and cannot return a response.
So, add another route on the work laptop:
[setevoy@setevoy-work ~] $ sudo ip route add 10.8.0.0/24 via 192.168.0.2 dev wlan0
Verify:
[setevoy@setevoy-work ~] $ ip r s 10.8.0.0/24 10.8.0.0/24 via 192.168.0.2 dev wlan0
And now there is also access from the home laptop to the work laptop:
root@setevoy-home:/etc/wireguard # ping -c1 192.168.0.165 PING 192.168.0.165 (192.168.0.165) 56(84) bytes of data. 64 bytes from 192.168.0.165: icmp_seq=1 ttl=63 time=6.19 ms --- 192.168.0.165 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms
To make these routes permanent – you can do it via NetworkManager CLI.
Delete the ones added manually:
[setevoy@setevoy-work ~] $ sudo ip route del 10.8.0.0/24 via 192.168.0.2 [setevoy@setevoy-work ~] $ sudo ip route del 192.168.100.0/24 via 192.168.0.2
Find the connection name:
[setevoy@setevoy-work ~] $ nmcli connection show NAME UUID TYPE DEVICE setevoy-tp-link-21-5 3a12a60d-7b37-4c20-b573-d27c47a94ae5 wifi wlan0 ...
Add the routes:
[setevoy@setevoy-work ~] $ nmcli connection modify setevoy-tp-link-21-5 +ipv4.routes "10.8.0.0/24 192.168.0.2,192.168.100.0/24 192.168.0.2"
Verify:
[setevoy@setevoy-work ~] $ nmcli connection show setevoy-tp-link-21-5 | grep ipv4.routes
ipv4.routes: { ip = 10.8.0.0/24, nh = 192.168.0.2 }; { ip = 192.168.100.0/24, nh = 192.168.0.2 }
Restart the connection:
[setevoy@setevoy-work ~] $ sudo nmcli connection down setevoy-tp-link-21-5 && sudo nmcli connection up setevoy-tp-link-21-5 Connection 'setevoy-tp-link-21-5' successfully deactivated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/15) Connection successfully activated (D-Bus active path: /org/freedesktop/NetworkManager/ActiveConnection/16)
Check the routes now:
[setevoy@setevoy-work ~] $ ip route get 10.8.0.3 10.8.0.3 via 192.168.0.2 dev wlan0 src 192.168.0.165 uid 1000 [setevoy@setevoy-work ~] $ ip route get 192.168.100.205 192.168.100.205 via 192.168.0.2 dev wlan0 src 192.168.0.165 uid 1000
Now we have ping from the office laptop to the home laptop:
[setevoy@setevoy-work ~] $ ping -c1 192.168.100.205 PING 192.168.100.205 (192.168.100.205) 56(84) bytes of data. 64 bytes from 192.168.100.205: icmp_seq=1 ttl=63 time=5.95 ms --- 192.168.100.205 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms
And from home to the work laptop:
root@setevoy-home:/etc/wireguard # ping -c1 192.168.0.165 PING 192.168.0.165 (192.168.0.165) 56(84) bytes of data. 64 bytes from 192.168.0.165: icmp_seq=1 ttl=63 time=5.67 ms --- 192.168.0.165 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms
Packet Filter Configuration
However, if we enable block all in pf, the connection from the office to the home laptop will break, because we currently only have rules for the FreeBSD host:
... # allow SSH from Office LAN (192.168.0.0/24) to FreeBSD host pass in log on em0 proto tcp from 192.168.0.0/24 to (em0) port 22 keep state ... # allow ICMP (ping) from Home network to FreeBSD host pass in on em0 proto icmp from 192.168.100.0/24 to (em0) keep state ...
Here:
- The first rule – allows SSH from the office network to the IP of the
em0interface on the FreeBSD host - The second rule – allows ping from the home network to the IP of the
em0interface on the FreeBSD host
So, let’s add two more rules – for SSH and ping from the office to the home network:
... # allow SSH from Office network to Home network pass in on em0 proto tcp from 192.168.0.0/24 to 192.168.100.0/24 port 22 keep state ... # allow ICMP from Home network to Office network pass in on em0 proto icmp from 192.168.0.0/24 to 192.168.100.0/24 keep state ...
Verify and reload the pf config:
root@setevoy-nas:/usr/local/etc/wireguard # pfctl -vnf /etc/pf.conf && service pf reload
set skip on { lo }
block drop log all
pass in log on em0 inet proto tcp from 192.168.0.0/24 to (em0) port = ssh flags S/SA keep state
pass in log on em0 inet proto tcp from 192.168.100.0/24 to (em0) port = ssh flags S/SA keep state
pass in on wg0 inet from 10.8.0.0/24 to (wg0) flags S/SA keep state
pass in on wg0 inet proto icmp from 10.8.0.0/24 to (wg0) keep state
pass in on wg0 inet from 10.8.0.0/24 to 192.168.0.0/24 flags S/SA keep state
pass in on wg0 inet from 10.8.0.0/24 to 192.168.100.0/24 flags S/SA keep state
pass in on em0 inet proto tcp from 192.168.0.0/24 to 192.168.100.0/24 port = ssh flags S/SA keep state
pass in on em0 inet proto icmp from 192.168.0.0/24 to 192.168.100.0/24 keep state
pass in on em0 proto udp from any to (em0) port = 51820 keep state
pass out all flags S/SA keep state
Reloading pf rules.
And now we have ping from home to the office:
root@setevoy-home:/etc/wireguard # ping -c1 192.168.0.165 PING 192.168.0.165 (192.168.0.165) 56(84) bytes of data. 64 bytes from 192.168.0.165: icmp_seq=1 ttl=63 time=8.09 ms --- 192.168.0.165 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms
SSH from home to the office:
root@setevoy-home:/etc/wireguard # ssh 192.168.0.165 [email protected]'s password:
Ping from the office to home:
[setevoy@setevoy-work ~] $ ping -c1 192.168.100.205 PING 192.168.100.205 (192.168.100.205) 56(84) bytes of data. 64 bytes from 192.168.100.205: icmp_seq=1 ttl=63 time=60.5 ms --- 192.168.100.205 ping statistics --- 1 packets transmitted, 1 received, 0% packet loss, time 0ms
And SSH from the office to home:
[setevoy@setevoy-work ~] $ ssh 192.168.100.205 [email protected]'s password:
Everything is working.
The full /etc/pf.conf is now as follows:
##################
### Interfaces ###
##################
# lan_if = "em0"
# wg_if = "wg0"
################
### Networks ###
################
# lan_net = "192.168.0.0/24"
# home_net = "192.168.100.0/24"
# wg_net = "10.8.0.0/24"
# vpn_nets = "{ 10.8.0.0/24, 192.168.100.0/24 }"
################
### Services ###
################
# ssh_ports = "{ 22 }"
# wg_port = "51820"
######################
### Basic settings ###
######################
# do not filter loopback traffic
set skip on lo
######################
### Default policy ###
######################
# block everything by default
block log all
#######################
### Inbound traffic ###
#######################
### SSH
# allow SSH from Office LAN (192.168.0.0/24) to FreeBSD host
pass in log on em0 proto tcp from 192.168.0.0/24 to (em0) port 22 keep state
# allow SSH from Home network (192.168.100.0/24) to FreeBSD host
pass in log on em0 proto tcp from 192.168.100.0/24 to (em0) port 22 keep state
# allow SSH from VPN clients to FreeBSD host
pass in on wg0 proto tcp from 10.8.0.0/24 to (wg0) port 22 keep state
### NEW
# allow SSH from Office netwrok to Home network
pass in on em0 proto tcp from 192.168.0.0/24 to 192.168.100.0/24 port 22 keep state
### TEST
# allow Office LAN to reach Home LAN via WireGuard
#pass in on em0 from 192.168.0.0/24 to 192.168.100.0/24 keep state
#pass out on wg0 from 192.168.0.0/24 to 192.168.100.0/24 keep state
# allow Home LAN to reach Office LAN via WireGuard
#pass in on wg0 from 192.168.100.0/24 to 192.168.0.0/24 keep state
#pass out on em0 from 192.168.100.0/24 to 192.168.0.0/24 keep state
### VPN
# allow WireGuard handshake (UDP/51820) on LAN interface
pass in on em0 proto udp to (em0) port 51820 keep state
# allow VPN clients (10.8.0.0/24) to access FreeBSD host itself
# this allows ping, ssh, etc. to the wg0 address
pass in on wg0 from 10.8.0.0/24 to (wg0) keep state
# allow VPN clients to access Office LAN (192.168.0.0/24)
pass in on wg0 from 10.8.0.0/24 to 192.168.0.0/24 keep state
# allow VPN clients to access Home network (192.168.100.0/24)
pass in on wg0 from 10.8.0.0/24 to 192.168.100.0/24 keep state
#
#pass in on em0 from 192.168.0.0/24 to 192.168.100.0/24 keep state
#pass in on wg0 from 192.168.100.0/24 to 192.168.0.0/24 keep state
### ICMP
# allow ICMP from VPN clients to FreeBSD host
pass in on wg0 proto icmp from 10.8.0.0/24 to (wg0) keep state
# allow ICMP from Home network to FreeBSD host
#pass in on em0 proto icmp from 192.168.100.0/24 to (em0) keep state
# allow ICMP from Home network to Office network
pass in on em0 proto icmp from 192.168.0.0/24 to 192.168.100.0/24 keep state
############################
### outbound traffic ###
############################
# allow all outbound traffic from FreeBSD
pass out keep state
Active connections in pftop:
Where:
In 192.168.0.165:50286=>192.168.0.2:22: SSH from work laptop to FreeBSDIn 178.***.***.236:56432=>192.168.0.2:51820: connection from home via NAT Port-forwarding on the office router to VPN on FreeBSDIn 10.8.0.3:39442=>192.168.0.165:22: SSH from home to the work laptopOut 10.8.0.1:50589=>10.8.0.3:22: SSH from FreeBSD to the home laptop
P.S. What an absolute blast – this “traditional networking” instead of all those AWS VPCs and their subnets…



