paulgorman.org/technical

ipfw

ipfw is the native FreeBSD firewall.

ipfw(8) https://www.freebsd.org/doc/handbook/firewalls-ipfw.html

Firewall configuration is done with a list of numbered rules (1-65535). Packets are compared to each rule by rule index number. Multiple rules may share the same number. Such rules are evaluated in the order they were inserted into the ruleset. Each rules includes a match criteria and associated action. Rules take the basic form: ipfw command [index] action [log] protocol options ipfw -q add 300 allow ssh keep-state

Evaluation of a packet concludes at the first matching ipfw rule. This is the opposite of OpenBSD’s pf, which concludes evaluation at the final matching rule (except for earlier rules flagged with ‘quick’).

Every ruleset includes a final default rule 65535. This defaults to deny, although it’s tunable in the kernel (/boot/loader.conf): net.inet.ip.fw.default_to_accept=“0”

Rules with ‘check-state’, ‘keep-state’, or ‘limit’ are stateful. On a new stateful match, ipfw creates a dynamic rule for the connection’s: - protocol - source - destination - ports Dynamic rules have a limited lifetime, which renews every time they match a packet. Show current dynamic rules: # ipfw -d show

Rules have counters that track packet count, byte count, log count, and time of the last match.

Rules are organized into thirty-two sets (0-31). Set 31 is reserved for the default rule. ipfw puts new rules in set zero unless we specify a different set. Sets are an organizational tool. Rules can be enabled/disabled/managed/swapped as a set.

Use the ‘-q’ quiet flag when making a batch of rule changes, as explained in ipfw(8).

Rule sets may contain a rule like: ipfw -q add 100 check-state At the point the ‘check-state’ rule appears, it causes the packet to be checked against any existing dynamic rules (i.e. rules created by a prior packet matching a ‘keep-state’ rule). If no ‘check-state’ rule is present, all dynamic rules will be evaluated at the first rule flagged with ‘keep-state’ or ‘limit’.

Ports may be noted by number, or by name (if listed in /etc/services). ipfw -q add 400 pass tcp from any to any 80,443 keep-state ipfw -q add 400 pass tcp from any to any http,https keep-state

The “antispoof” option: The source address on incoming packets is checked. If the source address belongs to a directly connected network, make sure that’s the packet came from the interface for that network. E.g. with 10.0.0.0/24 connected on em1, make sure any packet claiming to be from 10.0.0.0/24 actually entered from em1!

Show active rules with match count: # ipfw -a list Also show dynamic rules: # ipfw -ed list

Disable the firewall: # ipfw disable firewall

Safely test a new rule set (e.g. set 2) without locking us out: # ipfw set disable 2 # ipfw add NN set 2 … # etc. # ipfw set enable 2; echo done; sleep 60 && ipfw set disable 2 If everything is good, we Ctrl-c before the sleep ends. Or, if we’re loading rules from a shell script: # sh ./test-ipfw.rules; sleep 60 && sh /etc/ipfw.rules

We almost always want to prevent any logging rules from flooding the logs. Add this to /etc/sysctl.conf: net.inet.ip.fw.verbose_limit=5

Example: Simple Web Server

In /etc/rc.conf:

firewall_enable="YES"
firewall_script="/etc/ipfw.rules"
firewall_logging="YES"

A simple rule set we might use for a web server in /etc/ipfw.rules:

ipfw -q -f flush

add="ipfw -q add"

# Allow all lo0 traffic:
$add 100 pass all from any to any via lo0

# Reject IP spoofing:
$add 200 deny ip from any to any not antispoof in

$add 300 pass tcp from any to any http,https keep-state
$add 400 pass tcp from any to any domain out keep-state
$add 500 pass udp from any to any domain out
$add 600 pass tcp from any to any ssh in keep-state
$add 700 pass icmp from any to any keep-state
$add 800 udp from any to any ntp out

Example: NAT Firewall/Gateway

In /etc/rc.conf:

gateway_enable="YES"
firewall_enable="YES"
firewall_script="/etc/ipfw.rules"
firewall_nat_enable="YES"
firewall_nat_interface="em0"
firewall_logging="YES"

This example shows a firewall/gateway that NATs:

ipfw -q -f flush

add="ipfw -q add"
wan="em0"
lan="em1"

ipfw -q nat 1 config if $wan reset same_ports unreg_only

# Allow all lo0 traffic:
$add 100 pass all from any to any via $lan
$add 200 pass all from any to any via lo0

# Reject spoofing:
$add 300 deny ip from any to any not antispoof in

# NAT rule for incoming packets.
# NAT in rule must appear BEFORE check-state.
$add 400 nat 1 ip from any to any via $wan in

$add 500 check-state

# Port redirection rules to internal, skipto 10000
# ...

# Prevent lan from spamming
$add 1000 deny ip from not me to any smtp,smtps via $wan out

# Allow all other outgoing connections
$add 2000 skipto 10000 tcp from any to any via $wan out keep-state
$add 2100 skipto 10000 udp from any to any via $wan out keep-state

# I server web pages too:
$add 5000 allow tcp from any to me http,https via $wan in keep-state

$add 9000 allow icmp from any to any

$add 9999 deny all from any to any

# NAT rule for outgoing packets
$add 10000 nat 1 ip from any to any via $wan out

Example: NAT firewall & HAProxy to Jailed Web Servers

In /etc/rc.conf:

gateway_enable="YES"
firewall_enable="YES"
firewall_script="/etc/ipfw.rules"
firewall_nat_enable="YES"
firewall_nat_interface="em0"
firewall_logging="YES"
# Additional local interface for jails:
cloned_interfaces="lo1"
ifconfig_lo1="inet 172.16.0.1/32"
ifconfig_lo1_alias0="inet 172.16.0.2/32"
ifconfig_lo1_alias1="inet 172.16.0.3/32"

This example shows a firewall/gateway that NATs:

ipfw -q -f flush

add="ipfw -q add"
wan="em0"
lan="em1"
jails="l01"

ipfw -q nat 1 config if $wan reset same_ports unreg_only

# Allow all local traffic:
$add 100 pass all from any to any via lo0
$add 150 pass all from any to any via $jails
$add 200 pass all from any to any via $lan

# Reject spoofing:
$add 300 deny ip from any to any not antispoof in

# NAT rule for incoming packets.
# NAT in rule must appear BEFORE check-state.
$add 400 nat 1 ip from any to any via $wan in

$add 500 check-state

$add 1000 deny ip from any to any smtp,smtps via $wan out

# Allow all other outgoing connections
$add 2000 skipto 10000 tcp from any to any via $wan out keep-state
$add 2100 skipto 10000 udp from any to any via $wan out keep-state

$add 5000 allow tcp from any to any http,https keep-state

$add 6000 allow tcp from any to me ssh in keep-state

$add 9000 allow icmp from any to any

$add 9999 deny all from any to any

# NAT rule for outgoing packets
$add 10000 nat 1 ip from any to any via $wan out