强曰为道
与天地相似,故不违。知周乎万物,而道济天下,故不过。旁行而不流,乐天知命,故不忧.
文档目录

iptables 完全指南 / 第 14 章:规则持久化

第 14 章:规则持久化

本章目标:掌握 iptables 规则的持久化方法,确保服务器重启后防火墙规则自动恢复,包括 iptables-save/restore、iptables-persistent 和 systemd 三种方案。


14.1 为什么需要持久化

14.1.1 问题说明

通过 iptables 命令直接添加的规则只存在于内存中,服务器重启后所有规则都会丢失。

# 添加规则
iptables -A INPUT -p tcp --dport 22 -j ACCEPT

# 重启后规则消失!
reboot

# 验证
iptables -L INPUT -n
# 规则已清空

14.1.2 持久化方案对比

方案优点缺点适用发行版
iptables-save/restore简单直接需要手动操作所有发行版
iptables-persistent开机自动恢复依赖 apt 包Debian/Ubuntu
netfilter-persistent新版替代依赖 apt 包Debian 9+/Ubuntu 16.04+
systemd service灵活可控需要编写服务文件systemd 系统
脚本 + crontab定期备份不适合实时恢复所有发行版

14.2 iptables-save / iptables-restore

14.2.1 iptables-save

iptables-save 将当前内存中的规则以文本格式输出:

# 输出所有表的规则到标准输出
sudo iptables-save

# 只输出 filter 表
sudo iptables-save -t filter

# 只输出 nat 表
sudo iptables-save -t nat

# 输出到文件
sudo iptables-save > /etc/iptables/rules.v4

# 输出 IPv6 规则
sudo ip6tables-save > /etc/iptables/rules.v6

14.2.2 输出格式解读

$ sudo iptables-save
# Generated by iptables-save v1.8.9
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
COMMIT
*nat
:PREROUTING ACCEPT [0:0]
:INPUT ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
-A POSTROUTING -s 10.0.0.0/24 -o eth0 -j MASQUERADE
COMMIT
# Completed
含义
*filter开始一个表(filter)
:INPUT DROP [0:0]INPUT 链默认策略为 DROP,计数器为 [包数:字节数]
-A INPUT ...一条追加到 INPUT 链的规则
COMMIT提交该表的规则

14.2.3 iptables-restore

# 从文件恢复规则
sudo iptables-restore < /etc/iptables/rules.v4

# 恢复 IPv6 规则
sudo ip6tables-restore < /etc/iptables/rules.v6

# 原子性恢复:不先清空现有规则
sudo iptables-restore --noflush < /etc/iptables/new-rules.v4

# 计数器:保留原计数器值
sudo iptables-restore --counters < /etc/iptables/rules.v4

14.3 方案一:手动保存/恢复脚本

14.3.1 保存脚本

#!/bin/bash
# /usr/local/bin/save-firewall.sh
# 保存当前防火墙规则

BACKUP_DIR="/etc/iptables"
DATE=$(date +%Y%m%d_%H%M%S)

mkdir -p "$BACKUP_DIR"

# 保存当前规则
iptables-save > "$BACKUP_DIR/rules.v4"
ip6tables-save > "$BACKUP_DIR/rules.v6"

# 带日期的备份
cp "$BACKUP_DIR/rules.v4" "$BACKUP_DIR/rules.v4.$DATE"
cp "$BACKUP_DIR/rules.v6" "$BACKUP_DIR/rules.v6.$DATE"

# 只保留最近 30 天的备份
find "$BACKUP_DIR" -name "rules.v4.*" -mtime +30 -delete
find "$BACKUP_DIR" -name "rules.v6.*" -mtime +30 -delete

echo "Firewall rules saved to $BACKUP_DIR"
echo "  - rules.v4 (IPv4)"
echo "  - rules.v6 (IPv6)"

14.3.2 恢复脚本

#!/bin/bash
# /usr/local/bin/restore-firewall.sh
# 恢复防火墙规则

RULES_V4="/etc/iptables/rules.v4"
RULES_V6="/etc/iptables/rules.v6"

