Solved Issues with IPFW skipto Rule and Whitelisting Logic

I am using ipfw with these rules:

Bash:
#!/bin/sh

# Set rules command prefix
cmd="ipfw -q add "
cmd2="ipfw -q "

# Public interface
pif=`ifconfig -l | awk '{ print $1 }'`

# Flush all rules
ipfw -q -f flush

# Flush all tables
$cmd2 table 1 flush
$cmd2 table 3 flush

# Allow loopback and deny loopback spoofing
$cmd 00010 allow ip from any to any via lo0
$cmd 00020 deny ip from any to 127.0.0.0/8
$cmd 00030 deny ip from 127.0.0.0/8 to any

# Catch spoofing from outside.
$cmd 00031 deny ip from any to any not antispoof via $pif

# Checks stateful rules
$cmd 00050 check-state
$cmd 00060 deny tcp from any to any established

# ALLOW WHITELIST - IGNORE RULE 00100
$cmd2 00070 add skipto 00101 ip from 'table(3)' to any

# DENY INCOMING LIST
$cmd 00100 reset ip from 'table(1)' to any

# ICMP
$cmd 01010 allow icmp from any to any out via $pif keep-state
$cmd 01011 allow icmp from any to any in via $pif

# WWW
$cmd 10031 allow tcp from me to any dst-port 443 out via $pif setup keep-state
$cmd 10033 allow tcp from any to me dst-port 443 in via $pif setup keep-state

# Deny everything else, and log it
$cmd 56599 deny log all from any to any

And ipfw list includes:

Code:
00070 skipto 101 ip from table(3) to any
00100 reset ip from table(1) to any

Currently, table(1) holds about 1.9 million entries (both individual IPs and subnets), while table(3) contains about 10,000 entries (also a mix of single IPs and subnets).

These tables are populated using this script few times per day:

Bash:
#!/bin/sh

tempdir=$(mktemp -d /tmp/ipfw.XXXXXX)
trap "rm -rf $tempdir" EXIT

fetch -q -o "$tempdir/allow.txt" https://example.com/ipfw/allow.txt || exit 1
fetch -q -o "$tempdir/deny.txt" https://example.com/ipfw/deny.txt || exit 1

update_table() {
    table=$1
    file=$2
    current_file="$tempdir/current_table_$table.txt"
    ipfw -q table "$table" list | awk '{print $1}' | sed 's/\/32$//' | sort > "$current_file"
    cat "$file" | sed 's/\/32$//' | sort | uniq > "$tempdir/new_table_$table.txt"

    comm -13 "$tempdir/new_table_$table.txt" "$current_file" | while read -r ip; do
        [ -n "$ip" ] && ipfw -q table "$table" delete "$ip"
    done

    comm -23 "$tempdir/new_table_$table.txt" "$current_file" | while read -r ip; do
        [ -n "$ip" ] && ipfw -q table "$table" add "$ip"
    done
}

update_table 3 "$tempdir/allow.txt"
update_table 1 "$tempdir/deny.txt"

My intended logic is that any IP present in table(3) should always be allowed, even if it or its subnet also appears in table(1).

For instance, 175.178.167.241 is in table(3), while 175.178.0.0/16 is present in table(1).

After rebooting the server and populating the tables by running the update script for the first time, access from 175.178.167.241 works correctly. However, after subsequent runs of the update script - which only updates unrelated entries and does not modify 175.178.167.241 or 175.178.0.0/16 - access from 175.178.167.241 is no longer permitted.

When this issue occurs, I can resolve it by just removing 175.178.167.241 from table(3) and then immediately re-adding it to table(3).

These commands restore access for the IP:

Code:
ipfw table 3 delete 175.178.167.241
ipfw table 3 add 175.178.167.241

Does anyone have any ideas about what could be causing this behavior?
 

ipfw -q add 00032 count log logamount 0 ip from 175.178.0.0/16 to any



After that, I checked /var/log/security while trying to connect from 175.178.167.241 (I can only use a web interface they provide me to test the connection). During these tests, I saw DNS requests coming from 175.178.254.144 and 175.178.136.250 to port 53, which I assume are their DNS resolvers. Once I added those two IPs to table(3), I could no longer reproduce the issue. I will test again tomorrow, but I’m now quite sure the real problem was DNS resolution failing because those resolver IPs were blocked.
 
Back
Top