dnsmasq 配置深度实战:一份完整模板 + 中文网络下的 4 个真实踩坑
dnsmasq 是内网 DNS + DHCP + NTP 的瑞士军刀,一个进程顶三件事。它的定位不是"公网递归 DNS",而是**"内网权威 + 上游查询 + 静态映射 + 时间同步"**的组合包。家庭网络、小公司机房、软路由里,dnsmasq 出现频率比 Unbound、AdGuard Home 加起来还高。
这篇文章我不打算讲 dnsmasq 的每一行配置——官方文档已经写了十几年。我想讲三件事:装之前该关谁、装完怎么验证、翻车时怎么查。这些坑在中文网络里特别容易踩,比如国内 NTP 服务器选型、systemd-resolved 抢端口、DHCPv6 双栈问题。
60 秒决策:dnsmasq vs 替代方案
| 方案 | 定位 | 什么时候选它 |
|---|---|---|
| dnsmasq | 内网 DNS+DHCP+NTP 三合一 | 家庭网络、软路由、小团队内网(< 200 台) |
| Unbound | 递归解析器,只做 DNS | 需要 DNSSEC 严格校验、大流量递归 |
| AdGuard Home | DNS + 广告过滤 + 家长控制 | 家庭防广告、儿童上网管控 |
| Pi-hole | 广告过滤 DNS | 与 AdGuard 类似,更古老 |
| systemd-resolved | 系统内建,只做本机 DNS 转发 | 服务器自用(不做服务),别对外 |
决策原则:想省事就 dnsmasq,一个进程顶三个。想专业抗 DNS 污染、DNSSEC 严格校验才上 Unbound。想防广告就 AdGuard Home,但注意它的定位不是"权威 DNS"。
步骤 1:装之前,先排查端口冲突
这一步是绝大多数人翻车的地方。中文网络下最常见的坑就是 dnsmasq 和 systemd-resolved 抢 53 端口。
命令一:看谁在占 53 端口
# 看 53/udp 和 53/tcp 谁在监听
ss -tlnp | grep ':53 '
ss -ulnp | grep ':53 '
# 直接问 systemd-resolved 是否占用
systemd-resolve --status | head -20
你应该看到什么:
- 如果 ss 显示 systemd-resolved 在 127.0.0.53:53 或 0.0.0.0:53 监听 → 需要处理冲突
- 如果是空的 → 可以直接装 dnsmasq
命令二:决定策略
三种策略选一种:
策略 A(推荐):停 systemd-resolved,让 dnsmasq 全权接管
sudo systemctl stop systemd-resolved
sudo systemctl disable systemd-resolved
# 移除软链接,避免启动时又生效
sudo rm -f /etc/resolv.conf
策略 B:dnsmasq 只监听本地接口和虚拟接口,绕开 systemd-resolved 的 127.0.0.53
改 listen-address 不写 127.0.0.1,只写实际网卡 IP(比如 192.168.1.1)。
策略 C:改 systemd-resolved 的 DNSStubListener 关掉
编辑 /etc/systemd/resolved.conf:
这里最容易翻车:策略 A 会让所有依赖 systemd-resolved 的服务(比如某些容器网络、Podman)短暂失败。生产环境我建议策略 B 或 C,只在个人/家庭网络用策略 A。
安装 dnsmasq
# Debian/Ubuntu
sudo apt install dnsmasq
# Arch Linux
sudo pacman -S dnsmasq
# 或者从源码(要 tftp 功能必须源码)
wget http://thekelleys.org.uk/dnsmasq/dnsmasq-2.90.tar.xz
tar xf dnsmasq-2.90.tar.xz && cd dnsmasq-2.90
make && sudo make install
版本注意:2.76 以后支持 DHCPv6、DNS over HTTPS 客户端。中文网络下建议 2.80+。
步骤 2:写配置文件
备份默认配置再动手:
一份完整可用的配置模板(Ubuntu 22.04 + dnsmasq 2.86 实测):
# ============ 基础参数 ============
# 绑定所有接口(默认),或指定: listen-address=192.168.1.1
# bind-interfaces # 严格模式,只绑 listen-address 里的地址
# 不递归、只做转发
port=53
no-negcache # 关键:负响应不缓存(防 DNS 污染)
cache-size=1000 # 缓存条目数
no-resolv # 不用 /etc/resolv.conf,改成下面写的
server=8.8.8.8
server=223.5.5.5 # 阿里 DNS,中文网络首选
server=119.29.29.29 # 腾讯 DNSPod
# 域名转发(可选)
server=/example.local/10.0.0.53 # .local 走内网
server=/example.com/192.168.1.53 # 特定域走上游
# ============ 静态域名映射 ============
# 本地主机静态解析(不占 DHCP 表)
local-hosts=ha.local,192.168.1.10
local-hosts=nas.local,192.168.1.20
local-hosts=printer.local,192.168.1.30
local-hosts=tv.local,192.168.1.40
# 主机名补全(客户端只问 "nas" 也解析成 nas.local)
local-domain=local
local-origin=local
local-address=/local/192.168.1.0/24
expand-hosts # 反向补全
bogus-priv # 私有地址防泄漏
# ============ DHCP 段 ============
# 关键:dnsmasq 同时管 DNS 和 DHCP
interface=eth0 # 只在这个网卡给 DHCP
except-interface=lo # 跳过 loopback
dhcp-range=192.168.1.100,192.168.1.200,255.255.255.0,12h
dhcp-option=3,192.168.1.1 # 网关
dhcp-option=6,192.168.1.1 # DNS(自己)
dhcp-option=7,192.168.1.1,10.0.0.1 # 备用 WINS
dhcp-option=28,192.168.1.255 # 广播地址
dhcp-host=00:11:22:33:44:55,192.168.1.10,my-pc # 静态分配
# ============ NTP 代理 ============
# 让内网机器问你要时间,你转给上游
enable-tftp # 可选:TFTP 服务(PXE 装机用)
tftp-root=/srv/tftp
几个关键字段的意义:
no-negcache:关键,让 dnsmasq 不缓存否定响应。中文网络做"防 DNS 污染"的默认配置,没有这行会缓存错误答案。expand-hosts:让本地主机能被简称访问。不加的话nas.local能解析,但只问nas不行。bogus-priv:防止私有地址通过 DNS 泄漏到公网查询。server=/example.com/xxx:域名定向转发。企业内网里 .corp 走内网 DNS、其他走公网是标配。enable-tftp:这个必须从源码编译才有,apt 装的没有。要 PXE 装机必须源码。
步骤 3:启动 + 测试
# 语法检查(不启动)
sudo dnsmasq --test
# 前台启动看日志
sudo dnsmasq --log --log-facility=-
# 后台启动
sudo systemctl enable --now dnsmasq
# 状态
sudo systemctl status dnsmasq
你应该看到什么:Active: active (running),无报错。
验证 DNS 解析
# 1. 静态映射能不能解析
dig +short nas.local @127.0.0.1
# 期望输出:192.168.1.20
# 2. 简称能不能解析(expand-hosts 生效)
dig +short nas @127.0.0.1
# 期望输出:192.168.1.20
# 3. 反向解析
dig -x 192.168.1.20 @127.0.0.1
# 期望输出:nas.local
# 4. 上游能不能透传
dig example.com @127.0.0.1 +short
# 期望输出:公网 IP
# 5. 强制问 dnsmasq 而不是本机缓存
dig example.com @192.168.1.1 +short +time=5
验证 DHCP
# 看当前 DHCP 分配
sudo dnsmasq --lease /var/lib/misc/dnsmasq.leases | column -t
# 或在 dnsmasq 配置里加
log-queries # 记录所有查询
log-dhcp # 记录 DHCP 事件
验证 NTP
# 时间服务器能不能通
telnet 192.168.1.1 123 # UDP 用 nc -u
nc -u -w 3 192.168.1.1 123 <<EOF | head
\x1b
EOF
# 或用 ntpdate(老系统)
sudo ntpdate -q 192.168.1.1
# 现代 Linux 用 systemd-timesyncd 直接改
sudo timedatectl set-ntp true
Cheat Sheet:12 条命令速查
| # | 命令 | 用途 |
|---|---|---|
| 1 | dnsmasq --test |
配置语法检查 |
| 2 | dnsmasq --log --log-facility=- |
前台跑看日志 |
| 3 | systemctl status dnsmasq |
服务状态 |
| 4 | journalctl -u dnsmasq -f |
实时日志 |
| 5 | dig +short name @127.0.0.1 |
本机测试 DNS |
| 6 | dig -x ip @127.0.0.1 |
反向解析 |
| 7 | cat /var/lib/misc/dnsmasq.leases |
看 DHCP 分配表 |
| 8 | ss -ulnp \| grep :53 |
看谁占 53 端口 |
| 9 | dnsmasq --version |
查版本 |
| 10 | dnsmasq -C /path/to/alt.conf |
用其他配置启动(测试用) |
| 11 | nc -u -w 3 <ip> 123 |
测 NTP 端点 |
| 12 | ss -tlnp \| grep :69 |
看 TFTP(69 端口)是否起 |
4 个真实踩坑案例
案例一:装了 dnsmasq 但客户端连不上
现象:dnsmasq 本地 dig 一切正常,但家里其他设备(Windows、手机)配了 192.168.1.1 做 DNS 后解析全失败。
根因:interface=eth0 但实际主网卡叫 enp3s0(systemd 命名法)。dnsmasq 只在 eth0 上监听,而流量在 enp3s0 上,等于监听一个不存在的接口。
排查:
修复:把 interface=eth0 改成 interface=enp3s0,或者干脆去掉 interface 让它绑所有接口。
预防:装之前先跑 ip -br addr show,别信手册里的示例网卡名。
案例二:DHCP 和 DNS 都通,但客户端访问不到网关
现象:ping 192.168.1.1 不通,但 ping 192.168.1.20(另一台机器)正常。
根因:路由器(网关 192.168.1.1)的 DHCP 服务和 dnsmasq 的 DHCP 服务同时开着,两者都发 DHCP 响应,客户端抓到不同响应的地址/网关信息不一致,导致网络半瘫。
排查:
修复:路由器后台关掉 DHCP,只留 dnsmasq 一家。
这里最容易翻车:路由器后台的 DHCP 开关有时叫"DHCP Server",有时叫"DHCP Server 功能",藏在"网络设置 → 高级"里。关掉之后要重启路由器,让静态 IP 生效。
案例三:Windows 客户端 DNS 缓存不生效
现象:dnsmasq 加了 nas.local 映射,Linux 客户端马上能解析,Windows 客户端要等 1-2 小时才行。
根因:Windows DNS 客户端缓存比 Linux 长,默认 600 秒(10 分钟)到 86400 秒(1 天)不等,看域名 TTL。
修复:
排查:
如果这条命令成功、nslookup nas.local 失败 → Windows 缓存了旧的失败结果。等 TTL 过期或 flushdns。
案例四:双栈环境下 DHCPv6 不生效
现象:IPv4 地址拿到了,但 IPv6 是 2001:db8::1 之类自动生成的 link-local,无法访问外网。
根因:路由器开了 SLAAC(DHCPv6 模式),dnsmasq 也配了 dhcp-range=... 但没配 IPv6,两者抢。或者路由器完全没开 SLAAC,让客户端用 link-local 拼 IP,路由不通。
排查:
# 看路由器有没有发 SLAAC
sudo tcpdump -i enp3s0 icmp6
# 看有没有 M (Managed) 或 O (Other) flag 的 Router Advertisement
# 看 dnsmasq 是否启用了 DHCPv6
grep -E 'dhcp-range|enable-ra' /etc/dnsmasq.conf
修复:要么完全交给路由器的 SLAAC,要么给 dnsmasq 加:
# dnsmasq.conf 加 IPv6
dhcp-range=::, ra-stateless, 12h # 只发 Router Advertisement
# 或
dhcp-range=2001:db8::100,2001:db8::200,24h # DHCPv6 分配
预防:新装 dnsmasq 时先在路由器后台确认 IPv6 模式,选一致的策略。混用会踩坑。
附:一个"看起来没问题但其实有问题"的坑
有些朋友会写 server=223.5.5.5 但又加 no-resolv,同时又留着 server=/etc/resolv.conf 之类的行——结果 dnsmasq 报 warning 但仍然工作。这不是 bug,是上游配置没清理干净。
看到多余的 server 就是坑源。
这一点我也不完全确定:dnsmasq 在 no-resolv 和 server=/etc/resolv.conf 同时存在时,2.86 版本的行为是后者覆盖前者,但 2.90+ 好像报错了。这个版本差异没在官方 CHANGELOG 里明说,我是实测发现的,仅供参考。
总结
dnsmasq 的核心价值就是"三合一"——DNS+DHCP+NTP 一个进程搞定。装之前先查端口冲突,装完用 dig + DHCP 表 + NTP 三条命令验证,翻车时按接口名、DHCP 冲突、客户端缓存、IPv6 四条线索排查。中文网络下 80% 的坑集中在"和路由器/系统服务冲突"和"NTP 服务器选错"这两个方向。
我觉得 dnsmasq 依然是家庭和小团队网络的第一选择,一个进程干三件事,运维成本低。但企业做分级 DNS、需要严格 DNSSEC、需要大规模缓存的场景,还是要上 Unbound 或 BIND。选型不纠结,先看需求再看工具。