OpenBSD Router with PF

OpenBSD Router with PF

The operating system OpenBSD is used widely for network routing and firewall. Also really easy to install for you Virtual Machine lab environment. In this blog bost I want to explain how to turn an OpenBSD installation quick in router and NAT with PF for your environment.

This blog post will just explain the basic and for more advanced settings you can go from here.

Install OpenBSD

Make sure you create an new VM with at least 2 network interfaces, one connected to the internet and one to the internal.

For my test machine I have used OpenBSD version 6.0 that is available on the OpenBSD website http://www.openbsd.org/ or directly via this link from Amsterdam: ftp://mirror.meerval.net/pub/OpenBSD/6.0/amd64/install60.iso

The installation can be done with default settings, my preference was to not install the X Windows system.

Configure network

We will start first with the configuration of the network. Run the folliwing command to show the interfaces available:

ifconfig -a
lo0: flags=8049<UP,LOOPBACK,RUNNING,MULTICAST> mtu 32768
        index 4 priority 0 llprio 3
        groups: lo
        inet6 ::1 prefixlen 128
        inet6 fe80::1%lo0 prefixlen 64 scopeid 0x4
        inet 127.0.0.1 netmask 0xff000000
em0: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
        lladdr 00:00:00:00:00:00
        index 1 priority 0 llprio 3
        groups: egress
        media: Ethernet autoselect (1000baseT full-duplex,master)
        status: active
        inet 192.168.1.40 netmask 0xffffff00 broadcast 192.168.1.255
em1: flags=8843<UP,BROADCAST,RUNNING,SIMPLEX,MULTICAST> mtu 1500
        lladdr 00:00:00:00:00:00
        index 2 priority 0 llprio 3
        media: Ethernet autoselect (1000baseT full-duplex,master)
        status: active

Now we have the interface names, em0 and em1 we can configure them individual by creating (or edit if exists) the file hostname.em0 and hostname.em1. In OpenBSD you can use vi or use bash. In this example I will use an bash command to overwrite the files:

echo "dhcp" > /etc/hostname.em0
echo "inet 192.168.3.1 255.255.255.0 192.168.3.255" > /etc/hostname.em1

We set interface em0 to DHCP, assuming this is our external interface that is coupled to an DHCP scope. And we have set interface em1 to 192.168.3.1 that will be our internal network.

Enable IP forwarding

This command is the same for most linux distributions, we wil enable the IP forwarder in the kernel so we are allowed to receive and forward network packages that are not for this host. We can check if it’s enabled with the following command:

sysctl | grep forward
net.inet.ip.forwarding=0
net.inet.ip.mforwarding=0
net.inet6.ip6.forwarding=0
net.inet6.ip6.mforwarding=0

Now we enable this by setting this in the sysctl.conf file:

echo net.inet.ip.forwarding=1 >> /etc/sysctl.conf
echo net.inet6.ip6.forwarding=1 >> /etc/sysctl.conf

Reboot the OpenBSD installation to enable this configuration. Now we have enabled IP forwarding for IPv4 and IPv6.

With the IP forwarding enabled we can use the host already as IP router, configure the internal connected server to use this server as gateway and it will work. But wait, not everything will work. If you send an PING command with will arrive on the destination, but does it now the way back? Not if it’s an internet router, they only know the way to our router and not the hosts behind.

Configure NAT with PF

So we need to configure Network Address Translation so the session is saved on our host and the packet is forwarded with an new IP number. To turn our OpenBSD installation in an NAT router we will use the integrated PF (Packet Filter) configuration. Open the configuration file /etc/pf.conf with an text editor;

vi /etc/pf.conf

To start with an working example make sure you empty the file and add this configuration:

# Create blocks that are variable
ext_if="em0"
int_if="em1"
icmp_types="echoreq"

# Skip all loopback traffic
set skip on lo

# Perform NAT on external interface
match out on $ext_if from $int_if:network to any nat-to $ext_if

# Define default behavior
block in
pass out keep state

# Allow inbound traffic on internal interface
pass quick on $int_if

# Protect against spoofing
antispoof quick for { lo $int_if }

# Allow other traffic
pass in on $ext_if proto tcp to ($ext_if) port ssh flags S/SA keep state

In the example configuration we have created an variable for em0 = ext_if and em1 =int_if. If we need to change the interface later it’s easier to only change those variables. Further in the configuration you will find the NAT rule that is set so any internal network traffic will be going through this NAT. If you have an host connected to the internal and send traffic out you can see this in the PF state table;

pfctl -s state

Session states will now be held on the OpenBSD host we have just configured and NAT is done.

Inbound NAT

But wait,  we are able to send traffic from our internal network to external but what if we want to forward an network port to our internal network. For example we have web servers running behind our NAT router. We can enable TCP port 80 by adding this configuration to /etc/pf.conf :

pass in on $ext_if proto tcp from any to any port 80 rdr-to 192.168.1.5

Configuration showed above will pass traffic on TCP port 80 from our external interface to the internal host with IP 192.168.1.5. If we change the any to an specific IP number we can limit the source addresses that are allowed to visit our webserver. For example:

pass in on $ext_if proto tcp from 86.82.0.0/16 to any port 80 rdr-to 192.168.1.5

In the last example we only allow IP numbers from KPN ADSL in the Netherlands to our webserver. This configuration can be usefull for test environments where you can specify an smaller IP range from your home IP number for example.

More about Packet Filter can be found here: http://www.openbsd.org/faq/pf/filter.html

Thanks for reading and comment me on questions,

Comments are closed.