-
Notifications
You must be signed in to change notification settings - Fork 38.6k
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
Add PodSecurityPolicy information to audit logs #58143
Add PodSecurityPolicy information to audit logs #58143
Conversation
68fdf4c
to
3c05701
Compare
if err != nil { | ||
return admission.NewForbidden(a, err) | ||
} | ||
if apiequality.Semantic.DeepEqual(pod, allowedPod) { | ||
ae := a.GetAuditEvent() | ||
audit.LogAnnotations(ae, "podsecuritypolicy/validate", pspName) |
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.
we need better keys here, maybe podsecuritypolicy.admission.k8s.io/policy
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.
👍 we should always use full names
allowed bool | ||
errors []error | ||
allowed bool | ||
clusterRoleBinding string |
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.
would move these into its own struct. maybe struct reason
@@ -74,6 +82,21 @@ func (r *RBACAuthorizer) Authorize(requestAttributes authorizer.Attributes) (aut | |||
|
|||
r.authorizationRuleResolver.VisitRulesFor(requestAttributes.GetUser(), requestAttributes.GetNamespace(), ruleCheckingVisitor.visit) | |||
if ruleCheckingVisitor.allowed { | |||
ae := requestAttributes.GetAuditEvent() | |||
if ae != nil { | |||
if ruleCheckingVisitor.clusterRoleBinding != "" { |
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.
len(...) != 0
is our convention
@@ -32,9 +33,10 @@ type attributesRecord struct { | |||
object runtime.Object | |||
oldObject runtime.Object | |||
userInfo user.Info | |||
ae *audit.Event |
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.
auditEvent
// PSP will store admission information in Annotations, like name of the policy which admits | ||
// the request. | ||
// +optional | ||
Annotations map[string]string |
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.
@tallclair @crassirostris @ericchiang @soltysh this is about the API. Are we happy with this representation?
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.
I'm fine with this interface, but I remember someone was not happy with it
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.
We have plugin names (those which are also used in the apiserver flags). We could make this more structured using them if we concentrate on admission. We could render the whole chain here in the event.
RBAC would then be another step, probably with an explicit authorization field.
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.
I'm not too happy with the name, but can't come up with a better one, so I guess I'll just have to live with it ;).
As for the structured, having map[string]string
give us more freedom, if we decide to add additional information. Maybe we should name the field as AdditionalInfo or something like that? At the same time, I'm worried this might end up being a grab bag for all the things we want to have here. Hm....
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.
I just spoke with @sttts and we agreed it would be nice to:
- be able to decide which admission and rbac plugins are logged, so that would require additional rule changes
- Authorization logging happens in
authorizingVisitor
which is perfect, but admission happens only in PSP, I'd imagine this should happen a level higher to be able to do what I said in 1.
@tallclair @crassirostris @ericchiang @CaoShuFeng opinions?
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.
be able to decide which admission and rbac plugins are logged, so that would require additional rule changes
This feels like something that should be resolved with filters, if we ever get around to implementing them. We don't currently allow fine grained selection of what's logged, so I think the current approach is consistent.
Authorization logging happens in authorizingVisitor which is perfect,
Only for RBAC, it's an implementation detail.
but admission happens only in PSP, I'd imagine this should happen a level higher to be able to do what I said in 1.
I see this field as arbitrary metadata that plugins invoked in the serving path can add to. I think the authorization & admission interfaces should enable this on a case-by-case basis.
I'm happy with the current unstructured approach.
@@ -94,7 +94,7 @@ func createHandler(r rest.NamedCreater, scope RequestScope, typer runtime.Object | |||
audit.LogRequestObject(ae, obj, scope.Resource, scope.Subresource, scope.Serializer) | |||
|
|||
userInfo, _ := request.UserFrom(ctx) | |||
admissionAttributes := admission.NewAttributesRecord(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, userInfo) | |||
admissionAttributes := admission.NewAttributesRecordWithAudit(obj, nil, scope.Kind, namespace, name, scope.Resource, scope.Subresource, admission.Create, userInfo, ae) |
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.
do we really need this new func name?
} | ||
|
||
func NewAttributesRecord(object runtime.Object, oldObject runtime.Object, kind schema.GroupVersionKind, namespace, name string, resource schema.GroupVersionResource, subresource string, operation Operation, userInfo user.Info) Attributes { | ||
func NewAttributesRecordWithAudit(object runtime.Object, oldObject runtime.Object, kind schema.GroupVersionKind, namespace, name string, resource schema.GroupVersionResource, subresource string, operation Operation, userInfo user.Info, ae *audit.Event) Attributes { |
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.
I would keep the old func name and make that argument mandatory (possibly nil)
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.
OK.
I will modify all NewAttributesRecord
functions.
$ LANG=C grep "NewAttributesRecord" * -r | grep "\.go" | wc -l
262
:)
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.
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.
For easier review, please do that in another commit.
99% of those calls are in tests btw. Was surprised that the number is that high.
Much better. It's what I expected 👍 |
Thanks. I will address all your comments and add some unit tests. |
pkg/registry/rbac/validation/rule.go
Outdated
@@ -42,7 +42,7 @@ type AuthorizationRuleResolver interface { | |||
|
|||
// VisitRulesFor invokes visitor() with each rule that applies to a given user in a given namespace, and each error encountered resolving those rules. | |||
// If visitor() returns false, visiting is short-circuited. | |||
VisitRulesFor(user user.Info, namespace string, visitor func(rule *rbac.PolicyRule, err error) bool) | |||
VisitRulesFor(user user.Info, namespace string, visitor func(rule *rbac.PolicyRule, clusterRolebinding, clusterRole, roleBinding, role string, err error) bool) |
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.
prefer (*rbac.RoleRef, *rbac.PolicyRule, ...)
, since we have the reference already?
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.
hmm, I guess you need the binding name/namespace as well
@@ -61,6 +62,9 @@ type Attributes interface { | |||
|
|||
// GetPath returns the path of the request | |||
GetPath() string | |||
|
|||
// GetAuditEvent returns the audit event of the request | |||
GetAuditEvent() *audit.Event |
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.
this interface is supposed to be fully serializable so it can be delegated via a subject access review. an audit.Event pointer breaks that, and implies getting a writeable object out of the attributes, which reverses the information flow of this interface
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.
Which means that we have to expose the audit annotations (or any other data structure we choose) for authz/n in a different way here. Also a delgated authorizer should fill in values.
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.
authz already has the ability to return a reason along with the authorization decision. can we use that from the outside, rather than plumbing audit into all the authorizers? that would work with remote authorization as well without any changes to the authz APIs
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.
@tallclair @crassirostris can you comment whether the reason is enough?
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.
I would expect the following behavior:
- when authorization failed, the reason would contain aggregated reasons from all authorizers that provided failure reasons
- when authorization succeeded, the reason would contain the reason from the succeeding authorizer if it provided one
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.
the authorizer reason is currently plumbed all the way up to https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apiserver/pkg/endpoints/filters/authorization.go#L49 which seems like a much easier place to get a handle to the audit event
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.
PR that makes RBAC return a detailed reason for what it allows that includes the subject, binding, and role: #58531
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.
Done.
@@ -74,6 +82,21 @@ func (r *RBACAuthorizer) Authorize(requestAttributes authorizer.Attributes) (aut | |||
|
|||
r.authorizationRuleResolver.VisitRulesFor(requestAttributes.GetUser(), requestAttributes.GetNamespace(), ruleCheckingVisitor.visit) | |||
if ruleCheckingVisitor.allowed { | |||
ae := requestAttributes.GetAuditEvent() |
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.
getting an object out of requestAttributes in order to write back to it is a fundamental change in the way the authorizer attributes are used, and breaks remote delegation cases for that interface
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.
See above. We need an explicit data structure here. Not a huge event with unclear ownership.
3c05701
to
c77617a
Compare
/status approved-for-milestone |
[MILESTONENOTIFIER] Milestone Pull Request: Up-to-date for process @CaoShuFeng @justinsb @liggitt @sttts @tallclair Pull Request Labels
|
edeaa55
to
e71fa21
Compare
record.annotations = make(map[string]string) | ||
} | ||
if v, ok := record.annotations[key]; ok && v != value { | ||
return fmt.Errorf("admission annotations are not allowd to be overwritten, key:%q, old value: %q, new value:%q", key, record.annotations[key], value) |
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.
this switched to an error from a warning? Am fine with this.
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.
Yes.
According to @tallclair 's suggestion.
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.
👍
"podsecuritypolicy.admission.k8s.io/validate-policy": "privileged", | ||
"podsecuritypolicy.admission.k8s.io/admit-policy": "privileged", | ||
}, | ||
"unexpected final annotations") |
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.
nit: odd formatting. usually we put the )
on the next line.
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.
Done.
// AnnotationsGetter allows users to get annotations from Attributes. An alternate Attribute should implement | ||
// this interface. | ||
type AnnotationsGetter interface { | ||
GetAnnotations() map[string]string |
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.
why do we need this?
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.
I don't see this used. Premature abstraction? Would drop it.
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.
This interface is used according to @liggitt 's suggestion.
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.
It's used in WithAudit decorator to check the attribute interface.
Would be useful for alternate Attributes.
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.
ok, sounds reasonable.
e71fa21
to
2414228
Compare
WithAudit admission decorator log annotations to audit events set by the decorated admission controller
/lgtm |
@tallclair @liggitt approved? |
/approve |
[APPROVALNOTIFIER] This PR is APPROVED This pull-request has been approved by: CaoShuFeng, liggitt, sttts The full list of commands accepted by this bot can be found here. The pull request process is described here
Needs approval from an approver in each of these files:
Approvers can indicate their approval by writing |
/retest Review the full test history for this PR. Silence the bot with an |
/retest pull-kubernetes-e2e-kops-aws |
/retest |
Automatic merge from submit-queue (batch tested with PRs 61610, 64591, 58143, 63929). If you want to cherry-pick this change to another branch, please follow the instructions here. |
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. support annotations for admission webhook Depends on: #58143 **Release note**: ```release-note Support annotations for remote admission webhooks. ```
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. support annotations for admission webhook Depends on: kubernetes/kubernetes#58143 **Release note**: ```release-note Support annotations for remote admission webhooks. ``` Kubernetes-commit: 4e76bb487e50996468bea6638f8ade45911953de
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. support annotations for admission webhook Depends on: kubernetes/kubernetes#58143 **Release note**: ```release-note Support annotations for remote admission webhooks. ``` Kubernetes-commit: 4e76bb487e50996468bea6638f8ade45911953de
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. support annotations for admission webhook Depends on: kubernetes/kubernetes#58143 **Release note**: ```release-note Support annotations for remote admission webhooks. ``` Kubernetes-commit: 4e76bb487e50996468bea6638f8ade45911953de
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. support annotations for admission webhook Depends on: kubernetes/kubernetes#58143 **Release note**: ```release-note Support annotations for remote admission webhooks. ``` Kubernetes-commit: 4e76bb487e50996468bea6638f8ade45911953de
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. support annotations for admission webhook Depends on: kubernetes/kubernetes#58143 **Release note**: ```release-note Support annotations for remote admission webhooks. ``` Kubernetes-commit: 4e76bb487e50996468bea6638f8ade45911953de
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. support annotations for admission webhook Depends on: kubernetes/kubernetes#58143 **Release note**: ```release-note Support annotations for remote admission webhooks. ``` Kubernetes-commit: 4e76bb487e50996468bea6638f8ade45911953de
Depends on: #58806
Fix #56209
Release note: