Skip to content

Commit d1e1b4d

Browse files
committed
LOG-8967: Add TLS Scanner e2e test
1 parent 30295d6 commit d1e1b4d

10 files changed

Lines changed: 722 additions & 8 deletions

File tree

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export LOKI_OPERATOR_CHANNEL?=stable-6.4
3030
IMAGE_LOGGING_VECTOR?=quay.io/openshift-logging/vector:v0.54.0
3131
IMAGE_LOGFILEMETRICEXPORTER?=quay.io/openshift-logging/log-file-metric-exporter:latest
3232
IMAGE_LOGGING_EVENTROUTER?=quay.io/openshift-logging/eventrouter:v0.5.0
33+
IMAGE_TLS_SCANNER?=quay.io/openshift/tls-scanner:latest
3334

3435
REPLICAS?=0
3536
export E2E_TEST_EXCLUDES?=flowcontrol
@@ -223,6 +224,7 @@ test-env: ## Echo test environment, useful for running tests outside of the Make
223224
@echo \
224225
RELATED_IMAGE_VECTOR=$(IMAGE_LOGGING_VECTOR) \
225226
RELATED_IMAGE_LOG_FILE_METRIC_EXPORTER=$(IMAGE_LOGFILEMETRICEXPORTER) \
227+
IMAGE_TLS_SCANNER=$(IMAGE_TLS_SCANNER) \
226228

227229
.PHONY: test-functional
228230
test-functional: test-functional-benchmarker-vector
@@ -319,6 +321,7 @@ test-e2e: $(JUNITREPORT)
319321
RELATED_IMAGE_VECTOR=$(IMAGE_LOGGING_VECTOR) \
320322
RELATED_IMAGE_LOG_FILE_METRIC_EXPORTER=$(IMAGE_LOGFILEMETRICEXPORTER) \
321323
IMAGE_LOGGING_EVENTROUTER=$(IMAGE_LOGGING_EVENTROUTER) \
324+
IMAGE_TLS_SCANNER=$(IMAGE_TLS_SCANNER) \
322325
EXCLUDES="$(E2E_TEST_EXCLUDES)" CLF_EXCLUDES="$(CLF_TEST_EXCLUDES)" LOG_LEVEL=3 hack/test-e2e-olm.sh
323326

324327
.PHONY: test-e2e-local
@@ -328,6 +331,7 @@ test-e2e-local: $(JUNITREPORT) deploy-image
328331
RELATED_IMAGE_VECTOR=$(IMAGE_LOGGING_VECTOR) \
329332
RELATED_IMAGE_LOG_FILE_METRIC_EXPORTER=$(IMAGE_LOGFILEMETRICEXPORTER) \
330333
IMAGE_LOGGING_EVENTROUTER=$(IMAGE_LOGGING_EVENTROUTER) \
334+
IMAGE_TLS_SCANNER=$(IMAGE_TLS_SCANNER) \
331335
CLF_INCLUDES=$(CLF_TEST_INCLUDES) \
332336
EXCLUDES=$(E2E_TEST_EXCLUDES) \
333337
IMAGE_CLUSTER_LOGGING_OPERATOR=image-registry.openshift-image-registry.svc:5000/openshift/origin-cluster-logging-operator:$(CURRENT_BRANCH) \

bundle/manifests/cluster-logging.clusterserviceversion.yaml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ metadata:
8282
categories: OpenShift Optional, Logging & Tracing, Observability
8383
certified: "false"
8484
containerImage: quay.io/openshift-logging/cluster-logging-operator:latest
85-
createdAt: "2026-04-30T19:37:39Z"
85+
createdAt: "2026-05-08T15:11:18Z"
8686
description: The Red Hat OpenShift Logging Operator for OCP provides a means for
8787
configuring and managing log collection and forwarding.
8888
features.operators.openshift.io/cnf: "false"
@@ -2528,6 +2528,7 @@ spec:
25282528
kubectl.kubernetes.io/default-container: cluster-logging-operator
25292529
target.workload.openshift.io/management: '{"effect": "PreferredDuringScheduling"}'
25302530
labels:
2531+
app.kubernetes.io/name: cluster-logging-operator
25312532
control-plane: controller-manager
25322533
name: cluster-logging-operator
25332534
spec:

config/manager/manager.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ spec:
1313
kubectl.kubernetes.io/default-container: cluster-logging-operator
1414
target.workload.openshift.io/management: '{"effect": "PreferredDuringScheduling"}'
1515
labels:
16+
app.kubernetes.io/name: cluster-logging-operator
1617
name: cluster-logging-operator
1718
control-plane: controller-manager
1819
spec:

olm_deploy/scripts/env.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ set -eou pipefail
44
LOGGING_VERSION=${LOGGING_VERSION:-6.1}
55

