Cryptsus Blog rss-feed  |  We craft cyber security solutions.

Edge OpenBSD PF Firewall
Securing the first gate of your network

By: Jeroen van Kessel  |  August 20th, 2019 | 10 min read

You may wonder to yourself, why implement a firewall based on OpenBSD at the edge, also known as the gateway of your network? First of all, OpenBSD is rather stable, friendly to use, completely free and open, holds a track record of having the least amount of Remote Code Executions (RCE) in decades, and does not violate RFC based network standards. Besides that, the OpenBSD team is also the creator of the widely implemented OpenSSH. Long story short, the OpenBSD team knows their stuff.

OpenBSD comes with a built-in firewall called PF, short for Packet Filter. PF is ideal for firewalling in your DMZ zone. You can still use your Next Generation Firewall (NGFW), but only at the core of your network. This way you can implement network intrusion prevention and Deep Packet Inspection (DPI) on your NGFW core firewall without exposing 'vulnerable' devices to the Wide Area Network (WAN).

History has shown us it is bad practice to have a proprietary Cisco ASA, Palo Alto or any other big shot firewall brand with a GUI available to the whole WAN. This, this and this is why.

This blog post will show you how to create a robust edge firewall setup with OpenBSD 6.5.

Network overview

This OpenBSD Edge firewall box will have a total of 4 virtual Network Interface Cards (vNICs): The em0 interface is used for incoming ISP WAN connections. This can be an unfiltered IPv4-address from AWS, Azure, Vultr, DigitalOcean or a colocation. The em1 interface is used to directly connect to the core firewall. The em2 interface provides a direct connection to our VPN box. Lastly, the em3 interface is used for management over SSH.

Figure 1 shows the overall edge - core firewall architecture. You can see a malicious entity present at the WAN side and also within the internal network. As a security professional, you should always assume the worst, which is that your perimeter has already been breached. This assumption is according to the zero trust network model.

We leverage the robustness of OpenBSD to not let anyone compromise the edge firewall by closing all unnecessary ports from the inside and outside and apply Network Address Translation (NAT). The core firewall will help us with advanced threat detection by analysing sessions from the inside while looking for reverse shells on the internal network.

packet-filter-firewall Figure 1: Global logical Architecture of an OpenBSD Edge - Core Firewall Setup

Network configuration

All vNIC on the edge firewall have a prefix size of /30 or a 255.255.255.252 subnet mask which is ideal for point-to-point connections. We start with a 192.168.144.2 IP-address for this firewall box and 192.168.144.1 for the node on the other side of the p2p link, while having the last IP-address reserved for broadcast 192.168.144.3 purposes.

We do not want to leak any information based on our MAC-address. Therefore we 'spoof' our MAC-address lladdr to our liking. For debugging purposes, we add a description to each vNIC:

$ cat /etc/hostname.em*
#em0
inet 85.85.85.2 255.255.255.252 NONE lladdr 02:01:02:ff:ff:00 description "Edge firewall <> ISP WAN Unfiltered"
#em1
inet 192.168.144.2 255.255.255.252 NONE lladdr 02:01:02:ff:ff:01 description "Edge firewall <> Core Firewall"
#em2
inet 192.168.244.2 255.255.255.252 NONE lladdr 02:01:02:ff:ff:02 description "Edge firewall <> VPN Server"
#em3
inet 193.168.200.2 255.255.255.252 NONE lladdr 02:01:02:ff:ff:03 description "Edge firewall <> MGMT VLAN"

ARP settings

Static ARP entries provide some level of security against spoofing attacks. We can do a simple arp -a to list the corresponding MAC-addresses of the point-to-point IP-addresses and set them permanently:

$ arp -s 85.85.85.1 DE:AD:BE:EF:01:00 permanent
$ arp -s 192.168.144.1 DE:AD:BE:EF:01:01 permanent
$ arp -s 192.168.244.1 DE:AD:BE:EF:01:02 permanent
$ arp -s 192.168.200.1 DE:AD:BE:EF:01:03 permanent

Gateway

Unlike modern day Linux-based distributions, OpenBSD sets its gateway address in the mygate file:

$ vi /etc/mygate

85.85.85.1

DNS

Next, we configure our DNS servers. For testing purposes we pick CloudFlare's 1.1.1.1 and Google's 8.8.8.8 recursive DNS servers. It is recommended to deploy your own DNS daemon in order to have more control over your internal DNS lookups.

$ cat /etc/resolv.conf

search cryptsus.local
nameserver 1.1.1.1
nameserver 8.8.8.8
lookup file bind

Update your box

Before we continue configuring any package, we make sure we are running on the latest version:

$ cat /etc/installurl

https://cdn.openbsd.org/pub/OpenBSD

pkg_add -u
fw_update

Add SSH user

Next, we only allow SSH sessions with public key crypto over your management VLAN on a non-routable interface. This is how you create a strong and secure SSH key-pair:

$ useradd -m krabelize
usermod -G wheel krabelize
usermod -G admin krabelize
vi /home/krabelize/.ssh/authorized_keys
[your_ed25519_key_here]

passwd root
[20-char-root-password-stored-in-your-eas256-pwd-manager]

