使用 dnsmasq 搭建轻量级内网 DNS、DHCP 与 PXE 服务

欢迎你来读这篇博客,这篇博客主要围绕 dnsmasq 的内网 DNS、DNS 缓存、域名分流、DHCP、TFTP/PXE、OpenWrt 集成和生产化排障展开。

如果你之前用过 BIND,会发现 dnsmasq 的思路完全不一样:它不是一个“重量级权威 DNS
平台”,而是一个“小而顺手的网络基础设施瑞士军刀”。家庭实验室、小公司内网、开发测试环境、软路由、虚拟化网络、PXE
装机环境里,它非常好用;但如果你想管理大量正式公网 Zone、复杂主从复制、DNS 视图、企业级审计,那 BIND、PowerDNS 或 CoreDNS 会更合适。

序言

内网 DNS 这件事,看起来很小,实际很容易变成“玄学现场”:

  • 同一个服务,一会儿 nas.local 能访问,一会儿不行;
  • 服务器上 dig 正常,浏览器却解析不到;
  • dnsmasq 启动失败,结果发现 53 端口被 systemd-resolved 占了;
  • 在 OpenWrt 上装 dnsmasq-full,装到一半路由器自己断网;
  • 配了 address=/example.com/192.168.1.10,结果反向解析没有;
  • 想让某些域名走指定 DNS,最后把内网域名泄露到了公网 DNS。

dnsmasq 解决的是“小网络里 DNS/DHCP/TFTP/PXE 的一体化治理”。它的优点不是功能最全,而是配置简单、启动快、依赖少、能和 DHCP
租约天然联动。对内网环境来说,这个组合拳经常比 BIND 更顺手。BIND 像重型工程车,dnsmasq 像皮卡:不能拉摩天楼,但拉服务器、工具箱、网线和咖啡都刚刚好。

正文

chapter 1:dnsmasq 是什么

dnsmasq 是一个轻量级网络服务程序,常见能力包括:

能力 说明 常见场景
DNS Forwarder 接收客户端 DNS 查询,转发给上游 DNS 内网统一 DNS 出口
DNS Cache 缓存常见 A/AAAA/CNAME/PTR 等记录 减少重复解析,加快访问
Local DNS 通过 /etc/hosts、addn-hosts、host-record、address 提供本地域名 nas.home.arpa、gitlab.home.arpa
Split DNS 指定某些域名走特定上游 DNS VPN、公司内网、国内外分流
DHCPv4/DHCPv6 分配地址、网关、DNS、搜索域 小型局域网、实验室
Router Advertisement IPv6 RA IPv6 内网
TFTP/PXE 网络启动、装机引导 自动装机、实验室批量部署
OpenWrt 集成 路由器默认 DNS/DHCP 组件 软路由、旁路由、家庭网关

一句话:dnsmasq 最适合做内网的 DNS/DHCP 中枢,而不是替代大型权威 DNS 系统。

chapter 2:dnsmasq 和 BIND 怎么选

对比项 dnsmasq BIND
定位 轻量 DNS 转发、缓存、DHCP、PXE 完整 DNS 服务器
配置复杂度 低 中高
内网主机名解析 很方便,尤其结合 DHCP 可以做,但略重
DNS 缓存 支持 支持
递归解析 主要做转发,也可缓存 可做完整递归解析器
权威 DNS 支持简单 authoritative mode,但不是强项 强项
主从复制 有简单 zone transfer 场景,但不主打 成熟
DHCP 内置 不内置
PXE/TFTP 内置 不内置
适合场景 家庭实验室、小公司、软路由、开发环境 企业 DNS、公网 Zone、复杂 DNS 体系

我的建议:

  • 只是想让 gitlab.home.arpa、nas.home.arpa、jenkins.home.arpa 在内网可访问:选 dnsmasq。
  • 想让 DHCP 分配出来的主机名自动进入 DNS:选 dnsmasq。
  • 想做 PXE 装机:选 dnsmasq。
  • 想维护企业正式域名、主从 DNS、复杂 Zone 文件、DNSSEC 签名:选 BIND 或 PowerDNS。
  • 想做 Kubernetes 内部服务发现:选 CoreDNS。
  • 想做本地广告拦截平台:可以用 dnsmasq,也可以用 Pi-hole / AdGuard Home。

chapter 3:先理解 Linux 的域名解析链路

很多 dnsmasq 问题不是 dnsmasq 本身的问题,而是客户端解析链路的问题。

一个普通 Linux 程序解析域名,大致会经过:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
应用程序
↓
glibc / getaddrinfo()
↓
/etc/nsswitch.conf
↓
files → dns → mdns/resolve 等模块
↓
/etc/hosts
↓
systemd-resolved / NetworkManager / resolv.conf 中的 nameserver
↓
dnsmasq
↓
上游 DNS

所以要注意:

  1. dig / nslookup 通常直接查 DNS,不完全等同于浏览器或 Java 程序的解析链路。
  2. /etc/hosts 的优先级通常高于 DNS。
  3. Ubuntu/Fedora 新系统常见 systemd-resolved,默认会提供本机 stub resolver。
  4. NetworkManager 也可能启动自己的 dnsmasq 实例。
  5. .local 往往会被 mDNS/Bonjour 处理,不建议拿来做传统单播 DNS 域。

排查时不要只问“dnsmasq 有没有启动”,还要问:

1
2
3
4
5
6
cat /etc/nsswitch.conf
cat /etc/resolv.conf
resolvectl status
nmcli dev show | grep -i dns
ss -lntup | grep ':53'
ss -lnup | grep ':53'

DNS 不是玄学,只是它喜欢“绕路”。它一绕路,人就开始玄学了。

chapter 4:规划实验网络

下面用一个家庭实验室/小型开发网络举例。

项 值
网段 192.168.9.0/24
网关 192.168.9.1
dnsmasq 服务器 192.168.9.2
内网域名 home.arpa
DHCP 地址池 192.168.9.100 - 192.168.9.199
NAS 192.168.9.10 / nas.home.arpa
GitLab 192.168.9.20 / gitlab.home.arpa
Jenkins 192.168.9.21 / jenkins.home.arpa
跳板机 192.168.9.30 / jump.home.arpa

