如何解决中间证书缺失导致的TailScale DERP部署问题:从原理到实践
引言
在部署TailScale的DERP(中继穿透服务)时,许多开发者会遇到TLS证书验证失败的问题,尤其是安卓设备或Linux客户端提示certificate not trusted。这类问题通常由证书链不完整引起,而根本原因在于服务器未正确提供中间证书。本文将从证书链的核心概念出发,结合acme.sh工具和TailScale DERP的实际案例,一步步解析问题根源并提供解决方案。
1. 证书链与中间证书:信任的桥梁
什么是证书链?
证书链是由多个数字证书构成的层级信任关系,用于验证终端实体证书(如网站证书)的合法性。其结构通常分为三层:
- 根证书(Root CA):由权威机构自签名(如ISRG Root X1),预装在操作系统或浏览器中。
- 中间证书(Intermediate CA):由根证书签发,用于签发终端证书(如Let’s Encrypt R3)。
- 终端证书(End-Entity Certificate):直接用于服务的证书(如
example.com)。
中间证书的作用
- 安全性:根证书离线存储,中间证书作为“代理”,避免根证书直接暴露。
- 灵活性:支持层级化管理,例如Let’s Encrypt通过中间证书R3为终端用户签发证书。
- 信任传递:客户端通过证书链逐级验证,直到找到信任的根证书。若中间证书缺失,信任链断裂,证书将被标记为不可信。
2. acme.sh的默认行为与问题:缺失中间证书
acme.sh的证书输出
使用acme.sh申请Let’s Encrypt证书时,默认生成以下文件:
domain.com.cer:终端证书(由Let’s Encrypt R3签发)。domain.com.key:私钥。ca.cer:中间证书(Let’s Encrypt R3)。
但部分配置下,acme.sh可能未自动合并中间证书到证书链文件,导致生成的证书链文件(如fullchain.cer)不完整。
问题现象
若服务器仅配置终端证书(未包含中间证书):
- 客户端(如安卓设备、Linux的TailScale客户端)无法验证到根证书,报错
NET::ERR_CERT_AUTHORITY_INVALID。 - 部分浏览器(如Chrome)可能因缓存中间证书而暂时正常,但新设备或清除缓存后会失败。
3. 设备兼容性问题:为何安卓和Linux客户端更容易报错?
客户端证书缓存机制
- 现代浏览器:自动缓存常见中间证书(如Let’s Encrypt R3),即使服务器未发送中间证书,浏览器可能通过缓存补全证书链。
- 非浏览器客户端:如安卓App、Linux命令行工具(如
curl、TailScale客户端),通常不缓存中间证书,完全依赖服务器提供的证书链。
TailScale DERP的典型错误
若DERP服务器未配置完整证书链:
TailScale客户端日志:
DERP relay error: x509: certificate signed by unknown authority
此错误直接表明客户端未能找到中间证书,无法链接到信任的根证书。
4. 如何检测证书链问题:OpenSSL工具实战
方法1:验证证书链完整性
openssl verify -CAfile fullchain.cer domain.com.cer
# 若输出 "OK",表示证书链完整;否则提示缺失中间证书。
方法2:模拟客户端握手过程
openssl s_client -connect example.com:443 -showcerts
检查输出中的证书层级:
- 第一个证书应为终端证书(
s:CN=example.com)。 - 第二个证书应为中间证书(
s:CN=Let's Encrypt R3)。 - 最终需显示
Verify return code: 0 (ok)。
5. 解决方案:手动合并中间证书生成完整链
步骤1:合并终端证书与中间证书
# 合并终端证书和中间证书到fullchain.cer
cat /path/to/domain.com.cer /path/to/ca.cer > /path/to/fullchain.cer
步骤2:配置服务器使用完整链
-
Nginx示例:
server { listen 443 ssl; ssl_certificate /path/to/fullchain.cer; # 包含中间证书 ssl_certificate_key /path/to/domain.com.key; } -
TailScale DERP配置(
derp.yaml):certificates: - cert: "/path/to/fullchain.cer" # 完整证书链 key: "/path/to/domain.com.key"
步骤3:自动化处理(acme.sh续期钩子)
在证书续期时自动合并证书链并重启服务:
acme.sh --renew -d example.com \
--reloadcmd "cat /path/to/domain.com.cer /path/to/ca.cer > /path/to/fullchain.cer && systemctl restart derp-server"
6. 常见问题与注意事项
Q1:需要包含根证书吗?
不需要!根证书已内置在客户端,服务器只需提供终端证书+中间证书。
Q2:中间证书格式必须为PEM吗?
是的。确保文件以-----BEGIN CERTIFICATE-----开头,若为DER格式需转换:
openssl x509 -inform DER -in cert.der -out cert.pem
Q3:Let’s Encrypt中间证书路径
若acme.sh未提供中间证书,手动下载:
wget https://letsencrypt.org/certs/lets-encrypt-r3.pem -O /path/to/ca.cer
总结
证书链不完整是HTTPS/TLS部署中的常见问题,尤其在依赖中间证书的场景(如TailScale DERP)。通过理解证书链的信任机制,合理使用工具(如acme.sh合并证书链),并掌握OpenSSL验证方法,可快速定位并解决此类问题。对于开发者而言,始终牢记:服务器必须提供完整的证书链(终端+中间证书),而非仅仅终端证书。