From 769d8d6a2f5a14720f5abdfe3cc03b4105c5d947 Mon Sep 17 00:00:00 2001 From: jstuart Date: Wed, 15 Jul 2026 21:02:38 -0500 Subject: [PATCH 1/2] feat(EC-1957): verify base image release signatures in base_image_registries Add a third path in _image_ref_permitted that verifies a base image's release signature using ec.sigstore.verify_image() with a release_public_key from rule data. This provides stronger cryptographic assurance than registry prefix matching. - Add signature verification clause to _image_ref_permitted - Relax allowed_registries_provided to pass when only release_public_key is configured - Add deprecation warn rule when prefixes are used without a release key - Add schema validation for release_public_key (must be a string) - Update example rule_data.yml with release_public_key entry - Regenerate docs Co-Authored-By: Claude Opus 4.6 --- .../release_base_image_registries.adoc | 18 ++- .../modules/ROOT/pages/release_policy.adoc | 13 +- .../ROOT/partials/release_policy_nav.adoc | 3 +- example/data/rule_data.yml | 7 ++ .../base_image_registries.rego | 79 ++++++++++-- .../base_image_registries_test.rego | 113 ++++++++++++++++++ 6 files changed, 213 insertions(+), 20 deletions(-) diff --git a/antora/docs/modules/ROOT/pages/packages/release_base_image_registries.adoc b/antora/docs/modules/ROOT/pages/packages/release_base_image_registries.adoc index 42bb1c8c2..b4b2c03cd 100644 --- a/antora/docs/modules/ROOT/pages/packages/release_base_image_registries.adoc +++ b/antora/docs/modules/ROOT/pages/packages/release_base_image_registries.adoc @@ -9,9 +9,9 @@ This package is responsible for verifying the base (parent) images reported in t == Rules Included [#base_image_registries__allowed_registries_provided] -=== link:#base_image_registries__allowed_registries_provided[Allowed base image registry prefixes list was provided] +=== link:#base_image_registries__allowed_registries_provided[Allowed base image registry prefixes list or release public key was provided] -Confirm the `allowed_registry_prefixes` rule data was provided, since it's required by the policy rules in this package. +Confirm that either the `allowed_registry_prefixes` or `release_public_key` rule data was provided, since at least one is required by the policy rules in this package. *Solution*: Make sure to configure a list of trusted registries as a xref:cli:ROOT:configuration.adoc#_data_sources[data source]. @@ -23,7 +23,7 @@ Confirm the `allowed_registry_prefixes` rule data was provided, since it's requi [#base_image_registries__base_image_permitted] === link:#base_image_registries__base_image_permitted[Base image comes from permitted registry] -Verify that the base images used when building a container image come from a known set of trusted registries to reduce potential supply chain attacks. By default this policy defines trusted registries as registries that are fully maintained by Red Hat and only contain content produced by Red Hat. The list of permitted registries can be customized by setting the `allowed_registry_prefixes` list in the rule data. Base images that are found in the snapshot being validated are also allowed since EC will also validate those images individually. +Verify that the base images used when building a container image are permitted. Images can be permitted in three ways: by matching a registry prefix from `allowed_registry_prefixes` rule data (deprecated), by matching a component digest in the snapshot, or by having a valid release signature verified against the `release_public_key` rule data. The preferred approach is signature-based verification via `release_public_key`. Registry prefix matching is deprecated and will be removed in a future release. *Solution*: Make sure the image used in each task comes from a trusted registry. The list of trusted registries is a configurable xref:cli:ROOT:configuration.adoc#_data_sources[data source]. @@ -43,3 +43,15 @@ Verify the expected information was provided about which base images were used d * FAILURE message: `Base images information is missing` * Code: `base_image_registries.base_image_info_found` * https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/base_image_registries/base_image_registries.rego#L49[Source, window="_blank"] + +[#base_image_registries__registry_prefix_deprecated] +=== link:#base_image_registries__registry_prefix_deprecated[Registry prefix matching is deprecated] + +Using `allowed_registry_prefixes` to permit base images is deprecated. Configure `release_public_key` to verify base image release signatures instead, which provides stronger cryptographic assurance than registry prefix matching. + +*Solution*: Set the `release_public_key` in rule data to enable signature-based base image verification. The key can be an inline PEM-encoded public key or a k8s:// reference to a secret containing the key. + +* Rule type: [rule-type-indicator warning]#WARNING# +* WARNING message: `allowed_registry_prefixes is configured without release_public_key. Migrate to signature-based verification by setting release_public_key in rule data.` +* Code: `base_image_registries.registry_prefix_deprecated` +* https://github.com/conforma/policy/blob/{page-origin-refhash}/policy/release/base_image_registries/base_image_registries.rego#L99[Source, window="_blank"] diff --git a/antora/docs/modules/ROOT/pages/release_policy.adoc b/antora/docs/modules/ROOT/pages/release_policy.adoc index af50c437d..a7a871414 100644 --- a/antora/docs/modules/ROOT/pages/release_policy.adoc +++ b/antora/docs/modules/ROOT/pages/release_policy.adoc @@ -32,9 +32,10 @@ Rules included: * xref:packages/release_attestation_type.adoc#attestation_type__known_attestation_type[Attestation type: Known attestation type found] * xref:packages/release_attestation_type.adoc#attestation_type__known_attestation_types_provided[Attestation type: Known attestation types provided] * xref:packages/release_attestation_type.adoc#attestation_type__pipelinerun_attestation_found[Attestation type: PipelineRun attestation found] -* xref:packages/release_base_image_registries.adoc#base_image_registries__allowed_registries_provided[Base image checks: Allowed base image registry prefixes list was provided] +* xref:packages/release_base_image_registries.adoc#base_image_registries__allowed_registries_provided[Base image checks: Allowed base image registry prefixes list or release public key was provided] * xref:packages/release_base_image_registries.adoc#base_image_registries__base_image_permitted[Base image checks: Base image comes from permitted registry] * xref:packages/release_base_image_registries.adoc#base_image_registries__base_image_info_found[Base image checks: Base images provided] +* xref:packages/release_base_image_registries.adoc#base_image_registries__registry_prefix_deprecated[Base image checks: Registry prefix matching is deprecated] * xref:packages/release_cve.adoc#cve__cve_blockers[CVE checks: Blocking CVE check] * xref:packages/release_cve.adoc#cve__unpatched_cve_blockers[CVE checks: Blocking unpatched CVE check] * xref:packages/release_cve.adoc#cve__cve_results_found[CVE checks: CVE scan results found] @@ -75,7 +76,7 @@ Rules included: * xref:packages/release_maven_repos.adoc#maven_repos__policy_data_missing[All maven artifacts have known repository URLs: Policy data validation] * xref:packages/release_attestation_type.adoc#attestation_type__known_attestation_types_provided[Attestation type: Known attestation types provided] -* xref:packages/release_base_image_registries.adoc#base_image_registries__allowed_registries_provided[Base image checks: Allowed base image registry prefixes list was provided] +* xref:packages/release_base_image_registries.adoc#base_image_registries__allowed_registries_provided[Base image checks: Allowed base image registry prefixes list or release public key was provided] * xref:packages/release_buildah_build_task.adoc#buildah_build_task__disallowed_platform_patterns_pattern[Buildah build task: disallowed_platform_patterns format] * xref:packages/release_cve.adoc#cve__rule_data_provided[CVE checks: Rule data provided] * xref:packages/release_external_parameters.adoc#external_parameters__pipeline_run_params_provided[External parameters: PipelineRun params provided] @@ -115,9 +116,10 @@ Rules included: * xref:packages/release_attestation_type.adoc#attestation_type__known_attestation_type[Attestation type: Known attestation type found] * xref:packages/release_attestation_type.adoc#attestation_type__known_attestation_types_provided[Attestation type: Known attestation types provided] * xref:packages/release_attestation_type.adoc#attestation_type__pipelinerun_attestation_found[Attestation type: PipelineRun attestation found] -* xref:packages/release_base_image_registries.adoc#base_image_registries__allowed_registries_provided[Base image checks: Allowed base image registry prefixes list was provided] +* xref:packages/release_base_image_registries.adoc#base_image_registries__allowed_registries_provided[Base image checks: Allowed base image registry prefixes list or release public key was provided] * xref:packages/release_base_image_registries.adoc#base_image_registries__base_image_permitted[Base image checks: Base image comes from permitted registry] * xref:packages/release_base_image_registries.adoc#base_image_registries__base_image_info_found[Base image checks: Base images provided] +* xref:packages/release_base_image_registries.adoc#base_image_registries__registry_prefix_deprecated[Base image checks: Registry prefix matching is deprecated] * xref:packages/release_buildah_build_task.adoc#buildah_build_task__add_capabilities_param[Buildah build task: ADD_CAPABILITIES parameter] * xref:packages/release_buildah_build_task.adoc#buildah_build_task__buildah_uses_local_dockerfile[Buildah build task: Buildah task uses a local Dockerfile] * xref:packages/release_buildah_build_task.adoc#buildah_build_task__platform_param[Buildah build task: PLATFORM parameter] @@ -340,9 +342,10 @@ Rules included: * xref:packages/release_attestation_type.adoc#attestation_type__known_attestation_type[Attestation type: Known attestation type found] * xref:packages/release_attestation_type.adoc#attestation_type__known_attestation_types_provided[Attestation type: Known attestation types provided] * xref:packages/release_attestation_type.adoc#attestation_type__pipelinerun_attestation_found[Attestation type: PipelineRun attestation found] -* xref:packages/release_base_image_registries.adoc#base_image_registries__allowed_registries_provided[Base image checks: Allowed base image registry prefixes list was provided] +* xref:packages/release_base_image_registries.adoc#base_image_registries__allowed_registries_provided[Base image checks: Allowed base image registry prefixes list or release public key was provided] * xref:packages/release_base_image_registries.adoc#base_image_registries__base_image_permitted[Base image checks: Base image comes from permitted registry] * xref:packages/release_base_image_registries.adoc#base_image_registries__base_image_info_found[Base image checks: Base images provided] +* xref:packages/release_base_image_registries.adoc#base_image_registries__registry_prefix_deprecated[Base image checks: Registry prefix matching is deprecated] * xref:packages/release_buildah_build_task.adoc#buildah_build_task__add_capabilities_param[Buildah build task: ADD_CAPABILITIES parameter] * xref:packages/release_buildah_build_task.adoc#buildah_build_task__buildah_uses_local_dockerfile[Buildah build task: Buildah task uses a local Dockerfile] * xref:packages/release_buildah_build_task.adoc#buildah_build_task__privileged_nested_param[Buildah build task: PRIVILEGED_NESTED parameter] @@ -356,8 +359,8 @@ Rules included: * xref:packages/release_git_branch.adoc#git_branch__git_branch[Git branch checks: Builds have a trusted target branch] * xref:packages/release_github_certificate.adoc#github_certificate__gh_workflow_extensions[GitHub Certificate Checks: GitHub Workflow Certificate Extensions] * xref:packages/release_github_certificate.adoc#github_certificate__gh_workflow_name[GitHub Certificate Checks: GitHub Workflow Name] -* xref:packages/release_github_certificate.adoc#github_certificate__gh_workflow_ref[GitHub Certificate Checks: GitHub Workflow Repository] * xref:packages/release_github_certificate.adoc#github_certificate__gh_workflow_repository[GitHub Certificate Checks: GitHub Workflow Repository] +* xref:packages/release_github_certificate.adoc#github_certificate__gh_workflow_ref[GitHub Certificate Checks: GitHub Workflow Repository] * xref:packages/release_github_certificate.adoc#github_certificate__gh_workflow_trigger[GitHub Certificate Checks: GitHub Workflow Trigger] * xref:packages/release_github_certificate.adoc#github_certificate__rule_data_provided[GitHub Certificate Checks: Rule data provided] * xref:packages/release_hermetic_task.adoc#hermetic_task__hermetic[Hermetic task: Task called with hermetic param set] diff --git a/antora/docs/modules/ROOT/partials/release_policy_nav.adoc b/antora/docs/modules/ROOT/partials/release_policy_nav.adoc index deeb5b14e..6ae042050 100644 --- a/antora/docs/modules/ROOT/partials/release_policy_nav.adoc +++ b/antora/docs/modules/ROOT/partials/release_policy_nav.adoc @@ -19,9 +19,10 @@ **** xref:packages/release_attestation_type.adoc#attestation_type__known_attestation_types_provided[Known attestation types provided] **** xref:packages/release_attestation_type.adoc#attestation_type__pipelinerun_attestation_found[PipelineRun attestation found] *** xref:packages/release_base_image_registries.adoc[Base image checks] -**** xref:packages/release_base_image_registries.adoc#base_image_registries__allowed_registries_provided[Allowed base image registry prefixes list was provided] +**** xref:packages/release_base_image_registries.adoc#base_image_registries__allowed_registries_provided[Allowed base image registry prefixes list or release public key was provided] **** xref:packages/release_base_image_registries.adoc#base_image_registries__base_image_permitted[Base image comes from permitted registry] **** xref:packages/release_base_image_registries.adoc#base_image_registries__base_image_info_found[Base images provided] +**** xref:packages/release_base_image_registries.adoc#base_image_registries__registry_prefix_deprecated[Registry prefix matching is deprecated] *** xref:packages/release_buildah_build_task.adoc[Buildah build task] **** xref:packages/release_buildah_build_task.adoc#buildah_build_task__add_capabilities_param[ADD_CAPABILITIES parameter] **** xref:packages/release_buildah_build_task.adoc#buildah_build_task__buildah_uses_local_dockerfile[Buildah task uses a local Dockerfile] diff --git a/example/data/rule_data.yml b/example/data/rule_data.yml index 16b0bdfb4..9e7f31940 100644 --- a/example/data/rule_data.yml +++ b/example/data/rule_data.yml @@ -29,6 +29,13 @@ rule_data: - docker.io/ - registry.access.redhat.com + # Usage: https://conforma.dev/docs/policy/packages/release_base_image_registries.html#base_image_registries__base_image_permitted + # Verify base image release signatures. Accepts an inline PEM-encoded public key + # or a k8s:// reference to a secret (e.g., "k8s://my-namespace/my-secret"). + # When set, base images with a valid release signature are permitted regardless + # of allowed_registry_prefixes. + release_public_key: "k8s://openshift-pipelines/release-signing-key" + # Usage: https://conforma.dev/docs/policy/packages/task_step_image_registries.html allowed_step_image_registry_prefixes: - localhost:5000/ diff --git a/policy/release/base_image_registries/base_image_registries.rego b/policy/release/base_image_registries/base_image_registries.rego index c690ece81..bc553a57e 100644 --- a/policy/release/base_image_registries/base_image_registries.rego +++ b/policy/release/base_image_registries/base_image_registries.rego @@ -18,13 +18,13 @@ import data.lib.sbom # METADATA # title: Base image comes from permitted registry # description: >- -# Verify that the base images used when building a container image come from a known -# set of trusted registries to reduce potential supply chain attacks. By default this -# policy defines trusted registries as registries that are fully maintained by Red -# Hat and only contain content produced by Red Hat. The list of permitted registries -# can be customized by setting the `allowed_registry_prefixes` list in the rule data. -# Base images that are found in the snapshot being validated are also allowed since EC -# will also validate those images individually. +# Verify that the base images used when building a container image are permitted. +# Images can be permitted in three ways: by matching a registry prefix from +# `allowed_registry_prefixes` rule data (deprecated), by matching a component digest +# in the snapshot, or by having a valid release signature verified against the +# `release_public_key` rule data. The preferred approach is signature-based +# verification via `release_public_key`. Registry prefix matching is deprecated +# and will be removed in a future release. # custom: # short_name: base_image_permitted # failure_msg: Base image %q is from a disallowed registry @@ -74,10 +74,11 @@ deny contains result if { } # METADATA -# title: Allowed base image registry prefixes list was provided +# title: Allowed base image registry prefixes list or release public key was provided # description: >- -# Confirm the `allowed_registry_prefixes` rule data was provided, since it's -# required by the policy rules in this package. +# Confirm that either the `allowed_registry_prefixes` or `release_public_key` +# rule data was provided, since at least one is required by the policy rules +# in this package. # custom: # short_name: allowed_registries_provided # failure_msg: "%s" @@ -95,6 +96,33 @@ deny contains result if { result := metadata.result_helper_with_severity(rego.metadata.chain(), [error.message], error.severity) } +# METADATA +# title: Registry prefix matching is deprecated +# description: >- +# Using `allowed_registry_prefixes` to permit base images is deprecated. +# Configure `release_public_key` to verify base image release signatures instead, +# which provides stronger cryptographic assurance than registry prefix matching. +# custom: +# short_name: registry_prefix_deprecated +# failure_msg: >- +# allowed_registry_prefixes is configured without release_public_key. Migrate +# to signature-based verification by setting release_public_key in rule data. +# solution: >- +# Set the `release_public_key` in rule data to enable signature-based base image +# verification. The key can be an inline PEM-encoded public key or a k8s:// +# reference to a secret containing the key. +# collections: +# - minimal +# - redhat +# - redhat_security +# +warn contains result if { + prefixes := rule_data.get(_rule_data_key) + count(prefixes) > 0 + not _release_public_key_provided + result := metadata.result_helper(rego.metadata.chain(), []) +} + _image_ref_permitted(image_ref) if { allowed_prefixes := rule_data.get(_rule_data_key) some allowed_prefix in allowed_prefixes @@ -105,6 +133,22 @@ _image_ref_permitted(image_ref) if { img := image.parse(component.containerImage) } image.parse(image_ref).digest in allowed_digests +} else if { + key := rule_data.get(_release_key_rule_data_key) + is_string(key) + key != "" + info := ec.sigstore.verify_image(image_ref, {"public_key": key, "ignore_rekor": false}) + not _has_sig_errors(info) +} + +_has_sig_errors(info) if { + some _ in info.errors +} + +_release_public_key_provided if { + key := rule_data.get(_release_key_rule_data_key) + is_string(key) + key != "" } _cyclonedx_base_images := [_cyclonedx_image_ref(component) | @@ -170,8 +214,8 @@ _cyclonedx_image_ref(component) := image_ref if { image_ref := sbom.image_ref_from_purl(purl) } -# Verify allowed_registry_prefixes is a non-empty list of strings _rule_data_errors contains error if { + not _release_public_key_provided some e in j.validate_schema( rule_data.get(_rule_data_key), { @@ -188,4 +232,17 @@ _rule_data_errors contains error if { } } +_rule_data_errors contains error if { + val := rule_data.get(_release_key_rule_data_key) + val != [] + not is_string(val) + msg := sprintf( + "Rule data %s has unexpected format: expected a string, got %s", + [_release_key_rule_data_key, type_name(val)], + ) + error := {"message": msg, "severity": "failure"} +} + _rule_data_key := "allowed_registry_prefixes" + +_release_key_rule_data_key := "release_public_key" diff --git a/policy/release/base_image_registries/base_image_registries_test.rego b/policy/release/base_image_registries/base_image_registries_test.rego index bf74d3182..d133d6a9d 100644 --- a/policy/release/base_image_registries/base_image_registries_test.rego +++ b/policy/release/base_image_registries/base_image_registries_test.rego @@ -370,6 +370,119 @@ test_rule_data_validation if { with lib.sbom.spdx_sboms as [{}] } +test_signature_verified_base_image if { + sboms := [{"formulation": [{"components": [{ + "name": "ubi", + "type": "container", + "properties": [{"name": "konflux:container:is_base_image", "value": "true"}], + "purl": "pkg:oci/ubi@sha256:abc?repository_url=unknown.registry.io/ubi", + }]}]}] + + assertions.assert_empty(base_image_registries.deny) with lib.sbom.cyclonedx_sboms as sboms + with lib.sbom.spdx_sboms as [] + with data.rule_data.allowed_registry_prefixes as ["other.registry.io/"] + with data.rule_data.release_public_key as "-----BEGIN PUBLIC KEY-----\nMFkwEwYH..." + with ec.sigstore.verify_image as {"errors": []} +} + +test_signature_verification_failed if { + sboms := [{"formulation": [{"components": [{ + "name": "ubi", + "type": "container", + "properties": [{"name": "konflux:container:is_base_image", "value": "true"}], + "purl": "pkg:oci/ubi@sha256:abc?repository_url=unknown.registry.io/ubi", + }]}]}] + + expected := {{ + "code": "base_image_registries.base_image_permitted", + # regal ignore:line-length + "msg": "Base image \"unknown.registry.io/ubi@sha256:abc\" is from a disallowed registry", + "term": "unknown.registry.io/ubi", + }} + + assertions.assert_equal_results(base_image_registries.deny, expected) with lib.sbom.cyclonedx_sboms as sboms + with lib.sbom.spdx_sboms as [] + with data.rule_data.allowed_registry_prefixes as ["other.registry.io/"] + with data.rule_data.release_public_key as "-----BEGIN PUBLIC KEY-----\nMFkwEwYH..." + with ec.sigstore.verify_image as {"errors": ["signature verification failed"]} +} + +test_no_release_key_is_noop if { + sboms := [{"formulation": [{"components": [{ + "name": "ubi", + "type": "container", + "properties": [{"name": "konflux:container:is_base_image", "value": "true"}], + "purl": "pkg:oci/ubi@sha256:abc?repository_url=registry.redhat.io/ubi", + }]}]}] + + assertions.assert_empty(base_image_registries.deny) with lib.sbom.cyclonedx_sboms as sboms + with lib.sbom.spdx_sboms as [] +} + +test_allowed_with_only_release_key if { + sboms := [{"formulation": [{"components": [{ + "name": "ubi", + "type": "container", + "properties": [{"name": "konflux:container:is_base_image", "value": "true"}], + "purl": "pkg:oci/ubi@sha256:abc?repository_url=unknown.registry.io/ubi", + }]}]}] + + assertions.assert_empty(base_image_registries.deny) with lib.sbom.cyclonedx_sboms as sboms + with lib.sbom.spdx_sboms as [] + with data.rule_data as {"release_public_key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYH..."} + with ec.sigstore.verify_image as {"errors": []} +} + +test_deprecation_warning_registry_prefixes_without_key if { + expected := {{ + "code": "base_image_registries.registry_prefix_deprecated", + # regal ignore:line-length + "msg": "allowed_registry_prefixes is configured without release_public_key. Migrate to signature-based verification by setting release_public_key in rule data.", + }} + + d := {"allowed_registry_prefixes": ["registry.redhat.io/"]} + + assertions.assert_equal_results(base_image_registries.warn, expected) with lib.sbom.cyclonedx_sboms as [{}] + with lib.sbom.spdx_sboms as [{}] + with data.rule_data as d +} + +test_no_deprecation_warning_with_key if { + assertions.assert_empty(base_image_registries.warn) with lib.sbom.cyclonedx_sboms as [{}] + with lib.sbom.spdx_sboms as [{}] + with data.rule_data.release_public_key as "-----BEGIN PUBLIC KEY-----\nMFkwEwYH..." +} + +test_no_deprecation_warning_without_prefixes if { + assertions.assert_empty(base_image_registries.warn) with lib.sbom.cyclonedx_sboms as [{}] + with lib.sbom.spdx_sboms as [{}] + with data.rule_data as {"release_public_key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYH..."} +} + +test_release_public_key_validation if { + d := { + "release_public_key": 42, + "allowed_registry_prefixes": ["registry.redhat.io/"], + } + + expected := {{ + "code": "base_image_registries.allowed_registries_provided", + # regal ignore:line-length + "msg": "Rule data release_public_key has unexpected format: expected a string, got number", + "severity": "failure", + }} + + assertions.assert_equal_results(base_image_registries.deny, expected) with data.rule_data as d + with lib.sbom.cyclonedx_sboms as [{}] + with lib.sbom.spdx_sboms as [{}] +} + +test_allowed_registries_provided_with_release_key if { + assertions.assert_empty(base_image_registries.deny) with data.rule_data as {"release_public_key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYH..."} + with lib.sbom.cyclonedx_sboms as [{}] + with lib.sbom.spdx_sboms as [{}] +} + _spdx_sbom := [{"packages": [ { # regal ignore:line-length From 8fb2b7f8e9e266d5ead68d040fc86913a9b5fd90 Mon Sep 17 00:00:00 2001 From: jstuart Date: Wed, 15 Jul 2026 21:57:46 -0500 Subject: [PATCH 2/2] refactor(EC-1957): fold deprecation warning into _rule_data_errors Move the registry prefix deprecation from a standalone warn rule into _rule_data_errors with warning severity, and rename the deny rule title to "Base image is permitted" since verification is no longer registry-specific. Co-Authored-By: Claude Opus 4.6 --- .../base_image_registries.rego | 50 ++++++------------- .../base_image_registries_test.rego | 35 +++++++++---- 2 files changed, 41 insertions(+), 44 deletions(-) diff --git a/policy/release/base_image_registries/base_image_registries.rego b/policy/release/base_image_registries/base_image_registries.rego index bc553a57e..e20b88550 100644 --- a/policy/release/base_image_registries/base_image_registries.rego +++ b/policy/release/base_image_registries/base_image_registries.rego @@ -16,15 +16,14 @@ import data.lib.rule_data import data.lib.sbom # METADATA -# title: Base image comes from permitted registry +# title: Base image is permitted # description: >- # Verify that the base images used when building a container image are permitted. -# Images can be permitted in three ways: by matching a registry prefix from -# `allowed_registry_prefixes` rule data (deprecated), by matching a component digest -# in the snapshot, or by having a valid release signature verified against the -# `release_public_key` rule data. The preferred approach is signature-based -# verification via `release_public_key`. Registry prefix matching is deprecated -# and will be removed in a future release. +# Images can be permitted in three ways: by having a valid release signature +# verified against the `release_public_key` rule data (preferred), by matching +# a component digest in the snapshot, or by matching a registry prefix from +# `allowed_registry_prefixes` rule data (deprecated). Registry prefix matching +# is deprecated and will be removed in a future release. # custom: # short_name: base_image_permitted # failure_msg: Base image %q is from a disallowed registry @@ -96,33 +95,6 @@ deny contains result if { result := metadata.result_helper_with_severity(rego.metadata.chain(), [error.message], error.severity) } -# METADATA -# title: Registry prefix matching is deprecated -# description: >- -# Using `allowed_registry_prefixes` to permit base images is deprecated. -# Configure `release_public_key` to verify base image release signatures instead, -# which provides stronger cryptographic assurance than registry prefix matching. -# custom: -# short_name: registry_prefix_deprecated -# failure_msg: >- -# allowed_registry_prefixes is configured without release_public_key. Migrate -# to signature-based verification by setting release_public_key in rule data. -# solution: >- -# Set the `release_public_key` in rule data to enable signature-based base image -# verification. The key can be an inline PEM-encoded public key or a k8s:// -# reference to a secret containing the key. -# collections: -# - minimal -# - redhat -# - redhat_security -# -warn contains result if { - prefixes := rule_data.get(_rule_data_key) - count(prefixes) > 0 - not _release_public_key_provided - result := metadata.result_helper(rego.metadata.chain(), []) -} - _image_ref_permitted(image_ref) if { allowed_prefixes := rule_data.get(_rule_data_key) some allowed_prefix in allowed_prefixes @@ -232,6 +204,16 @@ _rule_data_errors contains error if { } } +_rule_data_errors contains error if { + prefixes := rule_data.get(_rule_data_key) + count(prefixes) > 0 + not _release_public_key_provided + error := { + "message": "allowed_registry_prefixes is configured without release_public_key. Migrate to signature-based verification by setting release_public_key in rule data.", + "severity": "warning", + } +} + _rule_data_errors contains error if { val := rule_data.get(_release_key_rule_data_key) val != [] diff --git a/policy/release/base_image_registries/base_image_registries_test.rego b/policy/release/base_image_registries/base_image_registries_test.rego index d133d6a9d..b06b1ddf6 100644 --- a/policy/release/base_image_registries/base_image_registries_test.rego +++ b/policy/release/base_image_registries/base_image_registries_test.rego @@ -363,6 +363,12 @@ test_rule_data_validation if { "msg": "Rule data allowed_registry_prefixes has unexpected format: 0: Invalid type. Expected: string, given: integer", "severity": "failure", }, + { + "code": "base_image_registries.allowed_registries_provided", + # regal ignore:line-length + "msg": "allowed_registry_prefixes is configured without release_public_key. Migrate to signature-based verification by setting release_public_key in rule data.", + "severity": "warning", + }, } assertions.assert_equal_results(base_image_registries.deny, expected) with data.rule_data as d @@ -435,26 +441,27 @@ test_allowed_with_only_release_key if { test_deprecation_warning_registry_prefixes_without_key if { expected := {{ - "code": "base_image_registries.registry_prefix_deprecated", + "code": "base_image_registries.allowed_registries_provided", # regal ignore:line-length "msg": "allowed_registry_prefixes is configured without release_public_key. Migrate to signature-based verification by setting release_public_key in rule data.", + "severity": "warning", }} d := {"allowed_registry_prefixes": ["registry.redhat.io/"]} - assertions.assert_equal_results(base_image_registries.warn, expected) with lib.sbom.cyclonedx_sboms as [{}] + assertions.assert_equal_results(base_image_registries.deny, expected) with lib.sbom.cyclonedx_sboms as [{}] with lib.sbom.spdx_sboms as [{}] with data.rule_data as d } test_no_deprecation_warning_with_key if { - assertions.assert_empty(base_image_registries.warn) with lib.sbom.cyclonedx_sboms as [{}] + assertions.assert_empty(base_image_registries.deny) with lib.sbom.cyclonedx_sboms as [{}] with lib.sbom.spdx_sboms as [{}] with data.rule_data.release_public_key as "-----BEGIN PUBLIC KEY-----\nMFkwEwYH..." } test_no_deprecation_warning_without_prefixes if { - assertions.assert_empty(base_image_registries.warn) with lib.sbom.cyclonedx_sboms as [{}] + assertions.assert_empty(base_image_registries.deny) with lib.sbom.cyclonedx_sboms as [{}] with lib.sbom.spdx_sboms as [{}] with data.rule_data as {"release_public_key": "-----BEGIN PUBLIC KEY-----\nMFkwEwYH..."} } @@ -465,12 +472,20 @@ test_release_public_key_validation if { "allowed_registry_prefixes": ["registry.redhat.io/"], } - expected := {{ - "code": "base_image_registries.allowed_registries_provided", - # regal ignore:line-length - "msg": "Rule data release_public_key has unexpected format: expected a string, got number", - "severity": "failure", - }} + expected := { + { + "code": "base_image_registries.allowed_registries_provided", + # regal ignore:line-length + "msg": "Rule data release_public_key has unexpected format: expected a string, got number", + "severity": "failure", + }, + { + "code": "base_image_registries.allowed_registries_provided", + # regal ignore:line-length + "msg": "allowed_registry_prefixes is configured without release_public_key. Migrate to signature-based verification by setting release_public_key in rule data.", + "severity": "warning", + }, + } assertions.assert_equal_results(base_image_registries.deny, expected) with data.rule_data as d with lib.sbom.cyclonedx_sboms as [{}]