恒美微站
首页
关于我们
建站服务
主题模板
案例展示
资讯中心
联系我们
Linux服务器IP访问控制实战:iptables与nftables规则配置解析
首页
资讯中心
/
Linux服务器IP访问控制实战:iptables与nftables规则配置解析
Linux服务器IP访问控制实战:iptables与nftables规则配置解析
发布时间:2026/10/4 2:48:24
在Linux服务器上做IP访问控制这个需求我前后经手了不下十次。每次都有几台服务器要封某个恶意IP、给办公网段放行SSH、或者限制某个应用只允许指定来源访问。标题里“禁止或允许指定IP的访问”看似简单但实际情况远比一条命令复杂规则写在哪个表、插在哪个位置、重启之后还在不在、会不会把自己锁在门外这些问题我都踩过。这篇文章就把我多年实操下来验证过的方案讲透从原理到命令再到排查适合刚接触Linux的运维新人也适合被这个问题反复折腾过的老手。1. 需求拆解与方案选型先想清楚再动手1.1 这个需求到底在解决什么问题“禁止或允许指定IP的访问”这句话拆开看至少要回答三个问题禁的是谁、禁的哪种访问、打算在哪一层禁。“禁的是谁”最容易理解就是源IP地址比如某个攻击者的IP、某个抓数据的爬虫IP、某个临时接入的陌生网段。“禁的哪种访问”就要区分了是干脆完全不能碰这台机器还是只不能访问某个端口、某个服务这两者的含义完全不同防火墙规则写起来也是两套思路。“在哪一层禁”是很多人忽略的关键点——Linux下面有网络层的iptables/nftables、有服务层面的TCP Wrapper/etc/hosts.allow和hosts.deny、还有Nginx或Apache这类应用层的deny配置。网络层拦截发生在数据包进入系统早期资源开销小、拦截范围广应用层拦截则能识别更细的信息比如URL路径、User-Agent但会让请求先到达服务进程消耗更多的CPU和内存。我见过不少同事一上来就写Nginx的deny结果发现爬虫根本不走Nginx或者直接打到其他端口也见过有人随手加一条iptables DROP规则把整个办公网段的正常开发和测试流量全断了。所以做访问控制之前先花两分钟确认这三个维度比急着敲命令有价值得多。以最常见的运维场景来说绝大多数需求都是“针对某个源IP或网段控制对SSH、Web、数据库等具体端口的访问”这种场景首选Linux内核自带的netfilter框架也就是iptables或它的继任者nftables。1.2 访问控制的三个层面搞清楚技术选型之前先认识一下Linux上做IP访问控制经常涉及的三个层面。第一个层面是网络层和传输层由netfilter在内核里实现对外暴露iptables、nftables、firewalld等工具。这一层的拦截点是所有网络数据包必经之路无论数据是发给Nginx、MySQL还是任何其他进程只要规则命中就会被丢弃或拒绝。这也是我最推荐的处理方式效率高、覆盖面广、不依赖具体业务进程。第二个层面是TCP Wrapper通过hosts.allow和hosts.deny两个文件控制对某些服务的访问。需要注意这个机制只对编译时链接了libwrap库的服务有效比如早期的telnet、部分版本的ssh而现在大部分服务都是独立实现网络逻辑不读这两个文件所以这个方案天然存在覆盖不全的问题我在现代发行版上基本不推荐依赖它。第三个层面是应用层面的访问控制Nginx有deny和allow指令Apache有mod_authz_hostSSH本身有AllowUsers、AllowGroupsMySQL有user表中的host字段。应用层控制粒度最细比如可以做到“只允许某个IP访问某个URL”但代价是请求已经进入业务栈没有网络层那么节省资源。合理的做法是分层配合网络层做粗粒度拦截应用层做细粒度控制而不是只用其中一层。1.3 工具选型对比iptables、firewalld、nftables、hosts.allowLinux下能完成IP访问控制的工具不少我把它们放一起做了个对比方便你按场景选择。工具底层框架特点适合场景主要缺点iptablesnetfilter规则直观、资料多、几乎所有Linux版本可用老系统、习惯命令行直接操作的运维场景规则成百上千后管理混乱被舍弃但长期维护firewalldnetfilter(动态)支持zone和rich-rule、支持运行时和永久配置分离RHEL/CentOS/Fedora等默认系统日常快速管理概念较多学习曲线比iptables略陡nftablesnetfilter语法更简洁、支持批量规则集、性能更好新安装的Debian 12/Ubuntu 22.10/RHEL 9资料相对少老教程多为iptables写法hosts.allow/hosts.denylibwrap配置文件简单、改完即时生效仅兼容libwrap的旧服务覆盖范围太小新服务基本不管用如果你正在用CentOS 7、Ubuntu 18.04这类还在支持期的老版本系统iptables依然是最稳的选择。如果你用的是RHEL 9、Ubuntu 22.04或者更新的系统我的建议是把nftables作为主要理解方向但因为兼容性和习惯原因不少人还是继续用iptables命令系统也会做兼容。我的经验是不要把工具之争上升到信仰层面先保障需求能解决再逐步向新工具平滑过渡。2. netfilter原理速览规则到底是怎么匹配的2.1 数据包进入Linux后的完整路径我一直跟新同事说理解iptables不用背所有细节只要图景清晰命令随手就能写出来。当一个数据包从网卡进入Linux系统后它会沿着一条固定路径移动先是网卡驱动收包进入内核协议栈然后经过路由决策决定这个包是发给本机的、还是要转发的。如果目标地址是本机数据包就会经过INPUT链最终交给对应的用户态进程比如sshd或nginx如果目标地址不是本机且开启了转发则经过FORWARD链继续转发。这个“本机包走INPUT、转发包走FORWARD”的区别非常关键。我遇到过不止一次在云服务器上明明用iptables放行了某个源IP的80端口访问但请求还是进不来查到最后发现服务跑在Docker容器里数据包先经过Docker的DNAT规则转到容器IP前面的INPUT规则根本没有参与“主角”的身份。这种场景的处理逻辑完全不同后面会专门聊。netfilter框架在这条路径上安排了多个钩子点每个点对应一条链数据包刚进来还没做路由决策时是PREROUTING发给本机时是INPUT本机发出时是OUTPUT转发时是FORWARD最后临出门前是POSTROUTING。你写“禁止或允许指定IP访问”时绝大多数情况下只需要关心INPUT链。2.2 四表五链别被吓到你只需要两条网上讲iptables都会提到“四表五链”这四个表分别是raw、mangle、nat、filter每条链可以挂多个表并且顺序固定。这个顺序我不建议死记只要记住一个原则做IP访问控制只需要filter表其余三个表各有专攻——raw表用来处理连接跟踪、mangle表用来修改数据包头、nat表用来做地址转换。filter表天然挂载在INPUT、FORWARD和OUTPUT三条链上我们绝大多数封禁和放行动作都发生在INPUT链上。比如下面这条命令iptables -A INPUT -s 192.168.1.100 -j DROP它的意思就是在filter表的INPUT链上追加一条规则如果源地址是192.168.1.100就直接丢弃。理解这条命令只需要知道三个部件-s指定源地址、-j指定动作、INPUT指定链。动作除了DROP还有REJECT两者的区别是DROP直接沉默丢掉数据包客户端表现像连接超时REJECT会回一个拒绝消息客户端会立刻看到“Connection refused”之类的报错。从隐蔽性角度抵御扫描时DROP更好因为它让攻击者难以判断端口是关了还是被防火墙拦了从排障方便角度REJECT更友好能快速区分规则拦截和网络不通。filter表最常用的动作还有ACCEPT就像它的名字一样直接放行。三段合一就构成了访问控制的最小完整逻辑先放行你信任的、再拒绝你不信任的、最后处理其他一切。2.3 规则匹配顺序为什么有时候“明明写了却没用”iptables规则是按插入位置从上到下逐条匹配的一条规则命中后除非动作明确不终止匹配否则数据包就不会再看后面的规则。这个特性引出了两个高频坑。第一个坑是“先DROP后ACCEPT等于没有ACCEPT”。如果INPUT链有这样的顺序iptables -A INPUT -s 192.168.1.100 -j DROP iptables -A INPUT -s 192.168.1.100 -p tcp --dport 22 -j ACCEPT那第二条放行规则永远不会执行因为第一条匹配到源IP就DROP了。想要“只封某IP的80端口、但允许它访问22端口”必须先写精确的端口规则、再写大范围的封禁规则顺序反过来就是给自己挖坑。第二个坑是“默认策略”的作用。iptables链的策略policy是所有规则都没匹配时兜底生效的策略是ACCEPT那就放行策略是DROP那就拒绝。很多安全加固教程会让你把INPUT默认策略改成DROP这种模式下你必须在前面显式放行SSH、80、443等必要端口否则重启规则后你就彻底连不上了。我见过有同学照着教程执行到最后回车一敲远程会话立刻卡死——因为默认策略改成DROP后已有的ESTABLISHED连接没放行SSH被自己砍了。在规则顺序上我总结了一套稳定可靠的习惯也是之后所有命令组合的基础在顶部先放行所有已建立连接再放行明确信任的源IP然后拒绝不想要的最后兜底策略。3. iptables实操禁用/放行指定IP的完整命令3.1 最常用命令拆解先看最基础的三条命令它们覆盖了80%的日常需求。# 禁止指定IP访问本机所有服务 iptables -I INPUT -s 192.168.1.100 -j DROP # 允许指定IP访问本机SSH服务 iptables -I INPUT -s 192.168.1.0/24 -p tcp --dport 22 -j ACCEPT # 放行已建立的连接和关联连接 iptables -I INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT逐条解释。第一条用了-I参数表示把规则插入到链的最前面这个细节很多人不留意后面会讲为什么必须插前面。第二条用-p tcp限定协议、--dport 22限定端口再加上-s指定的源网段组合起来就是“只允许192.168.1.0/24网段访问22端口”这是做SSH白名单最标准的写法。第三条是护身符不放行ESTABLISHED状态的话你主动出去的连接返回的数据包可能被视为新连接被拦掉表现就是“能连出去但收不到响应”这条规则应该常年待在规则列表最顶部。不少教程里还会用到-s参数配合!来排除比如“不允许某个IP但其他都允许”写成iptables -A INPUT -s ! 192.168.1.100 -j ACCEPT我实际工作中会用得比这个少因为否定匹配很容易让人读规则时产生误解而且如果后面还叠加其他规则排查难度翻倍。我倾向于把所有允许的都摆在前面让人一眼能看出白名单末尾统一拒绝这种规则的“可读性”比“精简性”重要得多。3.2 三个高频业务场景的命令组合场景一SSH服务只允许办公网段访问。这算是最常见也最刚需的访问控制。完整做下来建议按这个顺序执行# 先备份现有规则出问题能回滚 iptables-save /root/iptables-backup-$(date %F).rules # 放行已建立的连接 iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT # 放行办公网段访问SSH iptables -A INPUT -s 192.168.10.0/24 -p tcp --dport 22 -j ACCEPT # 放行本机回环接口很多服务之间的本地通信依赖它 iptables -A INPUT -i lo -j ACCEPT # 把所有其他INPUT流量默认丢弃 iptables -P INPUT DROP这套组合的关键在于顺序。默认策略DROP放在最后一步执行前面所有ACCEPT都准备好之后才“关门”。如果把DROP提前你后面再添加ACCEPT时会有一瞬间的断连风险我实际操作中尽量避免这种窗口。另外记得加回环接口放行曾经有个同事没放行lo口数据库本地连接全部失败排查了很久才发现是这条规则的问题。场景二封禁某个恶意IP访问Web服务。生产环境的临时封禁我通常这样写# 封禁恶意IP访问80和443端口 iptables -I INPUT -s 203.0.113.8 -p tcp -m multiport --dport 80,443 -j DROP # 或者更严格的封禁该IP完全不能碰这台服务器 iptables -I INPUT -s 203.0.113.8 -j DROP如果只是防攻击者打网站用第一种就够了精确到端口可以减少误伤如果是扫描器、漏洞探测这类行为直接整机封禁更省心。用-I插入而不是-A追加是因为如果当前规则列表里已有其他ACCEPT规则覆盖了这个IP追加在后面的DROP永远匹配不上插到前面才能确保新规则最先生效。这个细节曾经让我排查了大半天现在写封禁命令时已经形成肌肉记忆。场景三临时放行某个IP做远程调试。常见于让合作伙伴联调或者临时给异地同事开权限# 放行指定IP访问MySQL端口只给1小时 iptables -I INPUT -s 203.0.113.55 -p tcp --dport 3306 -j ACCEPT # 一小时之后手动删除该规则 iptables -D INPUT -s 203.0.113.55 -p tcp --dport 3306 -j ACCEPT删除规则时最稳妥的方式是完整复制添加时的命令把-I改成-D这样能保证删除的规则与被删的规则参数完全一致。若只按行号删除比如先iptables -L -n --line-numbers再按编号删一旦期间有人动过规则编号就错位了很容易误删其他重要规则。3.3 规则持久化重启不丢在CentOS 6/7时代大家习惯把规则写到/etc/sysconfig/iptables然后通过iptables-save和iptables-restore来回导。到了systemd时代不同发行版做法略有差异。CentOS/RHEL 7及以上如果你装了iptables-services这个包可以用iptables-save /etc/sysconfig/iptables systemctl enable iptables systemctl restart iptablesDebian/Ubuntu上更常见的是安装iptables-persistentapt install -y iptables-persistent netfilter-persistent save netfilter-persistent reload这里我要提醒一个很多教程不讲的点用firewalld作为默认防火墙的发行版上直接用iptables命令添加的规则在firewalld reload后会被清掉。原因在于firewalld重启时会根据自身的配置重新生成本身管理的那套iptables/nftables规则你手动加的规则不在它的配置体系里自然就被冲掉了。解决办法有两个要么统一用firewalld的配置方式管理后面第四章详细讲要么确认系统firewalld是停止状态再用纯iptables规则。最怕的是同时开两个管理工具规则互相覆盖排查起来让人头大。4. firewalld与nftables新系统上的简单姿势4.1 firewalld的两种配置方式firewalld相比传统iptables一个很大的改进是区分“运行时规则”和“永久规则”。运行时规则立即生效、重启后消失适合临时改永久规则需要配合--permanent参数reload之后生效适合长期配置。初次接触的人最容易犯的错是加了--permanent就以为立刻生效结果发现连接还在实际是忘了reload。firewalld里做IP访问控制最直观的方式是富规则rich rule。允许和禁止指定IP分别是这样# 允许指定IP访问22端口 firewall-cmd --permanent --add-rich-rulerule familyipv4 source address192.168.10.50 port port22 protocoltcp accept # 禁止指定IP访问本机所有端口 firewall-cmd --permanent --add-rich-rulerule familyipv4 source address203.0.113.8 drop # 重载使永久规则生效 firewall-cmd --reload富规则的语法初看复杂其实结构固定family指定协议族、source address指定源地址、port和protocol指定端口协议、最后是动作。多规则之间支持逻辑组合比如限定指定源IP只能访问指定服务端口这在传统iptables里要写好几条富规则一句话就能表达清楚。还有一种方式是直接管理源和端口。比如把某IP加进信任区它就能访问几乎所有被放行的服务firewall-cmd --permanent --add-source192.168.10.0/24 --zonetrusted firewall-cmd --reload用zone做管理适合业务网段和公网IP分层级处理的场景。trusted区是白名单思路public区是默认对外区把不同来源的流量丢进不同zone比在一个链里堆几十条规则清晰得多。4.2 从iptables平滑迁移到nftables新版系统上netfilter的默认命令已经逐渐切换为nftRHEL 9、Debian 12、Ubuntu 22.04之后的版本都支持。nftables的语法和iptables差异不小但表达同样逻辑时其实更简洁。封禁和放行指定IP对应的nft命令大体长这样# 禁止指定IP nft add rule inet filter input ip saddr 203.0.113.8 drop # 允许指定IP访问22端口 nft add rule inet filter input ip saddr 192.168.10.50 tcp dport 22 acceptnftables里“表”和“链”需要提前创建在例子中假定已经存在一张inet族的filter表和input链。inet族是个非常实用的设计它同时覆盖IPv4和IPv6不需要像iptables时代那样ipv4写一套、ipv6再写一套。如果你接收到一台新装的Linux不确定默认链在哪可以先nft list ruleset看看当前规则集这是nftables的推荐调试方式和iptables-save的作用类似。从iptables迁移的路径上很多人会直接用iptables-nft这个兼容层命令照旧底层翻译成nftables。短期应急没问题但我还是会建议逐步把关键规则用nft原生语法重写因为兼容层的翻译有时会产生一些不易阅读的规则排障时增加理解成本。4.3 怎么选我的建议工具越来越多选型没必要纠结到失眠。根据我的实操经验给出如下建议如果服务器是CentOS 7/8、Ubuntu 20.04及更早版本默认iptables就行资料多、踩坑经验写出来的人也多如果是RHEL 9、Rocky Linux 9、Ubuntu 22.10以上系统直接学nftables一步到位如果你管理的是云主机且官方提供了安全组那么第一道防线放在云安全组上第二道防线再在系统里用firewalld或nftables做精细化控制多层校验才能避免单点失误。我个人的习惯是云上服务器优先用云平台的安全组做粗粒度白名单比如只放行办公网段访问SSH和管理面端口到了操作系统内部再用firewalld做细粒度的临时封禁和放行。云安全组相当于小区门口的保安防火墙规则相当于房间的门锁两层职责不同配合才是完整的访问控制方案。5. 常见问题与排查技巧实录5.1 封了没效果的五个原因第一条排查顺序这是最常见的坑。如果你用-A追加规则而规则列表里恰好已有一条同源IP的ACCEPT在它之前那么追加的DROP永远不会被匹配。用iptables -L -n --line-numbers查行号把DROP规则插入到匹配规则之前即可。我习惯所有封禁规则都优先用-I就是为了省这个心。第二条要确认封的是不是IP协议栈的另一面。很多服务器对外有IPv4地址但监控、内网管理也可能走IPv6。你用iptables只封了IPv4攻击者用IPv6地址一样能进来。解决方法是同步处理ip6tables或者在支持nftables的新系统上用inet族的表一网打尽。封禁前先检查服务器的IPv6启用情况和流量情况这个步骤很多人会漏。第三条是Docker等容器环境。Docker会自动向iptables的DOCKER链和FORWARD链注入规则而且容器端口映射依赖DNAT规则。只写INPUT链的规则往往拦不住到容器服务的流量因为流量已经在PREROUTING阶段被做了目的地址转换送达阶段根本不属于INPUT链的管辖范围。遇到容器场景需要操作DOCKER-USER链或者直接用Docker的发布端口控制、配一个前端Nginx做访问控制这些方式都比硬写iptables有效。第四条是服务自身先拒绝了连接。有些服务有独立的ACL机制比如MySQL的user表host字段、SSH的Match Address、Nginx的allow/deny指令。如果防火墙规则已放行但连接还是失败先查服务日志很可能请求在应用层被拦了防火墙规则看起来“没生效”其实是被冤枉的。第五条是规则被覆盖或清空。前面讲过的firewalld reload覆盖手工iptables规则就是典型。排查时观察规则是否还在iptables -L -n -v看一下规则计数器的Packet列如果长期为0说明这条规则从未匹配到任何包优先怀疑规则顺序或链不对。这是我每次排障必看的数据比猜来猜去快得多。5.2 误封自己被锁在门外的急救这个话题很痛但不得不讲。远程改防火墙规则最怕的就是把自己锁在门外。我见过有人在办公室远程操作生产服务器一条默认策略DROP命令发出去人立刻就慌了——机器还在但所有SSH连接都断了。这种情况下真正的急救路径有这么几条按便利程度排序第一如果服务器有带外管理如IPMI、iDRAC、云平台VNC通过它登录进去把规则改回来这是最可靠的方式第二如果是云主机云厂商控制台一般有网页终端相当于带外管理第三如果都没有只能物理接触机器了。所以平时就要把这些路径摸清楚别等出事再找。更实用的是提前预防。我常年养成的习惯是任何添加封禁规则的操作之后立刻用定时任务做自动回滚# 5分钟后自动删除该封禁规则作为误封保险 echo iptables -D INPUT -s 203.0.113.8 -j DROP | at now 5 minutes如果规则确实要长期保留5分钟内再手动取消这个定时任务或者用cron做一个更复杂的确认脚本就行。用at命令来回滚是我推荐给所有新人的“保命招”成本极低效果极大。另外建议每次批量变更前先iptables-save备份把备份文件命名带上日期出问题随时iptables-restore还原。有同行还习惯用screen或tmux开着会话里面保持一个root shell这样SSH断开后规则还能通过残留会话操作也是一个思路。5.3 排查工具箱连不通和没拦住分别怎么查判断“某个IP到底能不能访问某个端口”我不建议一上来就上防火墙命令先做网络连通性判断更清晰。在客户端机器上可以用nc或telnet测试端口通不通nc -zv 目标IP 22这种写法很直观返回success说明网络层没问题telnet目标IP 22连上再断开则说明TCP握手成功。ping可以测基础连通性但很容易被防火墙策略干扰所以ping通不等于端口通、ping不通也不代表服务有故障。如果确认端口连不通回到服务器上按这个顺序排查先iptables -L -n -v看规则顺序和计数器再把默认策略、相关链的规则都过一遍然后iptables-save看完整规则集检查有没有冲突接着看服务是否监听在正确端口ss -lntp输出里主动监听列表会告诉你一切最后看服务日志很多被应用层拒绝的连接在日志里有明确记录。反过来如果“没拦住”比如明明加了DROP规则但IP还能访问优先查三件事规则是否在正确链上INPUT还是FORWARD、是否存在更靠前的ACCEPT规则、IPv6规则是否漏了。另外还有一种隐蔽情况是IP冲突局域网内别的机器占用了目标IP从你的服务器看流量行为就会异常混乱。排查IP冲突可以arping目标IP看是否有多个MAC响应这是网络环境里很容易被忽略的因素。5.4 易错点速查表把我在实际项目中碰到的高频问题整理成一张表贴在服务器上或者收藏起来写规则之前过一遍能少踩很多坑。易错点现象正确做法使用-A追加封禁规则封禁不生效封禁规则优先使用-I插入到顶部只封IPv4不封IPv6攻击者改用IPv6访问成功同步配置ip6tables或使用inet族表默认策略改为DROP后未放行现有连接SSH立刻断开先加ESTABLISHED,RELATED放行规则最后再改默认策略在firewalld环境手工加iptables规则重启或reload后规则丢失使用firewalld的永久规则reload管理容器服务只改INPUT链Docker端口映射流量依旧放行使用DOCKER-USER链或应用层访问控制忘记放行lo回环接口本机服务之间连接异常添加-i lo -j ACCEPT规则规则参数错误导致删除语句不匹配删除规则失败或误删其他规则复制添加命令仅将-I改为-D最后分享一个我个人的体会访问控制最终拼的不是命令记得多熟而是对数据包路径的理解、对规则顺序的敬畏、以及一套稳定的操作习惯。每次变更之前做好备份和回滚预案变更之后立刻验证连通性这两步做到了防火墙这块基本稳了。这套方法论我用了好几年不管在自建机房还是云环境都没出过大岔子。