How does one lock down OS X Server using the PF firewall

firewallosx-server

Assume I have ip blocks for China, Russia, North Korea etc. How would I configure pf to allow those IP addresses to only access ports 80 and 443, and be denied access to other ports?

I have an instance of OS X Server 2.1.1 (aka Mountain Lion) running in a data center with a static ip address exposed to the internet. So there is no hardware firewalls etc to protect the server against malicious users.

Looking through the logs, I noticed numerous failed attempts from countries like China, Russia to services necessary for me to remotely manage the server. Further, I noticed that ports were unnecessarily open to the internet.

Since OS X 10.6 Snow Leopard Server, I used the Server Admin to manage the firewall (ipfw). This has been removed in OS X Server 2.1.1 (Mountain Lion). And to make matters more interesting, the Apple documentation states that ipfw is deprecated and to use pf instead. Having read the man page, I'm a tad lost how to configure pf.

Googling "pf firewall tutorial" reveals tutorials aimed for NetBSD, FreeBSD and OpenBSD. Further filtering on "OS X" in that query reveals tutorials for "OS X", but it seems the authors assume some prior knowledge.

Best Answer

You should:

  1. understand pf basics - here is many guides on the Internet, you can safely read any Open/Free BSD guide. You must understand a few basic things:

    • with PF, last rule wins (opposite of IPFW's "first rule wins")
    • logging is in the pflog device if the 'tcpdump' format
    • check the pfctl command using man pfctl
    • also check man pf.conf
    • you can create many simple text files that contains IP addresses (called tables) and using them in the filtering rules - see the example below.
  2. AFTER this you can use two GUI frontends

PF is not too hard if you have some knowledge about how firewalling works in general.

Fragment of pf.conf for table based filtering:

interface = "en0"
allowed_ports = "{ 80, 443 }"
table <badips> persist
table <noroute> const { 192.168.0.0/16, 172.16.0.0/12, 10.0.0.0/8 }
block in on $interface from { <noroute>, <badips> } to any
pass in on $interface inet proto tcp from <badips> to $interface port $allowed_ports

The above example contains:

  • some basic definitions, like your interface name and some ports
  • definition for two tables, noroute for nonroutable addresses (RFC 1918) and the second badips that can contain your Geo IP based IP addresses
  • filtering rule - blocking anything from these tables
  • allowing ports 80 and 443 from badips (last rule wins)