Skip to content

11 - 加密传输指南 ​

概述 ​

TDNS 支持两种加密 DNS 传输协议,在传统 UDP/TCP DNS 之上提供加密和隐私保护:

协议RFC端口传输层特点
DNS-over-TLS (DoT)RFC 7858853TLS over TCP长连接、低开销
DNS-over-HTTPS (DoH)RFC 8484443TLS + HTTP/2浏览器兼容、穿透防火墙

加密传输功能需要启用 tls feature flag(默认启用)。

重要:DoT 和 DoH 共用 [tls] 段的证书和私钥(cert 和 key)。DoH 不需要单独配证书,复用 [tls] 的即可。监听 IP 统一使用 server.listen-addrs,各协议通过自己的 listen-port 指定端口。


快速上手:五步开启加密传输 ​

1. 获取 TLS 证书 ​

bash
# 方式一:Let's Encrypt(免费,推荐)
certbot certonly --standalone -d dns.example.com
# 证书路径:
# /etc/letsencrypt/live/dns.example.com/fullchain.pem
# /etc/letsencrypt/live/dns.example.com/privkey.pem

# 方式二:自签名证书(仅测试用)
openssl req -x509 -newkey rsa:2048 -keyout key.pem \
  -out cert.pem -days 365 -nodes \
  -subj "/CN=dns.example.com" \
  -addext "subjectAltName=DNS:dns.example.com"

2. 放置证书文件 ​

bash
sudo mkdir -p /etc/tdns
sudo cp fullchain.pem /etc/tdns/cert.pem
sudo cp privkey.pem /etc/tdns/key.pem
sudo chown tdns:tdns /etc/tdns/cert.pem /etc/tdns/key.pem
sudo chmod 640 /etc/tdns/cert.pem /etc/tdns/key.pem

3. 配置 ​

toml
# /etc/tdns/tdns.toml
[server]
listen-addrs = ["0.0.0.0", "::"]   # DoT/DoH 复用此地址
listen-port = 53
mode = "recursive"                 # 任意模式均可

[recursion]
enabled = true

[access-control]
allow-query = ["any"]
allow-recursion = ["any"]

# DNS-over-TLS
[tls]
enabled = true
listen-port = 853
cert = "/etc/tdns/cert.pem"         # 必填
key = "/etc/tdns/key.pem"           # 必填

# DNS-over-HTTPS(复用 [tls] 的证书)
[doh]
enabled = true
listen-port = 443
path = "/dns-query"

4. 开放防火墙 ​

bash
# firewalld
sudo firewall-cmd --permanent --add-port=853/tcp   # DoT
sudo firewall-cmd --permanent --add-port=443/tcp   # DoH
sudo firewall-cmd --reload

# 或 iptables
sudo iptables -A INPUT -p tcp --dport 853 -j ACCEPT
sudo iptables -A INPUT -p tcp --dport 443 -j ACCEPT

5. 验证配置 + 启动 ​

bash
# 验证配置
tdns --validate --config /etc/tdns/tdns.toml

# 启动
sudo systemctl start tdns

# 测试 DoT
kdig @dns.example.com example.com +tls

# 测试 DoH
curl -s -H "Content-Type: application/dns-message" \
  --data-binary @query.bin \
  https://dns.example.com/dns-query

# 或用 kdig
kdig @dns.example.com example.com +https

DNS-over-TLS (DoT) ​

工作原理 ​

DoT 在 TCP DNS 的基础上增加 TLS 加密层。使用 2 字节长度前缀 + TLS 加密的 DNS 消息格式,支持单连接多查询(长连接复用)。

连接管理 ​

参数默认值说明
最大并发连接1024(TCP Semaphore)超限拒绝新连接
空闲超时120 秒空闲连接超过此时间自动断开
最大消息大小65535 字节与 TCP DNS 一致
TCP_NODELAY启用禁用 Nagle 算法,降低延迟

TLS 实现 ​

  • 密码库:rustls + ring(无 OpenSSL 依赖)
  • 协议版本:TLS 1.2 / TLS 1.3(safe default)
  • 证书格式:PEM(支持证书链)
  • 私钥格式:PEM(RSA / ECDSA / Ed25519)
  • ALPN:dot(RFC 8310)
  • 客户端认证:不需要(with_no_client_auth)

客户端配置 ​

bash
# 使用 kdig (Knot DNS) 测试 DoT
kdig -d @dns.example.com example.com +tls

# dig 原生不支持 DoT,需第三方工具

# Android / iOS 设备
# 设置 → 网络 → 私人 DNS → dns.example.com

DNS-over-HTTPS (DoH) ​

工作原理 ​

DoH 将 DNS 消息封装在 HTTPS 请求中,使用 HTTP/2 over TLS 传输。支持 GET 和 POST 两种方法。

请求方法 ​

