
在Linux服务器上摸爬滚打这些年要说哪个命令最让人又爱又恨iptables绝对能排进前三。它是Linux内核netfilter框架的用户态配置工具负责数据包过滤、NAT地址转换、端口转发、流量审计这些最底层的网络能力平时它被各种云安全组、运维脚本藏在幕后可真到了网络不通、服务连不上、面试官追问Linux防火墙怎么工作的时候它总会被第一个拎出来。这篇文章我会从表、链、规则三个维度把它讲透再配上四个真实场景的配置示例和一堆亲测踩坑实录尤其是那条频繁出现在搜索引擎里的iptables v1.8.9 cant initialize nat table报错我会把排查思路完完整整捋一遍。目标读者是刚开始接触Linux防火墙的新手以及会用几个命令、但遇到异常就抓瞎的运维老手面试前拿来抱佛脚也管用。1. 先弄明白 iptables 到底在管什么1.1 五张表、五条链一次讲清楚把一台服务器想象成一个小区iptables就是小区入口的门禁系统。你要管理的不只是谁可以进还包括进来之后换不换门牌号包裹上要不要贴特殊标签这些更细的事。在Linux内核里负责这套网络包处理逻辑的框架叫netfilter而我们平时敲的iptables命令只是操作这个框架的用户态工具。很多人把iptables本身当成防火墙其实不太准确它是给netfilter框架下指令的管理员。netfilter框架下分了五张表每张表负责一类工作。最常用的是filter表也就是纯过滤决定数据包是放行还是丢弃其次是nat表做地址转换内网多台机器共享一个公网IP上网、公网访问内网服务全靠它mangle表用来修改数据包的特征比如TTL、TOSraw表的优先级最高用来关闭连接跟踪主要在高并发场景下提升性能还有一张security表配合SELinux做强制访问控制绝大多数人碰不到。你只要把filter和nat这两张表吃透日常运维基本就够用了。数据包进出服务器会经过五条内置的链chain。INPUT链管发往本机的包OUTPUT链管本机发出的包FORWARD链管经过本机转发、但目的地不是本机的包。还有两条链挂在更早和更晚的位置PREROUTING在路由决策之前处理包POSTROUTING在包即将离开网卡之前处理包。这两条链主要配合NAT使用后面做端口转发时会用到。为了方便记忆我把表和链的对应关系列成了一张表。表是功能维度链是路径维度两者交叉不是所有链在所有表里都存在表关联的内置链主要职责filterINPUT、FORWARD、OUTPUT数据包过滤放行或丢弃natPREROUTING、INPUT、OUTPUT、POSTROUTING源地址转换SNAT、目的地址转换DNATmangle全部五条链修改数据包标志、TTL、TOSrawPREROUTING、OUTPUT关闭连接跟踪securityINPUT、FORWARD、OUTPUTSELinux强制访问控制链路顺序这么记就行外部新连接进来先过PREROUTING内核然后做路由决策——目的地是本机就进INPUT本机处理后从OUTPUT发出目的地不是本机、但内核开了转发就走FORWARD直接出去。不管走哪条路离开网卡前都会经过POSTROUTING。如果涉及NATDNAT改的是包的目的地址所以放在路由决策前的PREROUTINGSNAT改的是包的源地址所以放在离开前的POSTROUTING。这个位置关系是死规定反过来配就完全失效。1.2 规则的从上到下匹配逻辑链不是一块铁板它是一排规则组成的队列。数据包到了这条链从上往下挨个比对一旦某条规则匹配就执行规则指定的动作target而且不再继续看后面剩下的规则。这就意味着iptables对规则的顺序极其敏感先放行特定来源的SSH再把其他人统统拒绝这两条规则如果不按顺序放白名单就完全失效因为重排序之后数据包先撞上DROP规则后面的ACCEPT根本轮不到。这里有两个常见的命令误区。第一个是-I和-A的区别-I默认把规则插到链的最前面-A则是追加到末尾。很多人想给已有的规则表补一条白名单随手敲了-I结果新规则插到了第一条把原本规划好的匹配顺序全打乱了。第二个是DROP和REJECT的区别DROP是把包悄悄扔掉对方等不到任何响应只能自己超时REJECT则是明确回一个类似目标不可达的错误包对方立刻就能感知。安全防护上DROP更隐蔽排错调试时REJECT更好用两者各有各的场合。顺带提一下自定义链。当规则多到几十条时主链会变得又臭又长你可以建一条自定义链把某一类规则比如所有来自恶意IP段的处理集中放进去在主链上用一条-j跳转规则引过去处理完再RETURN回来。它的作用类似门禁总闸后面分几个独立检查岗让整体结构清晰很多。2. 日常运维最常用的 iptables 命令2.1 查看、添加、删除规则的正确姿势先说查看。iptables -L -n -v --line-numbers是我每条命令里最常用的一条。-L列出规则-n不做域名反解不然会卡在DNS查询上-v显示每条规则的命中计数--line-numbers把规则从上到下编号。命中计数的价值非常大它直接告诉你流量有没有走到这条规则的管辖范围。我排查端口明明放行了但服务连不上时第一件事就是看对应规则下的计数器——如果计数是0说明包根本没有走到这条规则问题要么在更上游要么链路压根不经过本机这能迅速把排查范围缩小一大截。iptables -S也是宝贝它会把当前生效的规则按原始命令行格式打印出来。这个输出可以直接用来做备份、对比和回滚比看-L的表格化输出直观得多。查看的时候加上-t nat、-t mangle就能看对应表比如iptables -t nat -L -n。添加规则最常用的是-A追加和-I插入用法分别长这样iptables -A INPUT -p tcp --dport 22 -j ACCEPT iptables -I INPUT 2 -p tcp --dport 8080 -j ACCEPT第一条把SSH放行追加到INPUT链末尾第二条把8080端口的规则插到INPUT链的第2位。删除规则有两种方式一种按行号删一种按内容删我实际中用行号删更多因为省事且不容易误删iptables -D INPUT 3 iptables -D INPUT -p tcp --dport 22 -j ACCEPT-F清空规则也是高危操作。默认的iptables -F只清filter表你要是想清nat表必须写iptables -t nat -F。很多人图方便直接iptables -F一把梭结果发现NAT映射没消失其实是清错表了顺手就把容器网络或者NAT上网服务给搞挂了。规则里常用的匹配条件我整理成了表新建规则时对照着看就不容易漏参数匹配条件含义示例-s源地址/源网段-s 10.0.0.0/8-d目的地址/目的网段-d 203.0.113.10-p协议tcp/udp/icmp-p tcp--dport目的端口需配合-p--dport 443--sport源端口--sport 1024:-i从哪个网卡进来-i eth0-o从哪个网卡出去-o eth0-m state --state连接状态-m state --state ESTABLISHED,RELATED2.2 保存规则别让重启回到解放前我接手过的服务器里十台有八台出过规则配好了重启全没了的事故。原因很简单iptables命令改的是内核内存里的运行期规则断电或重启后内核一重新加载规则文件没被恢复你辛辛苦苦敲的命令等于白敲。Linux发行版上保存规则有各自的姿势。Debian/Ubuntu系装上iptables-persistent后执行netfilter-persistent save规则会写进/etc/iptables/rules.v4IPv6是rules.v6。Kylin/CentOS 7及以上默认防火墙服务是firewalld不是iptables服务你如果希望继续用iptables管理需要先把firewalld停掉并禁用再安装iptables-services然后把iptables服务enablesystemctl stop firewalld systemctl disable firewalld yum install -y iptables-services systemctl enable iptables systemctl start iptables我个人的习惯是无论哪种发行版都手动执行iptables-save /etc/iptables/rules.v4做快照再用iptables-restore /etc/iptables/rules.v4恢复。这样规则文件在明面上既能用git做版本管理也能在出问题时diff对比两个版本之间到底改了什么。注意iptables-restore对语法要求很严格手工编辑文件时千万别随手改格式。3. 四个真实场景下的配置实战3.1 给Web服务器只开80和443最基础也最刚需的场景是一台公网Web服务器我只希望外面的人能访问80和443其余端口的入站连接一律拒绝。先坐实默认策略再把需要放行的例外列出来这套配置顺序几乎是铁律# 1. 设置默认策略入站和转发默认丢弃出站默认放行 iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT ACCEPT # 2. 回环接口必须放行否则本机访问本机服务都会出问题 iptables -A INPUT -i lo -j ACCEPT # 3. 放行已建立连接及其相关连接否则出方向的连接回包全被拦 iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # 4. 放行 Web 服务端口 iptables -A INPUT -p tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp --dport 443 -j ACCEPT新手最容易犯的错误是把-P INPUT DROP放在最后执行。你想想默认策略一旦改成DROP而你还有放行SSH、放行established这些规则没来得及加下一次SSH长连接的保活包就再也进不来远程会话直接断掉。正确顺序是先放行回环、放行SSH如果走远程、放行ESTABLISHED,RELATED最后才把默认策略改成DROP。有些场景还需要额外考虑想ping通服务器就加一条iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT如果服务器跑着DHCP客户端记得放行-p udp --dport 67:68。这些细节不配好小问题排查起来特别闹心。3.2 做一张白名单优先的访问控制表什么叫白名单优先就是先把可信来源放行再用一条兜底DROP把其他人拦在外面。举个例子我只允许内网10.0.0.0/8网段的机器SSH登录其余来源想都别想iptables -A INPUT -p tcp --dport 22 -s 10.0.0.0/8 -j ACCEPT iptables -A INPUT -p tcp --dport 22 -j DROP这两条规则的顺序绝对不能反。先加ACCEPT再追加DROP匹配顺序天然正确如果反着写所有SSH连接都会先撞上DROP白名单形同虚设。这个放行靠前、拒绝兜底的原则是所有访问控制规则设计的基本盘。黑名单场景更简单一条iptables -A INPUT -s 203.0.113.7 -j DROP就能把某个IP拉黑。但生产环境里病毒扫描源、攻击者IP往往动辄几百上千个一条条写规则就不现实了。这种时候我强烈建议用ipset把IP集合管起来ipset create blacklist hash:ip ipset add blacklist 203.0.113.7 ipset add blacklist 203.0.113.0/24 iptables -A INPUT -m set --match-set blacklist src -j DROPipset用哈希表存IP集合一条iptables规则引用整个集合几万条恶意IP也不会拖慢匹配速度比几千条规则硬叠出来性能好一个量级。需要更新名单时只管往集合里增删不用碰iptables规则本体。3.3 用DNAT做端口转发端口转发是NAT表最经典的应用。举个例子内网Web服务器192.168.1.10:80外网客户通过防火墙公网IP203.0.113.10的8080端口来访问需要用DNAT把目的地址改写掉。第一步确认内核开了IP转发否则FORWARD链路根本走不通sysctl -w net.ipv4.ip_forward1 # 持久化写入 /etc/sysctl.conf然后是三条核心配置# 把目标为公网IP 8080端口的包目的地址改写为内网Web iptables -t nat -A PREROUTING -d 203.0.113.10 -p tcp --dport 8080 -j DNAT --to-destination 192.168.1.10:80 # 让内网Web回包时能正确返回客户端把源地址改成防火墙内网IP iptables -t nat -A POSTROUTING -d 192.168.1.10 -p tcp --dport 80 -j SNAT --to-source 192.168.1.1 # 放行FORWARD链路到内网Web的流量 iptables -A FORWARD -d 192.168.1.10 -p tcp --dport 80 -j ACCEPT很多人配DNAT时只记得PREROUTING那一条回包却不改源地址结果内网服务器回包时的源地址是客户端的公网IP它根本没有路由返回去连接就断了。所以POSTROUTING这条SNAT是灵魂步骤。如果防火墙出口IP不固定干脆用MASQUERADE自动取出口网卡当前IPiptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE这里我多说一句MASQUERADE在家庭和企业NAT上网场景里非常常见它把内网机器的源地址统一替换成出口IP正常网络管理中完全合规。配转发规则时始终记住一点——你改的不是单方向的包而是整条双向连接的回程路径少任何一半连接都建不起来。3.4 用limit模块给SSH加保险暴力破解SSH口令是最常见的网络攻击之一。除了换高位端口、禁用密码登录这些常规操作iptables自带的limit模块也能承担一部分流量压制任务# 只对新建连接限速不限制已建立的连接 iptables -A INPUT -p tcp --dport 22 -m state --state NEW \ -m limit --limit 5/minute --limit-burst 5 -j ACCEPT # 超过限制的新建连接直接丢弃 iptables -A INPUT -p tcp --dport 22 -m state --state NEW -j DROP--limit 5/minute表示长期平均允许每分钟5个新连接--limit-burst 5表示初始突发容量是5个。通俗讲桶里先装了5个令牌前5个新连接立刻放行之后每放行一个就消耗一个令牌令牌按每分钟5个的速度补充。效果就是正常运维的几台机器不受影响但攻击脚本大批量建立半开连接时会被压制住。需要注意limit模块只对新连接建立速率做限制真正的分布式暴力破解靠它挡不住完整方案应该是fail2ban做动态封禁、云安全组做外围过滤、SSH本身换成密钥登录。iptables的limit适合做第一道闸门不能当唯一防线。4. 这些坑我替你踩过了4.1 iptables 报 nat 表初始化失败怎么排查这个报错堪称iptables搜索词里的顶流原文长这样iptables v1.8.9 (legacy): cant initialize iptables table nat: table does not exist (do you need to insmod?)看到这个报错思维要按顺序走三条路。第一内核没加载nat相关模块。检查方法看/proc/net/ip_tables_names这个文件里面如果只有filter没有nat说明nat表没注册。试着手动加载模块modprobe nf_nat modprobe nf_conntrack加载完再执行iptables -t nat -L -n多半就正常了。第二发行版默认用的是nftables内核API而你的iptables命令是legacy后端。新系统里存在两套iptables实现legacy通过传统ip_tables内核模块工作nft通过nftables框架工作。可以用update-alternatives --config iptables查看当前指向切换到另一套试试。很多老脚本写死了legacy用法换了新系统后某个表就不被支持报错就是这个。第三内核编译时把Netfilter的NAT支持裁剪掉了。这种情况多出现在嵌入式设备或精简内核上软件层面再怎么折腾都白搭。拿这个报错的排查顺序总结一下先看内核模块有没有再看命令后端是不是对不上最后才怀疑内核裁剪。模块问题占了大多数别一上来就重装系统。4.2 重启后规则全部丢失一度以为被入侵我帮人排查过一台服务器头天配好了全部防火墙规则第二天早上起来全没了。第一反应是遭贼了看了一圈日志才发现压根没人动过纯粹是规则没有持久化。这台机器用的是Debian只装了iptables没装iptables-persistent我上次用systemctl enable iptables的习惯在他那儿根本没生效。这事的根源在发行版演进。CentOS 6那会儿service iptables save是标配规则自动存到/etc/sysconfig/iptables。CentOS 7之后默认防火墙换成了firewalldiptables服务不再是默认启用的服务了很多从老系统过来的运维没意识到这一点规则一写就丢。正确的永久化方案我放在上面2.2节了这里只提醒三个检查点第一iptables-save /etc/iptables/rules.v4是否执行过第二系统里有没有对应的systemd服务且处于启用状态第三如果是容器场景宿主机和容器各自有自己的network namespace规则要配到对应的命名空间里才有效。4.3 Docker 与 iptables 的相爱相杀一台机器上同时跑Docker和自定义iptables规则属于生产环境的高发事故区。Docker daemon启动时会直接操作iptables在nat表加MASQUERADE规则做NAT上网在filter表创建DOCKER链、改FORWARD链策略还会一路加规则进去。你要是自己也维护一套iptables规则两者很容易互相踩踏。最常见的症状你为了收紧安全策略把FORWARD链默认策略改成DROP结果Docker容器之间、容器访问外网全部瘫痪或者你执行了iptables -F清空规则Docker的端口映射全失效重启Docker后才恢复。如果你的机器上跑着Docker自定义防火墙规则有两条基本原则。第一不要随意清空FORWARD整条链尤其别碰名字里带DOCKER的链。第二新版Docker专门给你留了DOCKER-USER链这是用户自定义规则的官方位置Docker自己不会动这条链的规则你写在这儿的限制规则会优先生效。看上去就像系统管理员进小区门禁后在Docker家的门前又加了一道自己管辖的检查岗两边职责互不干扰。4.4 误封自己的SSH怎么自救配防火墙把自己锁在门外的经历我相信不少人都有过。真遇到这种事先深呼吸然后按优先级自救。第一优先级是云控制台。大多数云厂商都提供VNC登录或带外管理相当于跑到服务器屏幕前操作登录后执行iptables -F清空规则即可。没有云控制台也没关系用定时炸弹法提前在本地计划任务里放一条命令等两分钟后自动恢复给自己留出足够操作窗口echo iptables -F | at now 2 minutes或者写一个后台脚本sleep 60秒后清空规则#!/usr/bin/env bash sleep 60 iptables -F把它放到后台运行然后不慌不忙地调试你真正想加的规则。但最重要的还是预防。我的习惯是每次改规则前先备份iptables-save /backup/iptables.$(date %F_%H%M).save改完立刻开第二个SSH窗口做连接测试确认无问题后再继续改下一条。一次只改一条拒绝一口气贴十行这类操作。这套习惯养成后被锁在门外的概率会降到极低。5. 生产环境里的几条个人经验5.1 把规则当代码管别当临时命令iptables规则本质上是一份可版本化的配置文件但太多人把它当成敲一次就完事的临时命令导致出了问题无法回溯无法对比也无法快速还原。我的做法是所有规则收敛到一个脚本文件里每次改动都用git留下记录。脚本大致长这样#!/usr/bin/env bash # 统一应用本机iptables规则仅演示按环境调整网段和端口 set -e # 1. 清空现有规则 iptables -F iptables -X iptables -t nat -F iptables -t nat -X # 2. 设置默认策略 iptables -P INPUT DROP iptables -P FORWARD DROP iptables -P OUTPUT ACCEPT # 3. 回环和已建立连接 iptables -A INPUT -i lo -j ACCEPT iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # 4. 管理网段SSH白名单 iptables -A INPUT -p tcp --dport 22 -s 10.20.0.0/16 -j ACCEPT # 5. Web服务 iptables -A INPUT -p tcp --dport 80 -j ACCEPT iptables -A INPUT -p tcp --dport 443 -j ACCEPT # 6. 其他未放行的请求统一拒绝便于第一时间发现异常 iptables -A INPUT -j REJECT --reject-with icmp-host-prohibited比脚本更推荐的是用iptables-restore。把规则整理成纯文本*filter :INPUT DROP [0:0] :FORWARD DROP [0:0] :OUTPUT ACCEPT [0:0] -A INPUT -i lo -j ACCEPT -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT -A INPUT -p tcp --dport 22 -s 10.20.0.0/16 -j ACCEPT -A INPUT -p tcp --dport 80 -j ACCEPT -A INPUT -j REJECT --reject-with icmp-host-prohibited COMMIT然后iptables-restore rules.v4一次性加载。好处是格式整齐、支持注释、diff起来直观出问题时一行命令就能回滚到上一个版本。5.2 记录日志和审计思路防火墙不能只会挡还得会记录。iptables的LOG target能把命中的包写进内核日志适合拿来做攻击行为审计。典型组合是先LOG再DROPiptables -A INPUT -s 203.0.113.0/24 -j LOG --log-prefix IPT-BLOCK --log-level 4 iptables -A INPUT -s 203.0.113.0/24 -j DROP第一条LOG把来源IP、端口、协议记下来第二条才真正丢包。顺序反了LOG就永远不触发因为包已经被丢了。日志默认送进syslog量大时可以单独配置输出文件比如在rsyslog里按syslog facility和level过滤出来写到/var/log/iptables.log。这里有个坑LOG规则不能不加限制地乱挂。攻击扫描的包成千上万每命中一次就写一行日志磁盘容量会被日志瞬间淹没。生产环境给LOG本身也加上限速是个好习惯iptables -A INPUT -m limit --limit 10/minute --limit-burst 20 -j LOG --log-prefix IPT-BLOCK 还有一种更稳妥的审计思路先在疑似的恶意IP上放LOG但不DROP观察几天流量规律确认无误后再改成DROP。这样既不误伤又能留证据。排查时直接grep IPT-BLOCK /var/log/iptables.log | awk {print $3} | sort | uniq -c | sort -nr看哪个IP被拦得最凶思路一下清晰了。5.3 防火墙到底能不能关防火墙关闭有影响吗这个问题年度搜索量相当可观。会问这个的人大多遇到了软件连不上、服务无法访问、开发工具报错的场景想通过关防火墙一了百了。我的观点很直接能不开的别乱开能不全关的别全关。开发调试阶段临时把防火墙关了确实能加速定位问题但记得调试完恢复。生产环境关防火墙裸奔等于把家门钥匙挂在门外迟早出事。很多关了防火墙就能连上的问题本质是端口或者进程没放行而不是防火墙本身没价值。你放行具体端口、来源IP段效果和关防火墙一样风险却小得多。遇到服务连不上我的排查顺序固定是服务本身有没有监听、监听在哪个地址、进程有没有绑定端口、防火墙规则有没有命中、是不是路由或安全组拦截。一上来就iptables -F是最懒也最危险的做法。同样在Windows上装开发工具被提示关防火墙时正确做法是给对应进程或端口加出入站白名单而不是整个把防火墙关掉。跟iptables打了这些年交道我最深的体会是它不复杂但非常讲究秩序。规则从上到下生效改之前先看现状动之前先备份现状遇到报错先看内核模块再看命令后端。把它当成代码来管当成生产环境的基础设施来敬畏它就能成为你手里最顺手的那把刀而不是时不时跳出来咬你一口的坑。希望这篇能帮你少踩几个我踩过的坑。