66
LOGGING_VECTOR_VERSION=${LOGGING_VECTOR_VERSION:-v0.54.0}
7-
LOGGING_LOG_FILE_METRIC_EXPORTER_VERSION=${LOGGING_LOG_FILE_METRIC_EXPORTER_VERSION:-6.1}
7+
LOGGING_LOG_FILE_METRIC_EXPORTER_VERSION=${LOGGING_LOG_FILE_METRIC_EXPORTER_VERSION:-latest}
88

99
LOGGING_IS=${LOGGING_IS:-openshift-logging}
1010
export IMAGE_CLUSTER_LOGGING_OPERATOR_REGISTRY=${IMAGE_CLUSTER_LOGGING_OPERATOR_REGISTRY:-quay.io/${LOGGING_IS}/cluster-logging-operator-registry:${LOGGING_VERSION}}

test/e2e/operator/tls/e2e_test.go

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,217 @@
1+
package tls
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
clolog "github.com/ViaQ/logerr/v2/log/static"
8+
. "github.com/onsi/ginkgo/v2"
9+
. "github.com/onsi/gomega"
10+
configv1 "github.com/openshift/api/config/v1"
11+
obs "github.com/openshift/cluster-logging-operator/api/observability/v1"
12+
"github.com/openshift/cluster-logging-operator/internal/constants"
13+
internalruntime "github.com/openshift/cluster-logging-operator/internal/runtime"
14+
obsruntime "github.com/openshift/cluster-logging-operator/internal/runtime/observability"
15+
internaltls "github.com/openshift/cluster-logging-operator/internal/tls"
16+
"github.com/openshift/cluster-logging-operator/internal/utils/sets"
17+
"github.com/openshift/cluster-logging-operator/test/client"
18+
framework "github.com/openshift/cluster-logging-operator/test/framework/e2e"
19+
tlsscanner "github.com/openshift/cluster-logging-operator/test/framework/e2e/tls"
20+
"github.com/openshift/cluster-logging-operator/test/helpers/loki"
21+
corev1 "k8s.io/api/core/v1"
22+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
23+
"k8s.io/apimachinery/pkg/runtime"
24+
crclient "sigs.k8s.io/controller-runtime/pkg/client"
25+
)
26+
27+
var _ = Describe("[E2E][Operator][TLS] TLS Scanner Validation", func() {
28+
const (
29+
forwarderName = "tls-test-collector"
30+
)
31+
32+
var (
33+
e2e *framework.E2ETestFramework
34+
err error
35+
k8sClient crclient.Client
36+
profileSpec configv1.TLSProfileSpec
37+
)
38+
39+
BeforeEach(func() {
40+
e2e = framework.NewE2ETestFramework()
41+
42+
// Create a controller-runtime client with configv1 scheme for fetching APIServer TLS profile
43+
// This matches the production scheme setup in cmd/main.go
44+
scheme := runtime.NewScheme()
45+
Expect(configv1.AddToScheme(scheme)).To(Succeed())
46+
k8sClient, err = crclient.New(e2e.RestConfig, crclient.Options{Scheme: scheme})
47+
Expect(err).To(BeNil())
48+
49+
tlsProfile, err := internaltls.FetchAPIServerTlsProfile(k8sClient)
50+
Expect(err).To(BeNil(), "Failed to fetch APIServer TLS profile")
51+
52+
By("Fetching the cluster TLS profile")
53+
profileSpec = internaltls.GetClusterTLSProfileSpec(tlsProfile)
54+
clolog.Info("Cluster TLS Profile", "spec", profileSpec)
55+
})
56+
57+
AfterEach(func() {
58+
e2e.Cleanup()
59+
}, framework.DefaultCleanUpTimeout)
60+
61+
var (
62+
runTlsScanner = func(e2e *framework.E2ETestFramework, scanNS string) (results []tlsscanner.ScanResult, err error) {
63+
By("Deploying TLS Scanner")
64+
scanner := tlsscanner.NewScanner(e2e.KubeClient, &e2e.CleanupFns)
65+
e2e.AddCleanup(func() error {
66+
return e2e.KubeClient.BatchV1().Jobs(scanNS).Delete(context.TODO(), tlsscanner.Name, metav1.DeleteOptions{})
67+
})
68+
job, err := scanner.Deploy(scanNS, scanNS)
69+
Expect(err).To(BeNil(), "Failed to deploy TLS Scanner")
70+
Expect(job).NotTo(BeNil())
71+
72+
By("Waiting for TLS Scanner to complete")
73+
err = scanner.WaitForCompletion(job, tlsscanner.JobTimeout)
74+
Expect(err).To(BeNil(), "TLS Scanner job did not complete successfully. It may not have matched any components to scan")
75+
76+
By("Retrieving TLS scan results")
77+
results, err = scanner.GetResults(job)
78+
Expect(err).To(BeNil(), "Failed to retrieve TLS scan results")
79+
Expect(results).NotTo(BeEmpty(), "TLS Scanner returned no results")
80+
return results, err
81+
}
82+
83+
verifyResultsHaveComponents = func(results []tlsscanner.ScanResult, epxComponents ...string) {
84+
components := sets.NewString()
85+
for _, result := range results {
86+
components.Insert(result.Component)
87+
}
88+
Expect(components.List()).To(ConsistOf(epxComponents))
89+
}
90+
)
91+
92+
Context("when inspecting deployed ClusterLogForwarder", func() {
93+
94+
var (
95+
testNS string
96+
clf *obs.ClusterLogForwarder
97+
l *loki.Receiver
98+
sa *corev1.ServiceAccount
99+
)
100+
101+
BeforeEach(func() {
102+
103+
testNS = e2e.CreateTestNamespace(func(namespace *corev1.Namespace) {
104+
namespace.Labels = map[string]string{
105+
"pod-security.kubernetes.io/audit": "privileged",
106+
"pod-security.kubernetes.io/enforce": "privileged",
107+
"pod-security.kubernetes.io/warn": "privileged",
108+
}
109+
})
110+
111+
// Create service account for the collector with permissions for application and infrastructure logs
112+
sa, err = e2e.BuildAuthorizationFor(testNS, forwarderName).
113+
AllowClusterRole(framework.ClusterRoleCollectApplicationLogs).
114+
AllowClusterRole(framework.ClusterRoleCollectInfrastructureLogs).
115+
Create()
116+
Expect(err).To(BeNil())
117+
118+
// Deploy Loki receiver
119+
l = loki.NewReceiver(testNS, "loki-server")
120+
Expect(l.Create(client.Get())).To(Succeed())
121+
122+
// Deploy ClusterLogForwarder with both default inputs and receiver inputs
123+
// to ensure all input receiver types are running for TLS scanning
124+
clf = obsruntime.NewClusterLogForwarder(testNS, forwarderName, internalruntime.Initialize, func(clf *obs.ClusterLogForwarder) {
125+
clf.Spec.ServiceAccount.Name = sa.Name
126+
clf.Spec.Inputs = []obs.InputSpec{
127+
{
128+
Name: "http-receiver",
129+
Type: obs.InputTypeReceiver,
130+
Receiver: &obs.ReceiverSpec{
131+
Type: obs.ReceiverTypeHTTP,
132+
Port: 8080,
133+
HTTP: &obs.HTTPReceiver{
134+
Format: obs.HTTPReceiverFormatKubeAPIAudit,
135+
},
136+
},
137+
},
138+
{
139+
Name: "syslog-receiver",
140+
Type: obs.InputTypeReceiver,
141+
Receiver: &obs.ReceiverSpec{
142+
Type: obs.ReceiverTypeSyslog,
143+
Port: 10514,
144+
},
145+
},
146+
}
147+
clf.Spec.Outputs = []obs.OutputSpec{
148+
{
149+
Name: "loki-output",
150+
Type: obs.OutputTypeLoki,
151+
Loki: &obs.Loki{
152+
URLSpec: obs.URLSpec{
153+
URL: l.InternalURL("").String(),
154+
},
155+
},
156+
},
157+
}
158+
clf.Spec.Pipelines = []obs.PipelineSpec{
159+
{
160+
Name: "test-app",
161+
InputRefs: []string{string(obs.InputTypeApplication)},
162+
OutputRefs: []string{"loki-output"},
163+
},
164+
{
165+
Name: "test-receivers",
166+
InputRefs: []string{"http-receiver", "syslog-receiver"},
167+
OutputRefs: []string{"loki-output"},
168+
},
169+
}
170+
})
171+
172+
if err := e2e.CreateObservabilityClusterLogForwarder(clf); err != nil {
173+
Fail(fmt.Sprintf("Unable to create ClusterLogForwarder: %v", err))
174+
}
175+
176+
if err := e2e.WaitForDaemonSet(clf.Namespace, clf.Name); err != nil {
177+
Fail(fmt.Sprintf("Failed waiting for collector DaemonSet to be ready: %v", err))
178+
}
179+
})
180+
181+
It("should validate the TLS server configurations match the cluster TLS profile", func() {
182+
results, _ := runTlsScanner(e2e, testNS)
183+
clolog.Info("TLS Scanner found endpoints", "count", len(results))
184+
clolog.V(2).Info("TLS endpoint scanned", "result", results)
185+
verifyResultsHaveComponents(results, constants.VectorName)
186+
187+
By("Validating TLS compliance")
188+
err = tlsscanner.ValidateCompliance(results, profileSpec)
189+
Expect(err).To(BeNil(), "TLS compliance validation failed")
190+
})
191+
})
192+
193+
Context("when inspecting the operator and LogFileMetricExporter", func() {
194+
It("should validate the TLS configurations matches the cluster TLS profile", func() {
195+
196+
// Deploy LFME
197+
lfme := internalruntime.NewLogFileMetricExporter(constants.OpenshiftNS, constants.SingletonName)
198+
e2e.AddCleanup(func() error {
199+
return e2e.KubeClient.AppsV1().DaemonSets(constants.OpenshiftNS).Delete(context.TODO(), lfme.Name, metav1.DeleteOptions{})
200+
})
201+
if err := e2e.Create(lfme); err != nil {
202+
Fail(fmt.Sprintf("Unable to create LogFileMetricExporter: %v", err))
203+
}
204+
if err := e2e.WaitForDaemonSet(lfme.Namespace, constants.LogfilesmetricexporterName); err != nil {
205+
Fail(fmt.Sprintf("Failed waiting for lfme DaemonSet to be ready: %v", err))
206+
}
207+
208+
results, _ := runTlsScanner(e2e, constants.OpenshiftNS)
209+
clolog.Info("TLS Scanner found endpoints", "count", len(results))
210+
verifyResultsHaveComponents(results, constants.ClusterLoggingOperator, constants.LogfilesmetricexporterName)
211+
212+
By("Validating TLS compliance")
213+
err = tlsscanner.ValidateCompliance(results, profileSpec)
214+
Expect(err).To(BeNil(), "TLS compliance validation failed")
215+
})
216+
})
217+
})
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package tls
2+
3+
import (
4+
"testing"
5+
6+
. "github.com/onsi/ginkgo/v2"
7+
. "github.com/onsi/gomega"
8+
)
9+
10+
func TestSuite(t *testing.T) {
11+
RegisterFailHandler(Fail)
12+
RunSpecs(t, "[e2e][operator][tls] Suite")
13+
}

