单二进制三角色 + Dock 适配器 + Swarm stack 达到可部署态;mgr1 实测订阅经 central-proxy bootstrap,探活 alive=41/52。 Co-authored-by: Cursor <cursoragent@cursor.com>
76 lines
2.6 KiB
Go
76 lines
2.6 KiB
Go
// Package policy 对外门面:Guard + DomainTrie + RobotsChecker 组合与审计落点。
|
||
package policy
|
||
|
||
import (
|
||
"context"
|
||
"net/http"
|
||
"time"
|
||
|
||
"onesvm.com/onesvm/browser-server/internal/store"
|
||
)
|
||
|
||
// Engine 出站策略引擎(gateway 入队前 / httpx 每跳重验共用一个实例)。
|
||
type Engine struct {
|
||
Guard *Guard
|
||
Trie *DomainTrie
|
||
Robots *RobotsChecker
|
||
db *store.DB
|
||
robotsBypass map[string]bool // 特权 key 覆盖 robots 时打审计标记(首版预留)
|
||
}
|
||
|
||
// NewEngine 组装策略引擎(db 可为 nil:单测场景禁用审计落库)。
|
||
func NewEngine(db *store.DB, httpClient *http.Client) *Engine {
|
||
e := &Engine{
|
||
Guard: &Guard{},
|
||
Trie: NewDomainTrie(),
|
||
Robots: NewRobotsChecker(db, httpClient),
|
||
db: db,
|
||
}
|
||
// ITER-3 FIX-3:robots 拉取的每跳重验挂接(3xx 目标过 SSRF 门闩)。
|
||
e.Robots.SetRedirectCheck(e.RedirectCheck)
|
||
return e
|
||
}
|
||
|
||
// Reload 热载域名规则(rules 表 → trie)。
|
||
func (e *Engine) Reload() (int, error) {
|
||
return e.Trie.LoadFromStore(e.db)
|
||
}
|
||
|
||
// Authorize 综合放行判定:SSRF(含 DNS)→ 域名 trie。
|
||
// 任一拒绝即 DeniedError(含 rule_id,调用方落审计)。
|
||
func (e *Engine) Authorize(ctx context.Context, rawURL string) *DeniedError {
|
||
if d := e.Guard.CheckURL(rawURL); d != nil {
|
||
return d
|
||
}
|
||
host := parseHost(rawURL)
|
||
if a, ok := e.Trie.Lookup(host); ok && a == ActionDeny {
|
||
return &DeniedError{RuleID: "domain_deny", Reason: "域名黑名单命中"}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// Audit 合规拦截审计(denied 记录,design-arch §5.4 审计行)。
|
||
func (e *Engine) Audit(consumerID int64, url, ruleID string) {
|
||
if e.db == nil {
|
||
return
|
||
}
|
||
_ = e.db.AuditAppend(consumerID, url, ruleID)
|
||
}
|
||
|
||
// RedirectCheck 重定向每跳重验(ITER-3 FIX-3 接线:design-arch §5.4)。
|
||
// 供 httpx.Client CheckRedirect hook / robots 拉取 client 调用——新 URL 先 CheckURL
|
||
// 再 ResolveCheck(DNS 重解析比对私网)。出站面:robots 拉取 + 引擎内网调用;
|
||
// CDP 导航(lightpanda/headless-shell)不经 Go client,其重定向防护属引擎容器职责。
|
||
func (e *Engine) RedirectCheck(ctx context.Context, rawURL string) *DeniedError {
|
||
return e.Guard.CheckRedirect(ctx, rawURL)
|
||
}
|
||
|
||
// RobotsAllowed robots 判定(普通 key)。特权覆盖由 W2 调用方显式传 bypass 并打审计。
|
||
func (e *Engine) RobotsAllowed(ctx context.Context, rawURL string, bypass bool) (bool, error) {
|
||
if bypass {
|
||
return true, nil
|
||
}
|
||
cctx, cancel := context.WithTimeout(ctx, 12*time.Second)
|
||
defer cancel()
|
||
return e.Robots.Allowed(cctx, rawURL, "onesvm-browser-server")
|
||
}
|