Accessing firewalled services using PF on OpenBSD

Consider a scenario where all hosts in a network have public IP addresses, but only one subnet is accessible from the Internet because of a corporate firewall. Internal hosts communicate with each other normally. In order to access the SSH port of a firewalled machine, say with the hostname hidden, you may use another host, say middle, belonging in the accessible subnet, at a different port, and redirect traffic to and from hidden. Most of this is described in PF User's Guide, but the manual assumes a public and a private interface with NAT between the two. This guide is about using the same interface to do the forwarding. It can be done using a user-level proxy or completely in-kernel with PF. The configuration of the middle host is found below.

User-level proxy with inetd and netcat


# Proxy SSH to other machines with inetd
pass in quick on egress proto tcp from any to any port 1337 rdr-to (lo)


# SSH proxy using nc stream tcp nowait proxy /usr/bin/nc nc hidden 22



Kernel-level proxy only with PF rules


# Proxy SSH to other machines
pass in quick on egress proto tcp to port 1337 rdr-to hidden port ssh
pass out quick on egress proto tcp to hidden port ssh nat-to (egress)



Tips for SSH access

For seamless SSH access to the hidden host you can alias it to the middle host, and use the appropriate port like this.


Host hidden
    HostName middle
    Port 1337

The aliasing can also be done at the /etc/hosts or DNS level using a CNAME record for hidden.example.org that points to middle.example.org and use:


Host hidden.example.org
    Port 1337

Performance evaluation

A rough benchmarking of the two methods shows that the PF-only setup performs slightly better probably because it generates less local traffic and context switches. Throughput and latency was measured over ssh as shown below. The tests are a single large file transfer and the timing of login and logout. The load on the middle host is also displayed broken down as interrupts and system time.

# large file transfer
$ time scp install54.iso hidden:

# login operation
$ time ssh hidden echo

And the results:

#            throughput   latency   ints  system
user-level:     3.9MB/s   0.6461s    35%      5%
kernel-only:    3.9MB/s   0.6220s    23%      0%

Choose your destiny!