mirror of
https://github.com/netbirdio/netbird.git
synced 2026-09-18 20:59:07 +02:00
130 lines
7.9 KiB
Go
130 lines
7.9 KiB
Go
package ipcauth
|
|
|
|
const servicePath = "/daemon.DaemonService/"
|
|
|
|
// Request is what a rule decides on: the authorization plus the state and the
|
|
// message, which only the gate needs.
|
|
type Request struct {
|
|
Identity Identity
|
|
State DaemonState
|
|
Level AuthzLevel
|
|
Target string
|
|
Method string
|
|
Msg any
|
|
}
|
|
|
|
// Rule is an additional constraint beyond the method's level, for example
|
|
// checking permissions on a field of a message. Every rule on a method must pass.
|
|
type Rule func(Request) error
|
|
|
|
// The generated getters the profile RPCs expose.
|
|
type handleTargeted interface{ GetHandle() string }
|
|
type profileTargeted interface{ GetProfileName() string }
|
|
|
|
// targetProfile returns the profile a request names, and whether it carries a
|
|
// target field at all. Requests with no target act on the active profile.
|
|
func targetProfile(msg any) (string, bool) {
|
|
switch m := msg.(type) {
|
|
case handleTargeted:
|
|
return m.GetHandle(), true
|
|
case profileTargeted:
|
|
return m.GetProfileName(), true
|
|
default:
|
|
return "", false
|
|
}
|
|
}
|
|
|
|
// MethodPolicy is what a method requires to be authorized and then handled.
|
|
type MethodPolicy struct {
|
|
Level AuthzLevel
|
|
Rules []Rule
|
|
Audit bool
|
|
TargetsProfile bool
|
|
|
|
// Action and Command turn a privilege denial into guidance the caller can
|
|
// act on. Action reads as the subject of a sentence ("claiming a profile"),
|
|
// Command is the same operation run with the privileges it needs. Only read
|
|
// when Level is AuthzLevelPrivileged, the one denial a caller can fix by
|
|
// running as somebody else.
|
|
Action string
|
|
Command string
|
|
}
|
|
|
|
// methodPolicies is the complete authorization surface. Every RPC on
|
|
// DaemonService appears here exactly once.
|
|
var methodPolicies = map[string]MethodPolicy{
|
|
// Any identified caller.
|
|
servicePath + "Status": {Level: AuthzLevelIdentified, Rules: []Rule{RequireHolderForFullStatus}, Action: "reading status"},
|
|
servicePath + "AddProfile": {Level: AuthzLevelIdentified, Audit: true, Action: "adding a profile"},
|
|
servicePath + "ListProfiles": {Level: AuthzLevelIdentified, Action: "listing profiles"},
|
|
servicePath + "GetActiveProfile": {Level: AuthzLevelIdentified, Action: "reading the active profile"},
|
|
servicePath + "GetFeatures": {Level: AuthzLevelIdentified, Action: "reading feature flags"},
|
|
servicePath + "WailsUIReady": {Level: AuthzLevelIdentified, Action: "starting the UI"},
|
|
// If a higher level is used, the deny logs message is spammed on frequent UI polls.
|
|
servicePath + "RegisterUILog": {Level: AuthzLevelIdentified, Action: "registering the UI log"},
|
|
|
|
// Owner of the profile the request names.
|
|
servicePath + "GetConfig": {Level: AuthzLevelProfileOwner, TargetsProfile: true, Audit: true, Action: "reading the profile configuration"},
|
|
servicePath + "SetConfig": {Level: AuthzLevelProfileOwner, TargetsProfile: true, Audit: true, Action: "changing the profile configuration"},
|
|
servicePath + "Login": {Level: AuthzLevelSessionHolder, TargetsProfile: true, Audit: true, Action: "logging in"},
|
|
servicePath + "Logout": {Level: AuthzLevelProfileOwner, TargetsProfile: true, Audit: true, Action: "logging out"},
|
|
servicePath + "RenameProfile": {Level: AuthzLevelProfileOwner, TargetsProfile: true, Action: "renaming a profile"},
|
|
servicePath + "RemoveProfile": {Level: AuthzLevelProfileOwner, TargetsProfile: true, Audit: true, Action: "removing a profile"},
|
|
servicePath + "SwitchProfile": {Level: AuthzLevelSessionHolder, TargetsProfile: true, Audit: true, Action: "switching profile"},
|
|
|
|
// Owner of the active profile, which is what an empty target resolves to.
|
|
servicePath + "WaitSSOLogin": {Level: AuthzLevelProfileOwner, Audit: true, Action: "waiting for the login to finish"},
|
|
servicePath + "WaitJWTToken": {Level: AuthzLevelProfileOwner, Audit: true, Action: "waiting for the token"},
|
|
servicePath + "WaitExtendAuthSession": {Level: AuthzLevelProfileOwner, Action: "extending the session"},
|
|
|
|
// Owner of some profile
|
|
servicePath + "GetLogLevel": {Level: AuthzLevelProfileOwner, Action: "reading the log level"},
|
|
servicePath + "ListStates": {Level: AuthzLevelProfileOwner, Action: "listing stored state"},
|
|
servicePath + "GetInstallerResult": {Level: AuthzLevelProfileOwner, Action: "reading the installer result"},
|
|
|
|
// Session holder: the live engine and everything daemon-wide.
|
|
servicePath + "Up": {Level: AuthzLevelSessionHolder, TargetsProfile: true, Audit: true, Action: "connecting"},
|
|
servicePath + "Down": {Level: AuthzLevelSessionHolder, Audit: true, Action: "disconnecting"},
|
|
servicePath + "SubscribeStatus": {Level: AuthzLevelSessionHolder, Action: "following status"},
|
|
servicePath + "SubscribeEvents": {Level: AuthzLevelSessionHolder, Action: "following events"},
|
|
servicePath + "GetEvents": {Level: AuthzLevelSessionHolder, Action: "reading events"},
|
|
servicePath + "ListNetworks": {Level: AuthzLevelSessionHolder, Action: "listing networks"},
|
|
servicePath + "SelectNetworks": {Level: AuthzLevelSessionHolder, Audit: true, Action: "selecting networks"},
|
|
servicePath + "DeselectNetworks": {Level: AuthzLevelSessionHolder, Audit: true, Action: "deselecting networks"},
|
|
servicePath + "ForwardingRules": {Level: AuthzLevelSessionHolder, Action: "listing forwarding rules"},
|
|
servicePath + "ExposeService": {Level: AuthzLevelSessionHolder, Audit: true, Action: "exposing a service"},
|
|
servicePath + "GetPeerSSHHostKey": {Level: AuthzLevelSessionHolder, Action: "reading a peer SSH host key"},
|
|
servicePath + "RequestJWTAuth": {Level: AuthzLevelSessionHolder, Audit: true, Action: "starting authentication"},
|
|
servicePath + "RequestExtendAuthSession": {Level: AuthzLevelSessionHolder, Action: "extending the session"},
|
|
servicePath + "DismissSessionWarning": {Level: AuthzLevelSessionHolder, Action: "dismissing the session warning"},
|
|
servicePath + "DebugBundle": {Level: AuthzLevelSessionHolder, Audit: true, Action: "creating a debug bundle"},
|
|
servicePath + "SetLogLevel": {Level: AuthzLevelSessionHolder, Action: "changing the log level"},
|
|
servicePath + "SetSyncResponsePersistence": {Level: AuthzLevelSessionHolder, Action: "changing sync persistence"},
|
|
servicePath + "StartCapture": {Level: AuthzLevelSessionHolder, Audit: true, Action: "starting a packet capture"},
|
|
servicePath + "StartBundleCapture": {Level: AuthzLevelSessionHolder, Audit: true, Action: "starting a bundle capture"},
|
|
servicePath + "StopBundleCapture": {Level: AuthzLevelSessionHolder, Action: "stopping a bundle capture"},
|
|
servicePath + "StartCPUProfile": {Level: AuthzLevelSessionHolder, Action: "starting a CPU profile"},
|
|
servicePath + "StopCPUProfile": {Level: AuthzLevelSessionHolder, Action: "stopping a CPU profile"},
|
|
servicePath + "CleanState": {Level: AuthzLevelSessionHolder, Audit: true, Action: "clearing stored state"},
|
|
servicePath + "DeleteState": {Level: AuthzLevelSessionHolder, Audit: true, Action: "deleting stored state"},
|
|
servicePath + "TracePacket": {Level: AuthzLevelSessionHolder, Action: "tracing a packet"},
|
|
servicePath + "TriggerUpdate": {Level: AuthzLevelSessionHolder, Audit: true, Action: "starting an update"},
|
|
|
|
// Root or administrator only. Claiming names an arbitrary principal, so the
|
|
// caller asserts who a profile belongs to. Ownership does not enter it.
|
|
servicePath + "ClaimProfile": {
|
|
Level: AuthzLevelPrivileged,
|
|
TargetsProfile: true,
|
|
Audit: true,
|
|
Action: "claiming a profile",
|
|
Command: ElevatedCommand("netbird profile claim <profile>"),
|
|
},
|
|
}
|
|
|
|
func methodPolicyFor(method string) MethodPolicy {
|
|
if p, ok := methodPolicies[method]; ok {
|
|
return p
|
|
}
|
|
return MethodPolicy{Level: AuthzLevelPrivileged, Audit: true}
|
|
}
|