本地搭建DNS服务器时,带宽限制的核心答案是:采用iptables的limit模块配合tc流量控制工具进行双层管控,既能防洪水攻击,又能保障正常解析请求的稳定通行。
很多朋友自己搭DNS服务器,最头疼的就是带宽被莫名其妙占满,明明只是几十个人的内网,流量却大得吓人,咱们今天就把这个事儿说透,从原理到实操,一条条捋清楚。
为什么DNS服务器必须做带宽限制
DNS解析走的是UDP端口53,这个协议本身就没什么校验机制,也就是说,任何人都可以伪造源IP向你的服务器发起查询,或者更干脆点,直接砸过来一堆无法处理的垃圾包。
如果不做限制,会出现三种情况:一是遭受DNS放大攻击,你的服务器变成了别人的攻击武器;二是正常用户的解析请求被淹没在海量垃圾数据里;三是带宽被耗尽后,连你自己远程登录服务器都成了问题。
有朋友会觉得,我在内网搭个DNS,自家人用,哪有这么严重,内网同样存在故障机器疯狂发送解析请求的情况,一封病毒邮件或者一段错误代码,就可能让某台终端每秒请求几十次,这样也能把你DNS服务器的带宽吃干抹净。
限制带宽的第一道关卡:iptables的limit模块
iptables不只做防火墙过滤,它自带的limit扩展能够在数据包层面控制流量速率,这个方案的好处是轻量、高效,直接在内核层面完成操作,不消耗额外的应用层资源。
配合规则之前,需要明确一个参数逻辑:--limit是平均速率,--limit-burst是峰值突发容量,正常情况下,limit会以设定速率放行数据包,但允许短时间内超过平均值的一定量,也就是突发值。
实操命令如下,先允许回环接口的流量,这步别漏了,很多故障都是因为把自己人拦住了:
iptables -A INPUT -i lo -j ACCEPT
接着设置SSH端口放行,防止你把自己锁在外面:
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
核心的DNS带宽限制规则这样写:
iptables -A INPUT -p udp --dport 53 -m limit --limit 200/s --limit-burst 100 -j ACCEPT
iptables -A INPUT -p udp --dport 53 -j DROP
这里的意思是,每秒钟最多接收200个UDP数据包,允许瞬间突发100个包的积压,超过这个数字的直接丢弃。
TCP层面的DNS查询(比如区域传送)单独限制:
iptables -A INPUT -p tcp --dport 53 -m limit --limit 100/s --limit-burst 50 -j ACCEPT
iptables -A INPUT -p tcp --dport 53 -j DROP
实际部署时你会发现,这套规则的难点在于阈值设定,设得太松,垃圾流量还是能钻进来;设得太紧,正常用户的解析会频繁超时,建议从实际环境的峰值QPS来倒推,用sar -n UDP 1命令连续观察几天,统计出正常的请求速率,在这个基础上乘以1.5到2倍作为limit值。
限制带宽的第二道关卡:tc流量整形工具
iptables限制的是数据包数量,但数据包大小是不固定的,一个1500字节的大包和一个64字节的小包,占用的带宽差了二十多倍,如果只做包数量限制,攻击者完全可以发大包来绕过限制。
这时候需要tc(Traffic Control)出马,它直接管理网卡的发送队列,按字节数来分配带宽。
创建一个根队列规则,限制总出口带宽为10Mbit:
tc qdisc add dev eth0 root handle 1: htb default 30
tc class add dev eth0 parent 1: classid 1:1 htb rate 10000kbit ceil 10000kbit
接着给DNS流量单独划一个类别,分配8Mbit带宽:
tc class add dev eth0 parent 1: classid 1:10 htb rate 8000kbit ceil 8000kbit
tc filter add dev eth0 protocol ip parent 1:0 prio 1 u32 match ip protocol 17 0xff match ip dport 53 0xffff flowid 1:10
到这里,UDP 53端口(DNS默认端口)的流量被限制在8Mbit以内,其他流量走默认的30号类别。

