凭据:文件与 token

模块支持 三种凭据来源,三选一。同时写两个,nginx -t 直接报 凭据来源只能写一个(配置期 EMERG,不是运行时才发现)。

来源指令等价于 cloudflared
凭据 JSON 文件cfd_tunnel_credentials / 块内 credentials--credentials-file
内联 tokencfd_tunnel_token / 块内 token--token
token 文件cfd_tunnel_token_file / 块内 token_file--token-file

三者都与快速隧道互斥(cfd_tunnel_quick_tunnel on 时不能再写凭据)。

一、凭据 JSON 文件 ​

就是 cloudflared 的凭据格式:

json
{
  "AccountTag":   "0123456789abcdef0123456789abcdef",
  "TunnelID":     "11111111-2222-3333-4444-555555555555",
  "TunnelSecret": "<base64 的 32 字节>"
}
nginx
cfd_tunnel_credentials /etc/nginx/cloudflared/creds.json;

文件必须 worker 用户可读:

bash
sudo install -m 600 -o www-data -g www-data creds.json /etc/nginx/cloudflared/creds.json

读不到时 error_log 会明确报「读取凭据 … 失败」,而不是静默不启动隧道。

二、控制台给的 token ​

Cloudflare Zero Trust 控制台「添加隧道 → 安装连接器」页面只给一串 token, 长得像 eyJhIjoi...;cloudflared service install <token> 也只是把它写进 /etc/cloudflared/token,再让 systemd 单元用 --token-file 读它。0.14.0 起模块直接收:

nginx
# 写法 A:把控制台那串原样贴进来(等价 --token,不 trim)
cfd_tunnel_token        eyJhIjoi...;

# 写法 B:从文件读(等价 --token-file,也就是 service install 写出来的文件)
cfd_tunnel_token_file   /etc/nginx/cloudflared/token;

这串 token 到底是什么 ​

它是 base64(JSON),解开就是凭据文件里的那几个字段:

json
{"a":"<账号标签>","t":"<隧道 UUID>","s":"<base64 的 32 字节 secret>","e":"<可选区域>"}

cloudflared 自己在 cmd/cloudflared/tunnel/subcommands.go 的 ParseToken() 里就是 base64.StdEncoding.DecodeString + json.Unmarshal 到 connection.TunnelToken。 它不是新协议,只是 credentials 的第三种写法 —— 解析结果并进同一个凭据结构, 注册、心跳、注入三条路径完全不变。

照抄 cloudflared 的细节(不是疏忽) ​

行为原因
内联 cfd_tunnel_token 不 trimcloudflared 的 --token 原样用;尾随空格会让 base64 变非法
token_file 内容 先 TrimSpacecloudflared 的 --token-file 先 strings.TrimSpace;文件结尾换行没关系
base64 用标准 alphabet、必须有 = 填充StdEncoding 的规则;-/_(URL-safe)与空格一律拒
只忽略 base64 里的 \r\nGo 的 base64 解码器就是只跳过 \r\n
未知字符串键忽略、a/t/s 缺失不算错、s:null 允许、重复键后者胜与 json.Unmarshal 进 struct 的行为一致;缺字段留给边缘去拒
校验发生在 worker 启动期与 cloudflared 一样,所以 nginx -t 通过 ≠ token 对

两处有意偏离(都用真 cloudflared 二进制证明"它确实接受") ​

  1. token 带 e(endpoint / region)⇒ 模块启动期报错。 模块只按 cfd_tunnel_edge 连一个固定边缘,遇到 e 不猜地址、直接说清原因(例如 FedRAMP 区域)。
  2. JSON 里未知键的值是嵌套对象或数字 ⇒ 报错。 模块的 JSON 走查只接受 object + string/null 两类值;Go 的 json.Unmarshal 会忽略这种未知键。这条偏离的方向是「宁可拒绝也不误读」。

坏 token 长什么样 ​

启动期会明确写出是哪一种错,并且只跳过这一条隧道,nginx 其余功能照常:

text
cfd_tunnel: token 不是合法的 base64
cfd_tunnel: token 解出来不是 JSON 对象
cfd_tunnel: token 里的 t 不是合法 uuid
cfd_tunnel: token 里的 s 不是合法的 base64
cfd_tunnel: token 带 endpoint/region(e=...),本模块只连一个 cfd_tunnel_edge

日志只打来源、字节数与账号标签,从不打 token、不打 secret。

旧版本:一行命令转成凭据文件 ​

不想升级模块时,旧版本也能直接用:

bash
python3 -c 'import base64,json,sys; d=json.loads(base64.b64decode(sys.argv[1].strip()));
print(json.dumps({"AccountTag":d["a"],"TunnelID":d["t"],"TunnelSecret":d["s"]},indent=2))' \
    "$(cat /etc/cloudflared/token)" > /etc/nginx/cloudflared/creds.json

三、三种来源都没写 = 模块静默不启用 ​

装了 .so、也配了边缘地址,但既没有 cfd_tunnel_credentials、也没有 cfd_tunnel_token / cfd_tunnel_token_file、也没开快速隧道时:模块不建连接、不挑监听器、不占 fd。 这是刻意的 —— 让「装了但还没配」和「配错了」在日志上能区分开。

块内写法 ​

每条隧道一个来源:

nginx
cfd_tunnel {
    name          edge-a;
    token_file    /etc/nginx/cloudflared/a.token;
    inject_listen 127.0.0.1:8080;
}

块内 credentials / token / token_file / quick on 四选一,写两个该块的 nginx -t 就失败。详见 多隧道。

模块文档 · 对齐依据是 cloudflared 的源码与实测行为