-
Notifications
You must be signed in to change notification settings - Fork 13
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ATLAS-10930: log which RBAC check (rbac/entitlement) failed authz #12
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -27,6 +27,11 @@ const ( | |||||
TypeKey = ABACKey("ABACType") | ||||||
VerbKey = ABACKey("ABACVerb") | ||||||
ObKey = ObligationKey("obligations") | ||||||
|
||||||
// These Rego obligations constants are hard-coded in | ||||||
// https://github.com/Infoblox-CTO/ngp.authz/blob/develop/helm/authz/files/rego/authz.rego | ||||||
RBACObKey = "authz.rbac.rbac" | ||||||
EntitlementObKey = "authz.rbac.entitlement" | ||||||
) | ||||||
|
||||||
// Override to set your servicename | ||||||
|
@@ -35,9 +40,9 @@ var ( | |||||
) | ||||||
|
||||||
var ( | ||||||
ErrForbidden = status.Errorf(codes.PermissionDenied, "Request forbidden: not authorized") | ||||||
ErrUnknown = status.Errorf(codes.Unknown, "Unknown error") | ||||||
ErrInvalidArg = status.Errorf(codes.InvalidArgument, "Invalid argument") | ||||||
ErrForbidden = status.Errorf(codes.PermissionDenied, "Request forbidden: not authorized") | ||||||
ErrUnknown = status.Errorf(codes.Unknown, "Unknown error") | ||||||
ErrInvalidArg = status.Errorf(codes.InvalidArgument, "Invalid argument") | ||||||
) | ||||||
|
||||||
// DecisionInput is app/service-specific data supplied by app/service ABAC requests | ||||||
|
@@ -160,6 +165,7 @@ func (a *DefaultAuthorizer) Evaluate(ctx context.Context, fullMethod string, grp | |||||
|
||||||
logger := ctxlogrus.Extract(ctx).WithFields(log.Fields{ | ||||||
"application": a.application, | ||||||
"fullMethod": fullMethod, | ||||||
}) | ||||||
|
||||||
bearer, newBearer := athena_claims.AuthBearersFromCtx(ctx) | ||||||
|
@@ -252,6 +258,8 @@ func (a *DefaultAuthorizer) Evaluate(ctx context.Context, fullMethod string, grp | |||||
} | ||||||
|
||||||
if !opaResp.Allow() { | ||||||
forbidReason := opaResp.rbacForbiddenReason() | ||||||
logger.Infof("Request forbidden because these RBAC checks failed:%s", forbidReason) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this will make it easier to find in kibana
Suggested change
|
||||||
return false, ctx, ErrForbidden | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another way to surface error messages is to wrap them in the error being returned.
Suggested change
Caller verifies type of error with |
||||||
} | ||||||
|
||||||
|
@@ -353,6 +361,32 @@ func (o OPAResponse) Allow() bool { | |||||
return allow | ||||||
} | ||||||
|
||||||
// If reason is available, returns reason RBAC was forbidden | ||||||
func (o OPAResponse) rbacForbiddenReason() string { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the signature needs work here. We should distinguish lack of obligations from a specific obligation denial. You could also do that by emitting an error instead of a string. Also, how do services turn these logs off if they don't want them? Perhaps, don't log at all and return wrapped errors is a better way to enable implementation to choose behavior. |
||||||
unavailReason := "" | ||||||
|
||||||
obRaw, obFound := o[string(ObKey)] | ||||||
if !obFound { | ||||||
return unavailReason | ||||||
} | ||||||
|
||||||
obMap, isMap := obRaw.(map[string]interface{}) | ||||||
if !isMap { | ||||||
return unavailReason | ||||||
} | ||||||
|
||||||
var availReason strings.Builder | ||||||
if _, ok := obMap[RBACObKey]; !ok { | ||||||
availReason.WriteByte(' ') | ||||||
availReason.WriteString(RBACObKey) | ||||||
} | ||||||
if _, ok := obMap[EntitlementObKey]; !ok { | ||||||
availReason.WriteByte(' ') | ||||||
availReason.WriteString(EntitlementObKey) | ||||||
} | ||||||
return availReason.String() | ||||||
} | ||||||
|
||||||
// Obligations parses the returned obligations and returns them in standard format | ||||||
func (o OPAResponse) Obligations() (*ObligationsNode, error) { | ||||||
if obIfc, ok := o[string(ObKey)]; ok { | ||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't we first check that an obligation was expected? This is going to be triggered and have no message for 100% of authz failures today.