From f55818ac1fc13378e71ef05fa7384692c31ed956 Mon Sep 17 00:00:00 2001 From: Jordan Liggitt Date: Wed, 8 Feb 2023 13:35:03 -0500 Subject: [PATCH] Fix create role generation from audit logs of accepted create requests --- Makefile | 4 +- cmd/audit2rbac/audit2rbac.go | 10 ++++- cmd/audit2rbac/audit2rbac_test.go | 74 +++++++++++++++++++++++++++++++ 3 files changed, 84 insertions(+), 4 deletions(-) create mode 100644 cmd/audit2rbac/audit2rbac_test.go diff --git a/Makefile b/Makefile index 49a5391..4915c6c 100644 --- a/Makefile +++ b/Makefile @@ -29,10 +29,10 @@ clean: rm -fr bin test: - go test -v -race -cover ./pkg/... + go test -v -race -cover ./pkg/... ./cmd/... quicktest: - go test ./pkg/... + go test ./pkg/... ./cmd/... # Capture output and force failure when there is non-empty output fmt: diff --git a/cmd/audit2rbac/audit2rbac.go b/cmd/audit2rbac/audit2rbac.go index 58a3467..f7250d6 100644 --- a/cmd/audit2rbac/audit2rbac.go +++ b/cmd/audit2rbac/audit2rbac.go @@ -18,7 +18,6 @@ import ( "github.com/spf13/cobra" rbacv1 "k8s.io/api/rbac/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" @@ -422,7 +421,7 @@ func stream(sources []io.ReadCloser) <-chan *streamObject { func flatten(in <-chan *streamObject) <-chan *streamObject { out := make(chan *streamObject) - v1List := v1.SchemeGroupVersion.WithKind("List") + v1List := metav1.SchemeGroupVersion.WithKind("List") go func() { defer close(out) @@ -578,6 +577,13 @@ func eventToAttributes(event *audit.Event) authorizer.AttributesRecord { attrs.APIGroup = event.ObjectRef.APIGroup attrs.APIVersion = event.ObjectRef.APIVersion } + if event.Verb == "create" { + // The name attribute is not available to authorization of create requests + // (see https://kubernetes.io/docs/reference/access-authn-authz/rbac/#referring-to-resources), + // but is populated in audit events for successful create requests. + // Clear this to match what the authorizer will actually be asked to authorize. + attrs.Name = "" + } return attrs } diff --git a/cmd/audit2rbac/audit2rbac_test.go b/cmd/audit2rbac/audit2rbac_test.go new file mode 100644 index 0000000..b21ec63 --- /dev/null +++ b/cmd/audit2rbac/audit2rbac_test.go @@ -0,0 +1,74 @@ +package main + +import ( + "reflect" + "testing" + + "github.com/google/go-cmp/cmp" + + "k8s.io/apiserver/pkg/apis/audit" + "k8s.io/apiserver/pkg/authentication/user" + "k8s.io/apiserver/pkg/authorization/authorizer" +) + +func TestEventToAttributes(t *testing.T) { + testcases := []struct { + name string + event *audit.Event + expectedAttributes authorizer.AttributesRecord + }{ + { + name: "rejected create", + event: &audit.Event{ + Verb: "create", + ObjectRef: &audit.ObjectReference{ + APIGroup: "mygroup", + APIVersion: "myversion", + Resource: "myresources", + Namespace: "mynamespace", + // no name attribute in unauthorized create, request body is never parsed + }, + }, + expectedAttributes: authorizer.AttributesRecord{ + User: &user.DefaultInfo{}, + Verb: "create", + Namespace: "mynamespace", + APIGroup: "mygroup", + APIVersion: "myversion", + Resource: "myresources", + ResourceRequest: true, + }, + }, + { + name: "accepted create", + event: &audit.Event{ + Verb: "create", + ObjectRef: &audit.ObjectReference{ + APIGroup: "mygroup", + APIVersion: "myversion", + Resource: "myresources", + Namespace: "mynamespace", + Name: "myname", + }, + }, + expectedAttributes: authorizer.AttributesRecord{ + User: &user.DefaultInfo{}, + Verb: "create", + Namespace: "mynamespace", + APIGroup: "mygroup", + APIVersion: "myversion", + Resource: "myresources", + ResourceRequest: true, + }, + }, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + actualAttributes := eventToAttributes(tc.event) + if !reflect.DeepEqual(tc.expectedAttributes, actualAttributes) { + t.Errorf("unexpected diff:\n%s", cmp.Diff(tc.expectedAttributes, actualAttributes)) + } + }) + } +}