Let’s talk about APF. APF — Advanced Policy Firewall — is a policy-based iptables firewall system that provides simple, powerful control over your day-to-day server security. It might seem intimidating to be faced with all of the features and configuration tools in APF, but this blog should put your fears to rest.
APF is an iptables wrapper that works alongside iptables and extends its functionality. I personally don’t use iptables wrappers, but I have a lot of experience with them, and I’ve seen that they do offer some additional features that streamline policy management. For example, by employing APF, you’ll get several simple on/off toggles (set via configuration files) that make some complex iptables configurations available without extensive coding requirements. The flip-side of a wrapper’s simplicity is that you aren’t directly in control of the iptables commands, so if something breaks it might take longer to diagnose and repair. Before you add a wrapper like APF, be sure that you know what you are getting into. Here are a few points to consider:
- Make sure that what you’re looking to use adds a feature you need but cannot easily incorporate with iptables on its own.
- You need to know how to effectively enable and disable the iptables wrapper (the correct way … read the manual!), and you should always have a trusted failsafe iptables ruleset handy in the unfortunate event that something goes horribly wrong and you need to disable the wrapper.
- Learn about the basic configurations and rule changes you can apply via the command line. You’ll need to understand the way your wrapper takes rules because it may differ from the way iptables handles rules.
- You can’t manually configure your iptables rules once you have your wrapper in place (or at least you shouldn’t).
- Be sure to know how to access your server via the IPMI management console so that if you completely lock yourself out beyond repair, you can get back in. You might even go so far as to have a script or set of instructions ready for tech support to run, in the event that you can’t get in via the management console.
TL;DR: Have a Band-Aid ready!
Now that you have been sufficiently advised about the potential challenges of using a wrapper (and you’ve got your Band-Aid ready), we can check out some of the useful APF rules that make iptables administration a lot easier. Most of the configuration for APF is in
conf.apf. This file handles the default behavior, but not necessarily the specific blocking rules, and when we make any changes to the configuration, we’ll need to restart the APF service for the changes to take effect.
Let’s jump into
conf.apf and break down what we see. The first code snippit is fairly self-explanatory. It’s another way to make sure you don’t lock yourself out of your server as you are making configuration changes and testing them:
# !!! Do not leave set to (1) !!! # When set to enabled; 5 minute cronjob is set to stop the firewall. Set # this off (0) when firewall is determined to be operating as desired. DEVEL_MODE="1"
The next configuration options we’ll look at are where you can make quick high-level changes if you find that legitimate traffic is being blocked and you want to make APF a little more lenient:
# This controls the amount of violation hits an address must have before it # is blocked. It is a good idea to keep this very low to prevent evasive # measures. The default is 0 or 1, meaning instant block on first violation. RAB_HITCOUNT="1" # This is the amount of time (in seconds) that an address gets blocked for if # a violation is triggered, the default is 300s (5 minutes). RAB_TIMER="300" # This allows RAB to 'trip' the block timer back to 0 seconds if an address # attempts ANY subsiquent communication while still on the inital block period. RAB_TRIP="1" # This controls if the firewall should log all violation hits from an address. # The use of LOG_DROP variable set to 1 will override this to force logging. RAB_LOG_HIT="1" # This controls if the firewall should log all subsiqent traffic from an address # that is already blocked for a violation hit, this can generate allot of logs. # The use of LOG_DROP variable set to 1 will override this to force logging. RAB_LOG_TRIP="0"
Next, we have an option to adjust ICMP flood protection. This protection should be useful against some forms of DoS attacks, and the associated rules show up in your INPUT chain:
# Set a reasonable packet/time ratio for ICMP packets, exceeding this flow # will result in dropped ICMP packets. Supported values are in the form of: # pkt/s (packets/seconds), pkt/m (packets/minutes) # Set value to 0 for unlimited, anything above is enabled. ICMP_LIM="30/s"
If you wanted to add more ports to block for p2p traffic (which will show up in the P2P chain), you’ll update this code:
# A common set of known Peer-To-Peer (p2p) protocol ports that are often # considered undesirable traffic on public Internet servers. These ports # are also often abused on web hosting servers where clients upload p2p # client agents for the purpose of distributing or downloading pirated media. # Format is comma separated for single ports and an underscore separator for # ranges (4660_4678). BLK_P2P_PORTS="1214,2323,4660_4678,6257,6699,6346,6347,6881_6889,6346,7778"
The next few lines let you designate the ports that you want to have closed at all times. They will be blocked for INPUT and OUTPUT chains:
# These are common Internet service ports that are understood in the wild # services you would not want logged under normal circumstances. All ports # that are defined here will be implicitly dropped with no logging for # TCP/UDP traffic inbound or outbound. Format is comma separated for single # ports and an underscore separator for ranges (135_139). BLK_PORTS="135_139,111,513,520,445,1433,1434,1234,1524,3127"
The next important section to look at deals with conntrack. If you get “conntrack full” errors, this is where you’d increase the allowed connections. It’s not uncommon to need more connections than the default, so if you need to adjust that value, you’d do it here:
# This is the maximum number of "sessions" (connection tracking entries) that # can be handled simultaneously by the firewall in kernel memory. Increasing # this value too high will simply waste memory - setting it too low may result # in some or all connections being refused, in particular during denial of # service attacks. SYSCTL_CONNTRACK="65536"
We’ve talked about the ports we want closed at all times, so it only makes sense that we’d specify which ports we want open for all interfaces:
# Common inbound (ingress) TCP ports IG_TCP_CPORTS="22" # Common inbound (ingress) UDP ports IG_UDP_CPORTS="" # Common outbound (egress) TCP ports EG_TCP_CPORTS="21,25,80,443,43" # Common outbound (egress) UDP ports EG_UDP_CPORTS="20,21,53"
And when we want a special port allowance for specific users, we can declare it easily. For example, if we want port 22 open for user ID 0, we’d use this code:
# Allow outbound access to destination port 22 for uid 0 EG_TCP_UID="0:22"
The next few sections on
Remote Rule Imports and
Global Trust are a little more specialized, and I encourage you to read a little more about them (since there’s so much to them and not enough space to cover them here on the blog). An important feature of APF is that it imports block lists from outside sources to keep you safe from some attackers, so the
Remote Rule Imports can prove to be very useful. The
Global Trust section is incredibly useful for multi-server deployments of APF. Here, you can set up your global allow/block lists and have them all pull from a central location so that you can make a single update to the source and have the update propogated to all servers in your configuration. These changes are synced to the
glob_allow/deny.rules files, and they will be downloaded (and overwritten) on a regular basis from your specified source, so don’t make any manual edits in
As you can see,
apf.conf is no joke. It has a lot of stuff going on, but it’s very straightforward and documented well. Once we’ve set up apf.conf with the configurations we need, it’s time to look at the more focused
deny_hosts.rules files. These
.rules files are where where you put your typical firewall rules in place. If there’s one piece of advice I can give you about these configurations, it would be to check if your traffic is already allowed or blocked. Having multiple rules that do the same thing (possibly in different places) is confusing and potentially dangerous.
deny_hosts.rules configuration will look just like
allow_hosts.rules, but it’s performing the opposite function. Let’s check out an
allow_hosts.rules configuration that will allow the Nimsoft service to function:
The format is somewhat simplistic, but the file gives a little more context in the comments:
# The trust rules can be made in advanced format with 4 options # (proto:flow:port:ip); # 1) protocol: [packet protocol tcp/udp] # 2) flow in/out: [packet direction, inbound or outbound] # 3) s/d=port: [packet source or destination port] # 4) s/d=ip(/xx) [packet source or destination address, masking supported] # Syntax: # proto:flow:[s/d]=port:[s/d]=ip(/mask)
APF also uses
ds_hosts.rules to load the DShield.org blocklist, and I assume the
ecnshame_hosts.rules does something similar (can’t find much information about it), so you won’t need to edit these files manually. Additionally, you probably don’t need to make any changes to
log.rules, unless you want to make changes to what exactly you log. As it stands, it logs certain dropped connections, which should be enough. Also, it might be worth noting that this file is a script, not a configuration file.
The last two configuration files are the
postroute.rules that (unsurprisingly) are used to make routing changes. If you have been following my articles, this corresponds to the iptables chains for PREROUTING and POSTROUTING where you would do things like port forwarding and other advanced configuration that you probably don’t want to do in most cases.
APF Command Line Management
As I mentioned in the “points to consider” at the top of this post, it’s important to learn the changes you can perform from the command line, and APF has some very useful command line tools:
[root@server]# apf --help APF version 9.7 <firstname.lastname@example.org> Copyright (C) 2002-2011, R-fx Networks <email@example.com> Copyright (C) 2011, Ryan MacDonald <firstname.lastname@example.org> This program may be freely redistributed under the terms of the GNU GPL usage /usr/local/sbin/apf [OPTION] -s|--start ......................... load all firewall rules -r|--restart ....................... stop (flush) & reload firewall rules -f|--stop........ .................. stop (flush) all firewall rules -l|--list .......................... list all firewall rules -t|--status ........................ output firewall status log -e|--refresh ....................... refresh & resolve dns names in trust rules -a HOST CMT|--allow HOST COMMENT ... add host (IP/FQDN) to allow_hosts.rules and immediately load new rule into firewall -d HOST CMT|--deny HOST COMMENT .... add host (IP/FQDN) to deny_hosts.rules and immediately load new rule into firewall -u|--remove HOST ................... remove host from [glob]*_hosts.rules and immediately remove rule from firewall -o|--ovars ......................... output all configuration options
You can use these command line tools to turn your firewall on and off, add allowed or blocked hosts and display troubleshooting information. These commands are very easy to use, but if you want more fine-tuned control, you’ll need to edit the configuration files directly (as we looked at above).
I know it seems like a lot of information, but to a large extent, that’s all you need to know to get started with APF. Take each section slowly and understand what each configuration file is doing, and you’ll master APF in no time at all.