Router

OpenBSD does not pass on network traffic by default. We allow IPv4 packets travel between vNIC's which transforms our firewall box into a router. We control this traffic with PF which is described in the next section.

$ vi /etc/sysctl.conf

...
sysctl net.inet.ip.forwarding=1
...

Firewall configuration

Finally, it is time to configure our firewall. This firewall config is also available on GitHub. This book by Peter Hansteen is an excellent resource if you want to learn more on the configuration below.

$ vi /etc/pf.conf

#       $oPENbSD: pf.conf,v 1.55 2017/12/03 20:40:04 sthen Exp $
#
# See pf.conf(5) and /etc/examples/pf.conf

##################
#Variables       #
##################
isp="em0"
lan="em1"
openvpn="em2"
management="em3"

##################
#Ports           #
##################
#Allow outgoing traffic based on port IANA standards [LAN -> WAN]
#TCP 993 = IMAPS
#UDP 1194 = OpenVPN
#TCP 5222:5223 plus 5228 = WhatsApp

nat_tcp_ports="{ ssh, smtps, imaps, http, https, 993, 5222:5223, 5228 }"
nat_udp_ports="{ ntp, https, domain, 1194 }"

#Allow outgoing ping requests [LAN -> WAN]
icmp_types = "{ echoreq, unreach }"

##################
#Tables          #
##################
#RFC1918 and RFC5735 local address ranges which should not be routed over the WAN back and forth [LAN <> WAN]
table <martians> persist { 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 192.168.0.0/16 192.88.99.0/24 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 248.0.0.0/5 255.255.255.255/32 }

#Blacklist session of bad WAN IP-address(es) [LAN <> WAN]
table <blacklist> persist { 195.195.195.195/32 }
#SSH brute-force blacklist [Management network <> Edge firewall]
table <bruteforce> persist

##################
#Security rules  #
##################
#Skip any packet filtering on the localhost interface
set skip on lo0

#Drop all other traffic traffic unless it is whitelisted. PF automatically appends "keep state flags S/SA"
block drop all

#Dropping is less expensive than rejecting
set block-policy drop

#Reassemble packets
set reassemble yes

#Scrub packets
match in on $isp scrub (no-df max-mss 1440)
match out on $isp scrub (random-id)

#Block all traffic with a spoofed source IP-address from the network directly connected to the specified NIC from entering the system through any other NIC
antispoof quick for { $isp lo0 }

##################
#INPUT rules     #
##################
#[LAN <- Martians & blacklisted IP-addresses]
#Block packets from wrong address ranges and blacklisted IP-addresses
block drop in quick on $isp from {<martians>, <blacklist>} to any

#[LAN <> LAN]
#Allow all incoming traffic on the LAN interface. The core firewall should handle the firewalling in this segmentation of the network
pass in on $lan

#[Management NIC <> Management]
#Allow SSH to the firewall only through port 22 with brute-force protection
block drop in quick on $management from <bruteforce> to any
pass in on $management proto tcp from $management:network to $management port 22 flags S/SA keep state (max-src-conn 100, max-src-conn-rate 15/5, overload <bruteforce> flush global)

##################
#OUTPUT rules    #
##################
#[LAN ->  Martians & blacklisted IPs]
#Do not sent RFC1918 IP-addresses over the WAN and do not access blacklisted IP-addresses
block drop out quick on $isp from any to {<martians>, <blacklist>}

#Allow traffic from the edge firewall WAN interface to route to the WAN for ping, nslookup and system updates
#[(em0) Firewall -> WAN]
#pass out on $isp proto { tcp udp icmp } from $isp:network to any

##################
#NAT             #
##################
#[LAN <> WAN]
#Perform NAT on the WAN interface for any packet coming from the LAN and replace the source IP address with the WAN gateway

#UDP
pass out proto udp from $lan:network to any port $nat_udp_ports nat-to $isp keep state
#TCP
pass out proto tcp from $lan:network to any port $nat_tcp_ports nat-to $isp modulate state
#ICMP (protocol used for ping)
pass out proto icmp all icmp-type $icmp_types nat-to $isp keep state
#WARNING: This line allows sessions from any port the WAN. Enable this line only for troubleshooting purposes
#pass out from $lan:network to any nat-to $isp keep state

Next we apply and test the above firewall configuration:

$ pfctl -nvf /etc/pf.conf

Troubleshooting

The following command might come in handy when you need to trouble shoot your network or if you want more insight in your system resources or network sessions:

$ route show
$ systat states
$ pfctl -s rules
$ pfctl -s memory
$ pfctl -s info
$ pfctl -s states
$ pfctl -s all | more
$ netstat -f inet -at
$ sh /etc/netstart em3

What can the outside see?

We can use nmap or Shodan to get an overview of all running services on our edge firewall box. However, we cannot see much from the outside;)

$ sudo nmap -sS -sU -Pn -O 85.85.85.2

Nmap scan report for 85.85.85.2
Host is up.
All 2000 scanned ports on 85.85.85.2 are filtered (1000) or open|filtered (1000)

Nmap done: 1 IP address (1 host up) scanned in 403.43 seconds

Discussion and questions