diff --git a/docs/private-dest-acl-design.html b/docs/private-dest-acl-design.html index 059364c..49069c0 100644 --- a/docs/private-dest-acl-design.html +++ b/docs/private-dest-acl-design.html @@ -114,10 +114,11 @@
"route": {
"rules": [
{"action": "sniff"},
+ {"action": "resolve"},
- {"user": ["<我的uuid…>"], "domain": ["brain.51yanmei.com","git.51yanmei.com"],
+ {"auth_user": ["<我的uuid…>"], "domain": ["brain.51yanmei.com","git.51yanmei.com"],
"outbound": "direct"},
- {"user": ["<我的uuid…>"], "ip_cidr": ["182.92.213.171/32"],
+ {"auth_user": ["<我的uuid…>"], "ip_cidr": ["182.92.213.171/32"],
"port": [5001,3389,10022,10023], "outbound": "direct"},
{"domain": ["brain.51yanmei.com","git.51yanmei.com"], "action": "reject"},
@@ -128,7 +129,11 @@
],
"final": "direct"
}
- 已用本机 sing-box 1.13.13 sing-box check 验证形状合法(exit 0)。
auth_user,不认 user(2026-07-23 生产验证)放行规则匹配凭证必须用 auth_user 而非 user。sing-box check 对两者都语法通过,但 user 字段对 VLESS/REALITY 入站运行时根本不匹配——放行规则永不命中,结果连白名单用户也被兜底拒绝(全员进不去)。上线时正是踩了这个:节点端 git 一直 000,把本机 uuid 换着法加进白名单都没用。本地起一对真 VLESS 实例实测才定位:auth_user:["good"] 生效(good 通/bad 被 block),user 不生效。只跑 sing-box check 不足以验收访问控制,必须真连接跑一次。
已用本机 sing-box 1.13.13 真 VLESS 连接验证 auth_user 匹配 + resolve 后 ip_cidr/domain 仍匹配;节点端以 git 做「白名单一进一出」验证 per-user 放行/拒绝生效。
sniff 唯一且最先;放行先于拒绝;ACL 全部先于 WARPsing-box check 通过sing-box check 通过(注意:check 只验语法,不验 auth_user 运行时是否真匹配 VLESS——见 §4.2 血泪教训)auth_user 放行/拒绝生效;或节点端以某个白名单域名做「本机 uuid 一进一出」验证 per-user 生效。仅 sing-box check 绿=未验收。render.go:14 的「节点只见不透明 dp_uuid」不变式,属于知情接受,需在该注释处补一行说明。
assertNoIdentityFields(agentd/singbox_test.go)只作用于 state.json,从不检查渲染出的 sing-box 配置。本设计不扩大这个缺口,但也没有补上——补闸另开。brain/git,节点侧唯一的区分手段是 sniff 出的 SNI。攻击者(持有效 dp_uuid 的其他 pangolin 用户)向 182.92.213.171:443 发起不带 SNI 的 TLS、握手后用 Host: brain.51yanmei.com 头访问,则不匹配任何 domain 规则、也不匹配 ip_cidr(443 不在端口清单里)→ 落 final:direct → nginx 按 Host 路由放行。这在 sing-box 层无法闭合——不能整封 :443,否则 jiu/travel/sudoku/pay 一起死。因此下面这条 brain 鉴权不是「可选纵深」,而是本闸对 brain 的前置条件。brain/nas 直连(smartRoute 把国内 IP 分流成直连),则流量根本不到节点、直接打 ali,brain 得 403(nginx deny 非白名单源 IP)、nas 超时。故 brain/nas/git/win.51yanmei.com 必须在控制面 PANGOLIN_PRIVATE_SPLIT_DOMAINS 里(已配),且客户端改动后要重连一次才拿到新分流规则。上线验证时 git 走隧道正常、brain/nas 因客户端未重连仍直连——排查时先确认「域名是否真走了隧道」(curl -v 看连的是不是 pangolin 出口),再判 ACL。/etc/nginx/conf.d/brain.conf 在原有 allow 103.119.13.48; deny all 之上叠加 auth_basic(satisfy 默认 all → 源 IP 白名单 与 口令二者都需满足),口令存 Bitwarden「brain basic auth」,htpasswd 仅存 apr1 哈希(明文不落 ali)。acme 通道(:80)与 robots.txt 免密。这样即便本节点 ACL 闸失效(agent 挂了、配置手抖)或被上面的 SNI 手法绕过,brain——唯一无自带鉴权的私有服务——仍不裸奔。cd server && go build ./... && go test ./internal/agentd/... 全绿再提交。feat(agent): / test(agent): / refactor(agent):。执行后修订(2026-07-23,提交
2abe4d0)· 血泪教训:放行规则匹配凭证必须用auth_user而非本计划各处写的user。sing-box check对两者都语法通过,但user字段对 VLESS/REALITY 入站运行时不匹配,导致放行规则永不命中、连白名单用户也被兜底拒绝(全员进不去)。生产上线时踩中,本地起真 VLESS 连接实测才定位。凡本计划(Task 2rules()、Task 3 测试)出现r["user"] = uuids或断言r["user"]之处,一律应为auth_user。 且验收不能只跑sing-box check——必须真连接跑一次(节点端「白名单 uuid 一进一出」即可)。
Files:
@@ -1068,14 +1069,19 @@ git commit -m "docs(agent): 私有目的地 ACL 配置样例与常驻校验/etc/pangolin-agent/acl.json(权限 0600,属主 pangolin),systemctl reload pangolin-agent。journalctl -u pangolin-agent -n 30 确认已重渲染、无 [acl] ERROR / [acl] ALERT。sudo python3 -c "import json;print(json.load(open('/etc/sing-box/config.json'))['route'])" 确认规则顺序为 sniff → 放行 → 拒绝 → warp。sudo python3 -c "import json;print(json.load(open('/etc/sing-box/config.json'))['route'])" 确认规则顺序为 sniff → resolve → 放行 → 拒绝 → warp(resolve 见 I3:堵住 ip_cidr 目的地的域名形式绕过)。 config.json 内容正常不等于线上生效——SIGHUP 走的是 sing-box 自身校验,若新配置被 sing-box 拒绝,它会保留旧实例继续跑,agent 侧仍记"渲染成功"。reload 后必须额外确认:
systemctl is-active sing-box → active;journalctl -u sing-box -n5 | grep -v FATAL(有 FATAL 说明 sing-box 拒绝了新配置,旧实例还在跑,config.json 上的内容其实没生效)。ssh nas-r 通。curl https://brain.51yanmei.com 与 curl --resolve brain.51yanmei.com:443:103.119.13.48 https://brain.51yanmei.com;DSM 同理分别用 nas.51yanmei.com:5001 与 182.92.213.171:5001)——I3 表明目的地表达形式(域名 vs IP)会走到不同的 sing-box 匹配路径,只测一种形式验证不到位。acl.json 改坏 → systemctl reload pangolin-agent → 规则仍在、日志有 ERROR;恢复文件。