Linux上精确过滤域名,核心不在iptables,而在DNS解析层,白名单/黑名单最实用的路径是:单机用/etc/hosts,局域网用dnsmasq,防火墙配合ipset做兜底阻断。
先搞清Linux域名过滤和IP过滤区别
很多运维第一次做域名屏蔽,会直觉地往iptables里写域名,结果命令直接报错,或者当时生效、过两天失效,原因是iptables工作在网络层,只认IP地址和端口,不负责域名解析,域名到IP的转换发生在用户态,防火墙规则加载时并不知道某个域名当前对应哪些IP。
CDN和轮询DNS让这个问题更麻烦,一个域名可能对应几十个IP,还会动态增减,用IP过滤固定服务完全够用,但过滤大量域名或动态解析的域名,必须把DNS解析纳入控制范围。
下表对比三种常见方案的定位:
| 方案 | 过滤层级 | 精确度 | 维护成本 | 适用场景 |
|---|---|---|---|---|
| /etc/hosts | 本机DNS | 单域名精确 | 低 | 单台Linux服务器或桌面 |
| dnsmasq | 局域网DNS | 支持泛域名和通配 | 中 | 内网批量域名过滤 |
| iptables+ipset | 网络层兜底 | 依赖IP更新频率 | 高 | 阻断直连IP绕过DNS |
Linux hosts文件屏蔽域名:最轻量黑白名单命令
/etc/hosts 是最原始的本地DNS覆盖文件,格式很固定:IP地址 域名,系统在查询DNS之前,会先读这个文件,命中就直接返回,不再向上游DNS服务器发请求。
黑名单操作只需要一行命令:
sudo sh -c 'echo "0.0.0.0 bad.example.com" >> /etc/hosts'
把域名指向 0.0.0 比指向 0.0.1 更好。0.0.1 会回环到本机,如果本机恰好有Web服务,屏蔽页面可能变成自己的默认站点,造成混淆。0.0.0 是不可路由地址,连接会立即失败,多数情况下失败速度更快。
白名单用hosts直接做不了,hosts只能指定单个域名解析到指定IP,没有“默认拒绝其他所有域名”的规则,想用hosts做白名单,需要配合防火墙或DNS服务把未列出的域名全部拦截,否则其他域名仍然正常解析。
hosts的优点是不用装任何软件,改完立即生效,适合临时屏蔽某个跟踪域名或测试环境,缺点是不支持泛域名,.example.com 这种写法在hosts里无效;域名多时文件会膨胀,维护起来比较痛苦。
dnsmasq域名黑名单与DNS层精确过滤实操
dnsmasq是一个轻量DNS转发器,支持非常灵活的域名匹配规则,很多路由器固件和公司内网DNS都基于它构建。
安装命令按发行版区分:
# Debian/Ubuntu sudo apt install dnsmasq -y # CentOS/RHEL sudo yum install dnsmasq -y
主配置文件是 /etc/dnsmasq.conf,也可以把自定义规则放进 /etc/dnsmasq.d/ 目录下独立文件,便于管理。
黑名单示例:
# 在 /etc/dnsmasq.d/blacklist.conf 添加 address=/ads.example.com/0.0.0.0 address=/tracker.example.com/0.0.0.0
address=/域名/0.0.0.0 表示把该域名及其子域名都解析到 0.0.0,dnsmasq会自动处理 www.ads.example.com 等子域,比hosts灵活得多。
白名单示例:
# /etc/dnsmasq.d/whitelist.conf no-resolv server=/api.allowed.com/223.5.5.5 server=/monitor.allowed.com/119.29.29.29 address=/#/0.0.0.0
关键在最后一行 address=/#/0.0.0.0,它代表所有未明确匹配的域名都返回 0.0.0,前面的 server= 只放行白名单域名走指定上游DNS。no-resolv 是必须的,否则dnsmasq会读取 /etc/resolv.conf 里的上游DNS,导致 target.com 也能被解析,白名单失效。
重启服务并验证:
sudo systemctl restart dnsmasq dig @127.0.0.1 bad.example.com # 应返回 0.0.0.0 dig @127.0.0.1 api.allowed.com # 应返回白名单域名真实IP
局域网内要把客户机的DNS地址指向这台dnsmasq服务器,DHCP下发DNS选项或手动修改客户端 /etc/resolv.conf 都行,这样整个网段的域名过滤就统一管起来了。
Linux防火墙屏蔽域名命令:iptables/nftables的兜底方案
iptables本身不支持直接写域名,如果尝试:
iptables -A OUTPUT -d bad.example.com -j DROP
会收到 host/network 'bad.example.com' not found 报错,因为iptables只接受IP、网络段或主机名能在规则加载时解析一次,之后不会动态更新。
但在实际场景里,仍有人需要“用防火墙屏蔽某个域名”,做法分两步:先解析域名得到IP,再把这些IP写进ipset集合,由iptables引用。
# 创建IP集合 sudo ipset create blocked hash:ip # 添加一条OUTPUT规则,匹配集合内的目的IP直接丢弃 sudo iptables -I OUTPUT -m set --match-set blocked dst -j DROP # 解析域名并加入集合 ip=$(dig +short bad.example.com | tail -1) sudo ipset add blocked $ip
CDN域名会返回多个IP,需要把全部IP都加进去,更规范的做法是写一个cron脚本,每5分钟解析一次域名,清空旧集合再重新填充:
#!/bin/bash
ipset flush blocked
for ip in $(dig +short bad.example.com); do
ipset add blocked $ip
done
nftables同样不支持域名匹配,但可以用动态集合或配合系统解析服务实现类似逻辑,如果环境中已经全面切换到nftables,思路相同:维护一个IP集合,定期刷新。
这种方案的优点是能在网络层直接阻断连接,即使应用程序绕过本地DNS、直接使用IP连接也会被拦截,缺点是IP变化有滞后,且维护成本明显高于DNS层过滤。
只允许白名单域名出站:生产服务器精确过滤场景
生产服务器如果只允许访问固定的几个API域名和监控上报域名,其他域名全部拒绝,单纯靠DNS白名单还不够,攻击者或恶意程序可能内置IP地址,不走DNS解析,直接连C2服务器,要封死这条路,必须配合防火墙默认拒绝策略。
完整操作步骤如下:
- 安装dnsmasq并按前文白名单配置,只放行指定域名。
- 将本机DNS指向本地服务:
echo "nameserver 127.0.0.1" | sudo tee /etc/resolv.conf
设置iptables出站白名单:
sudo iptables -A OUTPUT -m state --state ESTABLISHED -j ACCEPT sudo iptables -A OUTPUT -p udp --dport 53 -d 127.0.0.1 -j ACCEPT sudo iptables -A OUTPUT -j DROP
第一条放行已建立连接,第二条只允许向本地DNS查询,第三条默认拒绝所有新出站连接。
测试验证:
curl https://api.allowed.com # 应正常返回 curl https://blocked.example.com # 应超时或连接拒绝
这种配置下,白名单域名通过DNS正确解析后,连接会被ESTABLISHED规则放行,未允许的域名要么被dnsmasq解析为0.0.0.0直接失败,要么即使程序绕过DNS直连IP,也会被OUTPUT默认DROP拦住,两条路径同时封死,才算精确过滤。
选型建议与避开加密DNS坑
不同场景的推荐方案差异较大:
- 单台Linux桌面临时屏蔽广告域名:直接用hosts,改动小、即时生效。
- 内网几十台服务器统一屏蔽恶意域名:部署dnsmasq做DNS黑名单,DHCP下发内网DNS地址。
- 高安全等级的生产服务器只开放白名单域名:dnsmasq白名单 + iptables默认拒绝出站。
- 需要防止用户改DNS绕过过滤:在网关防火墙上阻断外部53端口UDP/TCP出站,只允许内网DNS服务器查询上游。
业内专家指出,单纯依赖域名过滤存在被加密DNS绕过的风险,生产环境应结合IP信誉库和出站白名单策略,近年来DoH(DNS over HTTPS)流行后,传统DNS过滤的部分场景确实被削弱,因为查询内容加密后无法在DNS层看到域名,这类环境必须把防火墙管控前移,从连接目标IP上做兜底。
Linux精确过滤域名没有单一命令能包打天下,先控DNS,再加防火墙默认拒绝,白名单场景尤其要把两条路径同时封死。
常见问题
Linux服务器屏蔽某个域名最简单命令是什么?
使用hosts文件把域名指向 0.0.0:
sudo sh -c 'echo "0.0.0.0 bad.example.com" >> /etc/hosts'
该命令立即生效,不需要重启网络服务,但hosts不支持泛域名,域名数量多时建议改用dnsmasq。
Linux防火墙iptables可以直接配置域名白名单吗?
不能,iptables的 -d 参数只接受IP地址或网络段,不支持域名,即使某些工具在规则加载时解析一次域名,之后IP变化也会导致规则失效,正确的做法是解析域名后写入ipset,再让iptables引用ipset集合。
白名单过滤后为何还要设置防火墙DROP出站?
因为DNS白名单只能控制域名解析结果,无法阻止程序使用硬编码IP地址直连外部服务器,将iptables的OUTPUT链默认策略设为DROP,只放行到本地DNS的查询和已建立连接,可以阻断所有绕过DNS的直连尝试,默认DROP是域名白名单能落地的必要条件。
首发原创文章,作者:王坚,如若转载,请注明出处:https://idctop.com/article/662430.html





