From 023a7cef0f441f9cd805deee70dff0223d2e5855 Mon Sep 17 00:00:00 2001 From: Pierangelo Di Pilato Date: Tue, 27 Feb 2024 11:32:47 +0100 Subject: [PATCH] Add CA rotation tests Signed-off-by: Pierangelo Di Pilato --- test/rekt/apiserversource_test.go | 1 + test/rekt/channel_test.go | 1 + .../features/apiserversource/data_plane.go | 57 ++++++++++++ .../features/channel/eventing_tls_feature.go | 87 +++++++++++++++++++ test/rekt/features/pingsource/features.go | 44 ++++++++++ test/rekt/features/trigger/feature.go | 77 ++++++++++++++++ test/rekt/pingsource_test.go | 1 + .../resources/configmap/config-features.yaml | 15 +++- test/rekt/resources/configmap/configmap.go | 13 +++ .../resources/configmap/configmap_test.go | 55 ++++++++++++ test/rekt/trigger_test.go | 1 + 11 files changed, 351 insertions(+), 1 deletion(-) create mode 100644 test/rekt/resources/configmap/configmap_test.go diff --git a/test/rekt/apiserversource_test.go b/test/rekt/apiserversource_test.go index e2641fc3ba8..55abd203796 100644 --- a/test/rekt/apiserversource_test.go +++ b/test/rekt/apiserversource_test.go @@ -111,6 +111,7 @@ func TestApiServerSourceDataPlaneTLS(t *testing.T) { env.ParallelTest(ctx, t, apiserversourcefeatures.SendsEventsWithTLS()) env.ParallelTest(ctx, t, apiserversourcefeatures.SendsEventsWithTLSTrustBundle()) + env.ParallelTest(ctx, t, apiserversourcefeatures.SendsEventsWithTLSWithAdditionalTrustBundle()) } func TestApiServerSourceDataPlane_EventModes(t *testing.T) { diff --git a/test/rekt/channel_test.go b/test/rekt/channel_test.go index 6cca352dd22..478326e727b 100644 --- a/test/rekt/channel_test.go +++ b/test/rekt/channel_test.go @@ -358,6 +358,7 @@ func TestInMemoryChannelTLS(t *testing.T) { env.ParallelTest(ctx, t, channel.SubscriptionTLS()) env.ParallelTest(ctx, t, channel.SubscriptionTLSTrustBundle()) + env.ParallelTest(ctx, t, channel.SubscriptionTLSWithAdditionalTrustBundle()) } func TestChannelImplDispatcherAuthenticatesWithOIDC(t *testing.T) { diff --git a/test/rekt/features/apiserversource/data_plane.go b/test/rekt/features/apiserversource/data_plane.go index f88a2c9055d..a90b9f767a1 100644 --- a/test/rekt/features/apiserversource/data_plane.go +++ b/test/rekt/features/apiserversource/data_plane.go @@ -25,6 +25,7 @@ import ( duckv1 "knative.dev/pkg/apis/duck/v1" "knative.dev/pkg/network" "knative.dev/reconciler-test/pkg/environment" + "knative.dev/reconciler-test/pkg/knative" "knative.dev/eventing/pkg/eventingtls/eventingtlstesting" "knative.dev/eventing/test/rekt/resources/addressable" @@ -271,6 +272,62 @@ func SendsEventsWithTLSTrustBundle() *feature.Feature { return f } +func SendsEventsWithTLSWithAdditionalTrustBundle() *feature.Feature { + src := feature.MakeRandomK8sName("apiserversource") + sink := feature.MakeRandomK8sName("sink") + trustBundle := feature.MakeRandomK8sName("trust-bundle") + + f := feature.NewFeatureNamed("Send events to TLS sink - additional trust bundle") + + f.Prerequisite("should not run when Istio is enabled", featureflags.IstioDisabled()) + + f.Setup("install sink", eventshub.Install(sink, eventshub.StartReceiverTLS)) + + f.Setup("Add trust bundle to system namespace", func(ctx context.Context, t feature.T) { + + configmap.Install(trustBundle, knative.KnativeNamespaceFromContext(ctx), + configmap.WithLabels(map[string]string{"networking.knative.dev/trust-bundle": "true"}), + configmap.WithData("ca.crt", *eventshub.GetCaCerts(ctx)), + )(ctx, t) + }) + + sacmName := feature.MakeRandomK8sName("apiserversource") + f.Requirement("Create Service Account for ApiServerSource with RBAC for v1.Event resources", + setupAccountAndRoleForPods(sacmName)) + + cfg := []manifest.CfgFn{ + apiserversource.WithServiceAccountName(sacmName), + apiserversource.WithEventMode(v1.ResourceMode), + apiserversource.WithResources(v1.APIVersionKindSelector{ + APIVersion: "v1", + Kind: "Event", + }), + } + + f.Requirement("install ApiServerSource", func(ctx context.Context, t feature.T) { + cfg = append(cfg, apiserversource.WithSink(&duckv1.Destination{ + URI: &apis.URL{ + Scheme: "https", // Force using https + Host: network.GetServiceHostname(sink, environment.FromContext(ctx).Namespace()), + }, + CACerts: nil, // CA certs are in the new trust-bundle + })) + apiserversource.Install(src, cfg...)(ctx, t) + }) + f.Requirement("ApiServerSource goes ready", apiserversource.IsReady(src)) + + f.Stable("ApiServerSource as event source"). + Must("delivers events on sink with ref", + eventassert.OnStore(sink). + Match(eventassert.MatchKind(eventshub.EventReceived)). + MatchEvent(test.HasType("dev.knative.apiserver.resource.update")). + AtLeast(1), + ). + Must("Set sinkURI to HTTPS endpoint", source.ExpectHTTPSSink(apiserversource.Gvr(), src)) + + return f +} + // SendsEventsWithEventTypes tests apiserversource to a ready broker. func SendsEventsWithEventTypes() *feature.Feature { source := feature.MakeRandomK8sName("source") diff --git a/test/rekt/features/channel/eventing_tls_feature.go b/test/rekt/features/channel/eventing_tls_feature.go index 3bb633afef8..45b48b7e065 100644 --- a/test/rekt/features/channel/eventing_tls_feature.go +++ b/test/rekt/features/channel/eventing_tls_feature.go @@ -31,6 +31,7 @@ import ( "knative.dev/reconciler-test/pkg/eventshub" "knative.dev/reconciler-test/pkg/eventshub/assert" "knative.dev/reconciler-test/pkg/feature" + "knative.dev/reconciler-test/pkg/knative" "knative.dev/reconciler-test/pkg/resources/service" "knative.dev/reconciler-test/resources/certificate" @@ -38,6 +39,7 @@ import ( "knative.dev/eventing/test/rekt/features/featureflags" "knative.dev/eventing/test/rekt/resources/addressable" "knative.dev/eventing/test/rekt/resources/channel_impl" + "knative.dev/eventing/test/rekt/resources/configmap" "knative.dev/eventing/test/rekt/resources/subscription" ) @@ -243,3 +245,88 @@ func SubscriptionTLSTrustBundle() *feature.Feature { return f } + +func SubscriptionTLSWithAdditionalTrustBundle() *feature.Feature { + + channelName := feature.MakeRandomK8sName("channel") + subscriptionName := feature.MakeRandomK8sName("sub") + sink := feature.MakeRandomK8sName("sink") + source := feature.MakeRandomK8sName("source") + dlsName := feature.MakeRandomK8sName("dls") + dlsSubscriptionName := feature.MakeRandomK8sName("dls-sub") + trustBundle := feature.MakeRandomK8sName("trust-bundle") + + f := feature.NewFeature() + + f.Prerequisite("transport encryption is strict", featureflags.TransportEncryptionStrict()) + f.Prerequisite("should not run when Istio is enabled", featureflags.IstioDisabled()) + + f.Setup("Add trust bundle to system namespace", func(ctx context.Context, t feature.T) { + + configmap.Install(trustBundle, knative.KnativeNamespaceFromContext(ctx), + configmap.WithLabels(map[string]string{"networking.knative.dev/trust-bundle": "true"}), + configmap.WithData("ca.crt", *eventshub.GetCaCerts(ctx)), + )(ctx, t) + }) + + f.Setup("install sink", eventshub.Install(sink, eventshub.StartReceiverTLS)) + f.Setup("install sink", eventshub.Install(dlsName, eventshub.StartReceiverTLS)) + f.Setup("install channel", channel_impl.Install(channelName)) + f.Setup("channel is ready", channel_impl.IsReady(channelName)) + + f.Setup("install subscription", func(ctx context.Context, t feature.T) { + d := &duckv1.Destination{ + URI: &apis.URL{ + Scheme: "https", // Force using https + Host: network.GetServiceHostname(sink, environment.FromContext(ctx).Namespace()), + }, + CACerts: nil, // CA certs are in the new trust-bundle + } + subscription.Install(subscriptionName, + subscription.WithChannel(channel_impl.AsRef(channelName)), + subscription.WithSubscriberFromDestination(d))(ctx, t) + }) + f.Setup("subscription is ready", subscription.IsReady(subscriptionName)) + f.Setup("install dead letter subscription", func(ctx context.Context, t feature.T) { + d := &duckv1.Destination{ + URI: &apis.URL{ + Scheme: "https", // Force using https + Host: network.GetServiceHostname(dlsName, environment.FromContext(ctx).Namespace()), + }, + CACerts: nil, // CA certs are in the trust-bundle + } + + subscription.Install(dlsSubscriptionName, + subscription.WithChannel(channel_impl.AsRef(channelName)), + subscription.WithDeadLetterSinkFromDestination(d), + subscription.WithSubscriber(nil, "http://127.0.0.1:2468", ""))(ctx, t) + }) + f.Setup("subscription dead letter is ready", subscription.IsReady(dlsSubscriptionName)) + f.Setup("Channel has HTTPS address", channel_impl.ValidateAddress(channelName, addressable.AssertHTTPSAddress)) + + event := cetest.FullEvent() + event.SetID(uuid.New().String()) + + f.Requirement("install source", eventshub.Install(source, + eventshub.StartSenderToResourceTLS(channel_impl.GVR(), channelName, nil), + eventshub.InputEvent(event), + // Send multiple events so that we take into account that the certificate rotation might + // be detected by the server after some time. + eventshub.SendMultipleEvents(100, 3*time.Second), + )) + + f.Assert("Event sent", assert.OnStore(source). + MatchSentEvent(cetest.HasId(event.ID())). + AtLeast(1), + ) + f.Assert("Event received in sink", assert.OnStore(sink). + MatchReceivedEvent(cetest.HasId(event.ID())). + AtLeast(1), + ) + f.Assert("Event received in dead letter sink", assert.OnStore(dlsName). + MatchReceivedEvent(cetest.HasId(event.ID())). + AtLeast(1), + ) + + return f +} diff --git a/test/rekt/features/pingsource/features.go b/test/rekt/features/pingsource/features.go index acce020dd0e..c421ce5ad2d 100644 --- a/test/rekt/features/pingsource/features.go +++ b/test/rekt/features/pingsource/features.go @@ -27,6 +27,7 @@ import ( "knative.dev/reconciler-test/pkg/environment" "knative.dev/reconciler-test/pkg/eventshub" "knative.dev/reconciler-test/pkg/feature" + "knative.dev/reconciler-test/pkg/knative" "knative.dev/reconciler-test/pkg/manifest" "knative.dev/reconciler-test/pkg/resources/service" @@ -34,6 +35,7 @@ import ( "knative.dev/eventing/pkg/eventingtls/eventingtlstesting" "knative.dev/eventing/test/rekt/resources/addressable" "knative.dev/eventing/test/rekt/resources/broker" + "knative.dev/eventing/test/rekt/resources/configmap" "knative.dev/eventing/test/rekt/resources/eventtype" "knative.dev/eventing/test/rekt/resources/trigger" @@ -132,6 +134,48 @@ func SendsEventsTLSTrustBundle() *feature.Feature { return f } +func SendsEventsTLSWithAdditionalTrustBundle() *feature.Feature { + src := feature.MakeRandomK8sName("pingsource") + sink := feature.MakeRandomK8sName("sink") + trustBundle := feature.MakeRandomK8sName("trust-bundle") + + f := feature.NewFeature() + + f.Prerequisite("should not run when Istio is enabled", featureflags.IstioDisabled()) + + f.Setup("install sink", eventshub.Install(sink, eventshub.StartReceiverTLS)) + + f.Setup("Add trust bundle to system namespace", func(ctx context.Context, t feature.T) { + + configmap.Install(trustBundle, knative.KnativeNamespaceFromContext(ctx), + configmap.WithLabels(map[string]string{"networking.knative.dev/trust-bundle": "true"}), + configmap.WithData("ca.crt", *eventshub.GetCaCerts(ctx)), + )(ctx, t) + }) + + f.Requirement("install pingsource", func(ctx context.Context, t feature.T) { + d := &duckv1.Destination{ + URI: &apis.URL{ + Scheme: "https", // Force using https + Host: network.GetServiceHostname(sink, environment.FromContext(ctx).Namespace()), + }, + CACerts: nil, // CA certs are in the trust-bundle + } + + pingsource.Install(src, pingsource.WithSink(d))(ctx, t) + }) + f.Requirement("pingsource goes ready", pingsource.IsReady(src)) + + f.Stable("pingsource as event source"). + Must("delivers events", assert.OnStore(sink). + Match(eventassert.MatchKind(eventshub.EventReceived)). + MatchEvent(test.HasType("dev.knative.sources.ping")). + AtLeast(1)). + Must("Set sinkURI to HTTPS endpoint", source.ExpectHTTPSSink(pingsource.Gvr(), src)) + + return f +} + func SendsEventsWithSinkURI() *feature.Feature { source := feature.MakeRandomK8sName("pingsource") sink := feature.MakeRandomK8sName("sink") diff --git a/test/rekt/features/trigger/feature.go b/test/rekt/features/trigger/feature.go index 4896fd1c7d7..74663ca9c0e 100644 --- a/test/rekt/features/trigger/feature.go +++ b/test/rekt/features/trigger/feature.go @@ -27,6 +27,7 @@ import ( "knative.dev/reconciler-test/pkg/environment" "knative.dev/reconciler-test/pkg/eventshub" "knative.dev/reconciler-test/pkg/feature" + "knative.dev/reconciler-test/pkg/knative" "knative.dev/reconciler-test/pkg/manifest" "knative.dev/reconciler-test/pkg/resources/service" @@ -36,6 +37,7 @@ import ( "knative.dev/eventing/pkg/eventingtls/eventingtlstesting" "knative.dev/eventing/test/rekt/features/featureflags" "knative.dev/eventing/test/rekt/resources/broker" + "knative.dev/eventing/test/rekt/resources/configmap" "knative.dev/eventing/test/rekt/resources/pingsource" "knative.dev/eventing/test/rekt/resources/trigger" ) @@ -235,3 +237,78 @@ func TriggerWithTLSSubscriberTrustBundle() *feature.Feature { return f } + +func TriggerWithTLSSubscriberWithAdditionalCATrustBundles() *feature.Feature { + f := feature.NewFeatureNamed("Trigger with TLS subscriber and additional trust bundle") + + f.Prerequisite("should not run when Istio is enabled", featureflags.IstioDisabled()) + + brokerName := feature.MakeRandomK8sName("broker") + sourceName := feature.MakeRandomK8sName("source") + sinkName := feature.MakeRandomK8sName("sink") + triggerName := feature.MakeRandomK8sName("trigger") + dlsName := feature.MakeRandomK8sName("dls") + dlsTriggerName := feature.MakeRandomK8sName("dls-trigger") + trustBundle := feature.MakeRandomK8sName("trust-bundle") + + eventToSend := test.FullEvent() + + // Install 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 Sink + f.Setup("Install Sink", eventshub.Install(sinkName, eventshub.StartReceiverTLS)) + f.Setup("Install dead letter sink service", eventshub.Install(dlsName, eventshub.StartReceiverTLS)) + + f.Setup("Add trust bundle to system namespace", func(ctx context.Context, t feature.T) { + + configmap.Install(trustBundle, knative.KnativeNamespaceFromContext(ctx), + configmap.WithLabels(map[string]string{"networking.knative.dev/trust-bundle": "true"}), + configmap.WithData("ca.crt", *eventshub.GetCaCerts(ctx)), + )(ctx, t) + }) + + // Install Trigger + f.Setup("Install trigger", func(ctx context.Context, t feature.T) { + subscriber := &duckv1.Destination{ + URI: &apis.URL{ + Scheme: "https", // Force using https + Host: network.GetServiceHostname(sinkName, environment.FromContext(ctx).Namespace()), + }, + CACerts: nil, // CA certs are in the new trust-bundle + } + + trigger.Install(triggerName, brokerName, + trigger.WithSubscriberFromDestination(subscriber))(ctx, t) + }) + f.Setup("Wait for Trigger to become ready", trigger.IsReady(triggerName)) + + f.Setup("Install failing trigger", func(ctx context.Context, t feature.T) { + dls := service.AsDestinationRef(dlsName) + + linear := eventingv1.BackoffPolicyLinear + trigger.Install(dlsTriggerName, brokerName, + trigger.WithRetry(10, &linear, pointer.String("PT1S")), + trigger.WithDeadLetterSinkFromDestination(dls), + trigger.WithSubscriber(nil, "http://127.0.0.1:2468"))(ctx, t) + }) + f.Setup("Wait for failing Trigger to become ready", trigger.IsReady(dlsTriggerName)) + + // Install Source + f.Requirement("Install Source", eventshub.Install( + sourceName, + eventshub.StartSenderToResource(broker.GVR(), brokerName), + eventshub.InputEvent(eventToSend), + )) + + f.Assert("Trigger delivers events to TLS subscriber", assert.OnStore(sinkName). + MatchReceivedEvent(test.HasId(eventToSend.ID())). + AtLeast(1)) + f.Assert("Trigger delivers events to TLS dead letter sink", assert.OnStore(dlsName). + MatchReceivedEvent(test.HasId(eventToSend.ID())). + AtLeast(1)) + + return f +} diff --git a/test/rekt/pingsource_test.go b/test/rekt/pingsource_test.go index 1aab02f7e8c..8bc45a0f43d 100644 --- a/test/rekt/pingsource_test.go +++ b/test/rekt/pingsource_test.go @@ -61,6 +61,7 @@ func TestPingSourceTLS(t *testing.T) { env.ParallelTest(ctx, t, pingsource.SendsEventsTLS()) env.ParallelTest(ctx, t, pingsource.SendsEventsTLSTrustBundle()) + env.ParallelTest(ctx, t, pingsource.SendsEventsTLSWithAdditionalTrustBundle()) } func TestPingSourceWithSinkURI(t *testing.T) { diff --git a/test/rekt/resources/configmap/config-features.yaml b/test/rekt/resources/configmap/config-features.yaml index 017d7574d5e..b56566f5343 100644 --- a/test/rekt/resources/configmap/config-features.yaml +++ b/test/rekt/resources/configmap/config-features.yaml @@ -3,10 +3,23 @@ kind: ConfigMap metadata: name: {{ .name }} namespace: {{ .namespace }} + {{ if .labels }} + labels: + {{ range $key, $value := .labels }} + {{ $key }}: "{{ $value }}" + {{ end }} + {{ else }} labels: knative.dev/config-propagation: original knative.dev/config-category: eventing + {{ end }} data: + {{ if .data }} + {{ range $key, $value := .data }} + {{ $key }}: |- + {{ $value }} + {{ end }} + {{ else }} _example: | my-enabled-flag: "enabled" my-disabled-flag: "disabled" @@ -14,4 +27,4 @@ data: apiserversources.nodeselector.testkey: testvalue apiserversources.nodeselector.testkey1: testvalue1 apiserversources.nodeselector.testkey2: testvalue2 - + {{ end }} diff --git a/test/rekt/resources/configmap/configmap.go b/test/rekt/resources/configmap/configmap.go index 8776df0c0e6..f6e31fcd24a 100644 --- a/test/rekt/resources/configmap/configmap.go +++ b/test/rekt/resources/configmap/configmap.go @@ -19,6 +19,7 @@ package configmap import ( "context" "embed" + "strings" "knative.dev/reconciler-test/pkg/feature" "knative.dev/reconciler-test/pkg/manifest" @@ -44,3 +45,15 @@ func Install(name string, ns string, opts ...manifest.CfgFn) feature.StepFn { } } } + +var WithLabels = manifest.WithLabels + +func WithData(key, value string) manifest.CfgFn { + return func(m map[string]interface{}) { + if _, ok := m["data"]; !ok { + m["data"] = map[string]string{} + } + value = strings.ReplaceAll(value, "\n", "\n ") + m["data"].(map[string]string)[key] = value + } +} diff --git a/test/rekt/resources/configmap/configmap_test.go b/test/rekt/resources/configmap/configmap_test.go new file mode 100644 index 00000000000..d38491b9e0c --- /dev/null +++ b/test/rekt/resources/configmap/configmap_test.go @@ -0,0 +1,55 @@ +/* +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 configmap + +import ( + "os" + + testlog "knative.dev/reconciler-test/pkg/logging" + "knative.dev/reconciler-test/pkg/manifest" +) + +func Example_withData() { + ctx := testlog.NewContext() + images := map[string]string{} + cfg := map[string]interface{}{ + "name": "foo", + "namespace": "bar", + } + + WithData("ca.crt", "x\nx")(cfg) + WithLabels(map[string]string{"a": "b"})(cfg) + + files, err := manifest.ExecuteYAML(ctx, yaml, images, cfg) + if err != nil { + panic(err) + } + + manifest.OutputYAML(os.Stdout, files) + // Output: + // apiVersion: v1 + // kind: ConfigMap + // metadata: + // name: foo + // namespace: bar + // labels: + // a: "b" + // data: + // ca.crt: |- + // x + // x +} diff --git a/test/rekt/trigger_test.go b/test/rekt/trigger_test.go index 66981384270..6b17ae110e9 100644 --- a/test/rekt/trigger_test.go +++ b/test/rekt/trigger_test.go @@ -95,4 +95,5 @@ func TestTriggerTLSSubscriber(t *testing.T) { env.ParallelTest(ctx, t, trigger.TriggerWithTLSSubscriber()) env.ParallelTest(ctx, t, trigger.TriggerWithTLSSubscriberTrustBundle()) + env.ParallelTest(ctx, t, trigger.TriggerWithTLSSubscriberWithAdditionalCATrustBundles()) }