test/framework/e2e/framework.go

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -177,16 +177,22 @@ func (tc *E2ETestFramework) DeployCURLLogGeneratorWithNamespaceAndEndpoint(names
177177
return client.Get().WaitFor(pod, client.PodRunning)
178178
}
179179

180-
func (tc *E2ETestFramework) CreateTestNamespace() string {
181-
return tc.CreateTestNamespaceWithPrefix("clo-test")
180+
// CreateTestNamespace with a default prefix of 'clo-test'. Optionally include a list of functions to modify the object
181+
// before it is created
182+
func (tc *E2ETestFramework) CreateTestNamespace(visitors ...func(*corev1.Namespace)) string {
183+
return tc.CreateTestNamespaceWithPrefix("clo-test", visitors...)
182184
}
183185

184-
func (tc *E2ETestFramework) CreateTestNamespaceWithPrefix(prefix string) string {
186+
// CreateTestNamespaceWithPrefix using the given prefix. Optionally include a list of functions to modify the object
187+
// before it is created
188+
func (tc *E2ETestFramework) CreateTestNamespaceWithPrefix(prefix string, visitors ...func(*corev1.Namespace)) string {
185189
name := fmt.Sprintf("%s-%d", prefix, rand.Intn(10000)) //nolint:gosec
186-
return tc.CreateNamespace(name)
190+
return tc.CreateNamespace(name, visitors...)
187191
}
188192

189-
func (tc *E2ETestFramework) CreateNamespace(name string) string {
193+
// CreateNamespace using the given name. Optionally include a list of functions to modify the object
194+
// before it is created
195+
func (tc *E2ETestFramework) CreateNamespace(name string, visitors ...func(*corev1.Namespace)) string {
190196
if value, found := os.LookupEnv("GENERATOR_NS"); found {
191197
name = value
192198
} else {
@@ -200,6 +206,9 @@ func (tc *E2ETestFramework) CreateNamespace(name string) string {
200206
Name: name,
201207
},
202208
}
209+
for _, visitor := range visitors {
210+
visitor(namespace)
211+
}
203212

204213
if err := tc.Test.Recreate(namespace); err != nil {
205214
clolog.Error(err, "Error")

test/framework/e2e/splunk.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@ package e2e
33
import (
44
"context"
55
"fmt"
6-
"github.com/openshift/cluster-logging-operator/internal/runtime"
76
"net/url"
87

8+
"github.com/openshift/cluster-logging-operator/internal/runtime"
9+
910
"github.com/openshift/cluster-logging-operator/internal/constants"
1011
"github.com/openshift/cluster-logging-operator/internal/utils"
1112
"github.com/openshift/cluster-logging-operator/test/helpers/oc"

0 commit comments

Comments
 (0)