mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-09 15:09:08 +02:00
Remove Authorization context and fix bugs in rule and targetProfile
This commit is contained in:
@@ -44,15 +44,17 @@ func (g *AuthzGate) state() DaemonState {
|
|||||||
return g.st
|
return g.st
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequireHolderForFullStatus is a Rule that enforces the right AuthzLevel if
|
// RequireHolderForFullStatus escalates a StatusRequest that asks for peer detail
|
||||||
// "full status" or "should run probes" are requested in a StatusRequest.
|
// or for probes to be run.
|
||||||
func RequireHolderForFullStatus(r Request) error {
|
func RequireHolderForFullStatus(r Request) error {
|
||||||
full, ok := r.Msg.(interface{ GetGetFullPeerStatus() bool })
|
statusReq, ok := r.Msg.(interface {
|
||||||
if !ok || !full.GetGetFullPeerStatus() {
|
GetGetFullPeerStatus() bool
|
||||||
|
GetShouldRunProbes() bool
|
||||||
|
})
|
||||||
|
if !ok {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
probes, ok := r.Msg.(interface{ ShouldRunProbes() bool })
|
if !statusReq.GetGetFullPeerStatus() && !statusReq.GetShouldRunProbes() {
|
||||||
if !ok || !probes.ShouldRunProbes() {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return RequireLevel(AuthzLevelSessionHolder)(r)
|
return RequireLevel(AuthzLevelSessionHolder)(r)
|
||||||
@@ -85,83 +87,69 @@ func denyLevel(r Request, want AuthzLevel) error {
|
|||||||
// target-scoped.
|
// target-scoped.
|
||||||
func (g *AuthzGate) StreamPolicyInterceptor() grpc.StreamServerInterceptor {
|
func (g *AuthzGate) StreamPolicyInterceptor() grpc.StreamServerInterceptor {
|
||||||
return func(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
return func(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
|
||||||
authCtx, authErr := g.authorize(ss.Context(), info.FullMethod, nil)
|
authErr := g.authorize(ss.Context(), info.FullMethod, nil)
|
||||||
if authErr != nil {
|
if authErr != nil {
|
||||||
return authErr
|
return authErr
|
||||||
}
|
}
|
||||||
return handler(srv, &authorizedStream{ServerStream: ss, ctx: authCtx})
|
return handler(srv, ss)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// UnaryPolicyInterceptor authorizes each unary RPC before the handler runs.
|
// UnaryPolicyInterceptor authorizes each unary RPC before the handler runs.
|
||||||
func (g *AuthzGate) UnaryPolicyInterceptor() grpc.UnaryServerInterceptor {
|
func (g *AuthzGate) UnaryPolicyInterceptor() grpc.UnaryServerInterceptor {
|
||||||
return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {
|
return func(ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (resp any, err error) {
|
||||||
authCtx, authErr := g.authorize(ctx, info.FullMethod, req)
|
authErr := g.authorize(ctx, info.FullMethod, req)
|
||||||
if authErr != nil {
|
if authErr != nil {
|
||||||
return nil, authErr
|
return nil, authErr
|
||||||
}
|
}
|
||||||
return handler(authCtx, req)
|
return handler(ctx, req)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (g *AuthzGate) authorize(ctx context.Context, method string, msg any) (context.Context, error) {
|
func (g *AuthzGate) authorize(ctx context.Context, method string, msg any) error {
|
||||||
id, ok := CallerIdentity(ctx)
|
id, ok := CallerIdentity(ctx)
|
||||||
if !ok {
|
if !ok {
|
||||||
log.Warnf("ipc authz: DENY %s, caller identity unavailable", method)
|
log.Warnf("ipc authz: DENY %s, caller identity unavailable", method)
|
||||||
return nil, status.Error(codes.PermissionDenied,
|
return status.Error(codes.PermissionDenied,
|
||||||
"caller identity could not be verified on the daemon control channel")
|
"caller identity could not be verified on the daemon control channel")
|
||||||
}
|
}
|
||||||
st := g.state()
|
st := g.state()
|
||||||
if st == nil {
|
if st == nil {
|
||||||
log.Warnf("ipc authz: DENY %s for %s, daemon state not attached", method, id)
|
log.Warnf("ipc authz: DENY %s for %s, daemon state not attached", method, id)
|
||||||
return nil, status.Error(codes.Unavailable, "daemon not initialized")
|
return status.Error(codes.Unavailable, "daemon not initialized")
|
||||||
}
|
}
|
||||||
target, named := targetProfile(msg)
|
|
||||||
policy := methodPolicyFor(method)
|
policy := methodPolicyFor(method)
|
||||||
if policy.TargetsProfile && !named {
|
|
||||||
return nil, status.Errorf(codes.Internal, "%s is declared target-scoped but names no profile", method)
|
// Only a target-scoped method reads a profile off the request.
|
||||||
|
var target string
|
||||||
|
if policy.TargetsProfile {
|
||||||
|
named, ok := targetProfile(msg)
|
||||||
|
if !ok {
|
||||||
|
return status.Errorf(codes.Internal, "%s is declared target-scoped but names no profile", method)
|
||||||
|
}
|
||||||
|
target = named
|
||||||
}
|
}
|
||||||
auth := Authorization{
|
|
||||||
|
req := Request{
|
||||||
Identity: id,
|
Identity: id,
|
||||||
Level: resolveLevel(id, target, st),
|
Level: resolveLevel(id, target, st),
|
||||||
Target: target,
|
Target: target,
|
||||||
Method: method,
|
Method: method,
|
||||||
|
State: st,
|
||||||
|
Msg: msg,
|
||||||
}
|
}
|
||||||
req := Request{Authorization: auth, State: st, Msg: msg}
|
|
||||||
if req.Level < policy.Level {
|
if req.Level < policy.Level {
|
||||||
log.Warnf("ipc authz: DENY %s for %s (%s), requires %s", method, id, req.Level, policy.Level)
|
log.Warnf("ipc authz: DENY %s for %s (%s), requires %s", method, id, req.Level, policy.Level)
|
||||||
return nil, denyLevel(req, policy.Level)
|
return denyLevel(req, policy.Level)
|
||||||
}
|
}
|
||||||
for _, rule := range policy.Rules {
|
for _, rule := range policy.Rules {
|
||||||
if err := rule(req); err != nil {
|
if err := rule(req); err != nil {
|
||||||
log.Warnf("ipc authz: DENY %s for %s (%s): %v", method, id, req.Level, err)
|
log.Warnf("ipc authz: DENY %s for %s (%s): %v", method, id, req.Level, err)
|
||||||
return nil, err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if policy.Audit {
|
if policy.Audit {
|
||||||
log.Infof("ipc authz: allow %s for %s (%s)", method, id, req.Level)
|
log.Infof("ipc authz: allow %s for %s (%s)", method, id, req.Level)
|
||||||
}
|
}
|
||||||
return withAuthorization(ctx, auth), nil
|
return nil
|
||||||
}
|
|
||||||
|
|
||||||
// authorizedStream carries the authorized context into a streaming handler,
|
|
||||||
// which would otherwise see the one the stream was created with.
|
|
||||||
type authorizedStream struct {
|
|
||||||
grpc.ServerStream
|
|
||||||
ctx context.Context
|
|
||||||
}
|
|
||||||
|
|
||||||
type authorizationKey struct{}
|
|
||||||
|
|
||||||
func withAuthorization(ctx context.Context, a Authorization) context.Context {
|
|
||||||
return context.WithValue(ctx, authorizationKey{}, a)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Authorized returns the decision the interceptor made for this RPC.
|
|
||||||
//
|
|
||||||
// Read it to decide what a caller sees or who an action is attributed to. Do not
|
|
||||||
// read it to decide whether a call is allowed, which is the method table's job.
|
|
||||||
func Authorized(ctx context.Context) Authorization {
|
|
||||||
a, _ := ctx.Value(authorizationKey{}).(Authorization)
|
|
||||||
return a
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,22 +2,15 @@ package ipcauth
|
|||||||
|
|
||||||
const servicePath = "/daemon.DaemonService/"
|
const servicePath = "/daemon.DaemonService/"
|
||||||
|
|
||||||
// Authorization is the decision the interceptor reached for one RPC. It is what
|
|
||||||
// handlers read.
|
|
||||||
type Authorization struct {
|
|
||||||
Identity Identity
|
|
||||||
Level AuthzLevel
|
|
||||||
Target string
|
|
||||||
Method string
|
|
||||||
}
|
|
||||||
|
|
||||||
// Request is what a rule decides on: the authorization plus the state and the
|
// Request is what a rule decides on: the authorization plus the state and the
|
||||||
// message, which only the gate needs.
|
// message, which only the gate needs.
|
||||||
type Request struct {
|
type Request struct {
|
||||||
Authorization
|
Identity Identity
|
||||||
State DaemonState
|
State DaemonState
|
||||||
Method string
|
Level AuthzLevel
|
||||||
Msg any
|
Target string
|
||||||
|
Method string
|
||||||
|
Msg any
|
||||||
}
|
}
|
||||||
|
|
||||||
// Rule is an additional constraint beyond the method's level. Every rule on a
|
// Rule is an additional constraint beyond the method's level. Every rule on a
|
||||||
|
|||||||
Reference in New Issue
Block a user