From 6b8243958fe8e634c423772157b3c0833fb3e648 Mon Sep 17 00:00:00 2001 From: Watson Yuuma Sato Date: Tue, 21 Oct 2025 16:18:00 -0300 Subject: [PATCH 1/3] Change rule checking log forwarder uses tls existence key Change rule's existence checks to any_exist. This means that any ClusterLogForwarder that has a url key, needs to comply. If no such forwarder exists, it is fine. Previously, the rule would enforce that at least one such forwarder existed. --- .../audit_log_forwarding_uses_tls_observability_api/rule.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/applications/openshift/logging/audit_log_forwarding_uses_tls_observability_api/rule.yml b/applications/openshift/logging/audit_log_forwarding_uses_tls_observability_api/rule.yml index a66009133a76..935103e825a5 100644 --- a/applications/openshift/logging/audit_log_forwarding_uses_tls_observability_api/rule.yml +++ b/applications/openshift/logging/audit_log_forwarding_uses_tls_observability_api/rule.yml @@ -52,6 +52,7 @@ template: # By using the objects filter we ensure we are getting the object to query for its url. filepath: "{{{ openshift_filtered_path('/apis/observability.openshift.io/v1/namespaces/openshift-logging/clusterlogforwarders', 'try [.items[].spec.outputs[][]|objects|select(.url != null).url] catch []') }}}" yamlpath: "[:]" + check_existence: any_exist entity_check: "all" values: - value: "^(https|tls)://.*$" From 2851488f8e8edcad7d4c205acfa881d1105a08ba Mon Sep 17 00:00:00 2001 From: Watson Yuuma Sato Date: Mon, 27 Oct 2025 17:23:36 -0300 Subject: [PATCH 2/3] Use custom OVAL to ensure at least one CLF exists The yaml template can't generate the OVAL check we need to be able to assess the clusterlogforwarders correctly. Wee need a check that is capable of ensuring that: - there is at least one clusterlogforwarder - Any clusterlogforwarder object that has .url, is secure --- .../audit_log_forwarding_uses_tls/rule.yml | 3 +- .../oval/shared.xml | 75 +++++++++++++++++++ .../rule.yml | 36 ++++----- 3 files changed, 96 insertions(+), 18 deletions(-) create mode 100644 applications/openshift/logging/audit_log_forwarding_uses_tls_observability_api/oval/shared.xml diff --git a/applications/openshift/logging/audit_log_forwarding_uses_tls/rule.yml b/applications/openshift/logging/audit_log_forwarding_uses_tls/rule.yml index c29973daf6de..a1ad655d3f5d 100644 --- a/applications/openshift/logging/audit_log_forwarding_uses_tls/rule.yml +++ b/applications/openshift/logging/audit_log_forwarding_uses_tls/rule.yml @@ -37,7 +37,8 @@ ocil: |- warnings: - general: |- - {{{ openshift_cluster_setting() | indent(4) }}} + {{{ openshift_cluster_setting( + "/apis/observability.openshift.io/v1/namespaces/openshift-logging/clusterlogforwarders") | indent(4) }}} {{{ openshift_filtered_cluster_setting_suppressed({ "/apis/logging.openshift.io/v1/namespaces/openshift-logging/clusterlogforwarders/instance": 'try [.spec.outputs[].url] catch []', "/apis/observability.openshift.io/v1/namespaces/openshift-logging/clusterlogforwarders": 'try [.items[].spec.outputs[][]|objects|select(.url != null).url] catch []', diff --git a/applications/openshift/logging/audit_log_forwarding_uses_tls_observability_api/oval/shared.xml b/applications/openshift/logging/audit_log_forwarding_uses_tls_observability_api/oval/shared.xml new file mode 100644 index 000000000000..f97bb1428176 --- /dev/null +++ b/applications/openshift/logging/audit_log_forwarding_uses_tls_observability_api/oval/shared.xml @@ -0,0 +1,75 @@ +{{% set clf_path = '/apis/observability.openshift.io/v1/namespaces/openshift-logging/clusterlogforwarders' %}} +{{% set clf_filter = 'try [.items[].spec.outputs[][]|objects|select(.url != null).url] catch []' %}} + + + + {{{ oval_metadata("Ensure that Autidt Log Forwarding Uses TLS", rule_title=rule_title) }}} + + + + + + + + + + + + {{{ clf_path }}} + + + + + + + {{{ openshift_filtered_path(clf_path, clf_filter) }}} + + + + + + + + + + + .items[].spec.outputs[].name + + + + + + + + + + + [:] + + + + + ^(https|tls)://.*$ + + + + + + + + + + + + + + + diff --git a/applications/openshift/logging/audit_log_forwarding_uses_tls_observability_api/rule.yml b/applications/openshift/logging/audit_log_forwarding_uses_tls_observability_api/rule.yml index 935103e825a5..1266faf222df 100644 --- a/applications/openshift/logging/audit_log_forwarding_uses_tls_observability_api/rule.yml +++ b/applications/openshift/logging/audit_log_forwarding_uses_tls_observability_api/rule.yml @@ -37,24 +37,26 @@ ocil: |- warnings: - general: |- - {{{ openshift_cluster_setting() | indent(4) }}} + {{{ openshift_cluster_setting( + "/apis/observability.openshift.io/v1/namespaces/openshift-logging/clusterlogforwarders" + ) | indent(4) }}} {{{ openshift_filtered_cluster_setting_suppressed({ "/apis/observability.openshift.io/v1/namespaces/openshift-logging/clusterlogforwarders": 'try [.items[].spec.outputs[][]|objects|(select(.url != null).url] catch []', }) | indent(4) }}} -template: - name: yamlfile_value - vars: - ocp_data: "true" - # A list of clusterlogforwarders is available at https://docs.openshift.com/container-platform/4.16/observability/logging/logging-6.0/log6x-clf.html#outputs - # The log forwarder outputs consist of an object and two strings (name and type). - # The url is part of the object and its name will vary depending on its type. - # By using the objects filter we ensure we are getting the object to query for its url. - filepath: "{{{ openshift_filtered_path('/apis/observability.openshift.io/v1/namespaces/openshift-logging/clusterlogforwarders', 'try [.items[].spec.outputs[][]|objects|select(.url != null).url] catch []') }}}" - yamlpath: "[:]" - check_existence: any_exist - entity_check: "all" - values: - - value: "^(https|tls)://.*$" - entity_check: "all" - operation: "pattern match" +# template: +# name: yamlfile_value +# vars: +# ocp_data: "true" +# # A list of clusterlogforwarders is available at https://docs.openshift.com/container-platform/4.16/observability/logging/logging-6.0/log6x-clf.html#outputs +# # The log forwarder outputs consist of an object and two strings (name and type). +# # The url is part of the object and its name will vary depending on its type. +# # By using the objects filter we ensure we are getting the object to query for its url. +# filepath: "{{{ openshift_filtered_path('/apis/observability.openshift.io/v1/namespaces/openshift-logging/clusterlogforwarders', 'try [.items[].spec.outputs[][]|objects|select(.url != null).url] catch []') }}}" +# yamlpath: "[:]" +# check_existence: any_exist +# entity_check: "all" +# values: +# - value: "^(https|tls)://.*$" +# entity_check: "all" +# operation: "pattern match" From 90732449563fb88fd3ecefd862ad70b3740c2740 Mon Sep 17 00:00:00 2001 From: Lance Bragstad Date: Tue, 4 Nov 2025 09:26:44 -0800 Subject: [PATCH 3/3] Invert the secure TLS forwarding logic The cluster logging operator added a bunch of new forwarding implementations that allow users to ship their logs to various platforms and components. Some ClusterLogForwarders use a url, others use a host. In cases where we can inspect the url, we want to check to make sure it's not using an insecure protocol (http, tcp, udp). In other cases, where the forwarder is shipping logs to a dedicated service (like AzureMonitor), we can't actually inspect the protocol because the Azure forwarding configuration only exposes a `host` attribute, which doesn't use a protocol as part of the host string. Instead, it's baked into the forwarding implementation. This adds complexity to the rule because we need to: - Check that at least one ClusterLogForwarder exists - Each ClusterLogForwarder is configured to encrypt traffic to the forwarding endpoint - Short-circuit the check for special case forwarders, like AzureMonitor, that don't specify the protocol in the endpoint url/host Instead of looking for secure endpoints in each forwarder, which aren't implemented consistently, this commit reverses the logic so that it asserts no "insecure" endpoints are in a forwarding configuration. This works better for cases like AzureMonitor because if the rule doesn't find a `url` in the forwarder, is has nothing to compare the protocol check to, which means it passes. If a forwarder is configured to use plain old `http`, it will fail because the check asserts none exist against regular expression modeling unencrypted protocols. At the same time, we're maintaining the behavior where the rule fails is no forwarders exist at all. I believe this is ultimately due to the fact that "any_exists" OVAL checks will PASS if no pattern matches are made (filtering a log forwarder with url=http://example.com will not match a regular expression only looking for secure protocols, resuling in a PASS when it should actually fail due to how "any_exists" handles non-existent matches). --- .../oval/shared.xml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/applications/openshift/logging/audit_log_forwarding_uses_tls_observability_api/oval/shared.xml b/applications/openshift/logging/audit_log_forwarding_uses_tls_observability_api/oval/shared.xml index f97bb1428176..2e4b95ce3ad6 100644 --- a/applications/openshift/logging/audit_log_forwarding_uses_tls_observability_api/oval/shared.xml +++ b/applications/openshift/logging/audit_log_forwarding_uses_tls_observability_api/oval/shared.xml @@ -43,8 +43,8 @@ - + @@ -56,7 +56,7 @@ - ^(https|tls)://.*$ + ^(http|tcp|udp)://.*$