if [ ! -f "$RULES_V4" ]; then
    echo "ERROR: $RULES_V4 not found"
    exit 1
fi

# 恢复 IPv4 规则
iptables-restore < "$RULES_V4"
echo "IPv4 rules restored from $RULES_V4"

# 恢复 IPv6 规则
if [ -f "$RULES_V6" ]; then
    ip6tables-restore < "$RULES_V6"
    echo "IPv6 rules restored from $RULES_V6"
fi

14.4 方案二:iptables-persistent(Debian/Ubuntu)

14.4.1 安装

# Debian/Ubuntu 安装
sudo apt install iptables-persistent

# 安装过程中会提示是否保存当前规则
# 选择 "Yes" 保存当前规则到 /etc/iptables/rules.v4 和 rules.v6

14.4.2 使用方法

# 保存当前规则(修改规则后执行)
sudo netfilter-persistent save

# 等价于
sudo iptables-save > /etc/iptables/rules.v4
sudo ip6tables-save > /etc/iptables/rules.v6

# 重新加载规则
sudo netfilter-persistent reload

# 查看保存的规则
cat /etc/iptables/rules.v4
cat /etc/iptables/rules.v6

14.4.3 工作原理

iptables-persistent 使用 systemd 服务在启动时自动恢复规则:

# 查看服务状态
sudo systemctl status netfilter-persistent

# 服务文件位置
cat /lib/systemd/system/netfilter-persistent.service

# 主要逻辑:
# 1. 在网络启动前执行
# 2. 从 /etc/iptables/rules.v4 和 rules.v6 恢复规则

14.4.4 配置文件

# 规则文件位置
/etc/iptables/rules.v4     # IPv4 规则
/etc/iptables/rules.v6     # IPv6 规则

# 插件目录
/usr/share/netfilter-persistent/plugins.d/

14.5 方案三:systemd 服务

14.5.1 创建自定义 systemd 服务

# 创建规则文件目录
sudo mkdir -p /etc/iptables

# 保存当前规则
sudo iptables-save > /etc/iptables/rules.v4
sudo ip6tables-save > /etc/iptables/rules.v6
# /etc/systemd/system/iptables-restore.service
[Unit]
Description=Restore iptables firewall rules
Before=network-pre.target
Wants=network-pre.target

[Service]
Type=oneshot
ExecStart=/sbin/iptables-restore /etc/iptables/rules.v4
ExecStart=/sbin/ip6tables-restore /etc/iptables/rules.v6
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
# 启用服务
sudo systemctl daemon-reload
sudo systemctl enable iptables-restore.service

# 手动测试
sudo systemctl start iptables-restore.service
sudo systemctl status iptables-restore.service

14.5.2 使用脚本启动

# /etc/systemd/system/firewall.service
[Unit]
Description=Custom iptables firewall
Before=network-pre.target
Wants=network-pre.target

[Service]
Type=oneshot
ExecStart=/usr/local/bin/firewall-setup.sh
ExecStop=/usr/local/bin/firewall-stop.sh
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
#!/bin/bash
# /usr/local/bin/firewall-setup.sh
# 自定义防火墙初始化脚本

# 加载内核模块
modprobe nf_conntrack
modprobe nf_conntrack_ftp
modprobe nf_nat_ftp

# 应用规则
iptables-restore < /etc/iptables/rules.v4
ip6tables-restore < /etc/iptables/rules.v6

# 设置内核参数
sysctl -p /etc/sysctl.d/99-firewall.conf

echo "Firewall initialized."
#!/bin/bash
# /usr/local/bin/firewall-stop.sh
# 防火墙停止脚本

iptables -F
iptables -t nat -F
iptables -t mangle -F
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT

ip6tables -F
ip6tables -P INPUT ACCEPT
ip6tables -P FORWARD ACCEPT
ip6tables -P OUTPUT ACCEPT

echo "Firewall stopped."

14.6 方案四:Cron 定期备份

# 每小时备份一次防火墙规则
# crontab -e
0 * * * * /usr/local/bin/save-firewall.sh > /dev/null 2>&1

