mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 17:50:12 +00:00
feat(auth): add PAT auth, scoped REST/MCP access, CSRF, and tenant isolation
Introduce Personal Access Tokens (nzp_*) as a stateless auth path alongside
JWT, gated per-endpoint by a scope middleware (nezha:{resource}:{verb}) with
fail-closed empty-scope defaults and a server-id whitelist. Self-management
endpoints (profile, api-tokens, oauth2 bind, refresh-token) explicitly reject
PATs to block privilege-escalation chains. A revoke registry tears down active
long-lived connections (terminal, fm, ws, transfer, mcp) the moment a PAT is
deleted, with a tombstone closing the revoke->register race.
Add an MCP endpoint that proxies tool calls (exec, fs read/write/delete,
transfer) to agents over gRPC, guarded by origin/DNS-rebinding checks, a
per-token rate limiter, audit logging, and a kill switch. Serialize all
sends through the IOStream wrapper to honour grpc-go's concurrency contract.
Add CSRF double-submit protection on unsafe cookie-authenticated methods,
exempting authenticated PAT requests by context identity (not a forgeable
Authorization header). Apply visibility/whitelist filtering consistently
across list, get-by-id, and mutate paths to enforce tenant isolation.
Migrate legacy mcp:* scopes: rewrite read/exec to nezha:* equivalents and
drop dangerous write/delete/wildcard grants.
Co-authored-by: cloudcode <cloudcode@users.noreply.github.com>
This commit is contained in:
@@ -3,6 +3,7 @@ package model
|
||||
import (
|
||||
"slices"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/goccy/go-json"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
@@ -63,6 +64,62 @@ func (r *AlertRule) Enabled() bool {
|
||||
return r.Enable != nil && *r.Enable
|
||||
}
|
||||
|
||||
// HasPermission extends the default owner/admin check with PAT
|
||||
// server_ids whitelist enforcement. AlertRule.Snapshot fans out across
|
||||
// every owner-visible server filtered only by Rule.Ignore semantics
|
||||
// (RuleCoverAll: deny-list; RuleCoverIgnoreAll: allow-list). A
|
||||
// server-limited PAT must therefore satisfy the same cover-fanout rule
|
||||
// the cron / service paths use — otherwise it can create or update a
|
||||
// rule that monitors servers outside its whitelist (admin owner: any
|
||||
// server in the system).
|
||||
//
|
||||
// Unknown Rule.Cover is fail-closed: Snapshot's switch defaults to
|
||||
// "monitor everything", so persisting it would defeat the PAT cover
|
||||
// guard. createAlertRule / updateAlertRule should also reject unknown
|
||||
// covers at write time; this method is the runtime safety net.
|
||||
func (r *AlertRule) HasPermission(ctx *gin.Context) bool {
|
||||
if !r.Common.HasPermission(ctx) {
|
||||
return false
|
||||
}
|
||||
v, ok := ctx.Get(CtxKeyAPIToken)
|
||||
if !ok {
|
||||
return true
|
||||
}
|
||||
tok, _ := v.(APITokenAccessor)
|
||||
if tok == nil {
|
||||
return true
|
||||
}
|
||||
if wl, ok := tok.(APITokenWhitelistView); ok && len(wl.ServerIDs()) == 0 {
|
||||
return true
|
||||
}
|
||||
for _, rule := range r.Rules {
|
||||
if rule == nil {
|
||||
continue
|
||||
}
|
||||
switch rule.Cover {
|
||||
case RuleCoverAll:
|
||||
denyIDs := make([]uint64, 0, len(rule.Ignore))
|
||||
for id, ignored := range rule.Ignore {
|
||||
if ignored {
|
||||
denyIDs = append(denyIDs, id)
|
||||
}
|
||||
}
|
||||
if !DenyListSafeForLimitedPAT(tok, r.GetUserID(), denyIDs) {
|
||||
return false
|
||||
}
|
||||
case RuleCoverIgnoreAll:
|
||||
for id, monitored := range rule.Ignore {
|
||||
if monitored && !tok.CanAccessServer(id) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Snapshot 对传入的Server进行该报警规则下所有type的检查 返回每项检查结果
|
||||
func (r *AlertRule) Snapshot(cycleTransferStats *CycleTransferStats, server *Server, db *gorm.DB) []bool {
|
||||
point := make([]bool, len(r.Rules))
|
||||
|
||||
Reference in New Issue
Block a user