I had a few wrinkles because I’d already set up networking, and I didn’t want to have to go through and redo all my static IPs. But I wanted to make my network more rational. I had devices on 192.168.150.*/24 and I wanted something bigger, so I decided to go with /20. That gave me a range of 92.168.144.1–192.168.159.254. (I admit I used an IP subnet calculator for that.) My ISP gives me IPv4, so I’m using that and not worrying about IPv6.
First things first: let’s make the box forward packets:
# sysctl net.inet.ip.forwarding=1
# echo 'net.inet.ip.forwarding=1' >> /etc/sysctl.conf
Configure Network Adapters
I have two Intel NICs. I’m connecting igc0 to my cable modem. I’m connecting igc1 to a 16-port switch which is my internal network. I’ve got another network port that was autodetected, but I’m not using it yet, so I’ll remove it.
# echo "inet autoconf" > /etc/hostname.igc0
# echo "inet 192.168.144.1 255.255.240.0 NONE" > /etc/hostname.igc1
# rm /etc/hostname.igc2
# sh /etc/netstart.sh
Setting up the PF packet filter
Now, set up PF. I got my instructions for this mostly from home.nuug.no/~peter/pf/en/ftpproblem.html. Here’s how I edited /etc/pf.conf:
#---------------------------------#
# Macros
#---------------------------------#
ext_if="igc0"
lan_if="igc1"
ftpproxy="127.0.0.1"
ftpproxyport="8021"
#---------------------------------#
# Tables
#---------------------------------#
table <localonly> { \
# Addresses that can talk to the local network but not to the rest of the world
\
# Local printer
192.168.158.1/32 \
}
# This is a table of non-routable private addresses.
table <martians> { 0.0.0.0/8 10.0.0.0/8 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 224.0.0.0/3 \
192.168.0.0/16 198.18.0.0/15 198.51.100.0/24 \
203.0.113.0/24 }
#---------------------------------#
# Protect and block by default
#---------------------------------#
set skip on lo0
set block-policy drop
# Spoofing protection for all NICs.
block in from no-route
block in quick from urpf-failed
# Block non-routable private addresses.
# We use the "quick" parameter here to make this rule the last.
block in quick on $ext_if from <martians> to any
block return out quick on $ext_if from any to <martians>
# Default blocking all traffic in on all LAN NICs from any computer or device
# attached.
block return in on { $lan_if }
# Default blocking all traffic in on the external NIC from the Internet/ISP,
# we'll log that too.
block drop in log on $ext_if
# Don't allow ICMP from outside. Commented out this section.
# Yeah, I know people hate that.
#
#match in on $ext_if inet proto icmp icmp-type {echoreq } tag ICMP_IN
#block drop in on $ext_if proto icmp
#pass in proto icmp tagged ICMP_IN max-pkt-rate 100/10
# We need the router to have access to the Internet, so we'll default allow
# packets to pass out from our router through the external NIC to the Internet.
pass out inet from $ext_if
#---------------------------------#
# LAN Setup
#---------------------------------#
# Allow any computer or device on the LAN to send data packets in through the NIC.
# This means any computer attached to this network interface can pass in data
# reaching anywhere, i.e. the Internet or any of the computers attached to the
# router.
pass in on $lan_if
# Always block DNS queries not addressed to our DNS server.
block return in quick on $lan_if proto { udp tcp } to ! $lan_if port { 53 853 }
# Block localonly from seeing the internet
block in quick on lan_if from <localonly>
# Allow data packets to pass from the router out through the NIC to the
# computers or devices attached to it on the lan NIC.
# Without this we can't even ping computers attached to the lan NIC from
# the router itself.
pass out on $lan_if inet keep state
#---------------------------------#
# FTP
#---------------------------------#
# allow ftp clients to work
anchor "ftp-proxy/*"
pass in quick on $lan_if inet proto tcp to port ftp divert-to $ftpproxy port $ftpproxyport
# no ftp servers so don't pass out
# pass out proto tcp from $ftpproxy to any port ftp
#---------------------------------#
# NAT
#---------------------------------#
pass out on $ext_if inet from $lan_if:network to any nat-to ($ext_if)
Then you can test things out and restart pf. I think my network connection dropped at this point:
# pfctl -n -f /etc/pf.conf
# pfctl -F all
# pfctl -f /etc/pf.conf
Configure dhcpd
Now I need to configure serving IPs from this machine. (My old firewall is still on the network at this point and serving real IPs as well as doing DNS, but I need to get the new firewall enabled.) So time to edit /etc/dhcpd.conf:
# $OpenBSD: dhcpd.conf,v 1.1 1998/08/19 04:25:45 form Exp $
#
# DHCP server options.
# See dhcpd.conf(5) and dhcpd(8) for more information.
#
# This is for 192.168.144.* to 192.168.159.*
subnet 192.168.144.0 netmask 255.255.240.0 {
option domain-name "lan.example.net";
option domain-name-servers 192.168.144.1;
option routers 192.168.144.1;
########################################
# Dynamic IP addresses
########################################
range 192.168.145.1 192.168.149.254;
########################################
# Fixed IP machines that humans don't
# (normally) see - WAPs etc.
########################################
host wap {
hardware ethernet dc:9f:44:11:22:33;
fixed-address 192.168.144.13;
}
########################################
# Fixed IP machines that humans see.
########################################
# These are machines I normally ssh
# or telnet to, or run servers on.
#
host fileserver {
hardware ethernet 22:33:44:55:66:77;
fixed-address 192.168.150.171;
option host-name "fileserver";
}
host weather {
hardware ethernet 88:99:00:AA:BB:CC;
fixed-address 192.168.150.177;
option host-name "weather";
}
#... etc...
# I'm putting my laptop on a non-150 address for testing purposes.
# That way I can plug it into the server and get a DHCP address that
# should route on to the internet.
host laptop {
hardware ethernet 00:22:44:66:88:ff;
fixed-address 192.168.161.2;
}
} # subnet
Next, start serving DHCP:
rcctl enable dhcpd
rcctl start dhcpd
At this point I could plug my laptop into the new firewall and get an IP address.
Start Unbound for DNS
On my old firewall I was using bind. That was heavyweight, so I decided to do something different here. Originally I thought I needed NSD and Unbound for DNS (I was looking at some instructions at jamsek.dev/posts/2019/Jul/28/openbsd-dns-server-with-unbound-and-nsd/) but eventually realized I could get away with just unbound.
# rcctl enable unbound
# rcctl start unbound
Next, I changed my DNS server so the new firewall was getting it locally instead of going to the old firewall. In /etc/resolv.conf:
nameserver 127.0.0.1
lookup file bind
After that, configure DNSSEC:
# unbound-anchor -a "/var/unbound/db/root.key"
# ftp -S do -o /var/unbound/db/root.hints https://www.internic.net/domain/named.root
With root.key downloaded, I could set up my /var/unbound/etc/unbound.conf:
# $OpenBSD: unbound.conf,v 1.21 2020/10/28 11:35:58 sthen Exp $
server:
interface: 192.168.144.1
interface: ::1
# override the default "any" address to send queries; if multiple
# addresses are available, they are used randomly to counter spoofing
access-control: 0.0.0.0/0 refuse
access-control: ::0/0 refuse
access-control: 127.0.0.0/8 allow
access-control: 192.168.144.0/20 allow
access-control: ::1 allow
hide-identity: yes
hide-version: yes
# Perform DNSSEC validation.
#
auto-trust-anchor-file: "/var/unbound/db/root.key"
root-hints: "/var/unbound/db/root.hints"
qname-minimisation: yes
val-log-level: 2
# Synthesize NXDOMAINs from DNSSEC NSEC chains.
# https://tools.ietf.org/html/rfc8198
#
aggressive-nsec: yes
# Serve zones authoritatively from Unbound to resolver clients.
# Not for external service.
#
local-zone: "lan.example.net." static
#
# Host addresses - infrastructure
#
local-data: "firewall.lan.example.net. IN A 192.168.144.1"
local-data-ptr: "192.168.144.1 firewall.lan.example.net"
#
# Host addresses - named servers
#
local-data: "fileserver.lan.example.net. IN A 192.168.150.171"
local-data-ptr: "192.168.150.171 fileserver.lan.example.net"
local-data: "weather.lan.example.net. IN A 192.168.150.177"
local-data-ptr: "192.168.150.177 weather.lan.example.net"
# ... etc...
remote-control:
control-enable: yes
control-interface: /var/run/unbound.sock
With all that set up, I could make sure I didn’t have any syntax errors and restart unbound:
# unbound-checkconf
# rcctl restart unbound
I think it was at this point that I made the switch from using the old firewall to the new firewall. I changed the new firewall to have the same MAC as the old one (my ISP wanted to see a specific MAC) and then put the new one in place:
# echo "inet autoconf lladdr 11:22:33:44:55:66" > /etc/hostname.igc0
# sh /etc/netstart
Having two devices with the same MAC is a Bad Thing, and I had a lot of weirdness on my network until I unplugged the old firewall.
This post is part of a series on setting up an OpenBSD 7.4 firewall device.