ctx->guides->openbsd-gateway
Setting up a home gateway with OpenBSD + other goodies
If you have a spare box with two or more NICs, you can turn it into a powerful OpenBSD router.
In this tutorial, I will walk you through my gateway configuration.
Remember! Do not blindly copy paste the configuration files!
Available hardware and network layout
My router is a Shuttle XH81V. It has two Realtek NICs.
I have a single local subnet, 10.0.0.0/24.
Because of lack of additional NICs or a VLAN capable switch, there is no DMZ. To avoid exposing many services to the outside, I typically use ssh tunneling or a VPN to access local services behind the gateway.
I have a dedicated server hosted in a DC. I use tinc in a bridged mode configuration to make the server appear on my local subnet. This way, I can access the server transparently even on machines on my local network that I cannot install tinc to.
For IPv6, I use a Hurricane Electric tunnel. Their service has proven reliable with virtually no downtime experienced in the past year.
Topics covered
The following topics will be discussed:
- Firewall, routing and NAT configuration
- DHCP server configuration
- Split horizon DNS + dnscrypt_proxy
- PXE booting
- Configuring an IPv6 gif(4) tunnel with Hurricane Electric
- NetFlow sensor and collector configuration
- Debugging tips
Firewall, routing and NAT configuration
There is an excellent tutorial in the pf FAQ.
Fore more information, check the pf.conf(5),
hostname.if(5) and
ifconfig(8) manpages.
/etc/pf.conf
int_if = "re1"
sshbox = "10.0.0.2"
martians = "{ 0.0.0.0/8 10.0.0.0/8 100.64.0.0/10 127.0.0.0/8 \
169.254.0.0/16 172.16.0.0/12 192.0.0.0/24 192.0.2.0/24 \
192.168.0.0/16 198.18.0.0/15 198.51.100.0/24 203.0.113.0/24 \
224.0.0.0/4 240.0.0.0/4 255.255.255.255/32 }"
set loginterface egress
set skip on lo0
set block-policy return
match in all scrub (no-df random-id)
match out on egress inet from !(egress:network) to any nat-to (egress:0)
block drop in quick on egress from { no-route urpf-failed $martians }
block return out quick on egress to $martians
block
pass in on $int_if
pass in on egress inet proto icmp to (egress)
pass in on egress inet proto tcp to (egress) port ssh rdr-to $sshbox
pass out
/etc/sysctl.conf
net.inet.ip.forwarding=1
/etc/hostname.re0
description "WAN"
dhcp
/etc/hostname.re1
description "LAN"
inet 10.0.0.1 255.255.255.0 10.0.0.255
up
Reboot the router. This isn't required but it is a good idea to test that your changes are correctly set after a fresh boot.
DHCP server configuration
I use 2f30.org as the default search domain. I have a split horizon DNS configuration so I can access my machines from my local network as well as from the outside.
/etc/dhcpd.conf
option domain-name "2f30.org";
option domain-name-servers 10.0.0.1;
subnet 10.0.0.0 netmask 255.255.255.0 {
option routers 10.0.0.1;
range 10.0.0.32 10.0.0.127;
host sshbox {
hardware ethernet aa:bb:cc:dd:ee:ff;
fixed-address 10.0.0.2;
}
}
Update /etc/rc.conf.local:
dhcpd_flags="re1"
Restart dhcpd:
/etc/rc.d/dhcpd restart
Split horizon DNS
I am using unbound(8) as a caching DNS resolver.
/var/unbound/etc/unbound.conf
server:
interface: 10.0.0.1
access-control: 10.0.0.0/24 allow
local-data: "gw.2f30.org. IN A 10.0.0.1"
local-data-ptr: "10.0.0.1 gw.2f30.org."
local-data: "sshbox.2f30.org. IN A 10.0.0.2"
local-data-ptr: "10.0.0.2 sshbox.2f30.org."
forward-zone:
name: "."
forward-addr: 208.67.222.222
forward-addr: 208.67.220.220
Update /etc/rc.conf.local:
unbound_flags=
Restart unbound:
/etc/rc.d/unbound restart
At this point, you should be able to plug a machine to your switch, get an IP address and browse the web.
You should also be able to access $sshbox from the outside over ssh on the default port. In my configuration this is a separate machine but could just as well be the router itself.
Using dnscrypt_proxy with unbound
First install dnscrypt_proxy from packages.
Adjust /etc/rc.conf.local:
dnscrypt_proxy_flags="-l /dev/null -R dnscrypt.eu-nl -a 127.0.0.1:53"
pkg_scripts="dnscrypt_proxy"
Start it:
/etc/rc.d/dnscrypt_proxy start
Then adjust the unbound configration:
/var/unbound/etc/unbound.conf
server:
interface: 10.0.0.1
access-control: 10.0.0.0/24 allow
do-not-query-localhost: no # needed as dnscrypt is listening on localhost
local-data: "gw.2f30.org. IN A 10.0.0.1"
local-data-ptr: "10.0.0.1 gw.2f30.org."
local-data: "sshbox.2f30.org. IN A 10.0.0.2"
local-data-ptr: "10.0.0.2 sshbox.2f30.org."
forward-zone:
name: "."
forward-addr: 127.0.0.1
forward-addr: 208.67.220.220
Restart unbound:
/etc/rc.d/unbound restart
You should use tcpdump(8) to confirm that DNS requests are encrypted.
PXE booting
I use PXE booting on my laptop to upgrade OpenBSD. I run a tftp server on my router to serve the latest bsd.rd.
Setting up tftpd and dhcpd for PXE booting
Prepare /tftpboot:
mkdir /tftpboot
cp /usr/mdec/pxeboot /tftpboot
Update /etc/dhcpd.conf:
subnet 10.0.0.0 netmask 255.255.255.0 {
filename "pxeboot";
next-server 10.0.0.1;
...
}
Update /etc/rc.conf.local:
tftpd_flags="-l 10.0.0.1 /tftpboot"
Restart dhcpd and tftpd:
/etc/rc.d/tftpd restart
/etc/rc.d/dhcpd restart
Cron job to fetch latest bsd.rd
Use crontab -e as root to add a new job as follows:
15 10 * * * /usr/bin/ftp -o /tftpboot/bsd.rd http://ftp.openbsd.org/pub/OpenBSD/snapshots/amd64/bsd.rd 1>/dev/null
It will download bsd.rd once a day at 10:15 in the morning.
To test, plug your laptop to the switch and choose to boot over the network. Once you get to the OpenBSD boot prompt, type /bsd.rd and hit return.
Configuring an IPv6 gif(4) tunnel with Hurricane Electric
First of all, you will have to create an account on their website. From there, follow their guide to set up a tunnel. You will basically have to choose the tunnel endpoint. Find the one with the minimum latency.
/etc/sysctl.conf
net.inet6.ip6.forwarding=1
/etc/hostname.gif0
description "Hurricane Electric 6in4 link"
tunnel <your-ipv4-endpoint> <their-ipv4-endpoint>
mtu 1480
!ifconfig gif0 inet6 alias 2001:XXXX:XXXX:XXXX::2 2001:XXXX:XXXX:XXXX::1 prefixlen 128
!route -n add -inet6 default 2001:XXXX:XXXX:XXXX::1
/etc/hostname.re1
inet6 alias 2001:XXXX:XXXX:XXXX::1 64
This will add an IPv6 alias on your router's internal interface.
/etc/pf.conf
pass in on egress inet proto 41 from <their-ipv4-endpoint> to (egress)
pass in on gif0 inet6
Update /etc/rc.conf.local:
rtadvd_flags="re1"
Reboot your router.
On your OpenBSD client, enable autoconfiguration:
ifconfig em0 inet6 autoconf
NetFlow sensor and collector configuration
I use NetFlow to get an idea of what kind of traffic passes through my gateway.
To configure a netflow sensor on the gateway:
/etc/pf.conf
set state-defaults pflow
/etc/hostname.pflow0
flowsrc 10.0.0.1 flowdst 10.0.0.2:5555
Activate sensor:
pfctl -f /etc/pf.conf
sh /etc/netstart pflow0
On the receiver, in this case the box with address 10.0.0.2 we'll install flowd. It is a secure and minimal netflow collector written by Damien Miller.
First, install flowd from ports.
/etc/flowd.conf
logfile "/var/log/flowd"
listen on 10.0.0.2:5555
flow source 10.0.0.1
store ALL
Restart flowd:
/etc/rc.d/flowd restart
Give it a moment and use flowd-reader(8) on the specified logfile to examine the flows.
Debugging tips
One of the advantages of using OpenBSD as opposed to a standard consumer grade router is that you have all the needed tools at your disposal for debugging your network. The following manpages should be of interest.
Keep your configuration as simple as possible. Do not randomly poke on sysctl knobs you do not understand. Rely on the defaults unless you have a good reason not to.
If you have made extensive changes on a running system, do a final reboot to make sure everything comes back up as expected.
Consider having a second machine connected over serial to your router. This way you can capture a trace if the router crashes. It can also be used as an out-of-band mechanism to configure your router without hooking up a monitor and a keyboard.
Reading material
I've found the following references highly informative and useful.