feat(agent): ACL 渲染放行/拒绝规则对,空白名单仍拒绝

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
wangjia
2026-07-23 01:40:11 +08:00
parent d4156b81e2
commit 597d6ab68b
2 changed files with 118 additions and 0 deletions
+35
View File
@@ -141,3 +141,38 @@ func cleanStrings(in []string) []string {
}
return out
}
// rules 产出 ACL 的 sing-box route 规则:每个 target 一对 —— 先放行白名单、再兜底拒绝。
//
// 顺序是安全性的一部分,不可重排:
// 1. 全部放行规则排在全部拒绝规则之前。不能按 target 交错(放行A/拒绝A/放行B/拒绝B),
// 因为 target 之间可能重叠,交错会让 B 的成员被 A 的拒绝规则先命中。
// 2. 拒绝规则不带 user 维度 —— 它要对「白名单之外的所有人」生效。
// 3. 同一 target 的放行与拒绝,目的地条件由同一个 matchFields() 生成,保证逐字相同。
// 任何不对称都会造成「我自己也被拒」或「有人漏网」。
//
// 白名单为空时只产出拒绝规则(谁都不许进),这是 fail-closed 的核心:空名单的语义是
// 「没有人」而非「所有人」。
func (ac *ACLConfig) rules() []any {
if !ac.active() {
return nil
}
uuids := ac.cleanUUIDs()
targets := ac.cleanTargets()
out := make([]any, 0, len(targets)*2)
if len(uuids) > 0 {
for _, t := range targets {
r := t.matchFields()
r["user"] = uuids
r["outbound"] = directOutboundTag
out = append(out, r)
}
}
for _, t := range targets {
r := t.matchFields()
r["action"] = "reject"
out = append(out, r)
}
return out
}