Ubuntu 22.04 安装 djbdns 1.05 指南
Ubuntu 22.04 安装 djbdns 1.05 全流程指南:apt 发行版包安装与官方源码编译(含 errno/TLS 补丁排障)双方案,tinydns 权威服务器与 dnscache 递归缓存的配置、启动与 dig 验证,附常见问题与卸载清理。
Ubuntu 22.04 安装 djbdns 1.05 指南
目标:从零开始在 Ubuntu 22.04 服务器上安装 djbdns 1.05,完成 tinydns 权威服务器与 dnscache 递归缓存的配置、启动与 dig 验证。本篇已在 Ubuntu 22.04(glibc 2.35)环境实测通过,覆盖发行版包安装与源码编译两种方案,并记录了现代 Linux 下编译 errno/TLS 报错的排障过程。
一、环境信息
- 操作系统:Ubuntu 22.04 LTS(glibc 2.35,内核 5.15+)
- 架构:x86_64
- djbdns 版本:1.05(Ubuntu 打包版
1:1.05-15fakesync1) - 依赖:daemontools 0.76、ucspi-tcp
- tinydns 监听:10.0.0.1:53(UDP+TCP)
- dnscache 监听:127.0.0.1:53(UDP+TCP)
- 测试域名:example.test
- 查询日期:2026-10-25
二、前置条件
- 一台 Ubuntu 22.04 服务器,具有 root 权限
- 服务器已配置好网络,能访问 Ubuntu 软件源或官方源码站点
- 53 端口(UDP+TCP)未被其他 DNS 服务占用(如 systemd-resolved 占用 127.0.0.53:53 时,需将 djbdns 绑定到其他 IP)
- 已创建专用运行用户(安装脚本会自动创建,也可手动创建)
⚠️ 注意:如果服务器上已有 systemd-resolved 或其他 DNS 服务监听 53 端口,需要为 tinydns/dnscache 指定不同的监听 IP,避免端口冲突。Ubuntu 22.04 默认 systemd-resolved 监听 127.0.0.53:53,因此 dnscache 可以绑定 127.0.0.1:53(不冲突),tinydns 绑定对外网卡 IP。
三、版本选型说明
djbdns 上游最后一个版本是 1.05(2001 年发布),此后无更新。各发行版通过打包补丁维护,主要修复现代 Linux 下的编译问题和安全补丁。
| 方案 | 版本 | 来源 | 适用场景 |
|---|---|---|---|
| 发行版包安装 | 1:1.05-15fakesync1 | Ubuntu universe 源 | 快速部署、生产环境 |
| 源码编译 | 1.05 | cr.yp.to/djbdns/djbdns-1.05.tar.gz | 需要定制、学习排障 |
💡 说明:Ubuntu 的打包版已经包含 errno 编译补丁和 2008 年 DNS 缓存投毒修复补丁(源端口随机化),开箱即用。源码编译需要手动打 errno 补丁。
四、在线安装方案(A:apt 发行版包)
4.1 方案说明
Ubuntu 22.04 的 universe 仓库提供了 djbdns 的拆分包:tinydns(权威服务器二进制)、dnscache(递归缓存二进制)、djbdns-conf(*-conf 配置工具)、djbdns-utils(客户端 CLI 工具)。同时需要安装 daemontools 和 daemontools-run 用于进程监管。
4.2 安装软件包
apt update
apt install -y tinydns dnscache djbdns-conf djbdns-utils daemontools daemontools-run ucspi-tcp
安装完成后,关键二进制位于:
ls -la /usr/sbin/tinydns /usr/sbin/dnscache /usr/sbin/tinydns-data /usr/sbin/tinydns-conf /usr/sbin/dnscache-conf
ls -la /usr/bin/dnsq /usr/bin/dnstrace /usr/bin/dnsfilter /usr/bin/random-ip
实测安装后二进制路径:
/usr/sbin/tinydns
/usr/sbin/dnscache
/usr/sbin/tinydns-data
/usr/sbin/tinydns-conf
/usr/sbin/dnscache-conf
/usr/bin/dnsq
/usr/bin/dnstrace
/usr/bin/dnsfilter
/usr/bin/random-ip
4.3 配置 tinydns 权威服务器
首先创建专用运行用户:
useradd -r -s /usr/sbin/nologin tinydns
useradd -r -s /usr/sbin/nologin dnslog
使用 tinydns-conf 生成服务目录:
tinydns-conf tinydns dnslog /etc/tinydns 10.0.0.1
参数说明:
tinydns:运行 tinydns 的用户dnslog:运行日志进程的用户/etc/tinydns:服务目录10.0.0.1:tinydns 监听的 IP 地址
生成的目录结构:
ls -la /etc/tinydns/
drwxr-sr-t 5 root root 4096 Oct 25 11:53 .
drwxr-xr-x 1 root root 4096 Oct 25 11:53 ..
drwxr-sr-x 2 root root 4096 Oct 25 11:53 env
drwxr-xr-x 3 root root 4096 Oct 25 11:53 log
drwxr-sr-x 2 root root 4096 Oct 25 11:53 root
-rwxr-xr-x 1 root root 93 Oct 25 11:53 run
环境变量文件:
cat /etc/tinydns/env/IP
cat /etc/tinydns/env/ROOT
10.0.0.1
/etc/tinydns/root
run 脚本内容:
cat /etc/tinydns/run
#!/bin/sh
exec 2>&1
exec envuidgid tinydns envdir ./env softlimit -d400000 /usr/sbin/tinydns
4.4 添加 DNS 记录
进入数据目录,使用辅助脚本添加记录:
cd /etc/tinydns/root
./add-host ns1.example.test 10.0.0.1
./add-alias www.example.test 10.0.0.1
./add-alias mail.example.test 10.0.0.1
./add-mx example.test 10.0.0.1 10
./add-ns example.test 10.0.0.1
手动添加 CNAME、TXT 和反向域 NS 记录:
echo "Cftp.example.test:www.example.test:3600" >> data
echo "'txt.example.test:v=spf1 include\072example.test ~all:3600" >> data
echo ".1.0.0.10.in-addr.arpa:10.0.0.1:a:259200" >> data
⚠️ 注意:TXT 记录中的冒号必须用八进制转义
\072,否则tinydns-data会把冒号当作字段分隔符截断文本。这是实测中踩到的坑:直接写v=spf1 include:example.test ~all会被解析成 TTL 为example.test ~all:3600(无效),TXT 内容只剩v=spf1 include。
查看生成的 data 文件:
cat data
=ns1.example.test:10.0.0.1:86400
+www.example.test:10.0.0.1:86400
+mail.example.test:10.0.0.1:86400
@example.test:10.0.0.1:a::86400
.example.test:10.0.0.1:a:259200
Cftp.example.test:www.example.test:3600
'txt.example.test:v=spf1 include\072example.test ~all:3600
.1.0.0.10.in-addr.arpa:10.0.0.1:a:259200
4.5 编译数据并启动 tinydns
编译 data 文件为 data.cdb:
cd /etc/tinydns/root
make
/usr/sbin/tinydns-data
确认 data.cdb 已生成:
ls -la data.cdb
-rw-r--r-- 1 root root 2848 Oct 25 11:54 data.cdb
启动 tinydns(使用 daemontools supervise 或直接后台运行):
cd /etc/tinydns
setsid envuidgid tinydns envdir ./env softlimit -d400000 /usr/sbin/tinydns </dev/null >/var/log/tinydns.log 2>&1 &
确认进程运行:
ps aux | grep "[t]inydns"
tinydns 3448 0.0 0.0 2776 1420 ? Ss 11:54 0:00 /usr/sbin/tinydns
4.6 验证 tinydns
使用 dig 查询各类型记录:
dig @10.0.0.1 www.example.test A +short
dig @10.0.0.1 example.test MX +short
dig @10.0.0.1 ftp.example.test CNAME +short
dig @10.0.0.1 txt.example.test TXT +short
dig @10.0.0.1 example.test NS +short
dig @10.0.0.1 -x 10.0.0.1 +short
实测回显:
=== dig A www.example.test ===
10.0.0.1
=== dig MX example.test ===
0 a.mx.example.test.
=== dig CNAME ftp.example.test ===
www.example.test.
=== dig TXT txt.example.test ===
"v=spf1 include:example.test ~all"
=== dig NS example.test ===
a.ns.example.test.
=== dig PTR 10.0.0.1 ===
ns1.example.test.
完整 A 查询回显:
dig @10.0.0.1 www.example.test A
; <<>> DiG 9.18.39-0ubuntu0.22.04.7-Ubuntu <<>> @10.0.0.1 www.example.test A
; (1 server found)
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 18855
;; flags: qr aa rd; QUERY: 1, ANSWER: 1, AUTHORITY: 1, ADDITIONAL: 1
;; WARNING: recursion requested but not available
;; QUESTION SECTION:
;www.example.test. IN A
;; ANSWER SECTION:
www.example.test. 86400 IN A 10.0.0.1
;; AUTHORITY SECTION:
example.test. 259200 IN NS a.ns.example.test.
;; ADDITIONAL SECTION:
a.ns.example.test. 259200 IN A 10.0.0.1
;; Query time: 0 msec
;; SERVER: 10.0.0.1#53(10.0.0.1) (UDP)
💡 说明:
WARNING: recursion requested but not available是正常的,因为 tinydns 是纯权威服务器,不提供递归服务。响应中的aa(authoritative answer)标志表示这是权威应答。
4.7 配置 dnscache 递归缓存
创建 dnscache 用户并生成服务目录:
useradd -r -s /usr/sbin/nologin dnscache
dnscache-conf dnscache dnslog /etc/dnscache 127.0.0.1
⚠️ 注意:dnscache 默认监听 0.0.0.0:53,会与 tinydns(监听 10.0.0.1:53)冲突。这里改为监听 127.0.0.1,避免端口冲突。
修改监听 IP:
echo "127.0.0.1" > /etc/dnscache/env/IP
允许内网客户端递归查询(创建以 IP 前缀命名的空文件):
touch /etc/dnscache/root/ip/10.0.0
touch /etc/dnscache/root/ip/127.0.0.1
查看 dnscache 环境变量:
for f in /etc/dnscache/env/*; do echo "$(basename $f): $(cat $f)"; done
CACHESIZE: 1000000
DATALIMIT: 4000000
IP: 127.0.0.1
IPSEND: 0.0.0.0
ROOT: /etc/dnscache/root
根服务器列表(内置 13 个根服务器 IP):
cat /etc/dnscache/root/servers/@
198.41.0.4
192.228.79.201
192.33.4.12
128.8.10.90
192.203.230.10
192.5.5.241
192.112.36.4
128.63.2.53
192.36.148.17
192.58.128.30
193.0.14.129
199.7.83.42
202.12.27.33
启动 dnscache:
cd /etc/dnscache
setsid envdir ./env sh -c "exec envuidgid dnscache softlimit -o250 -d 4000000 /usr/sbin/dnscache" </dev/null >/var/log/dnscache.log 2>&1 &
验证递归解析:
dig @127.0.0.1 google.com A +short
142.251.46.78
五、源码编译方案(B:官方源码 + errno 补丁)
5.1 方案说明
当发行版不提供 djbdns 包,或需要定制编译时,从官方站点下载源码编译。现代 Linux(glibc 2.34+)下必须应用 errno 补丁,否则链接失败。
5.2 安装编译依赖
apt install -y gcc make wget
5.3 下载源码
cd /usr/local/src
wget https://cr.yp.to/djbdns/djbdns-1.05.tar.gz
tar xzf djbdns-1.05.tar.gz
cd djbdns-1.05
5.4 直接编译(复现 errno 报错)
echo "gcc -O2" > conf-cc
make
实测报错(关键输出):
/usr/bin/ld: errno: TLS definition in /lib/x86_64-linux-gnu/libc.so.6 section .tbss mismatches non-TLS reference in buffer.a(buffer_put.o)
/usr/bin/ld: /lib/x86_64-linux-gnu/libc.so.6: error adding symbols: bad value
collect2: error: ld returned 1 exit status
make: *** [Makefile:26: auto-str] Error 1
⚠️ 排障实录:这个报错是 djbdns 在现代 glibc 上的经典问题。原因是 djbdns 源码中
error.c定义了全局变量errno,而现代 glibc 把errno改为线程局部存储(TLS)宏,两者冲突导致链接器报错。官方安装文档给出的解决方案是在编译时强制包含<errno.h>,让 glibc 的errno宏覆盖源码中的定义。
5.5 应用 errno 补丁并重新编译
按照官方安装文档的说明,修改 conf-cc 加入 -include /usr/include/errno.h:
echo "gcc -O2 -include /usr/include/errno.h" > conf-cc
cat conf-cc
gcc -O2 -include /usr/include/errno.h
清理并重新编译:
make clean
make
编译成功后检查生成的二进制:
ls -la tinydns dnscache tinydns-data dnsq dnstrace dnsfilter random-ip
-rwxr-xr-x 1 root root 71936 Oct 25 12:00 dnscache
-rwxr-xr-x 1 root root 39264 Oct 25 12:00 dnsfilter
-rwxr-xr-x 1 root root 47424 Oct 25 12:00 dnsq
-rwxr-xr-x 1 root root 51520 Oct 25 12:00 dnstrace
-rwxr-xr-x 1 root root 18688 Oct 25 12:00 random-ip
-rwxr-xr-x 1 root root 35104 Oct 25 12:00 tinydns
-rwxr-xr-x 1 root root 35072 Oct 25 12:00 tinydns-data
5.6 安装到系统
make setup check
此命令会把二进制安装到 /usr/local/bin 和 /usr/local/sbin,并安装 dnsroots.global 到 /etc/dnsroots.global。后续配置步骤与方案 A 相同(tinydns-conf、dnscache-conf、添加记录、编译、启动、验证)。
六、方案对比与选择建议
| 维度 | 方案 A:apt 发行版包 | 方案 B:源码编译 |
|---|---|---|
| 版本 | 1:1.05-15fakesync1(含维护补丁) | 1.05(原始上游) |
| 是否需联网 | 是(apt 源) | 是(下载源码) |
| 编译复杂度 | 无需编译 | 需 gcc + make + errno 补丁 |
| 安全补丁 | 已包含源端口随机化等补丁 | 需自行评估补丁 |
| 安装路径 | /usr/sbin、/usr/bin | /usr/local/sbin、/usr/local/bin |
| 适合场景 | 生产环境、快速部署 | 定制需求、学习排障 |
选择建议:生产环境优先选方案 A(apt 包),已包含必要补丁且便于后续升级;只有在发行版无包或需要深度定制时才选方案 B。
七、配置说明
7.1 tinydns 关键配置
/etc/tinydns/env/IP:tinydns 监听的 IP 地址,由 tinydns-conf 生成/etc/tinydns/env/ROOT:数据目录(含 data 和 data.cdb)/etc/tinydns/root/data:文本格式的 DNS 记录源文件/etc/tinydns/root/data.cdb:编译后的二进制数据库,tinydns 实际读取此文件
7.2 dnscache 关键配置
/etc/dnscache/env/IP:dnscache 监听的 IP/etc/dnscache/env/CACHESIZE:缓存大小(字节),默认 1000000/etc/dnscache/env/DATALIMIT:进程数据段上限(字节),默认 4000000/etc/dnscache/root/servers/@:根服务器 IP 列表/etc/dnscache/root/ip/<前缀>:允许递归的客户端 IP 前缀
7.3 进程监管
djbdns 设计上由 daemontools 监管。生产环境建议通过 daemontools 的 svscan 启动服务,将服务目录软链接到 /etc/service/:
ln -s /etc/tinydns /etc/service/tinydns
ln -s /etc/dnscache /etc/service/dnscache
daemontools 会自动拉起 run 脚本,并在进程退出时自动重启。
八、常见问题
8.1 编译报错 errno: TLS definition mismatches
现象:make 时报 errno: TLS definition in libc.so.6 section .tbss mismatches non-TLS reference。
原因:djbdns 源码中 error.c 定义了全局 errno,与现代 glibc 的 TLS 版 errno 冲突。
解决:在 conf-cc 中加 -include /usr/include/errno.h:
echo "gcc -O2 -include /usr/include/errno.h" > conf-cc
make clean && make
8.2 tinydns 启动失败 fatal: $GID not set
现象:直接运行 tinydns 报 fatal: $GID not set。
原因:tinydns 需要通过 envuidgid 设置 $UID 和 $GID 环境变量来降权运行,不能直接以 root 运行。
解决:使用 run 脚本中的完整命令启动:
envuidgid tinydns envdir ./env softlimit -d400000 /usr/sbin/tinydns
8.3 PTR 查询超时或无响应
现象:A 记录查询正常,但 PTR 反向查询超时。
原因:tinydns 虽然在 data 中通过 = 记录生成了 PTR 记录,但如果没有为反向域(如 1.0.0.10.in-addr.arpa)声明 NS 记录(. 行),tinydns 不认为自己对该反向域有权威,因而不响应 PTR 查询。
解决:在 data 文件中添加反向域的 NS 记录:
.1.0.0.10.in-addr.arpa:10.0.0.1:a:259200
重新 make 编译后,PTR 查询即可正常返回。
8.4 TXT 记录内容被截断
现象:TXT 记录中的内容在冒号处被截断,如 v=spf1 include:example.test 变成 v=spf1 include。
原因:tinydns-data 以冒号为字段分隔符,TXT 文本中的冒号被误解析。
解决:TXT 文本中的冒号用八进制转义 \072:
'txt.example.test:v=spf1 include\072example.test ~all:3600
8.5 53 端口冲突
现象:启动时提示 address already in use。
原因:服务器上已有其他 DNS 服务(如 systemd-resolved)占用 53 端口。
解决:将 tinydns/dnscache 绑定到不同 IP。例如 tinydns 绑定对外网卡 IP,dnscache 绑定 127.0.0.1。确保两者不监听同一 IP 的 53 端口。
九、卸载清理
9.1 停止服务
svc -d /etc/service/tinydns
svc -d /etc/service/dnscache
9.2 移除 daemontools 服务链接
rm -f /etc/service/tinydns
rm -f /etc/service/dnscache
9.3 卸载软件包(方案 A)
apt remove -y tinydns dnscache djbdns-conf djbdns-utils daemontools daemontools-run ucspi-tcp
apt autoremove -y
9.4 删除服务目录和用户
rm -rf /etc/tinydns /etc/dnscache
userdel tinydns 2>/dev/null
userdel dnscache 2>/dev/null
userdel dnslog 2>/dev/null
9.5 源码编译安装的清理(方案 B)
cd /usr/local/src/djbdns-1.05
make clean
rm -rf /usr/local/src/djbdns-1.05
rm -f /usr/local/sbin/tinydns /usr/local/sbin/dnscache /usr/local/sbin/tinydns-data
rm -f /usr/local/bin/dnsq /usr/local/bin/dnstrace /usr/local/bin/dnsfilter
十、参考链接
- djbdns 官方安装文档:https://cr.yp.to/djbdns/install.html (查询日期 2026-10-25)
- djbdns 安全担保:https://cr.yp.to/djbdns/guarantee.html (查询日期 2026-10-25)
- tinydns-data 数据格式:https://cr.yp.to/djbdns/tinydns-data.html (查询日期 2026-10-25)
- djbdns 下载:https://cr.yp.to/djbdns/djbdns-1.05.tar.gz (查询日期 2026-10-25)
- Ubuntu djbdns 包:https://packages.ubuntu.com/jammy/djbdns-conf (查询日期 2026-10-25)