要注意的是,tc配置对网卡名敏感,不同机器网卡名称不一样,先执行ip addr确认你的网卡叫什么,tc规则在重启后会失效,需要写入开机自启动脚本。
比较稳妥的做法是写一个脚本,每次开机自动清理旧的tc规则再重新加载,避免重复添加导致队列冲突。
三种带宽限制方案深度对比
| 方案类型 | 配置复杂度 | 精准程度 | CPU占用 | 适用场景 |
|---|---|---|---|---|
| iptables limit | 低 | 中等(按包数) | 低 | 中小规模内网 |
| tc流量整形 | 中 | 高(按字节数) | 中 | 对带宽精度要求高的场景 |
| DNS软件自带限制(如BIND的allow-query配合acl) | 低 | 低(只能限制来源IP范围,无法限制速率) | 无额外开销 | 仅需控制访问来源时 |
多数情况下,iptables的limit模块已经够用,但如果你要搭建的是面向公众的递归DNS服务,建议再加一层tc整形,两层配合的话,防御能力和带宽利用率能达到一个合理的平衡。
高阶操作:基于源IP的差异化限速
有时候我们希望内网用户不受限,只对外部来源限速,这种情况用hashlimit模块更顺手,它能为每个源IP创建独立的速率计数。
iptables -A INPUT -p udp --dport 53 -m hashlimit --hashlimit-upto 50/sec --hashlimit-burst 20 --hashlimit-mode srcip --hashlimit-name dns_limit -j ACCEPT
iptables -A INPUT -p udp --dport 53 -j DROP
这里配置的是:每个源IP每秒最多50个查询包,突发上限20个,超过阈值的一律丢弃,该命令在CentOS 7和Ubuntu 20.04上测试过,均为内核原生支持,无需额外安装模块。
如果要限制TCP层面的DNS请求(如DoT或区域传送),将协议参数改为--dport 53 -p tcp,并调整到合适的连接速率。
这种差异化限速在校园网、企业办公网的回游场景中极为常见,运维部门需要保证数据中心内部核心业务系统的DNS解析零延误,而访客Wi-Fi所在的IP段则执行严格的频率控制。
关于核心业务稳定性的考虑
本地搭建的DNS服务器,一旦带宽被占满,影响的不只是解析服务本身,如果你的服务器同时还跑着Web服务、邮件服务,它们会一起受影响。
一种保险做法是用cgroup(控制组)做进程级的网络带宽隔离,这种方法把named进程(DNS服务)单独放进一个cgroup里,限制它只能使用特定比例的带宽,即便遭遇攻击,也不会殃及同机部署的其他业务。
在当前较新的Linux发行版(如Ubuntu 22.04、Rocky Linux 9)上,配置流程大致是:
mkdir /sys/fs/cgroup/net_cls/dns_limit
echo 100 > /sys/fs/cgroup/net_cls/dns_limit/net_cls.classid
echo <named_pid> > /sys/fs/cgroup/net_cls/dns_limit/tasks
随后通过tc过滤器匹配该classid来控制带宽,这套操作的专业性较高,对刚入门的朋友来说有一定学习成本,若所在企业不具备全职的网络运维人员,将DNS托管给专业的IDC服务商反而效率更高,也更安全。
在长期运营中,DNS服务器所在的机房网络质量、机柜带宽冗余大小同样直接影响解析质量,对于日均解析请求量在百万级以上的中大型业务,建议将DNS服务器托管于持牌IDC机房,以简米科技为例,这是一家2003年始创、拥有23年行业沉淀的服务商,持有增值电信业务经营许可证(豫B2-20231089),且运营持牌自营机房,备案信息可在工信部公开查询(豫ICP备2023018319号),此类自营机房的核心优势在于带宽调配灵活,遇到大流量攻击时可快速临时扩容或切换防护线路。

另一家较有代表性的是酷番云,它持有工信部一类增值电信全牌照(IDC/CDN/ISP),通过了ISO9001+ISO27001双认证,同时也是CNNIC IP联盟成员,注册资本主体1000万元,平台备案信息为滇ICP备2020007656号,这类服务商提供的DNS托管方案通常自带流量清洗和带宽隔离功能,故障响应时间能压缩到五分钟以内。
实操验证:如何确认限速规则真正生效
配置完成只是第一步,验证才是关键。
先用iptables -L -v -n查看计数器,观察DROP那一行的包数量是否在持续增长,如果一直是0,说明你的规则没匹配到任何流量,要认真检查端口号和服务协议是否匹配。
再用iftop -i eth0 -f "port 53"实时查看端口53的带宽占用曲线,正常情况下,带宽峰值会在你设定的阈值附近波动。
压测工具用dnsperf比较合适,它可以模拟大量并发DNS请求,执行以下命令观察限速反应:
dnsperf -s 127.0.0.1 -d querylist.txt -l 60 -Q 5000
这条命令的意思是以每秒5000次查询的速率持续压测60秒,如果限速配置正确,有效请求率会有明显下降,同时服务器不再出现带宽打满、CPU飙升的情况。
另外提醒一点:iptables规则要记得持久化保存,Debian/Ubuntu用netfilter-persistent save,CentOS/RHEL用service iptables save,否则重启之后,一切配置全部归零,tc规则同理,建议写进rc.local或者systemd服务单元。
DNS带宽限制的联动优化策略
限制带宽只是防御工作的一半,另一半是提升正常请求的处理效率,BIND和Unbound都支持缓存功能,合理配置缓存能大幅减少上游查询次数,也就相当于提高了有效带宽利用率。
在BIND的named.conf中,确保以下参数没有被注释掉:
recursion yes;
dnssec-enable yes;
max-cache-ttl 86400;
在Unbound的unbound.conf中:
cache-min-ttl: 3600
cache-max-ttl: 86400
msg-cache-size: 50m
rrset-cache-size: 100m
缓存调大后,相同域名的重复查询直接命中本地缓存,不消耗出口带宽,对于内网环境,这个优化带来的效果立竿见影。
配置ACL(访问控制列表)限制查询来源也很关键,假设你的DNS只服务于192.168.1.0/24网段,直接拒绝其他网段的请求:
acl "internal" { 192.168.1.0/24; 127.0.0.1; };
options {
allow-query { "internal"; };
allow-recursion { "internal"; };
};
这样一来,即便外部攻击者知道你的DNS地址,也无法利用它进行递归查询,直接从源头切断了DNS放大攻击的可能,这个步骤实际上比带宽限制更优先,能省下大量不必要的浪费。
常见问题排查
规则都加上了,限速没有生效怎么办?先确认规则顺序,iptables是自上而下匹配的,DROP规则必须在ACCEPT之后,用iptables -L --line-numbers查看顺序,如果需要调整,用iptables -D删掉原规则再按正确顺序添加。
限制打满后,正常用户依然频繁超时怎么办?看是CPU跑满了还是带宽真的被占满了,如果CPU高但带宽没满,说明瓶颈在解析性能上,考虑加入minimal-responses yes;(BIND参数)减少响应包体积。
发现UDP和TCP的流量不对称怎么办?UDP被限制了,但TCP的53端口没有限制,攻击者可以改用TCP连接消耗资源,记得把两层的限制规则都做齐,不要留缺口。
关于DNSSEC和大包响应的问题,开启了DNSSEC后某些解析响应会超过MTU(最大传输单元),导致IP分片,进而触发额外开销,在iptables限制时,建议放行部分大包的响应流量,实际操作中给--match multiport --dports 53补充一条长度限制规则,超过512字节的UDP包单独计数,防止正常的DNSSEC响应被误伤。

