Persisting iptables rules
511 words, 3 minutes

If you’ve done any networking configuration on Linux, you probably used iptables. If you have, you probably know that iptables does not persist rules, and so all your rule mangling is lost on a reboot.

This isn’t exactly a new problem, and so there are many ways to ‘save’ state, the most commonly cited one being iptables-save to dump the rules to a file:

$ iptables-save > /etc/iptables/rules.v4

and then iptables-restore to bring them back again, like so:

$ iptables-restore < /etc/iptables/rules.v4

This is the defacto file in Debian/Ubuntu. For RHEL/CentOS, the file is /etc/sysconfig/iptables.

Taking this a step further, netfilter-persistent is another useful tool (which seems to have been renamed/split from the old iptables-persistent).

This allows you to write a plugin (i.e. a script) that is called by netfilter-persistent on certain events.

The possible events are start, restart, reload, force-reload, save, and flush.

The event name - one of the above - is passed as the first argument to any script that is present in /usr/share/netfilter-persistent/plugins.d.

Simon Richter and Jonathan Wiltshire have a great reference script within netfilter-persistent that demonstrates what it can do.

If you have an application that requires some rules in iptables, you simply need to place a similar script in the plugins directory.

#!/bin/sh

# This file is part of netfilter-persistent
# (was iptables-persistent)
# Copyright (C) 2009, Simon Richter <[email protected]>
# Copyright (C) 2010, 2014 Jonathan Wiltshire <[email protected]>
# 
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation, either version 3
# of the License, or (at your option) any later version.

exit 0

set -e

rc=0

load_rules()
{
        #load IPv6 rules
        if [ ! -f /etc/iptables/rules.v6 ]; then
                echo "Warning: skipping IPv6 (no rules to load)"
                exit 0
        else
                ip6tables-restore < /etc/iptables/rules.v6 2> /dev/null
                if [ $? -ne 0 ]; then
                        rc=1
                fi
        fi
}

save_rules()
{
        #save IPv6 rules
        #need at least ip6table_filter loaded:
        /sbin/modprobe -q ip6table_filter
        if [ ! -f /proc/net/ip6_tables_names ]; then
                log_action_cont_msg "Warning: skipping IPv6 (no modules loaded)"
        elif [ -x /sbin/ip6tables-save ]; then
                touch /etc/iptables/rules.v6
                chmod 0640 /etc/iptables/rules.v6
                ip6tables-save > /etc/iptables/rules.v6
                if [ $? -ne 0 ]; then
                        rc=1
                fi
        fi
}

flush_rules()
{
        if [ ! -f /proc/net/ip6_tables_names ]; then
                echo "Warning: skipping IPv6 (no module loaded)"
        elif [ -x /sbin/ip6tables ]; then
                for param in F Z X; do /sbin/ip6tables -$param; done
                for table in $(cat /proc/net/ip6_tables_names)
                do
                        /sbin/ip6tables -t $table -F
                        /sbin/ip6tables -t $table -Z
                        /sbin/ip6tables -t $table -X
                done
                for chain in INPUT FORWARD OUTPUT
                do
                        /sbin/ip6tables -P $chain ACCEPT
                done
        fi
}

case "$1" in
start|restart|reload|force-reload)
        load_rules
        ;;
save)
        save_rules
        ;;
stop)
        # Why? because if stop is used, the firewall gets flushed for a variable
        # amount of time during package upgrades, leaving the machine vulnerable
        # It's also not always desirable to flush during purge
        echo "Automatic flushing disabled, use \"flush\" instead of \"stop\""
        ;;
flush)
        flush_rules
        ;;
*)
    echo "Usage: $0 {start|restart|reload|force-reload|save|flush}" >&2
    exit 1
    ;;
esac

exit $rc