DPD 在 RadSec 里的等价物是 Status-Server;开它就有保活/判死能力。
动态发现优先用 SRV 就能跑,NAPTR 是“SRV 的上一层”,需要时再加。
最小可行:本机起个 dnsmasq 写 SRV 记录 → radsecproxy 用 DynamicLookupCommand srv:_radsec._tcp → StatusServer auto。
生产化关键:证书主机名匹配(CN/SAN)、阻塞首包(BlockingStartup)、避免重复 include。


0️⃣ 背景:为什么要把 DPD 和动态发现绑在一起?

RADIUS/UDP 天生“无连接”,上游死了就换一个;
RADIUS/TLS(RadSec)是长连接:需要你自己探测对端(DPD)、断开重连,并且当你启用了动态对端发现(DNS NAPTR/SRV)时,上游 peer 可能随时间变化。

所以要做的是:

  1. 用 DNS(NAPTR/SRV)发现谁可用;
  2. 用 Status-Server 做 DPD,发现谁“死了”;
  3. 配合 LB/Fail-over,把请求发给“当前最健康”的那个。

1️⃣ NAPTR / SRV:一句话原理 + 示例

SRV: 直接告诉客户端“某服务在哪个主机名、哪个端口、优先级/权重是多少”。
例如:

1
_radsec._tcp.edu.test → radius-a.edu.test:2083, radius-b.edu.test:2083

NAPTR: 再高一层的“指路牌”。
它先返回“服务类型 + 下一跳域名”,通常把你引导到 SRV 名称,然后再走 SRV。
适合多协议/多传输的选择(如 radius.tls / radius.udp)。


1.1 SRV 最小示例(建议先用它跑通)

1
2
3
4
5
6
7
; _radsec._tcp SRV 指向两台上游
_radsec._tcp.edu.test. 3600 IN SRV 50 10 2083 radius-a.edu.test.
_radsec._tcp.edu.test. 3600 IN SRV 50 20 2083 radius-b.edu.test.

; A 记录
radius-a.edu.test. 3600 IN A 192.168.8.228
radius-b.edu.test. 3600 IN A 192.168.8.179

1.2 NAPTR 示例(可选再加)

1
2
; NAPTR:把 edu.test 的 RADIUS/TLS 引导到 _radsec._tcp.edu.test(再走 SRV)
edu.test. 3600 IN NAPTR 100 10 "s" "x-eduroam:radius.tls" "" _radsec._tcp.edu.test.

流程:
客户端(这里是 radsecproxy)查 NAPTR edu.test → 得到 _radsec._tcp.edu.test → 再查 SRV → 解析到主机与端口。


2️⃣ 最小可行环境:用 dnsmasq 在本机出 SRV

1
2
3
4
5
6
7
8
9
10
sudo apt-get install -y dnsmasq

# SRV + A
sudo tee /etc/dnsmasq.d/edu-test.conf >/dev/null <<'EOF'
domain=edu.test
srv-host=_radsec._tcp.edu.test,radius-a.edu.test,2083,10,50
srv-host=_radsec._tcp.edu.test,radius-b.edu.test,2083,20,50
address=/radius-a.edu.test/192.168.8.228
address=/radius-b.edu.test/192.168.8.179
EOF
1
2
3
4
5
6
# 上游 DNS
sudo tee /etc/dnsmasq.d/01-upstream.conf >/dev/null <<'EOF'
no-resolv
server=1.1.1.1
server=8.8.8.8
EOF
1
2
3
# 53 端口被 systemd-resolved 占用时:
sudo sed -i 's/^#\?DNSStubListener=.*/DNSStubListener=no/' /etc/systemd/resolved.conf
sudo systemctl restart systemd-resolved
1
2
3
4
5
6
# 只监听本地
sudo tee /etc/dnsmasq.d/00-listen.conf >/dev/null <<'EOF'
listen-address=127.0.0.1
bind-interfaces
EOF
sudo systemctl restart dnsmasq
1
2
# 改本机解析
echo -e "nameserver 127.0.0.1\noptions edns0" | sudo tee /etc/resolv.conf >/dev/null

验证:

1
2
dig +short _radsec._tcp.edu.test SRV
dig +short radius-a.edu.test A

3️⃣ radsecproxy:把“动态发现 + DPD”接进去

3.1 目录结构(分片加载一次就行)

1
2
3
4
5
6
7
8
9
10
/usr/local/etc/radsecproxy.conf
/usr/local/etc/radsecproxy.d/
00-logging.conf
10-listen.conf
20-tls.conf
30-clients.conf
40-radius-a.conf
41-radius-b.conf
42-dynamic.conf # ← 新增(动态发现 + DPD)
50-realms.conf # ← realm 选路

主配置:

