使用 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/hostsaddn-hostshost-recordaddress 提供本地域名 nas.home.arpagitlab.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.arpanas.home.arpajenkins.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 提供 dignslookup 等工具。

RHEL / CentOS Stream / Rocky Linux / AlmaLinux / Fedora

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

bind-utils 提供 dignslookuphost 等工具。

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:几个核心配置项解释

interfacelisten-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-interfacesbind-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-resolvserver

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-orderall-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:addresshost-record 怎么选?

  • 单个主机:优先 host-recordaddn-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日
许可协议