# 每天检查规则是否被修改(与上次保存的对比)
0 8 * * * /usr/local/bin/check-firewall.sh
#!/bin/bash
# /usr/local/bin/check-firewall.sh
# 检查防火墙规则是否有变化

SAVED="/etc/iptables/rules.v4"
CURRENT=$(mktemp)

iptables-save > "$CURRENT"

if ! diff -q "$SAVED" "$CURRENT" > /dev/null 2>&1; then
    echo "WARNING: Firewall rules have been modified!"
    echo "Diff:"
    diff "$SAVED" "$CURRENT"
    logger -t firewall-check "Firewall rules modified!"
fi

rm -f "$CURRENT"

14.7 规则管理最佳实践

14.7.1 版本控制

#!/bin/bash
# /usr/local/bin/manage-firewall.sh
# 使用 Git 管理防火墙规则版本

RULES_DIR="/etc/iptables"
REPO_DIR="/etc/iptables-repo"

# 初始化 Git 仓库(仅首次)
if [ ! -d "$REPO_DIR/.git" ]; then
    mkdir -p "$REPO_DIR"
    cd "$REPO_DIR"
    git init
    git config user.email "[email protected]"
    git config user.name "Firewall Manager"
fi

# 保存并提交
save_and_commit() {
    local msg="${1:-Update firewall rules}"
    
    iptables-save > "$RULES_DIR/rules.v4"
    ip6tables-save > "$RULES_DIR/rules.v6"
    
    cp "$RULES_DIR/rules.v4" "$REPO_DIR/"
    cp "$RULES_DIR/rules.v6" "$REPO_DIR/"
    
    cd "$REPO_DIR"
    git add rules.v4 rules.v6
    git commit -m "$msg"
    
    echo "Rules saved and committed: $msg"
}

# 查看历史
show_history() {
    cd "$REPO_DIR"
    git log --oneline -20
}

# 回滚到指定版本
rollback() {
    local commit=$1
    cd "$REPO_DIR"
    git checkout "$commit" -- rules.v4 rules.v6
    iptables-restore < rules.v4
    ip6tables-restore < rules.v6
    echo "Rolled back to commit: $commit"
}

case "$1" in
    save)
        save_and_commit "$2"
        ;;
    history)
        show_history
        ;;
    rollback)
        rollback "$2"
        ;;
    *)
        echo "Usage: $0 {save [msg]|history|rollback <commit>}"
        ;;
esac
# 使用示例
sudo manage-firewall.sh save "Added SSH rate limiting"
sudo manage-firewall.sh save "Added web server rules"
sudo manage-firewall.sh history
# abc1234 Added web server rules
# def5678 Added SSH rate limiting
sudo manage-firewall.sh rollback def5678

14.7.2 变更前自动备份

#!/bin/bash
# 自动备份脚本,用于在修改规则前调用

backup_before_change() {
    local timestamp=$(date +%Y%m%d_%H%M%S)
    local backup_dir="/etc/iptables/backup"
    mkdir -p "$backup_dir"
    
    iptables-save > "$backup_dir/rules.v4.$timestamp"
    ip6tables-save > "$backup_dir/rules.v6.$timestamp"
    
    echo "Backup created: $timestamp"
}

# 在修改规则前调用
backup_before_change

# 然后进行修改
iptables -A INPUT -p tcp --dport 8080 -j ACCEPT

14.8 CentOS/RHEL 的持久化

14.8.1 CentOS 7 及更早版本

# 保存规则
service iptables save
# 等价于
iptables-save > /etc/sysconfig/iptables
ip6tables-save > /etc/sysconfig/ip6tables

# 服务管理
systemctl start iptables
systemctl enable iptables

# IPv6
systemctl start ip6tables
systemctl enable ip6tables

14.8.2 CentOS 8 / RHEL 8+(使用 nftables)

# CentOS 8+ 默认使用 nftables
# 如果仍使用 iptables,安装 iptables-services
dnf install iptables-services

