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,