第 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-save或netfilter-persistent save,否则重启后规则丢失。
⚠️ 规则文件的安全性:
/etc/iptables/rules.v4包含完整的防火墙配置,应该设置适当的文件权限(chmod 600)。
⚠️ 回滚方案:在修改生产环境的防火墙规则前,务必备份当前规则并验证恢复流程。
⚠️ 启动顺序:iptables-restore 服务必须在网络服务启动前执行,否则在规则加载前可能有短暂的窗口期没有防护。
14.11 扩展阅读
| 资源 | 说明 |
|---|---|
man iptables-save | 保存工具手册 |
man iptables-restore | 恢复工具手册 |
man netfilter-persistent | 持久化工具手册 |
| systemd.service | systemd 服务配置文档 |
本章小结
| 方案 | 命令 | 适用场景 |
|---|---|---|
| 手动保存 | iptables-save > file | 临时备份 |
| iptables-persistent | netfilter-persistent save | Debian/Ubuntu 生产环境 |
| systemd 服务 | iptables-restore.service | 所有 systemd 系统 |
| CentOS | service iptables save | CentOS/RHEL |
| 版本控制 | Git + 脚本 | 需要审计的环境 |
下一章:第 15 章:Docker 与 iptables,将学习 Docker 如何使用 iptables 管理容器网络。