# 保存规则
service iptables save
# 或
iptables-save > /etc/sysconfig/iptables

# 启用服务
systemctl enable iptables

14.8.3 规则文件位置

发行版IPv4 规则文件IPv6 规则文件
Debian/Ubuntu/etc/iptables/rules.v4/etc/iptables/rules.v6
CentOS/RHEL/etc/sysconfig/iptables/etc/sysconfig/ip6tables

14.9 完整持久化流程

#!/bin/bash
# ═══════════════════════════════════════════════════
# 防火墙规则管理完整流程
# ═══════════════════════════════════════════════════

# ─── 步骤 1:编写防火墙脚本 ───
cat > /usr/local/bin/setup-firewall.sh << 'FIREWALL'
#!/bin/bash
set -e

# 清空
iptables -F && iptables -t nat -F && iptables -t mangle -F && iptables -X
ip6tables -F && ip6tables -X

# 默认策略
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
ip6tables -P INPUT DROP
ip6tables -P FORWARD DROP
ip6tables -P OUTPUT ACCEPT

# IPv4 规则
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
iptables -A INPUT -i lo -j ACCEPT
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
iptables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT
iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT

# IPv6 规则
ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type neighbor-solicitation -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type neighbor-advertisement -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type echo-request -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type destination-unreachable -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp --icmpv6-type packet-too-big -j ACCEPT
ip6tables -A INPUT -p ipv6-icmp -j DROP
ip6tables -A INPUT -p tcp --dport 22 -j ACCEPT
ip6tables -A INPUT -p tcp -m multiport --dports 80,443 -j ACCEPT

echo "Firewall setup complete."
FIREWALL
chmod +x /usr/local/bin/setup-firewall.sh

# ─── 步骤 2:执行脚本 ───
/usr/local/bin/setup-firewall.sh

# ─── 步骤 3:保存规则 ───
mkdir -p /etc/iptables
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6

# ─── 步骤 4:配置开机自动恢复 ───
cat > /etc/systemd/system/iptables-restore.service << 'SERVICE'
[Unit]
Description=Restore iptables rules
Before=network-pre.target
Wants=network-pre.target

[Service]
Type=oneshot
ExecStart=/sbin/iptables-restore /etc/iptables/rules.v4
ExecStart=/sbin/ip6tables-restore /etc/iptables/rules.v6
RemainAfterExit=yes

[Install]
WantedBy=multi-user.target
SERVICE

systemctl daemon-reload
systemctl enable iptables-restore.service

# ─── 步骤 5:验证 ───
echo "=== 验证 IPv4 规则 ==="
iptables -L INPUT -n --line-numbers

echo ""
echo "=== 验证 IPv6 规则 ==="
ip6tables -L INPUT -n --line-numbers

echo ""
echo "✅ 防火墙规则已持久化,重启后自动恢复。"

14.10 注意事项

⚠️ 修改规则后必须保存:每次通过 iptables 命令修改规则后,都必须执行 iptables-savenetfilter-persistent save,否则重启后规则丢失。

⚠️ 规则文件的安全性/etc/iptables/rules.v4 包含完整的防火墙配置,应该设置适当的文件权限(chmod 600)。

⚠️ 回滚方案:在修改生产环境的防火墙规则前,务必备份当前规则并验证恢复流程。

⚠️ 启动顺序:iptables-restore 服务必须在网络服务启动前执行,否则在规则加载前可能有短暂的窗口期没有防护。


14.11 扩展阅读

资源说明
man iptables-save保存工具手册
man iptables-restore恢复工具手册
man netfilter-persistent持久化工具手册
systemd.servicesystemd 服务配置文档

本章小结

方案命令适用场景
手动保存iptables-save > file临时备份
iptables-persistentnetfilter-persistent saveDebian/Ubuntu 生产环境
systemd 服务iptables-restore.service所有 systemd 系统
CentOSservice iptables saveCentOS/RHEL
版本控制Git + 脚本需要审计的环境

下一章第 15 章:Docker 与 iptables,将学习 Docker 如何使用 iptables 管理容器网络。