diff --git a/deploy/single-node/acl.json.example b/deploy/single-node/acl.json.example new file mode 100644 index 0000000..8229bae --- /dev/null +++ b/deploy/single-node/acl.json.example @@ -0,0 +1,19 @@ +{ + "_comment": "私有目的地访问控制。复制到 /etc/pangolin-agent/acl.json 并填入自己的 dp_uuid。改完执行 systemctl reload pangolin-agent(不要 restart,restart 会冷启 sing-box 踢掉全部在线用户)。", + "enabled": true, + "allow_dp_uuids": [ + "TODO-设备级-dp-uuid", + "TODO-账户级-dp-uuid-供-sub-订阅链接用" + ], + "targets": [ + { + "_comment": "与公开站共用 ali:443 的私有 vhost,只能靠 SNI 区分", + "domain": ["brain.51yanmei.com", "git.51yanmei.com"] + }, + { + "_comment": "独占端口的服务:DSM 5001 / RDP 3389 / NAS SSH 10022 / Win SSH 10023", + "ip_cidr": ["182.92.213.171/32"], + "port": [5001, 3389, 10022, 10023] + } + ] +} diff --git a/docs/private-dest-acl-design.html b/docs/private-dest-acl-design.html index 0e9e974..f5d7bc6 100644 --- a/docs/private-dest-acl-design.html +++ b/docs/private-dest-acl-design.html @@ -185,7 +185,7 @@
不动:控制面、数据库(零 migration)、proto、客户端、管理后台、CI。改动全部收敛在 internal/agentd/。
新增/更换设备后,重新生成白名单(UNION 那一半是账户级 dp_uuid,供 /sub 订阅链接用,别漏):
新增/更换设备后,重新生成白名单(UNION 那一半是账户级 dp_uuid,供 /sub 订阅链接用,别漏)。样例文件见 deploy/single-node/acl.json.example,复制到 /etc/pangolin-agent/acl.json 后按下方 SQL 填入 uuid。
sqlite3 /var/lib/pangolin/pangolin.db \
"SELECT d.dp_uuid FROM devices d JOIN users u ON u.id = d.user_id
WHERE u.email = '<我的邮箱>' AND d.dp_uuid IS NOT NULL
diff --git a/server/internal/agentd/acl_test.go b/server/internal/agentd/acl_test.go
index a974848..5ae4159 100644
--- a/server/internal/agentd/acl_test.go
+++ b/server/internal/agentd/acl_test.go
@@ -490,3 +490,27 @@ func TestACL_ColdStartAfterDeletedFileFallsBackToDisk(t *testing.T) {
t.Fatal("冷启动(acl.json 删除)未回退到磁盘 last-good,私有服务已敞开")
}
}
+
+// 样例文件必须始终可被解析且产出预期规则,防止改了 ACLTarget 形状却忘了同步样例。
+func TestACLExampleFileStaysValid(t *testing.T) {
+ path := filepath.Join("..", "..", "..", "deploy", "single-node", "acl.json.example")
+ ac, err := LoadACLConfig(path)
+ if err != nil {
+ t.Fatalf("样例文件解析失败 %s: %v", path, err)
+ }
+ if ac == nil {
+ t.Fatalf("样例文件不存在: %s", path)
+ }
+ if !ac.active() {
+ t.Error("样例文件应当是一份 active 的 ACL")
+ }
+ if len(ac.Targets) != 2 {
+ t.Errorf("样例 targets = %d, want 2", len(ac.Targets))
+ }
+ // 样例里的 uuid 是占位符,不该是真实 uuid(真实 uuid 属标识信息,不入 git)
+ for _, u := range ac.AllowDpUUIDs {
+ if !strings.HasPrefix(u, "TODO-") {
+ t.Errorf("样例文件混入了非占位 uuid %q —— 真实 dp_uuid 不得入 git", u)
+ }
+ }
+}