From b58b30d96a50870e330a9461d6fa0ee3d3dd63be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christoph=20St=C3=A4bler?= Date: Thu, 8 Aug 2024 23:19:56 +0200 Subject: [PATCH] Add e2e test for Broker authorization (#8132) * Enable AuthZ tests * Refactor eventpolicy test rekt resource for easier usage * Add authorization e2e test for Broker * Fix style issues * Don't pass test env namespace and use default * Run OIDC and AuthZ e2e tests together --- test/e2e-rekt-tests.sh | 4 +- test/rekt/broker_test.go | 15 ++ test/rekt/features/broker/authz_feature.go | 157 ++++++++++++++++++ .../rekt/resources/eventpolicy/eventpolicy.go | 106 +++++++----- .../resources/eventpolicy/eventpolicy_test.go | 66 +++----- 5 files changed, 257 insertions(+), 91 deletions(-) create mode 100644 test/rekt/features/broker/authz_feature.go diff --git a/test/e2e-rekt-tests.sh b/test/e2e-rekt-tests.sh index 4f8bcb0e254..382b41802cc 100755 --- a/test/e2e-rekt-tests.sh +++ b/test/e2e-rekt-tests.sh @@ -44,10 +44,10 @@ kubectl apply -Rf "$(dirname "$0")/config-transport-encryption" go_test_e2e -timeout=1h ./test/rekt -run TLS || fail_test -echo "Running E2E OIDC Reconciler Tests" +echo "Running E2E Auth Reconciler Tests" kubectl apply -Rf "$(dirname "$0")/config-authentication-oidc" -go_test_e2e -timeout=1h ./test/rekt -run OIDC || fail_test +go_test_e2e -timeout=1h ./test/rekt -run "OIDC|AuthZ" || fail_test success diff --git a/test/rekt/broker_test.go b/test/rekt/broker_test.go index d606d5ea77c..bc071eceb2d 100644 --- a/test/rekt/broker_test.go +++ b/test/rekt/broker_test.go @@ -284,3 +284,18 @@ func TestBrokerSendsEventsWithOIDCSupport(t *testing.T) { env.TestSet(ctx, t, broker.BrokerSendEventWithOIDC()) } + +func TestBrokerSupportsAuthZ(t *testing.T) { + t.Parallel() + + ctx, env := global.Environment( + knative.WithKnativeNamespace(system.Namespace()), + knative.WithLoggingConfig, + knative.WithTracingConfig, + k8s.WithEventListener, + environment.Managed(t), + eventshub.WithTLS(t), + ) + + env.TestSet(ctx, t, broker.BrokerSupportsAuthZ()) +} diff --git a/test/rekt/features/broker/authz_feature.go b/test/rekt/features/broker/authz_feature.go new file mode 100644 index 00000000000..2feeb06f468 --- /dev/null +++ b/test/rekt/features/broker/authz_feature.go @@ -0,0 +1,157 @@ +/* +Copyright 2024 The Knative Authors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package broker + +import ( + "context" + + "github.com/cloudevents/sdk-go/v2/test" + sourcesv1 "knative.dev/eventing/pkg/apis/sources/v1" + "knative.dev/eventing/test/rekt/features/featureflags" + "knative.dev/eventing/test/rekt/resources/broker" + "knative.dev/eventing/test/rekt/resources/eventpolicy" + "knative.dev/eventing/test/rekt/resources/pingsource" + "knative.dev/eventing/test/rekt/resources/trigger" + duckv1 "knative.dev/pkg/apis/duck/v1" + "knative.dev/reconciler-test/pkg/eventshub" + "knative.dev/reconciler-test/pkg/eventshub/assert" + "knative.dev/reconciler-test/pkg/feature" + "knative.dev/reconciler-test/pkg/resources/service" +) + +func BrokerSupportsAuthZ() *feature.FeatureSet { + return &feature.FeatureSet{ + Name: "Broker supports authorization", + Features: []*feature.Feature{ + BrokerAcceptsEventsFromAuthorizedSender(), + BrokerRejectsEventsFromUnauthorizedSender(), + }, + } +} + +func BrokerAcceptsEventsFromAuthorizedSender() *feature.Feature { + f := feature.NewFeatureNamed("Broker accepts events from a authorized sender") + + f.Prerequisite("OIDC Authentication is enabled", featureflags.AuthenticationOIDCEnabled()) + f.Prerequisite("transport encryption is strict", featureflags.TransportEncryptionStrict()) + f.Prerequisite("should not run when Istio is enabled", featureflags.IstioDisabled()) + + source := feature.MakeRandomK8sName("source") + brokerName := feature.MakeRandomK8sName("broker") + sink := feature.MakeRandomK8sName("sink") + triggerName := feature.MakeRandomK8sName("trigger") + eventPolicyName := feature.MakeRandomK8sName("eventpolicy") + + // Install the broker + f.Setup("Install Broker", broker.Install(brokerName, broker.WithEnvConfig()...)) + f.Setup("Broker is ready", broker.IsReady(brokerName)) + f.Setup("Broker is addressable", broker.IsAddressable(brokerName)) + + // Install the sink + f.Setup("Install Sink", eventshub.Install( + sink, + eventshub.StartReceiver, + )) + + f.Setup("Install the Trigger", trigger.Install(triggerName, + trigger.WithBrokerName(brokerName), + trigger.WithSubscriber(service.AsKReference(sink), ""))) + + f.Setup("Install the EventPolicy", eventpolicy.Install( + eventPolicyName, + eventpolicy.WithToRef( + broker.GVR().GroupVersion().WithKind("Broker"), + brokerName), + eventpolicy.WithFromRef( + pingsource.Gvr().GroupVersion().WithKind("PingSource"), + source, + ""), + )) + + // Install source + f.Requirement("Install Pingsource", func(ctx context.Context, t feature.T) { + brokeruri, err := broker.Address(ctx, brokerName) + if err != nil { + t.Error("failed to get address of broker", err) + } + + pingsource.Install(source, + pingsource.WithSink(&duckv1.Destination{URI: brokeruri.URL, CACerts: brokeruri.CACerts, Audience: brokeruri.Audience}), + pingsource.WithData("text/plain", "hello, world!"))(ctx, t) + }) + f.Requirement("PingSource goes ready", pingsource.IsReady(source)) + + f.Alpha("Broker"). + Must("accepts event from valid sender", assert.OnStore(sink).MatchEvent( + test.HasType(sourcesv1.PingSourceEventType)).AtLeast(1)) + + return f +} + +func BrokerRejectsEventsFromUnauthorizedSender() *feature.Feature { + f := feature.NewFeatureNamed("Broker rejects events from an unauthorized sender") + + f.Prerequisite("OIDC Authentication is enabled", featureflags.AuthenticationOIDCEnabled()) + f.Prerequisite("transport encryption is strict", featureflags.TransportEncryptionStrict()) + f.Prerequisite("should not run when Istio is enabled", featureflags.IstioDisabled()) + + source := feature.MakeRandomK8sName("source") + brokerName := feature.MakeRandomK8sName("broker") + sink := feature.MakeRandomK8sName("sink") + triggerName := feature.MakeRandomK8sName("trigger") + eventPolicyName := feature.MakeRandomK8sName("eventpolicy") + + event := test.FullEvent() + + // Install the broker + f.Setup("Install Broker", broker.Install(brokerName, broker.WithEnvConfig()...)) + f.Setup("Broker is ready", broker.IsReady(brokerName)) + f.Setup("Broker is addressable", broker.IsAddressable(brokerName)) + + // Install the sink + f.Setup("Install Sink", eventshub.Install( + sink, + eventshub.StartReceiver, + )) + + f.Setup("Install the Trigger", trigger.Install(triggerName, + trigger.WithBrokerName(brokerName), + trigger.WithSubscriber(service.AsKReference(sink), ""))) + f.Setup("Trigger goes ready", trigger.IsReady(triggerName)) + + // Install an event policy for Broker allowing from a sample subject, to not fall back to the default-auth-mode + f.Setup("Install an EventPolicy", eventpolicy.Install( + eventPolicyName, + eventpolicy.WithToRef( + broker.GVR().GroupVersion().WithKind("Broker"), + brokerName), + eventpolicy.WithFromSubject("sample-sub"))) + + // Send event + f.Requirement("Install Source", eventshub.Install( + source, + eventshub.StartSenderToResourceTLS(broker.GVR(), brokerName, nil), + eventshub.InputEvent(event), + )) + + f.Alpha("Broker"). + Must("event is sent", assert.OnStore(source).MatchSentEvent( + test.HasId(event.ID())).Exact(1)). + Must("broker rejects event with a 403 response", assert.OnStore(source).Match(assert.MatchStatusCode(403)).Exact(1)) + + return f +} diff --git a/test/rekt/resources/eventpolicy/eventpolicy.go b/test/rekt/resources/eventpolicy/eventpolicy.go index 070b3f48d2c..879eabfd7e5 100644 --- a/test/rekt/resources/eventpolicy/eventpolicy.go +++ b/test/rekt/resources/eventpolicy/eventpolicy.go @@ -22,8 +22,6 @@ import ( "time" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - eventingv1alpha1 "knative.dev/eventing/pkg/apis/eventing/v1alpha1" - "k8s.io/apimachinery/pkg/runtime/schema" "knative.dev/reconciler-test/pkg/feature" "knative.dev/reconciler-test/pkg/k8s" @@ -52,69 +50,85 @@ func Install(name string, opts ...manifest.CfgFn) feature.StepFn { } } -func WithTo(tos ...eventingv1alpha1.EventPolicySpecTo) manifest.CfgFn { +func WithToRef(gvk schema.GroupVersionKind, name string) manifest.CfgFn { + return func(cfg map[string]interface{}) { + if _, set := cfg["to"]; !set { + cfg["to"] = []map[string]interface{}{} + } + + res := cfg["to"].([]map[string]interface{}) + + to := map[string]interface{}{ + "ref": map[string]interface{}{ + "apiVersion": gvk.GroupVersion().String(), + "kind": gvk.Kind, + "name": name, + }} + + res = append(res, to) + + cfg["to"] = res + } +} + +func WithToSelector(gvk schema.GroupVersionKind, labelSelector *metav1.LabelSelector) manifest.CfgFn { return func(cfg map[string]interface{}) { if _, set := cfg["to"]; !set { cfg["to"] = []map[string]interface{}{} } res := cfg["to"].([]map[string]interface{}) - for _, ref := range tos { - to := map[string]interface{}{} - if ref.Ref != nil { - to = map[string]interface{}{ - "ref": map[string]interface{}{ - "apiVersion": ref.Ref.APIVersion, - "kind": ref.Ref.Kind, - "name": ref.Ref.Name, - }} - } - - if ref.Selector != nil { - selector := labelSelectorToStringMap(ref.Selector.LabelSelector) - selector["apiVersion"] = ref.Selector.APIVersion - selector["kind"] = ref.Selector.Kind - - to = map[string]interface{}{ - "selector": selector, - } - } - - res = append(res, to) + + selector := labelSelectorToStringMap(labelSelector) + selector["apiVersion"] = gvk.GroupVersion().String() + selector["kind"] = gvk.Kind + + to := map[string]interface{}{ + "selector": selector, } + res = append(res, to) + cfg["to"] = res } } -func WithFrom(froms ...eventingv1alpha1.EventPolicySpecFrom) manifest.CfgFn { +func WithFromRef(gvk schema.GroupVersionKind, name, namespace string) manifest.CfgFn { + return func(cfg map[string]interface{}) { + if _, set := cfg["from"]; !set { + cfg["from"] = []map[string]interface{}{} + } + + res := cfg["from"].([]map[string]interface{}) + + from := map[string]interface{}{ + "ref": map[string]interface{}{ + "apiVersion": gvk.GroupVersion().String(), + "kind": gvk.Kind, + "name": name, + "namespace": namespace, + }} + + res = append(res, from) + + cfg["from"] = res + } +} + +func WithFromSubject(subject string) manifest.CfgFn { return func(cfg map[string]interface{}) { if _, set := cfg["from"]; !set { cfg["from"] = []map[string]interface{}{} } res := cfg["from"].([]map[string]interface{}) - for _, ref := range froms { - from := map[string]interface{}{} - if ref.Ref != nil { - from = map[string]interface{}{ - "ref": map[string]interface{}{ - "apiVersion": ref.Ref.APIVersion, - "kind": ref.Ref.Kind, - "name": ref.Ref.Name, - "namespace": ref.Ref.Namespace, - }} - } - - if ref.Sub != nil && *ref.Sub != "" { - from = map[string]interface{}{ - "sub": *ref.Sub, - } - } - - res = append(res, from) + + from := map[string]interface{}{ + "sub": subject, } + res = append(res, from) + cfg["from"] = res } } @@ -126,7 +140,7 @@ func IsReady(name string, timing ...time.Duration) feature.StepFn { func labelSelectorToStringMap(selector *metav1.LabelSelector) map[string]interface{} { if selector == nil { - return nil + return map[string]interface{}{} } r := map[string]interface{}{} diff --git a/test/rekt/resources/eventpolicy/eventpolicy_test.go b/test/rekt/resources/eventpolicy/eventpolicy_test.go index 944aff4c199..0832330d040 100644 --- a/test/rekt/resources/eventpolicy/eventpolicy_test.go +++ b/test/rekt/resources/eventpolicy/eventpolicy_test.go @@ -20,11 +20,10 @@ import ( "embed" "os" + "knative.dev/eventing/test/rekt/resources/broker" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "knative.dev/eventing/pkg/apis/eventing/v1alpha1" "knative.dev/eventing/test/rekt/resources/eventpolicy" - "knative.dev/pkg/ptr" - testlog "knative.dev/reconciler-test/pkg/logging" "knative.dev/reconciler-test/pkg/manifest" ) @@ -82,49 +81,30 @@ func Example_full() { } cfgFn := []manifest.CfgFn{ - eventpolicy.WithTo([]v1alpha1.EventPolicySpecTo{ - { - Ref: &v1alpha1.EventPolicyToReference{ - Name: "my-broker", - Kind: "Broker", - APIVersion: "eventing.knative.dev/v1", + eventpolicy.WithToRef( + broker.GVR().GroupVersion().WithKind("Broker"), + "my-broker"), + eventpolicy.WithToSelector( + broker.GVR().GroupVersion().WithKind("Broker"), + &metav1.LabelSelector{ + MatchLabels: map[string]string{ + "matchlabel1": "matchlabelvalue1", + "matchlabel2": "matchlabelvalue2", }, - }, - { - Selector: &v1alpha1.EventPolicySelector{ - LabelSelector: &metav1.LabelSelector{ - MatchLabels: map[string]string{ - "matchlabel1": "matchlabelvalue1", - "matchlabel2": "matchlabelvalue2", - }, - MatchExpressions: []metav1.LabelSelectorRequirement{ - { - Key: "matchlabelselector1", - Values: []string{"matchlabelselectorvalue1"}, - Operator: metav1.LabelSelectorOpIn, - }, - }, - }, - TypeMeta: &metav1.TypeMeta{ - APIVersion: "eventing.knative.dev/v1", - Kind: "Broker", + MatchExpressions: []metav1.LabelSelectorRequirement{ + { + Key: "matchlabelselector1", + Values: []string{"matchlabelselectorvalue1"}, + Operator: metav1.LabelSelectorOpIn, }, }, - }, - }...), - eventpolicy.WithFrom([]v1alpha1.EventPolicySpecFrom{ - { - Ref: &v1alpha1.EventPolicyFromReference{ - APIVersion: "eventing.knative.dev/v1", - Name: "my-broker", - Kind: "Broker", - Namespace: "my-ns-2", - }, - }, - { - Sub: ptr.String("my-sub"), - }, - }...), + }), + eventpolicy.WithFromRef( + broker.GVR().GroupVersion().WithKind("Broker"), + "my-broker", + "my-ns-2", + ), + eventpolicy.WithFromSubject("my-sub"), } for _, fn := range cfgFn {