1
include /usr/local/etc/radsecproxy.d/*.conf

3.2 TLS 基础(20-tls.conf)

1
2
3
4
5
tls myradsec {
CACertificateFile /etc/radsecproxy/certs/ca.pem
CertificateFile /etc/radsecproxy/certs/client.pem
CertificateKeyFile /etc/radsecproxy/certs/client.key
}

3.3 动态模板(42-dynamic.conf)——关键!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
server dyn-radsec {
type tls
tls myradsec

# 动态发现:按 realm 查 SRV(要 NAPTR 时改成 naptr:...)
DynamicLookupCommand srv:_radsec._tcp
# DynamicLookupCommand naptr:x-eduroam:radius.tls # 可选

# DPD 等价机制:Status-Server
StatusServer auto

# 阻塞首包直到解析/建连完成
BlockingStartup on

# 动态生成的 server 也要带上密钥
secret radsec1234567890

# 证书名校验
CertificateNameCheck on
TCPKeepalive on
}

3.4 静态回退(可选)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server radius-a {
type tls
host 192.168.8.228
port 2083
secret radsec1234567890
tls myradsec
TCPKeepalive on
}

server radius-b {
type tls
host 192.168.8.179
port 2083
secret radsec1234567890
tls myradsec
TCPKeepalive on
}

3.5 realm 选路(50-realms.conf)

1
2
3
4
5
6
# 所有 realm 先走动态;也可以只匹配某域
realm * {
server dyn-radsec
server radius-a
server radius-b
}

4️⃣ 启动 & 验证

1
radsecproxy -f -c /usr/local/etc/radsecproxy.conf -d 4

期望日志关键字:

1
2
3
4
5
dynamicconfigsrv: starting SRV lookup ...
using SRV target radius-a.edu.test:2083 ...
TLS connection to dynamic:edu.test (...) up
sending Status-Server / got status server response
Access-Accept/Challenge ... from dynamic:edu.test

发送一个测试请求触发动态解析:

1
2
echo 'User-Name = "[email protected]", User-Password = "pass"' \
| radclient -x 127.0.0.1:1812 auth testing123

4.1 证书名校验的坑(今天真遇到了)

启用 CertificateNameCheck on 后,radsecproxy 会用“要连接的主机名”(SRV 返回的 FQDN)匹配证书 CN/SAN。
如果日志出现:

1
certnamecheck: cn not matching host radius-a.edu.test

说明证书里没有这个 FQDN。三种修法:

A(推荐): 给上游证书加 SAN: DNS:radius-a.edu.test / DNS:radius-b.edu.test
B: 把 SRV 目标换成证书已有的名字,并给它配 A 记录。
C(过渡): 先关 CertificateNameCheck on → off,或用 MatchCertificateAttribute CN:/^radius-server-(1|2)$/ 钉死可接受证书。


5️⃣ DPD(Status-Server)到底帮了啥?

  • 空闲心跳: 空闲后发 Code 12(Status-Server),对端回包则认为活着;
  • 判死/重连: 不回包就标记为 down,触发 Fail-over;
  • 动态发现组合: 判死后重查 SRV/NAPTR,连接新的 peer。

在测试日志里出现:

1
replyh: got status server response

代表 DPD 已启用成功。


6️⃣ 今天踩过的坑 & 一键诊断清单

  • 🚫 重复 include → 只保留一行 include /usr/local/etc/radsecproxy.d/*.conf
  • 🌀 53 端口被占用 → 设置 DNSStubListener=no + 只监听 127.0.0.1。
  • 🔑 动态模板没写 secret → 上游不回包。
  • ⏳ 首包回退 → 开 BlockingStartup on
  • ❌ 证书主机名不匹配 → 按 4.1 的 A/B/C 修。

验证命令:

1
2
3
dig +short _radsec._tcp.edu.test SRV
radsecproxy -f -c /usr/local/etc/radsecproxy.conf -d 4
echo 'User-Name="[email protected]",User-Password="pass"' | radclient -x 127.0.0.1:1812 auth testing123

7️⃣ 进阶与生产化建议

  • 🎯 只对特定 realm 启用动态:
    realm /@.*\.edu\.test$/ { server dyn-radsec; ... }
  • 🧩 保留静态回退:SRV 解不出或目标 down 时不致中断。
  • ⚖️ 健康度与权重:SRV 自带优先级/权重,可结合本地统计优化。
  • 📈 可观测性:记录 Status-Server RTT、失败次数、重连次数。
  • 🔐 TLS 与 PKI:证书写全 SAN,保持 CertificateNameCheck on

🧠 8️⃣ 结语

RadSec 的“难点”不是哪个指令,而是**连接生命周期(DPD)与对端发现(NAPTR/SRV)**的组合。
这篇的最小方案(dnsmasq + SRV + DynamicLookupCommand + StatusServer auto)已经足以落地;
之后再把 NAPTR、证书 SAN、LB 策略慢慢完善,就是一套“能跑、可观测、可维护”的生产配置。


评论区可以贴日志/配置片段,我帮你一起看。