方法URL请求体内容类型
POSThttps://dns.example.com/dns-queryDNS wire-format 二进制application/dns-message
GEThttps://dns.example.com/dns-query?dns=<base64url>无(参数编码)application/dns-message

GET 方法的 dns 参数使用 base64url 编码(无 padding),RFC 8484 §4.1 规定。

响应格式 ​

  • 状态码:200(成功)+ Content-Type: application/dns-message
  • 响应体:DNS wire-format 二进制数据
  • 错误处理:
    • 400 Bad Request:DNS 消息格式错误
    • 413 Payload Too Large:请求体超过 65535+512 字节
    • 500 Internal Server Error:响应编码失败

HTTP/2 强制 ​

DoH 强制使用 HTTP/2(通过 TLS ALPN 协商 h2 协议)。不支持 HTTP/1.1 降级。

CORS 支持 ​

DoH 端点启用 CorsLayer::permissive(),允许浏览器跨域请求。适合前端应用直接调用 DoH 端点。

客户端配置 ​

bash
# 使用 curl 测试 DoH POST
curl -s -H "Content-Type: application/dns-message" \
  --data-binary @query.bin \
  https://dns.example.com/dns-query

# 使用 curl 测试 DoH GET(需 base64url 编码查询)
curl -s "https://dns.example.com/dns-query?dns=AAABAAABAAAAAAAAB2V4YW1wbGUDY29tAA==" \
  -H "Accept: application/dns-message"

# 使用 kdig
kdig @dns.example.com example.com +https

共享 TLS 配置 ​

DoT 和 DoH 共用同一套证书和私钥:

toml
[tls]
enabled = true
listen-port = 853
cert = "/etc/tdns/cert.pem"
key = "/etc/tdns/key.pem"

[doh]
enabled = true
listen-port = 443
path = "/dns-query"
# DoH 自动复用 [tls] 的 cert 和 key,无需重复配置

证书需包含正确的 Subject Alternative Name (SAN),覆盖 DoT 和 DoH 使用的域名。


证书自动续期 ​

certbot + cron ​

bash
# 续期脚本
echo "0 3 * * * certbot renew --quiet --deploy-hook 'systemctl reload tdns'" | crontab -

续期成功后通过 --deploy-hook 触发 TDNS 热重载(SIGHUP),无需停机。

证书路径权限 ​

确保 TDNS 进程用户对证书文件有读权限:

bash
# 将 tdns 用户加入 ssl-cert 组(Debian/Ubuntu)
sudo usermod -aG ssl-cert tdns
sudo chmod 640 /etc/letsencrypt/live/*/privkey.pem
sudo chmod 644 /etc/letsencrypt/live/*/fullchain.pem

证书管理日常操作 ​

bash
# 查看证书有效期
openssl x509 -in /etc/tdns/cert.pem -noout -dates

# 检查证书 SAN
openssl x509 -in /etc/tdns/cert.pem -noout -text | grep -A1 "Subject Alternative Name"

# 证书过期告警(过期前 30 天提醒)
echo "0 9 * * * openssl x509 -in /etc/tdns/cert.pem -checkend 2592000 -noout || echo 'Certificate expiring soon!'" | crontab -

# 手动续期
sudo certbot renew --quiet
sudo systemctl reload tdns

端口与防火墙 ​

协议端口防火墙规则
UDP DNS53/udpiptables -A INPUT -p udp --dport 53 -j ACCEPT
TCP DNS53/tcpiptables -A INPUT -p tcp --dport 53 -j ACCEPT
DoT853/tcpiptables -A INPUT -p tcp --dport 853 -j ACCEPT
DoH443/tcpiptables -A INPUT -p tcp --dport 443 -j ACCEPT

DoT 和 DoH 可与标准 DNS 同时运行,客户端按需选择协议。


协议选择建议 ​

场景推荐协议原因
移动设备隐私 DNSDoT低开销、Android 原生支持
浏览器应用DoHHTTP/2 兼容、穿透防火墙
企业内部加密 DNSDoT长连接高效、运维简单
穿透限制 53 端口的网络DoH使用 443 端口
高性能加密 DNSDoTTLS 握手开销低于 HTTPS
需要 CORS 的 Web 应用DoH内置 CORS 支持

生产环境建议同时启用 DoT(端口 853)和 DoH(端口 443),覆盖所有客户端类型。


指标监控 ​

加密传输相关 Prometheus 指标:

bash
# 查看加密连接指标
curl -s http://localhost:9090/metrics | grep -E "connections|traffic"
指标标签说明
tdns_active_connectionsprotocol="dot" / "doh"当前活跃连接数
tdns_connections_totalprotocol="dot" / "doh"累计连接数
tdns_traffic_bytes_totalprotocol, direction="rx"/"tx"收发字节数
tdns_queries_totalprotocol="dot" / "doh"查询计数

详细监控配置见 15-监控与可观测性。