OpenBSD as a Simple NAT Router

To setup a simple NAT router/firewall using OpenBSD, use these steps as a general guideline.  I’m assuming that you have general knowledge of OpenBSD.

First, configure the network interfaces appropriately.  Typically, this will involve editing the hostname.<NIC type> file.  In a VMware ESX Server environment, OpenBSD uses pcn0 for the first virtual NIC, pcn1 for the second virtual NIC, etc., so the appropriate configuration files would be hostname.pcn0, hostname.pcn1, and so forth.

Next, enable IP forwarding by editing /etc/sysctl.conf and making the following change (the line is present in a default installation, you just need to uncomment it):


Next, we’ll need to enable the OpenBSD packet filter, pf.  This is typically done by creating/editing the file /etc/rc.conf.local and making sure the following line is present:


Next, we’ll configure pf for network address translation (NAT) and simple packet filtering.  If you’ve never configured pf before, I highly recommend this OpenBSD PF guide; it will introduce you to the functionality of this very powerful packet filtering engine.  (Sometimes I wish Mac OS X would switch to using pf.)  You configure pf by placing a ruleset into /etc/pf.conf.

Here’s a quick sample ruleset (keep in mind this is based on OpenBSD running as a virtual machine in a VMware environment):

# Set some variables for use later

# Skip all loopback traffic
set skip on lo

# Scrub all traffic
scrub in

# Perform NAT on external interface
nat on $ext_if from $int_if:network -> ($ext_if:0)

# 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
pass in inet proto icmp from $allowed_hosts icmp-type $icmp_types keep state

This is a really, really simple configuration, but it will get the job done.  (I did title this “OpenBSD as a Simple NAT Router”, after all.)

For more advanced configurations, I highly recommended reviewing the OpenBSD documentation (which, by the way, is very thorough and very extensive; kudos to the OpenBSD team for their documentation efforts.)

Tags: , , , ,

  1. Greg Hennessy’s avatar

    Hi Scott,

    If you allow tcp states to be created on anything other than the initial 3 way handshake, tcp window scaling will cause pf to drop traffic. This has been discussed on the PF mailing list passim.

    Some recommended changes

    Stealth is a pointless exercise if the next hop router permits icmp unreachables, setting

    set block-policy return

    politely tells non permitted to traffic to go forth and multiply. Inadvertently blocked application traffic will degrade gracefully and not sit there waiting for a TCP timeout before failing.

    Rule 0.

    block log all

    Not logging dropped traffic makes trouble shooting far more difficult. There’s a very simple recipe for immediate pf logging to syslog here

    rule 1:
    split into rules matching TCP with explcit use of flags S/SA
    & rules matching udp/icmp.

    Ideally you should keep state on all filtering rules, ingress and egress.

    Not creating state forces a traversal of the rule base for each and every packet, rather than a much quicker search of the state table to match established permitted flows.

    Hope that gives you an idea or three,

    Great blog BTW :-)



  2. George’s avatar

    You may also try SONaFR – One floppy OpenBSD router

  3. slowe’s avatar


    Thanks for the tip, I’ll have to take a closer look.