Skip to content

12 - 安全与访问控制 ​

概述 ​

TDNS 内置多层安全防护机制,覆盖访问控制、速率限制、缓存投毒防护、传输安全和 API 认证。大部分防护默认生效,无需配置。


ACL 访问控制 ​

快速上手 ​

toml
# 定义命名 ACL 组
[acl.trusted]
addresses = ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"]

[acl.management]
addresses = ["127.0.0.1", "::1"]

# 绑定到访问控制规则
[access-control]
allow-query = ["trusted", "management"]    # 谁可以查询
allow-recursion = ["trusted"]              # 谁可以递归(必填,启用递归时)
allow-transfer = ["management"]           # 谁可以区域传输

内置关键字 ​

关键字含义匹配范围
any匹配所有地址0.0.0.0/0 + ::/0
none匹配空(拒绝所有)无匹配规则
localhost本机地址127.0.0.0/8 + ::1/128

内联 CIDR ​

规则中可直接使用 CIDR 或 IP 地址,无需定义 ACL 组:

toml
[access-control]
allow-query = ["any"]                       # 全部允许
allow-recursion = ["10.0.0.0/8", "::1/128"] # 内联 CIDR
allow-transfer = ["none"]                   # 全部禁止

CIDR 匹配 ​

格式示例说明
IPv4 CIDR10.0.0.0/8匹配 10.0.0.0 ~ 10.255.255.255
IPv4 主机10.0.0.1等同 /32
IPv6 CIDR2001:db8::/32匹配 2001:db8::/32
IPv6 主机::1等同 /128

IPv4 与 IPv6 不交叉匹配:IPv4 规则不匹配 IPv6 地址,反之亦然。

空规则行为 ​

规则为空行为
allow-query = []允许所有查询(无限制)
allow-recursion = []允许所有递归(危险!)
allow-transfer = []允许所有传输

启用递归时强制要求配置 allow-recursion,否则拒绝启动(防开放递归)。

查询处理中的 ACL 检查 ​

查询到达
  │
  ├─ allow-query 检查 → 不通过 → REFUSED
  │
  ├─ 权威区域命中 → 返回(不需要递归 ACL)
  │
  ├─ allow-recursion 检查 → 不通过 → REFUSED
  │
  └─ 执行递归/转发

速率限制 (RRL) ​

工作原理 ​

RRL(Response Rate Limiting)基于令牌桶算法,限制相同响应(按客户端网络前缀 + 查询名分组)的发送速率,缓解 DNS 放大攻击。

遵循 BIND RRL 设计:客户端按 /24(IPv4)或 /64(IPv6)分组,超限后丢弃大部分响应,仅每 slip 次放行一个截断响应(TC=1),引导合法客户端切换到 TCP 重试。

配置 ​

toml
[security.rrl]
enabled = true                  # 总开关(默认启用)
responses-per-second = 10       # 每秒最大相同响应数
slip = 2                        # 每 2 次超限放行 1 次
window = 5                      # 空闲桶存活秒数

令牌桶行为 ​

状态行为
令牌 ≥ 1消费 1 个令牌,允许响应(Allow)
令牌 < 1 且 slip > 0每 slip 次放行 1 个截断响应(Slip),其余丢弃(Drop)
令牌 < 1 且 slip = 0全部丢弃(Drop)

令牌按 responses-per-second 速率恢复,最大容量为 1 秒的令牌数(允许短时突发)。

分组键 ​

键 = (客户端网络前缀, 查询名)
  • IPv4 客户端 → /24 前缀(前 3 个八位组)
  • IPv6 客户端 → /64 前缀(前 8 个八位组)
  • 查询名 → 小写归一化,补全 FQDN

相同 /24 或 /64 内的客户端共享令牌桶,防止单 IP 切换绕过限制。

RRL 决策类型 ​

决策调用方行为
Allow正常发送完整响应
Slip发送截断响应(TC=1),引导客户端 TCP 重试
Drop静默丢弃响应

监控 RRL ​

bash
# 查看 RRL 丢弃统计
curl -s http://localhost:9090/metrics | grep rrl

# 指标
# tdns_rrl_dropped_total{reason="rate_limited"} 500
# tdns_rrl_dropped_total{reason="slipped"} 50

缓存投毒防护 ​

