
1. 先说清楚iptables 到底是干什么的很多朋友接触 iptables往往是从“抄命令”开始的在搜索引擎里搜到一条封禁 IP 的命令复制下来跑一遍看起来也生效了但第二天换了个场景就完全不会改。原因很简单——你没有把 iptables 当成一套体系去理解而是把它当成了一条条零散咒语。iptables 是 Linux 内核里 netfilter 框架的命令行管理工具它负责的本质上只有一件事在网络数据包流经内核协议栈的各个关键节点时决定放行还是拦截。云服务器、物理机、容器宿主机上的防火墙绝大多数底层走的都是这套机制。这篇文章面向三类人一是刚入行、第一次在服务器上配规则的运维新手二是自己搭过网站、被各类访问日志骚扰过的独立开发者三是已经在用 iptables 但每次都要现查手册的“半熟手”。内容不会停留在“背命令”层面我会把表、链、规则这三层结构讲透再以“禁止一个具体 IP202.98.7.148” 为实战案例把增、删、查、存以及排错过程完整走一遍。看完之后你至少能独立写出一条安全的、可维护的防火墙规则而不是东拼西凑。1.1 把 netfilter 框架装进一个“物业”故事里理解 iptables 最快的方式是把它想象成小区门口的物业管理体系。这里的核心抽象有三个表table、链chain、规则rule。表就是不同职能的管理部门。filter 表管“进出许可”最常用nat 表管“地址改写”比如端口转发、共享上网mangle 表管“报文修改”比如调整 TTL 或 DSCPraw 表管“连接跟踪豁免”。链则是在数据包必经之路上挂着的检查岗比如 INPUT 链负责检查所有进入本机的数据包OUTPUT 链负责检查本机发出的数据包FORWARD 链负责检查只是路过本机、转发到别处的数据包。规则就是挂在每个检查岗上的具体执行条款一条规则包含“匹配条件”什么样的包和“处理动作”放行、丢弃、拒绝、跳到别的链等。数据包每经过一个检查岗就会按顺序核对岗上的每一条规则。匹配了就执行动作没匹配就继续看下一条。这个“顺序核对”的机制是整个 iptables 的灵魂也是绝大多数“规则不生效”问题的根源。你之后会看到一条规则放的位置不对哪怕内容完全正确也可能永远轮不到它发挥作用。1.2 数据包进入 Linux 的完整旅途要搞清楚规则该加在哪条链就得先搞清楚数据包的旅行路线。我们把“到达本机”“经过本机”“本机发出”三种情况分开看到达本机的数据包从网卡进来后先经过 PREROUTING 链然后内核做一次路由判断。如果目的地址是本机就进入 INPUT 链最终交给本机进程处理。只经过本机、需要转发的数据包同样先经过 PREROUTING路由判断后发现目的地址不是本机于是进入 FORWARD 链处理完再从 POSTROUTING 链出去。本机进程自己发出的数据包进程发出后先经过 OUTPUT 链再走 POSTROUTING 链最后从网卡送出。这五条链看着各自独立但表在里面是有执行顺序的。同一个检查岗不同的表会按固定次序依次执行在 PREROUTING 上顺序是 raw → mangle → nat在 INPUT 上是 mangle → filter在 FORWARD 上是 mangle → filter在 OUTPUT 上是 raw → mangle → nat → filter在 POSTROUTING 上是 mangle → nat。这就是为什么很多新手写iptables -t nat -A INPUT ...会一头雾水——nat 表根本不参与 INPUT 链的处理命令虽然不报错但永远不会生效。对于日常 90% 的需求你只需要记住一句话想拦“发到本机”的流量就在 filter 表的 INPUT 链上加规则想拦“本机发出去”的流量就在 filter 表的 OUTPUT 链上加规则想拦“被本机转发”的流量就在 filter 表的 FORWARD 链上加规则。看到这里你已经比很多直接抄命令的人强了因为你知道规则应该归属在哪条链上。2. 每天都要用的几个高频命令查、增、删、改我平时在服务器上排查网络问题翻 iptables 规则的频率非常高。这节讲的是最基础、也最容易被忽略的“日常四件事”怎么看、怎么加、怎么删、怎么改。熟练掌握这四条你就不需要再怕动防火墙了。2.1 查询规则要养成的三个好习惯查看规则的标准姿势是这条iptables -nL INPUT --line-numbers -v拆开讲-n表示不对 IP 做反向域名解析否则规则里出现一个 IP系统还要去 DNS 查一遍既慢又容易卡住-L是列出规则后面跟链名就可以只看某一条链--line-numbers显示行号这在删除规则时至关重要下面会专门讲-v显示统计信息包括每个规则匹配了多少个数据包、多少字节、走的是哪个网卡接口。加了-v之后规则末尾会出现pkts和bytes两列这两列数字在排查“规则到底有没有生效”时是最有力的证据。另一个习惯是看默认策略。每条内置链开头有一行policy ACCEPT或policy DROP它决定了没有匹配到任何规则的数据包最终怎么处理。看iptables -L输出时很多人只盯着具体规则忽略了这行 Policy结果发现“所有包都没匹配上却还是通了/断了”其实就是默认策略在起作用。还有一个小习惯别只 filter 表。很多场景需要看 nat 表建议直接养成用-t指定表的习惯iptables -t nat -nL --line-numbers2.2 “追加”还是“插入”这两个操作差别很大添加规则有两个动作-Aappend追加到链尾-Iinsert插入到链的指定位置。默认情况下iptables -A INPUT -s 202.98.7.148 -j DROP是放在 INPUT 链最后一条的。听起来无所谓但防火墙规则是自上而下逐个匹配的一旦前面的规则先匹配到了流量并执行了 ACCEPT后面的 DROP 就永远等不到机会。所以“把拦截规则放到链首”往往比放到链尾更稳妥这也是-I存在的意义。一条规则里面可以叠加多个匹配条件比如源地址、目标端口、协议、网卡接口等。一条规则内部的多个条件之间是“并且”的关系必须同时满足才算匹配而不同规则之间则是“或者”的关系先匹配到的先执行。举个例子下面这条规则的意思是来自 192.168.1.0/24 网段、访问本机 TCP 22 端口的包全部放行iptables -A INPUT -p tcp --dport 22 -s 192.168.1.0/24 -j ACCEPT-p指定协议--dport是目标端口-s是源地址。注意--dport只有在-p tcp或-p udp被指定时才有意义单独写--dport 22而不写协议规则也能加进去但行为很可能不是你以为的那样。2.3 改错了怎么办替换、清空与默认策略删除规则的推荐做法是优先按行号删。先用查询命令看清行号然后iptables -D INPUT 1这表示删除 INPUT 链第 1 条规则。删完再查一遍确认删的是你想要的那条。如果规则较多也可以用完整条件来删但一定要与添加时的条件完全一致否则容易删错甚至删不掉iptables -D INPUT -s 202.98.7.148 -j DROP替换规则用-R指定要替换的行号和一个完整规则即可。清空某条链用-F比如iptables -F INPUT会把 INPUT 链上所有规则清空。清空所有链用iptables -F但这里要特别提醒清空规则不等于关闭防火墙因为链的默认策略还在如果之前有人把默认策略设成了DROP你-F之后所有包都会被默认策略挡掉直接把自己锁在门外。这种“手滑断网”事故我见过太多次所以每次操作前都会先看一遍policy那行。设置默认策略的方式是-Piptables -P INPUT DROP这条命令的意思是所有 INPUT 链上没有匹配规则的包一律丢弃。一般来说我不建议新手一上来就把默认策略设成 DROP尤其在你还没搞懂自己机器上跑的每个服务之前。更安全的做法是先明确放行 SSH、HTTP 等必要端口再把默认策略收紧。3. 实战拆解用一条命令禁止 202.98.7.148这一节我们回到开头那个热搜场景iptables 禁止 202.98.7.148。假设你在日志里看到这个 IP 频繁扫描端口或者它就是某个攻击源你需要在服务器上把它拉黑。3.1 禁令命令逐字段拆解最简单、最直接的一条命令如下iptables -t filter -A INPUT -s 202.98.7.148 -j DROP逐段拆开看含义-t filter指定操作 filter 表。因为 filter 表是默认表所以也可以省略不写但显式写出来能让阅读命令的人立刻知道你的意图。-A INPUT追加到 INPUT 链末尾。如果你想让它排在链的最前面改成-I INPUT 1更合适。-s 202.98.7.148匹配源地址为这个 IP 的所有数据包。-j DROP匹配后执行的动作是“丢弃”。如果只想禁止这个 IP 访问本机的某个端口把条件收紧一点。比如禁止它访问 SSHiptables -A INPUT -s 202.98.7.148 -p tcp --dport 22 -j DROP如果它攻击的是某个 Web 服务就把端口换成 80 或 443。如果要封禁一整段 IP比如同网段可以用 CIDR 写法iptables -A INPUT -s 202.98.7.0/24 -j DROP这条会把 202.98.7.0 到 202.98.7.255 全封禁适用于大规模扫描的场合。局限性也要说清楚它只拦截源 IP通过代理、跳板或伪造源地址的方式不一定拦得住但在常规防护里已经是性价比最高的一步了。3.2 DROP 与 REJECT 怎么选别再只用一个新手最常见的问题是“DROP 和 REJECT 到底有什么区别我该用哪个”两者的核心区别在于对连接方的反馈DROP直接丢弃数据包不返回任何消息。客户端表现是连接超时一直卡在那里直到超时。REJECT丢弃数据包同时返回一个拒绝回应比如 ICMP port unreachable。客户端表现是立即收到“连接被拒绝”。在封禁攻击 IP 时我用得更多的是DROP因为对方不容易通过回应判断你的防火墙策略。但如果你封禁的是自己的内部测试机用REJECT反而更容易定位问题因为客户端会立刻报错而不是挂在那里等超时。下面这张对照表可以收藏动作客户端感知适用场景DROP连接超时无响应隔离攻击源、减少信息暴露REJECT立即提示拒绝调试规则、对内网服务做访问控制需要强调的一点是DROP不会主动拒绝已建立的 TCP 连接只是让新的数据包进不来。对正在进行的连接是否有影响取决于是否匹配了状态相关的规则这一点等会儿在排查章节展开。3.3 封禁之后验证、解封与留痕规则加完之后建议立刻验证三件事第一规则是否真的在链里。用编号查看一下iptables -nL INPUT --line-numbers第二规则是否真的生效。可以去另一台机器 ping 一下本机或者盯住规则里的计数iptables -L INPUT -nv --line-numbers看对应规则行前的pkts是否在增长。计数增长说明规则被匹配到了如果计数一直是 0就算规则写得再漂亮也说明流量没走到这条链上。第三如果想留个日志可以在 DROP 之前加一条 LOG 规则。比如iptables -A INPUT -s 202.98.7.148 -j LOG --log-prefix BLOCKED_IP: --log-level 4 iptables -A INPUT -s 202.98.7.148 -j DROP注意 LOG 动作执行之后还会继续匹配下一条规则所以它不会拦截流量真正拦截的是后面那条 DROP。日志默认写到内核日志里一般在/var/log/kern.log或journalctl -k里能看到。不过这种日志量可能很大生产环境慎用最好配合频率限制的模块一起用否则日志磁盘会被刷爆。解封也很简单把命令里的-A换成-D条件保持一致iptables -D INPUT -s 202.98.7.148 -j DROP或者按行号删除先查--line-numbers再iptables -D INPUT 3删完务必复查。这个“删完复查”的习惯能救你很多次。4. 规则持久化别让一重启就变成“裸奔”命令敲完规则生效但你如果现在直接重启服务器会发现所有规则全部消失了。原因很简单iptables 规则默认只存在内核内存里并没有写入任何配置文件。对一台要长期运行的服务器来说这是不可接受的。所以学会把规则保存下来、并在开机时自动加载是 iptables 使用里必须跨过的一道坎。4.1 把当前规则导出成文件导出规则的标准命令是iptables-save。用法很直观iptables-save /etc/iptables/rules.v4它会把你当前所有表、所有链的规则以统一格式导出。注意不同发行版放的路径不一样比如 Debian/Ubuntu 的传统路径是/etc/iptables/rules.v4CentOS 7 上有时候是/etc/sysconfig/iptables。如果你不确定该放哪先统一放到/etc/iptables/目录下再通过服务加载。导入则用iptables-restore 文件。实际操作时我通常不会只导 filter 表。如果机器上还做了 NAT 转发IPv6 规则也要单独处理常见做法是规则文件分 v4 和 v6 两份。虽然在大多数场景下我们只关心 IPv4但开了 IPv6 又没配规则可能意味着你的 IPv6 地址完全裸奔这个隐患要记在心里。4.2 在 Debian/Ubuntu 上让它开机自动加载最省事的方法是安装iptables-persistent包apt install iptables-persistent netfilter-persistent save netfilter-persistent reloadsave会把当前规则写入配置文件reload会重新加载配置文件。这种方式适合规则变化不频繁的场景。如果你频繁改规则每次改完都要顺手执行一次netfilter-persistent save否则重启前没保存的规则照样丢。在 CentOS/RHEL 系上情况会复杂一些。老版本可以安装iptables-services然后用systemctl enable iptables开启开机加载新版本默认走的是 firewalld而 firewalld 虽然底层仍然调用 iptables 或者 nftables但它有自己的一套配置方式。你在 firewalld 环境下直接敲 iptables 命令规则可以生效但重启后可能不会保留因为 firewalld 会在启动时重置规则。我的建议是系统里如果默认装了 firewalld就用firewall-cmd来管理而不是混用裸 iptables 命令两者并存时规则容易互相打架。4.3 装了 Docker 的机器千万别无脑iptables -F这一条非常重要。Docker 在启动时会往 iptables 里注入大量规则尤其是 FORWARD 链和 NAT 表。它依赖这些规则实现容器网络隔离、端口映射。如果你在装有 Docker 的机器上执行了iptables -F容器网络大概率立刻出问题端口映射全部失效容器之间也可能互相访问不了。遇到这种情况也别慌最快的恢复办法一般是重启 Dockersystemctl restart dockerDocker 会重新写入自己的规则。更安全的做法是在 Docker 环境下永远不要用iptables -F这种“大扫除”操作改成针对具体链、具体行号精确增删。对于容器网络优先使用 Docker 自带的网络策略而不是在宿主机的 FORWARD 链上手工加拦截规则那样很容易和 Docker 自动维护的规则发生冲突。5. 排查实录加了规则“没效果”的几种典型原因写了这么多年 iptables 规则我踩过的坑、帮别人排过的错可以沉淀成一份问题速查表。这一节我们不讲理论直接进入“规则加了事态却没有任何变化”的现场看看到底是哪里出了岔子。5.1 第一件事永远是看命中计数别改来改去当你觉得规则没生效先别急着删了重写先加-v看命中计数iptables -L INPUT -nv --line-numbers如果对应规则的pkts计数在涨说明流量确实被规则匹配到了你觉得“没生效”可能是因为对操作结果的理解错了。比如你加了 DROP结果客户端看着是超时你可能以为没堵住其实它已经被丢弃了只是回包方式让你误判。如果计数完全不动再往下排查。有一种常见低级错误规则加错了链。你想拦到达本机的流量却加到了 OUTPUT 链上那当然不会匹配。这种错在查行号时一眼就能看出来因为 OUTPUT 链里躺着一条带-s的规则明摆着目的不对。5.2 顺序错了ACCEPT 在前后面的 DROP 直接失去意义这是新手最容易踩的大坑。假设你之前写了一条放行规则iptables -A INPUT -s 202.98.7.148 -j ACCEPT后面又加了一条拦截规则iptables -A INPUT -s 202.98.7.148 -j DROP因为-A是追加到链尾所以链里的顺序是 ACCEPT 在前、DROP 在后。数据包一来先匹配 ACCEPT直接放行DROP 根本不会被匹配到。这种情况下你以为自己已经“封禁”了实际上流量畅通无阻。解决办法把拦截规则放在前面。我平时对“禁止黑名单 IP”这类规则一律用-I INPUT 1插到链首然后再在链尾放各种业务放行规则。这样无论后面怎么追加规则黑名单的优先级始终最高。5.3 回环地址和状态匹配时容易犯迷糊另一个非常隐蔽的坑来自本地回环。你给某端口加了 DROP 规则然后用本机客户端去访问本机服务发现居然能连通于是开始怀疑人生。事实上本机访问本机的数据包走的网卡是lo如果规则里没有限制-i lo但系统通常有允许 lo 的全部流量规则那么回环包的放行在更早就被匹配掉了。我的习惯是在 INPUT 链第一条就放行回环iptables -A INPUT -i lo -j ACCEPT这样本地服务之间的互访不受防火墙干扰需要拦截的是真正的对外网卡流量。这也是很多 Linux 默认配置里的标准做法。说到状态匹配还有个场景让不少人困惑你已经禁止某个 IP 访问 TCP 80 端口但对方之前已经建立的连接还在继续传输数据。原因是你只拦了 NEW 状态的连接请求而已经完成握手的连接被“允许已建立连接”的规则提前放行了。很多默认规则集里会有一行iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT这行的本意是让响应流量顺利回流但它也会让旧连接不受新规则约束。如果你确实需要立刻切断现有连接那就把这条放在新规则之后或者干脆把它暂时去掉再重新加载规则。5.4 源地址和目标地址别搞反排查现场里还有一类问题我想“禁止服务器访问某个 IP”结果却在 INPUT 链上用-s写了那个 IP。这里语义完全不同-s 202.98.7.148的意思是“来自这个 IP 的包”用于禁止别人来访问我。如果想禁止“本机去访问某个地址”要写在 OUTPUT 链上用-d指定目标地址。实际场景里比如内网某台机器被要求不能访问外网特定服务正确命令大概是iptables -A OUTPUT -d 202.98.7.148 -j DROP这类错误最迷惑人的地方在于命令不报错、规则也在列表里、看着也合理就是不起作用。排查时把每条规则的链、方向、-s/-d过一遍比盲目重写快得多。6. 再往下走一步自定义链、NAT 与我的使用习惯到这里iptables 最常用的部分已经讲完了。但既然这篇标题是“基础”我想再送大家几个能显著提高效率的小技巧它们不算复杂的进阶内容却能让你后续的管理工作更舒服。6.1 用自定义链管理黑名单规则再多也不乱当你要封的 IP 越来越多把几十条 DROP 规则全部塞在 INPUT 链里会很难维护。更清晰的做法是建一条自定义链专门放黑名单iptables -N BLOCKLIST iptables -A INPUT -j BLOCKLIST iptables -A BLOCKLIST -s 202.98.7.148 -j DROP第一行创建一条名为 BLOCKLIST 的空链第二行让所有进入 INPUT 的流量先跳到 BLOCKLIST 里过一遍第三行在 BLOCKLIST 里写真正的拦截规则。以后要封新 IP就往 BLOCKLIST 里加不需要动 INPUT 链要解封就在 BLOCKLIST 里精确删除。要清理整条链可以用iptables -F BLOCKLIST清空再用iptables -X BLOCKLIST删除自定义链本身。配合注释模块使用规则的可读性会大幅提升iptables -A BLOCKLIST -s 202.98.7.148 -j DROP -m comment --comment office scanner 2024-06规则列表里会直接显示注释内容过几个月再回来维护你不会看着一堆 IP 发愣完全想不起来当时为什么封它。6.2 顺带做个一分钟端口转发iptables 不只是干“拦截”这一件事NAT 表里的 DNAT/MASQUERADE 可以做端口转发和内网共享上网。举一个最简单的场景把公网网卡上的 8080 端口转发给内网一台机器的 80 端口。第一步开启转发echo 1 /proc/sys/net/ipv4/ip_forward然后加入 NAT 规则iptables -t nat -A PREROUTING -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.10:80 iptables -t nat -A POSTROUTING -d 192.168.1.10 -p tcp --dport 80 -j MASQUERADE第一条把进来的 8080 流量改写到内网主机的 80 端口第二条保证内网主机回包时源地址正确。这个配置本身不难难的是很多人分不清 DNAT 和 SNAT 的位置DNAT 改目的地址必须放在 PREROUTINGSNAT/MASQUERADE 改源地址必须放在 POSTROUTING。只要位置对了成功率就很高。6.3 我自己留存下来的几个使用习惯如果只让我留下一段话给刚学 iptables 的人我会说永远用--line-numbers查看规则永远在修改前先备份当前规则永远在变更后立刻验证一遍。我自己在线上服务器操作时习惯先把每一条新规则写成脚本文件加注释然后执行脚本而不是一行行敲命令。这样一旦出了问题能快速回滚leader 审查规则时也能看到一个完整清晰的变更记录。另外还有一个细节很多人只关注 filter 表却忘了看 nat 表和 mangle 表。强烈建议在查看规则时把filter、nat、mangle三张表都扫一眼至少每个星期检查一次。很多被入侵的机器问题恰恰出在一段没人记得是谁加的 NAT 规则上。iptables 不是“配好就不用管”的东西它是一面需要持续维护的墙定期看一遍规则其实就是服务器安全管理里最便宜也最有效的一道工序。