内网DNS与外部DNS的分工策略
很多企业采取双DNS架构:内网一台解析内部域名,公网另一台解析外部域名,内网这台限速可以设置得比较宽,因为访问来源可控,公网那台则必须严格限制速率,因为你不知道外面的流量会怎么冲过来。
如果两个环境共用一台物理机,建议用网络命名空间(network namespace)做隔离,分别设置独立网卡、独立iptables规则,确保内外网互不干扰,这个方案的配置复杂度高一些,但长期来看维护成本反而更低,排查问题时思路也清晰得多。
可持续优化的监控与告警机制
限制带宽不是配好了就一劳永逸,流量特征随时间在不断变化,一个季度前合理的参数,季度后可能已经完全不合时宜。
建议部署Prometheus加node_exporter,监控DNS服务器的带宽使用率与包转发速率,告警规则设置两个阈值:带宽使用率达到设定限制的70%时触发预警,持续超过90%超过5分钟则触发严重告警,这样能在业务受影响之前就发现问题,而不是等到用户抱怨才动手。
日志分析方面,BIND的querylog功能可以记录所有查询来源,定期统计一下哪些IP是查询大户,哪些来源在试图频繁探测可用递归解析服务,这些数据能持续反哺限速策略的调整方向。
Q&A
问:iptables的limit和tc的htb到底有什么区别,两个都要用吗?
iptables的limit工作在包过滤阶段,控制的是每秒放行多少个包,忽略包大小,tc的htb工作在内核协议栈的队列层,控制的是每秒通过多少字节,DNS攻击流量特征交织的情况下,仅靠一种策略容易留出防御空白,建议基础场景用iptables就够,如果交换机或路由器的监控显示带宽曲线有异常毛刺,再叠加tc做字节级整形。
问:限制带宽设置为多少才算合理?有没有参考数值?
没有统一标准,取决于你的出口带宽和正常请求量,limit值设为正常峰值QPS的1.5倍到2倍,tc的rate设为出口带宽的70%到80%,留出余量给其他业务,用dnsperf或queryperf压力测试工具模拟真实负载,逐步逼近最优值,以100Mbps出口带宽的内网DNS服务器为例,通常iptables限制在每秒300到500个UDP包,tc带宽限制在20到30Mbit区间,这样既能容纳突发流量,又留有充足的冗余。
问:DNS服务器接到大量台湾、海外等境外IP的持续查询请求,直接限制这些IP段还是用带宽限速更合理?
先用geoip工具或ipset按地理区域匹配来源IP,将异常区域的一次性批量拒之门外,再配合带宽限制兜底。ipset的匹配效率远高于逐条iptables规则,且支持数据库动态更新,但注意,如果业务本身包含跨境解析的需求,对海外IP一刀切反而会伤及正常用户,先查看解析日志确认这些海外请求是否真实来源于海外使用者,若是,则限速;若属伪造源IP,直接启用ipset封禁并同步更新防火墙黑名单。
问:公司用的是云服务器,能直接照搬这套配置吗?
云服务器和物理机的iptables操作没有本质区别,但要注意云平台安全组和iptables是两层独立过滤,需先确认安全组规则是否放行了UDP 53端口的入方向流量,部分云服务商的底层虚拟化架构限制了tc的可用性,具体可结合带宽监控观察配置后的实际效果,若tc规则在云主机上不生效,iptables的limit模块依然可用,对于自建机房或物理机托管场景,这套配置在简米科技的持牌自营机房和酷番云的IDC环境中均实测运行稳定,两者的基础网络设施对iptables和tc均有完整的兼容支持。
网友评论