TDNS 对递归和转发返回的上游响应实施多重验证,默认生效,无需配置:

验证项机制失败处理
TXID 匹配响应 TXID 必须与查询 TXID 一致丢弃,标记 SpoofedResponse
源 IP 匹配响应源 IP 必须与上游 IP 一致丢弃,标记 SpoofedResponse
源端口匹配响应源端口必须与上游端口一致丢弃,标记 SpoofedResponse
响应方向必须是响应消息(QR=1),非查询忽略

三项验证任一不通过,响应被丢弃并标记为欺骗响应(SpoofedResponse),自动尝试下一个上游。


查询安全验证 ​

以下检查默认生效:

检查项行为RFC 参考
Opcode 验证仅接受 QUERY (0),其他返回 NOTIMPRFC 6895
ANY 查询拒绝返回 HINFO 替代 ANYRFC 8482
CNAME 循环检测HashSet 跟踪已访问名称—
查询深度限制CNAME 链最大追踪深度可配置(默认 16)—
负缓存容量限制最大 50,000 条目—
正缓存容量保护插入时二次容量检查—

API 安全 ​

Bearer Token 认证 ​

toml
[api]
enabled = true
listen = "127.0.0.1:8080"       # 单个 IP:Port 字符串
auth-token = "your-secure-random-token"   # 必填

所有 API 请求必须在 Authorization 头中携带 Bearer Token:

bash
curl -H "Authorization: Bearer your-secure-random-token" \
  http://127.0.0.1:8080/api/v1/server

未携带或 Token 不匹配的请求返回 401 Unauthorized。

生成强 Token ​

bash
# 生成 256 位随机 Token
openssl rand -hex 32

CORS 安全 ​

REST API 启用 CORS(跨域资源共享)层,默认禁止跨域。需要浏览器前端调用时配置具体来源:

toml
[api]
enabled = true
listen = "127.0.0.1:8080"
auth-token = "your-token"
cors-origins = ["https://admin.example.com"]

API 绑定地址 ​

建议将 API 监听地址限制为本机或内网:

toml
[api]
listen = "127.0.0.1:8080"     # 仅本机访问(推荐)
# listen = "10.0.0.1:8080"   # 内网管理口
# listen = "0.0.0.0:8080"    # 不推荐!需配合防火墙

TSIG 认证 ​

TSIG(Transaction SIGnature)用于动态更新(DDNS)和区域传输的认证。

1. 生成密钥 ​

bash
# 使用 tsig-keygen(BIND 工具)
tsig-keygen -a HMAC-SHA256 update-key

# 或手动生成
echo -n "your-secret" | base64

2. 配置 ​

toml
[[keys]]
name = "update-key."
algorithm = "hmac-sha256"
secret = "<base64-encoded-secret>"

[[zones]]
name = "example.com"
file = "/etc/tdns/zones/example.com.zone"
allow-update = ["update-key."]    # 允许此密钥执行 DDNS
allow-transfer = ["management"]   # 区域传输 ACL

3. 使用 nsupdate 发送动态更新 ​

bash
nsupdate -k Kupdate-key.+157+12345.key << EOF
server 127.0.0.1 53
zone example.com
update add www.example.com 300 A 192.0.2.100
show
send
EOF

4. 监控 TSIG ​

bash
curl -s http://localhost:9090/metrics | grep tsig
# tdns_tsig_results_total{result="valid"}    # 验证成功
# tdns_tsig_results_total{result="invalid"}  # 验证失败

TSIG + DDNS 完整使用流程见 07-权威DNS服务。


安全配置检查清单 ​

部署前逐项检查:

  • [ ] allow-recursion 已限制为内网地址(非空、非 any)
  • [ ] allow-query 已根据需求限制范围
  • [ ] allow-transfer 已限制为管理网段
  • [ ] RRL 已启用(security.rrl.enabled = true,默认启用)
  • [ ] API auth-token 已设置且足够复杂
  • [ ] API listen 非 0.0.0.0(除非有防火墙保护)
  • [ ] DoT/DoH 证书有效且定期续期
  • [ ] 缓存投毒防护已生效(默认启用,无需配置)
  • [ ] 负缓存容量限制已设置(max-negative-entries)
  • [ ] 服务以非 root 用户运行(特权端口通过 capabilities 授权)