为什么用 home.arpa?

  • home.arpa 是为家庭/本地网络保留的特殊用途域。
  • .local 是 mDNS 常用域,容易和 Apple Bonjour、Avahi、systemd-resolved 产生冲突。
  • .lan、.home 虽然很多路由器默认使用,但它们不是最规范的选择。

如果你是公司内网,最好用公司真实域名的子域,比如:

1
2
3
corp.example.com
dev.example.com
lab.example.com

例如:

1
2
3
gitlab.dev.example.com
nacos.dev.example.com
jenkins.dev.example.com

这比自己拍脑袋造一个 .local、.corp、.internal 更稳。

chapter 5:安装 dnsmasq

Debian / Ubuntu

1
2
3
sudo apt update
sudo apt install -y dnsmasq dnsutils
dnsmasq --version

dnsutils 提供 dig、nslookup 等工具。

RHEL / CentOS Stream / Rocky Linux / AlmaLinux / Fedora

1
2
sudo dnf install -y dnsmasq bind-utils
dnsmasq --version

bind-utils 提供 dig、nslookup、host 等工具。

OpenWrt

OpenWrt 默认通常已经安装并启用了 dnsmasq:

1
2
opkg list-installed | grep dnsmasq
/etc/init.d/dnsmasq status

如果需要 ipset / nftset 等高级能力,可能需要 dnsmasq-full。不过 OpenWrt 上替换 dnsmasq 要谨慎,因为它本身负责
DNS/DHCP;卸载默认包后,如果新包没装上,路由器可能直接失去解析能力。

安全做法:

1
2
3
4
5
opkg update
opkg download dnsmasq-full
opkg remove dnsmasq
opkg install ./dnsmasq-full_*.ipk
/etc/init.d/dnsmasq restart

如果担心断网,先临时写一个外部 DNS:

1
echo "nameserver 223.5.5.5" > /tmp/resolv.conf

chapter 6:配置文件组织方式

dnsmasq 默认主配置文件通常是:

1
/etc/dnsmasq.conf

推荐不要把所有配置都塞进主文件,而是采用目录拆分:

1
2
3
4
5
6
7
8
/etc/dnsmasq.conf
/etc/dnsmasq.d/
├── 00-base.conf
├── 10-upstream.conf
├── 20-local-records.conf
├── 30-dhcp.conf
├── 40-pxe.conf
└── 90-debug.conf

在 /etc/dnsmasq.conf 中保留一行:

1
conf-dir=/etc/dnsmasq.d,*.conf

注意这里的 *.conf 是“只加载 .conf 后缀文件”。这样可以避免误加载临时备份:

1
2
3
20-local-records.conf.bak
20-local-records.conf~
.#20-local-records.conf

修改配置后检查:

1
sudo dnsmasq --test

启动和重启:

1
2
3
sudo systemctl enable --now dnsmasq
sudo systemctl restart dnsmasq
sudo systemctl status dnsmasq --no-pager

查看日志:

1
journalctl -u dnsmasq -f

chapter 7:最小可用 DNS 配置

创建基础配置:

1
2
sudo mkdir -p /etc/dnsmasq.d
sudo vim /etc/dnsmasq.d/00-base.conf

写入:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
# 只监听指定接口或地址,避免变成公网开放递归 DNS
interface=eth0
listen-address=127.0.0.1,192.168.9.2

# 如果同一台机器上还有 systemd-resolved、BIND、CoreDNS、Docker DNS 等,建议启用
bind-interfaces

# 基础安全项
domain-needed
bogus-priv
stop-dns-rebind
local-service

# 内网域名,不向上游泄露
local=/home.arpa/
domain=home.arpa
expand-hosts

# 读取额外 hosts
addn-hosts=/etc/dnsmasq.hosts

# 不使用系统 /etc/resolv.conf 里的 DNS,改用自己指定的上游
no-resolv

# 上游 DNS
server=223.5.5.5
server=119.29.29.29
server=1.1.1.1
server=8.8.8.8

# DNS 缓存
cache-size=1000

# 新版建议保持 1232,除非你非常清楚为什么要改
edns-packet-max=1232

创建本地域名记录:

1
sudo vim /etc/dnsmasq.hosts

写入:

1
2
3
4
192.168.9.10 nas nas.home.arpa
192.168.9.20 gitlab gitlab.home.arpa
192.168.9.21 jenkins jenkins.home.arpa
192.168.9.30 jump jump.home.arpa

检查并重启:

1
2
sudo dnsmasq --test
sudo systemctl restart dnsmasq

测试:

1
2
3
dig @127.0.0.1 nas.home.arpa +short
dig @192.168.9.2 gitlab.home.arpa +short
dig @192.168.9.2 www.baidu.com +short

预期:

1
2
192.168.9.10
192.168.9.20

chapter 8:几个核心配置项解释

interface 与 listen-address

1
2
interface=eth0
listen-address=127.0.0.1,192.168.9.2

interface 限制监听网卡,listen-address 限制监听地址。

如果你只写:

1
listen-address=192.168.9.2

dnsmasq 不一定会自动监听 127.0.0.1,所以本机测试可能失败。建议明确写上:

1
listen-address=127.0.0.1,192.168.9.2

bind-interfaces 与 bind-dynamic

1
bind-interfaces

bind-interfaces 会让 dnsmasq 真正绑定到指定接口/地址,而不是先绑定通配地址再丢弃不该处理的请求。它常用于:

  • 同机运行多个 DNS 服务;
  • 避免和 systemd-resolved、BIND、CoreDNS 冲突;
  • 容器或虚拟化环境中控制监听范围。

如果网卡地址会动态变化,可以考虑:

1
bind-dynamic

但它只在 Linux 上完整支持。

domain-needed

1
domain-needed

不把没有点号的短名称转发到上游 DNS。例如:

1
2
3
nas
gitlab
jenkins

如果本地没有记录,直接返回不存在,不把这些名字丢给公网 DNS。内网主机名不应该出去裸奔。

bogus-priv

1
bogus-priv

对于私有地址的反向解析,如果本地没有记录,不转发给上游 DNS,直接返回不存在。比如:

1
2
3
10.in-addr.arpa
168.192.in-addr.arpa
16.172.in-addr.arpa ~ 31.172.in-addr.arpa

local=/home.arpa/

1
local=/home.arpa/

表示 home.arpa 是本地域,dnsmasq 只从本地 hosts、DHCP 租约、配置记录里回答,不转发到公网 DNS。

这个配置非常重要。否则你拼错了一个内网域名,可能会把内部命名习惯泄露给上游 DNS。

no-resolv 与 server

1
2
3
4
no-resolv
server=223.5.5.5
server=119.29.29.29
server=1.1.1.1

no-resolv 表示不要读取系统的 /etc/resolv.conf。这样 dnsmasq 的上游 DNS 完全由 server= 控制。

如果不加 no-resolv,dnsmasq 会继续读取系统 resolver 配置,容易出现环路:

1
系统 resolv.conf → 127.0.0.1 → dnsmasq → resolv.conf → 127.0.0.1 → dnsmasq → ...

DNS 环路不是浪漫,是自嗨。

strict-order 与 all-servers

这两个不要随便一起用。

1
strict-order

严格按配置顺序查询上游 DNS。适合你有明确主备关系:

1
2
3
4
server=192.168.9.1
server=223.5.5.5
server=1.1.1.1
strict-order
1
all-servers

同时向所有上游发起查询,谁先回来用谁。适合追求响应速度,但会增加查询量,也会把查询同时发给多个上游,隐私性更差。

建议:

  • 稳定优先:strict-order
  • 速度优先:all-servers
  • 普通家庭/实验室:默认即可,不一定非要加

cache-size

1
cache-size=1000

默认缓存较小。内网几十台机器,1000 ~ 5000 通常够用。不要无脑写成 1000000,缓存不是越大越好,太大反而影响性能和内存。

查看缓存统计:

1
2
sudo kill -USR1 "$(pidof dnsmasq)"
journalctl -u dnsmasq -n 100

如果开启了 log-queries,也可以观察命中情况。

edns-packet-max

1
edns-packet-max=1232

新版 dnsmasq 默认已经是 1232。老版本曾经默认 4096,这在 DNS Flag Day 2020 后不再推荐,也曾引出安全问题。除非你非常清楚网络路径
MTU 和 EDNS 行为,否则保持 1232。

chapter 9:本地域名记录怎么写

dnsmasq 提供很多写法,容易混。实战里常用四种。

方式一:addn-hosts

适合静态主机名,一行一个 IP 和多个域名。

1
addn-hosts=/etc/dnsmasq.hosts

/etc/dnsmasq.hosts:

1
2
192.168.9.10 nas nas.home.arpa
192.168.9.20 gitlab gitlab.home.arpa

优点:

  • 简单;
  • 可读性好;
  • 和 /etc/hosts 语法一致;
  • 适合 NAS、GitLab、Jenkins、Nacos、数据库等固定服务。

缺点:

  • 不适合泛域名;
  • 多环境很多记录时不如拆分配置灵活。

方式二:host-record

适合同时定义 A/AAAA/PTR 记录。

1
2
3
host-record=nas.home.arpa,nas,192.168.9.10
host-record=gitlab.home.arpa,gitlab,192.168.9.20
host-record=jenkins.home.arpa,jenkins,192.168.9.21

优点:

  • 比 address 更像“真实主机记录”;
  • 可自动处理 PTR 的语义更清晰;
  • 支持 TTL。

例如:

1
host-record=nas.home.arpa,nas,192.168.9.10,300

方式三:address

适合泛域名、强制劫持、黑洞地址。

1
2
3
4
address=/gitlab.home.arpa/192.168.9.20
address=/dev.home.arpa/192.168.9.20
address=/ads.example.com/0.0.0.0
address=/doubleclick.net/0.0.0.0

注意:

1
address=/dev.home.arpa/192.168.9.20

会匹配:

1
2
3
dev.home.arpa
a.dev.home.arpa
b.c.dev.home.arpa

它更像“域名规则”,不是单纯主机记录。

缺点:

  • 不适合需要完整 PTR 的主机;
  • 配多了以后容易误伤;
  • 拿它做广告屏蔽可以,但维护大型规则集更建议用专门工具。

方式四:ptr-record

适合手工指定反向解析。

1
2
ptr-record=10.9.168.192.in-addr.arpa,nas.home.arpa
ptr-record=20.9.168.192.in-addr.arpa,gitlab.home.arpa

验证:

1
dig @192.168.9.2 -x 192.168.9.10 +short

chapter 10:域名分流:让指定域名走指定 DNS

这是 dnsmasq 很常用的功能。

指定 .cn 走国内 DNS

1
2
server=/cn/223.5.5.5
server=/cn/119.29.29.29

指定公司内网域名走 VPN DNS

假设公司内网 DNS 是 10.8.0.53:

1
2
server=/corp.example.com/10.8.0.53
server=/10.in-addr.arpa/10.8.0.53

这样:

1
dig @192.168.9.2 gitlab.corp.example.com

会转发给 10.8.0.53。

指定某个域名不转发

1
2
local=/home.arpa/
server=/home.arpa/

local 本身就是 server 的同义语法,表达上更清楚:这是本地域。

匹配规则注意点

1
server=/google.com/1.1.1.1

会匹配:

1
2
3
google.com
www.google.com
mail.google.com

但不会匹配:

1
supergoogle.com

如果你写:

1
server=/.google.com/1.1.1.1

历史原因上,它和 /google.com/ 基本等价。

如果你真的想匹配“任意以 google.com 结尾的字符串”,需要用:

1
server=/*google.com/1.1.1.1

但这种写法要非常谨慎,容易误伤。

chapter 11:客户端如何使用 dnsmasq

dnsmasq 服务端配好了,不代表客户端会用它。

临时测试

Linux/macOS:

1
2
dig @192.168.9.2 nas.home.arpa +short
nslookup nas.home.arpa 192.168.9.2

Linux 临时改 DNS

1
sudo vim /etc/resolv.conf

写入:

1
2
nameserver 192.168.9.2
search home.arpa

但很多系统会自动覆盖 /etc/resolv.conf,所以这通常只适合临时验证。

NetworkManager

1
2
3
4
5
nmcli con show
nmcli con mod "Wired connection 1" ipv4.dns "192.168.9.2"
nmcli con mod "Wired connection 1" ipv4.ignore-auto-dns yes
nmcli con down "Wired connection 1"
nmcli con up "Wired connection 1"

查看:

1
nmcli dev show | grep -i dns

systemd-resolved

查看状态:

1
resolvectl status

给某个网卡设置 DNS:

1
2
sudo resolvectl dns eth0 192.168.9.2
sudo resolvectl domain eth0 home.arpa

如果要持久化,建议通过 Netplan 或 NetworkManager 配置。

Ubuntu Netplan

示例:

1
2
3
4
5
6
7
8
9
10
11
12
network:
version: 2
ethernets:
ens160:
dhcp4: true
dhcp4-overrides:
use-dns: false
nameservers:
addresses:
- 192.168.9.2
search:
- home.arpa

应用:

1
sudo netplan apply

macOS

查看 DNS:

1
scutil --dns

设置 DNS 可以在系统设置中配置,也可以命令行:

1
2
networksetup -setdnsservers "Wi-Fi" 192.168.9.2
networksetup -setsearchdomains "Wi-Fi" home.arpa

恢复自动 DNS:

1
networksetup -setdnsservers "Wi-Fi" Empty

最推荐方式:通过 DHCP 下发 DNS

如果 dnsmasq 同时做 DHCP,那直接通过 DHCP 下发:

1
2
dhcp-option=option:dns-server,192.168.9.2
dhcp-option=option:domain-search,home.arpa

这样客户端不用逐台配置。

chapter 12:Ubuntu/Fedora 上的 53 端口冲突

这是新版 Linux 最常见的坑。

Ubuntu 默认可能有:

1
systemd-resolved

它通常监听:

1
127.0.0.53:53

查看:

1
2
3
ss -lntup | grep ':53'
ss -lnup | grep ':53'
resolvectl status

方案一:dnsmasq 只监听局域网 IP

如果 dnsmasq 只给局域网提供 DNS,不一定要占用 127.0.0.53。

配置:

1
2
3
interface=eth0
listen-address=192.168.9.2
bind-interfaces

这样经常可以和 systemd-resolved 共存。

方案二:关闭 systemd-resolved 的 stub listener

如果你希望 dnsmasq 监听本机 127.0.0.1:53,或者监听所有地址 0.0.0.0:53,就需要处理冲突。

创建配置:

1
2
3
4
5
sudo mkdir -p /etc/systemd/resolved.conf.d
sudo tee /etc/systemd/resolved.conf.d/no-stub.conf >/dev/null <<'EOF'
[Resolve]
DNSStubListener=no
EOF

重建 /etc/resolv.conf 链接:

1
2
3
sudo rm -f /etc/resolv.conf
sudo ln -s /run/systemd/resolve/resolv.conf /etc/resolv.conf
sudo systemctl restart systemd-resolved

然后重启 dnsmasq:

1
sudo systemctl restart dnsmasq

不建议直接禁用 systemd-resolved 的情况

很多文章会让你:

1
sudo systemctl disable --now systemd-resolved

这不是绝对不能用,但要谨慎。原因:

  • VPN 可能依赖它做 split DNS;
  • Netplan/桌面网络管理可能会受影响;
  • 禁用后你要自己维护 /etc/resolv.conf;
  • 服务挂了以后宿主机可能无法解析域名。

更稳的做法是关闭 stub listener,而不是直接把整个服务扬了。粗暴能解决问题,但容易把下一个问题请进门。

chapter 13:使用 dnsmasq 提供 DHCP

如果你的路由器已经提供 DHCP,就不要再随便开第二个 DHCP。一个网段两个 DHCP,客户端会像抽盲盒一样拿配置,运气好叫“高可用”,运气差叫“全网事故”。

启用 DHCP 示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# /etc/dnsmasq.d/30-dhcp.conf

interface=eth0
bind-interfaces

# DHCP 地址池
dhcp-range=192.168.9.100,192.168.9.199,255.255.255.0,12h

# 网关
dhcp-option=option:router,192.168.9.1

# DNS 服务器
dhcp-option=option:dns-server,192.168.9.2

# 搜索域
dhcp-option=option:domain-search,home.arpa

# 声明本 DHCP 对当前网段权威,适合你确认没有其他 DHCP 的场景
dhcp-authoritative

# 静态地址绑定:MAC, 主机名, IP, 租期
dhcp-host=AA:BB:CC:DD:EE:01,nas,192.168.9.10,infinite
dhcp-host=AA:BB:CC:DD:EE:02,gitlab,192.168.9.20,infinite
dhcp-host=AA:BB:CC:DD:EE:03,jenkins,192.168.9.21,infinite

重启:

1
2
sudo dnsmasq --test
sudo systemctl restart dnsmasq

查看租约:

1
cat /var/lib/misc/dnsmasq.leases

不同发行版 lease 文件位置可能不同,也可能在:

1
2
/var/lib/dnsmasq/dnsmasq.leases
/tmp/dhcp.leases

DHCP 分配的主机会自动进入 DNS

如果配置了:

1
2
domain=home.arpa
expand-hosts

并且 DHCP 客户端上报 hostname,例如 workstation01,那么 dnsmasq 可以解析:

1
2
workstation01
workstation01.home.arpa

这就是 dnsmasq 比单独维护 /etc/hosts 更舒服的地方。

chapter 14:DHCP 多网段与标签

dnsmasq 支持多个 DHCP range:

1
2
dhcp-range=set:office,192.168.9.100,192.168.9.199,255.255.255.0,12h
dhcp-range=set:lab,192.168.8.100,192.168.8.199,255.255.255.0,12h

给不同网段下发不同网关:

1
2
dhcp-option=tag:office,option:router,192.168.9.1
dhcp-option=tag:lab,option:router,192.168.8.1

给不同设备下发不同配置:

1
2
dhcp-host=set:printer,AA:BB:CC:DD:EE:10,printer01,192.168.9.50,infinite
dhcp-option=tag:printer,option:dns-server,192.168.9.2

基于 MAC 厂商匹配:

1
2
dhcp-mac=set:apple,DC:A6:32:*:*:*
dhcp-option=tag:apple,option:domain-search,home.arpa

这个能力适合小规模网络。如果你要做复杂 IPAM、审批、审计、租约报表,那还是上专门的 DHCP/IPAM 系统。

chapter 15:PXE / TFTP 网络启动

dnsmasq 内置 TFTP 和 PXE 支持,很适合实验室自动装机。

基础配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# /etc/dnsmasq.d/40-pxe.conf

interface=eth0
bind-interfaces

dhcp-range=192.168.9.100,192.168.9.199,255.255.255.0,12h
dhcp-option=option:router,192.168.9.1
dhcp-option=option:dns-server,192.168.9.2

enable-tftp
tftp-root=/srv/tftp
tftp-secure

# BIOS PXE
dhcp-boot=pxelinux.0

创建目录:

1
2
sudo mkdir -p /srv/tftp
sudo chown -R dnsmasq:dnsmasq /srv/tftp || true

RHEL/Rocky/AlmaLinux 如果启用了 SELinux:

1
2
sudo semanage fcontext -a -t tftpdir_t "/srv/tftp(/.*)?"
sudo restorecon -Rv /srv/tftp

如果没有 semanage:

1
sudo dnf install -y policycoreutils-python-utils

UEFI 启动通常需要不同文件,例如:

1
2
3
dhcp-match=set:efi-x86_64,option:client-arch,7
dhcp-boot=tag:efi-x86_64,efi/BOOTX64.EFI
dhcp-boot=tag:!efi-x86_64,pxelinux.0

更完整的 iPXE 方式:

1
2
3
4
5
6
7
8
# iPXE 客户端已经启动后,给它脚本
dhcp-userclass=set:ipxe,iPXE
dhcp-boot=tag:ipxe,http://192.168.9.2/boot.ipxe

# 普通 PXE 客户端先引导到 iPXE
dhcp-match=set:efi-x86_64,option:client-arch,7
dhcp-boot=tag:efi-x86_64,ipxe/snponly.efi
dhcp-boot=tag:!efi-x86_64,ipxe/undionly.kpxe

示例 boot.ipxe:

1
2
3
4
5
6
#!ipxe
dhcp
set base-url http://192.168.9.2/images/ubuntu
kernel ${base-url}/vmlinuz ip=dhcp url=${base-url}/ubuntu.iso
initrd ${base-url}/initrd
boot

注意:PXE 涉及 DHCP、TFTP、HTTP、启动固件,排障时要分层:

1
2
3
4
journalctl -u dnsmasq -f
tcpdump -i eth0 -nn port 67 or port 68 or port 69
tftp 192.168.9.2 -c get pxelinux.0
curl -I http://192.168.9.2/boot.ipxe

chapter 16:OpenWrt 上的 dnsmasq

OpenWrt 的 dnsmasq 配置不完全等于普通 Linux,因为它有 UCI 配置系统。常见文件:

1
2
3
4
5
/etc/config/dhcp
/etc/dnsmasq.conf
/etc/dnsmasq.d/
/tmp/resolv.conf.d/resolv.conf.auto
/tmp/dhcp.leases

OpenWrt 常用 UCI 配置:

1
uci show dhcp.@dnsmasq[0]

设置本地域:

1
2
3
4
5
uci set dhcp.@dnsmasq[0].domain='home.arpa'
uci set dhcp.@dnsmasq[0].local='/home.arpa/'
uci set dhcp.@dnsmasq[0].expandhosts='1'
uci commit dhcp
/etc/init.d/dnsmasq restart

设置缓存:

1
2
3
uci set dhcp.@dnsmasq[0].cachesize='1000'
uci commit dhcp
/etc/init.d/dnsmasq restart

添加额外配置目录:

1
2
3
4
uci set dhcp.@dnsmasq[0].confdir='/etc/dnsmasq.d'
uci commit dhcp
mkdir -p /etc/dnsmasq.d
/etc/init.d/dnsmasq restart

添加自定义域名:

1
2
3
4
5
6
7
8
cat >/etc/dnsmasq.d/20-local.conf <<'EOF'
host-record=nas.home.arpa,nas,192.168.9.10
host-record=gitlab.home.arpa,gitlab,192.168.9.20
server=/corp.example.com/10.8.0.53
address=/dev.home.arpa/192.168.9.20
EOF

/etc/init.d/dnsmasq restart

dnsmasq-full、ipset 与 nftset

老教程里常见:

1
ipset=/example.com/saveresult

这是为了把域名解析结果写入 ipset,再交给 iptables 做策略路由或访问控制。

但新版 OpenWrt、nftables 生态里,很多场景开始转向 nftset。所以你要先确认当前 dnsmasq 编译能力:

1
dnsmasq --version

观察输出里是否有:

1
2
ipset
nftset

如果没有对应能力,配置写了也没用,甚至可能启动失败。

示例 nftset 思路:

1
2
nftset=/github.com/4#inet#fw4#github4
nftset=/github.com/6#inet#fw4#github6

然后在 nftables/firewall 里使用这些 set 做路由或放行。不同 OpenWrt 版本、firewall4
配置差异较大,生产前一定要先在测试环境验证。路由器不是练胆子的地方,尤其是远程改防火墙的时候。

chapter 17:安全加固

dnsmasq 一旦开放给公网,就可能变成 DNS 放大攻击工具。所以安全配置不是可选项。

只监听内网地址

1
2
3
interface=eth0
listen-address=127.0.0.1,192.168.9.2
bind-interfaces

不要无脑监听:

1
listen-address=0.0.0.0

限制只服务本地网络

1
local-service

如果你已经显式写了 interface / listen-address,也不要因此忽视防火墙。

防止内网域名泄露

1
2
3
domain-needed
bogus-priv
local=/home.arpa/

防 DNS Rebinding

1
stop-dns-rebind

如果你确实需要某些域名返回私网地址,例如公司 VPN 域名:

1
rebind-domain-ok=/corp.example.com/

不要直接全局关闭 rebind 防护。

控制日志

调试时:

1
2
log-queries
log-facility=/var/log/dnsmasq.log

生产中谨慎开启 log-queries,因为它会记录大量访问域名,既影响性能,也涉及隐私。

推荐生产:

1
log-async=25

防火墙

RHEL/Rocky/AlmaLinux:

1
2
sudo firewall-cmd --permanent --add-rich-rule='rule family="ipv4" source address="192.168.9.0/24" service name="dns" accept'
sudo firewall-cmd --reload

如果启用 DHCP:

1
2
sudo firewall-cmd --permanent --add-service=dhcp
sudo firewall-cmd --reload

如果启用 TFTP:

1
2
sudo firewall-cmd --permanent --add-service=tftp
sudo firewall-cmd --reload

Ubuntu UFW:

1
2
3
4
sudo ufw allow in on eth0 from 192.168.9.0/24 to any port 53 proto udp
sudo ufw allow in on eth0 from 192.168.9.0/24 to any port 53 proto tcp
sudo ufw allow in on eth0 from 192.168.9.0/24 to any port 67 proto udp
sudo ufw allow in on eth0 from 192.168.9.0/24 to any port 69 proto udp

版本安全

查看版本:

1
dnsmasq --version

建议:

  • 优先使用发行版安全仓库里的 dnsmasq;
  • 不要盲目源码编译覆盖系统包;
  • 关注发行版安全公告;
  • 老版本尤其要注意 EDNS.0 UDP packet size、DNSSEC 相关 CVE;
  • OpenWrt 上注意 dnsmasq / dnsmasq-full 的编译选项,不同固件差异明显。

chapter 18:DNSSEC 怎么看

dnsmasq 可以做 DNSSEC validation,但前提是编译时启用了 DNSSEC 支持,并且配置了 trust anchor。

查看:

1
dnsmasq --version | grep -i dnssec

配置示例:

1
2
dnssec
trust-anchor=.,20326,8,2,E06D44B80B8F1D39A95C0B0D7C65D08458E880409BBC683457104237C7F8EC8D

不过实战建议:

  • 家庭/实验室一般不必一上来就启用 DNSSEC;
  • 如果上游已经是可信 validating resolver,可以先简化;
  • 如果你启用 dnsmasq DNSSEC,必须确保系统时间准确;
  • DNSSEC 会增加查询和缓存复杂度;
  • 出问题时 SERVFAIL 会明显增多,要能排查。

可以测试:

1
2
dig @192.168.9.2 dnssec-failed.org
dig @192.168.9.2 cloudflare.com +dnssec

如果你只是想“内网域名解析稳定”,先不要把 DNSSEC 当第一优先级。先让车能开,再装氮气加速。

chapter 19:完整推荐配置

下面给一个较完整的内网 DNS + DHCP 配置。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
# /etc/dnsmasq.d/00-lab.conf

########################################
# 1. 监听范围
########################################
interface=eth0
listen-address=127.0.0.1,192.168.9.2
bind-interfaces
local-service

########################################
# 2. 基础安全
########################################
domain-needed
bogus-priv
stop-dns-rebind
dns-loop-detect

########################################
# 3. 本地域
########################################
local=/home.arpa/
domain=home.arpa
expand-hosts
addn-hosts=/etc/dnsmasq.hosts

########################################
# 4. 上游 DNS
########################################
no-resolv
server=223.5.5.5
server=119.29.29.29
server=1.1.1.1

# 公司 VPN 域名
server=/corp.example.com/10.8.0.53
server=/10.in-addr.arpa/10.8.0.53

########################################
# 5. 缓存与 EDNS
########################################
cache-size=2000
edns-packet-max=1232

########################################
# 6. DHCP
########################################
dhcp-range=192.168.9.100,192.168.9.199,255.255.255.0,12h
dhcp-option=option:router,192.168.9.1
dhcp-option=option:dns-server,192.168.9.2
dhcp-option=option:domain-search,home.arpa
dhcp-authoritative

# 静态租约
dhcp-host=AA:BB:CC:DD:EE:01,nas,192.168.9.10,infinite
dhcp-host=AA:BB:CC:DD:EE:02,gitlab,192.168.9.20,infinite
dhcp-host=AA:BB:CC:DD:EE:03,jenkins,192.168.9.21,infinite

########################################
# 7. 日志
########################################
log-async=25
# 调试时再打开
# log-queries
# log-facility=/var/log/dnsmasq.log

/etc/dnsmasq.hosts:

1
2
3
4
192.168.9.10 nas nas.home.arpa
192.168.9.20 gitlab gitlab.home.arpa
192.168.9.21 jenkins jenkins.home.arpa
192.168.9.30 jump jump.home.arpa

验证:

1
2
3
4
5
6
7
sudo dnsmasq --test
sudo systemctl restart dnsmasq

dig @127.0.0.1 nas.home.arpa +short
dig @192.168.9.2 gitlab.home.arpa +short
dig @192.168.9.2 www.baidu.com +short
dig @192.168.9.2 -x 192.168.9.10 +short

chapter 20:排障手册

1. dnsmasq 启动失败

查看:

1
2
3
systemctl status dnsmasq --no-pager
journalctl -u dnsmasq -n 100 --no-pager
sudo dnsmasq --test

常见原因:

  • 配置项拼写错误;
  • conf-dir 指向不存在目录;
  • 同一个配置项重复定义;
  • 53 端口冲突;
  • DHCP 地址池和网卡网段不匹配;
  • TFTP root 不存在;
  • SELinux 拦截。

2. 53 端口被占用

1
2
3
sudo ss -lntup | grep ':53'
sudo ss -lnup | grep ':53'
sudo lsof -i :53

可能占用者:

  • systemd-resolved
  • named
  • coredns
  • dnsmasq
  • NetworkManager 启动的内置 dnsmasq
  • Docker / Kubernetes 相关 DNS 组件
  • Pi-hole / AdGuard Home

3. 本机能解析,客户端不能解析

检查服务端是否监听 LAN IP:

1
2
ss -lntup | grep ':53'
ss -lnup | grep ':53'

检查防火墙:

1
2
sudo firewall-cmd --list-all
sudo ufw status

客户端直接指定 DNS 测试:

1
dig @192.168.9.2 nas.home.arpa +short

如果这个成功,但普通访问失败,说明客户端没有把 192.168.9.2 当 DNS。

4. dig 成功,浏览器失败

检查:

1
2
3
cat /etc/nsswitch.conf
cat /etc/resolv.conf
resolvectl status

可能原因:

  • 浏览器开启了 DoH;
  • 系统走了 mDNS;
  • /etc/hosts 覆盖;
  • VPN 改写了 DNS;
  • NetworkManager/resolved 没刷新。

Chrome/Edge/Firefox 都可能启用“安全 DNS / DNS over HTTPS”,这会绕过你的内网 DNS。内网解析要稳定,浏览器 DoH 也要纳入治理。

5. .local 解析异常

不要用 .local 做传统内网 DNS。它经常被 mDNS 接管。

把:

1
2
nas.local
gitlab.local

改成:

1
2
nas.home.arpa
gitlab.home.arpa

或者公司真实子域:

1
2
nas.dev.example.com
gitlab.dev.example.com

6. 外网能解析,内网域名不能解析

检查是否配置:

1
2
3
4
local=/home.arpa/
addn-hosts=/etc/dnsmasq.hosts
domain=home.arpa
expand-hosts

检查记录文件是否存在:

1
2
ls -l /etc/dnsmasq.hosts
cat /etc/dnsmasq.hosts

重载 hosts:

1
sudo systemctl reload dnsmasq

注意:dnsmasq 收到 SIGHUP 会重新读取 hosts、租约等文件,但不一定重新读取主配置文件。改了主配置后,建议直接 restart。

7. DHCP 不分配地址

检查:

1
2
journalctl -u dnsmasq -f
tcpdump -i eth0 -nn port 67 or port 68

常见原因:

  • 网段里已有另一个 DHCP;
  • dhcp-range 不在接口网段内;
  • 防火墙拦 UDP 67/68;
  • dnsmasq 没监听正确接口;
  • 虚拟机网络模式不允许 DHCP 广播;
  • 交换机/VLAN 隔离。

8. PXE 客户端拿不到启动文件

检查:

1
2
3
tcpdump -i eth0 -nn port 67 or port 68 or port 69
journalctl -u dnsmasq -f
ls -lah /srv/tftp

TFTP 测试:

1
tftp 192.168.9.2 -c get pxelinux.0

常见原因:

  • enable-tftp 未开启;
  • tftp-root 目录错误;
  • 文件权限错误;
  • SELinux context 错误;
  • UEFI/BIOS 启动文件不匹配;
  • DHCP Option 93 架构判断错误。

chapter 21:dnsmasq 与 Docker / 虚拟化

很多开发机上会出现这些 DNS:

1
2
3
127.0.0.11       Docker 容器内部 DNS
192.168.122.1 libvirt dnsmasq
127.0.0.53 systemd-resolved

libvirt 默认会给虚拟网络启动自己的 dnsmasq:

1
2
3
ps aux | grep dnsmasq
virsh net-list --all
virsh net-dumpxml default

这不一定和系统 dnsmasq 冲突,因为它可能监听在虚拟网桥地址,比如:

1
192.168.122.1:53

如果你自己也运行 dnsmasq,要注意:

1
2
3
interface=eth0
except-interface=virbr0
bind-interfaces

或者只监听明确地址:

1
listen-address=127.0.0.1,192.168.9.2

容器内使用宿主机 dnsmasq,可以在 Docker daemon 配置:

1
2
3
4
5
{
"dns": [
"192.168.9.2"
]
}

保存到:

1
/etc/docker/daemon.json

重启:

1
sudo systemctl restart docker

但注意:Docker 容器内默认的 127.0.0.11 是 Docker 自己的嵌入式 DNS,应用看到的不一定是宿主机 DNS。

chapter 22:高可用怎么做

dnsmasq 本身不是强 HA 系统,但小规模可以这么做。

方案一:两个 dnsmasq,客户端配置两个 DNS

1
2
DNS1: 192.168.9.2
DNS2: 192.168.9.3

优点简单,缺点是不同客户端对 DNS1/DNS2 的使用策略不完全一致。

方案二:Keepalived VIP

1
2
3
VIP: 192.168.9.53
node1: 192.168.9.2
node2: 192.168.9.3

客户端只配置:

1
192.168.9.53

Keepalived 检测 dnsmasq 进程,失败时漂移 VIP。

方案三:路由器下发多个 DNS

如果你的 DHCP 在路由器上,可以下发:

1
2
192.168.9.2
192.168.9.3

DHCP 高可用要谨慎

DNS 做双活容易,DHCP 做双活要小心。两个 DHCP 如果没有明确划分地址池,可能重复分配 IP。

可以拆池:

1
2
node1: 192.168.9.100 - 192.168.9.149
node2: 192.168.9.150 - 192.168.9.199

但静态租约、主机名、租约状态仍要考虑同步。小网络可以接受,企业场景建议上专业 DHCP/IPAM。

chapter 23:和 BIND / AdGuard Home / Pi-hole 的组合

dnsmasq + BIND

常见组合:

1
客户端 → dnsmasq → BIND → 公网递归

dnsmasq 负责:

  • DHCP;
  • 本地主机名;
  • 简单域名分流。

BIND 负责:

  • 复杂 Zone;
  • 递归缓存;
  • DNSSEC validation;
  • 企业 DNS 策略。

配置示例:

1
2
server=/corp.example.com/192.168.9.5
server=192.168.9.5

dnsmasq + AdGuard Home / Pi-hole

常见组合:

1
客户端 → AdGuard Home/Pi-hole → dnsmasq → 上游 DNS

或:

1
客户端 → dnsmasq → AdGuard Home/Pi-hole → 上游 DNS

推荐前者:

  • AdGuard/Pi-hole 做过滤;
  • dnsmasq 做内网主机名和 DHCP;
  • 过滤平台转发内网域名给 dnsmasq。

例如 AdGuard 上游设置:

1
2
3
[/home.arpa/]192.168.9.2
https://dns.alidns.com/dns-query
https://cloudflare-dns.com/dns-query

chapter 24:常用命令速查

服务管理

1
2
3
4
sudo systemctl enable --now dnsmasq
sudo systemctl restart dnsmasq
sudo systemctl reload dnsmasq
sudo systemctl status dnsmasq --no-pager

配置检查

1
2
sudo dnsmasq --test
dnsmasq --version

端口检查

1
2
3
ss -lntup | grep ':53'
ss -lnup | grep ':53'
lsof -i :53

DNS 测试

1
2
3
4
5
dig @192.168.9.2 nas.home.arpa +short
dig @192.168.9.2 www.baidu.com
dig @192.168.9.2 -x 192.168.9.10 +short
nslookup nas.home.arpa 192.168.9.2
host nas.home.arpa 192.168.9.2

客户端状态

1
2
3
4
5
cat /etc/resolv.conf
cat /etc/nsswitch.conf
resolvectl status
nmcli dev show | grep -i dns
scutil --dns

日志

1
2
journalctl -u dnsmasq -f
journalctl -u dnsmasq -n 200 --no-pager

抓包

1
2
3
sudo tcpdump -i eth0 -nn port 53
sudo tcpdump -i eth0 -nn port 67 or port 68
sudo tcpdump -i eth0 -nn port 69

chapter 25:FAQ

Q1:dnsmasq 能不能替代 BIND?

看场景。内网小型 DNS/DHCP 可以,企业复杂权威 DNS 不建议。

Q2:address 和 host-record 怎么选?

  • 单个主机:优先 host-record 或 addn-hosts。
  • 泛域名:用 address。
  • 反向解析强需求:用 host-record 或补 ptr-record。

Q3:为什么不要用 .local?

.local 常用于 mDNS,会被 Avahi、Bonjour、systemd-resolved 等处理。你用传统 DNS 配 .local,客户端不一定问 dnsmasq。

Q4:dnsmasq 修改配置后 reload 还是 restart?

  • 改 hosts、租约相关文件:reload 通常可以。
  • 改主配置、监听地址、DHCP、PXE:建议 restart。
1
sudo systemctl restart dnsmasq

Q5:为什么 resolv-file 里不要写自己?

例如:

1
resolv-file=/etc/dnsmasq-resolv.conf

如果里面写:

1
nameserver 127.0.0.1

就容易出现 dnsmasq 查询自己,形成转发环路。上游 DNS 文件应该写真正上游,例如:

1
2
nameserver 223.5.5.5
nameserver 119.29.29.29

或者直接用:

1
2
3
no-resolv
server=223.5.5.5
server=119.29.29.29

Q6:为什么 cache-size=10000 不一定好?

缓存大不是免费的。内网几十台机器没有必要上来就巨大缓存。一般:

1
cache-size=1000

已经够用。大量域名过滤、企业出口场景再逐步调大,并通过 SIGUSR1 观察统计。

Q7:OpenWrt 上配置写 /etc/dnsmasq.conf 还是 /etc/config/dhcp?

优先理解 OpenWrt 的 UCI:

  • 常规 DNS/DHCP 行为:写 /etc/config/dhcp;
  • 自定义高级规则:通过 confdir 加载 /etc/dnsmasq.d/*.conf;
  • 不建议和 UCI 重复定义同一项,否则容易冲突。

Q8:dnsmasq 能不能做广告屏蔽?

可以,典型写法:

1
2
address=/ads.example.com/0.0.0.0
address=/tracker.example.com/0.0.0.0

但大型广告规则维护、统计、Web 管理、自动更新,建议用 Pi-hole 或 AdGuard Home。

Q9:内网 DNS 要不要启用 DNSSEC?

不是第一优先级。先确保:

  • 内网域名不泄露;
  • 上游可靠;
  • 解析链路稳定;
  • 时间同步正常;
  • 日志和排障可控。

有这些基础后,再考虑 DNSSEC。

Q10:dnsmasq 能不能做生产?

可以,但要定义“生产”边界。

适合:

  • 小型办公室;
  • 实验室;
  • 单机房内部服务名;
  • PXE 装机网络;
  • OpenWrt/软路由;
  • 虚拟化测试环境。

不适合:

  • 大规模多地域企业 DNS;
  • 大量权威 Zone;
  • 强审计和审批;
  • 复杂主从复制;
  • 公网 DNS 服务。

参考资料

启示录

dnsmasq 的价值不在于“替代所有 DNS 工具”,而在于它把小网络里最常见的 DNS、DHCP、TFTP、PXE
串成了一条很短的路径。路径越短,越容易维护;配置越少,越不容易凌晨背锅。

富贵岂由人,时会高志须酬。

能成功于千载者,必以近察远。


使用 dnsmasq 搭建轻量级内网 DNS、DHCP 与 PXE 服务
https://allendericdalexander.github.io/2026/06/16/devops/infra/dnsmasq-private-dns-dhcp-pxe-hexo-blog/
作者
AtLuoFu
发布于
2026年6月16日
许可协议