From 1b3ecae9b30f7fd4e5cf54dab10d38cbd973a683 Mon Sep 17 00:00:00 2001 From: Simon Baird Date: Fri, 20 Mar 2026 14:13:43 -0400 Subject: [PATCH] Several keyless verification task improvements Here's what's included: - Support for the -regexp versions of the keyless verification params. Prefer the non-regexp param if (for some reason) both are present. - Make it so we never use --ignore-rekor when doing keyless verification even if IGNORE_REKOR is true. This is because you need a transparency log entry from Rekor to do keyless verification. - Some minor bash env var handling logic tweaks related to handling of unlikely edge cases. Note that we're still trying not to add a layer of bash logic for param sanitizing as per the comment there. This could be broken up into multiple commits, and originally it was, but I've been working on a previous version of PR too long and I don't think it's worth the effort right now. Ref: https://redhat.atlassian.net/browse/EC-1652 --- .../pages/verify-conforma-konflux-ta.adoc | 6 +- .../pages/verify-enterprise-contract.adoc | 6 +- .../__snapshots__/task_validate_image.snap | 201 ++++++++++++++++++ features/task_validate_image.feature | 65 +++++- .../0.1/verify-conforma-konflux-ta.yaml | 66 +++++- .../0.1/verify-enterprise-contract.yaml | 66 +++++- 6 files changed, 389 insertions(+), 21 deletions(-) diff --git a/docs/modules/ROOT/pages/verify-conforma-konflux-ta.adoc b/docs/modules/ROOT/pages/verify-conforma-konflux-ta.adoc index 844d2c63d..7d1e3e6d4 100644 --- a/docs/modules/ROOT/pages/verify-conforma-konflux-ta.adoc +++ b/docs/modules/ROOT/pages/verify-conforma-konflux-ta.adoc @@ -21,11 +21,13 @@ You can also specify a policy configuration using a git url, e.g. + *Default*: `enterprise-contract-service/default` -*PUBLIC_KEY* (`string`):: Public key used to verify traditional long-lived signatures. Must be a valid k8s cosign reference, e.g. k8s://my-space/my-secret where my-secret contains the expected cosign.pub attribute. Required for traditional signing key verification. Will be ignored if either of CERTIFICATE_IDENTITY or CERTIFICATE_OIDC_ISSUER are provided. +*PUBLIC_KEY* (`string`):: Public key used to verify traditional long-lived signatures. Must be a valid k8s cosign reference, e.g. k8s://my-space/my-secret where my-secret contains the expected cosign.pub attribute. Required for traditional signing key verification. Will be ignored if any of CERTIFICATE_IDENTITY, CERTIFICATE_IDENTITY_REGEXP, CERTIFICATE_OIDC_ISSUER, or CERTIFICATE_OIDC_ISSUER_REGEXP are provided. *REKOR_HOST* (`string`):: Rekor host for transparency log lookups *CERTIFICATE_IDENTITY* (`string`):: Expected identity in the signing certificate for keyless verification. This should be the email or URI that was used when signing. You should provide both CERTIFICATE_OIDC_ISSUER and CERTIFICATE_IDENTITY for keyless verification. The PUBLIC_KEY param will be ignored if this is provided. *CERTIFICATE_OIDC_ISSUER* (`string`):: Expected OIDC issuer in the signing certificate for keyless verification. This should match the issuer that provided the identity token used for signing. You should provide both CERTIFICATE_OIDC_ISSUER and CERTIFICATE_IDENTITY for keyless verification. The PUBLIC_KEY param will be ignored if this is provided. -*IGNORE_REKOR* (`string`):: Skip Rekor transparency log checks during validation. +*CERTIFICATE_IDENTITY_REGEXP* (`string`):: Similar to CERTIFICATE_IDENTITY but the value is a regexp that will be matched. Note that CERTIFICATE_IDENTITY takes precedence over this if both are present. +*CERTIFICATE_OIDC_ISSUER_REGEXP* (`string`):: Similar to CERTIFICATE_OIDC_ISSUER but a regexp that will be matched. Note that CERTIFICATE_OIDC_ISSUER takes precedence over this if both are present. +*IGNORE_REKOR* (`string`):: Skip Rekor transparency log checks during validation. Compatible with traditional signing secret signature checks only. If any of the CERTIFICATE_* keyless verification params are present, this value is disregarded and Rekor transparency log checks are included. + *Default*: `false` *TUF_MIRROR* (`string`):: TUF mirror URL. Provide a value when NOT using public sigstore deployment. diff --git a/docs/modules/ROOT/pages/verify-enterprise-contract.adoc b/docs/modules/ROOT/pages/verify-enterprise-contract.adoc index 2a0a9c25a..9f4a46e0f 100644 --- a/docs/modules/ROOT/pages/verify-enterprise-contract.adoc +++ b/docs/modules/ROOT/pages/verify-enterprise-contract.adoc @@ -32,11 +32,13 @@ You can also specify a policy configuration using a git url, e.g. + *Default*: `enterprise-contract-service/default` -*PUBLIC_KEY* (`string`):: Public key used to verify traditional long-lived signatures. Must be a valid k8s cosign reference, e.g. k8s://my-space/my-secret where my-secret contains the expected cosign.pub attribute. Required for traditional signing key verification. Will be ignored if either of CERTIFICATE_IDENTITY or CERTIFICATE_OIDC_ISSUER are provided. +*PUBLIC_KEY* (`string`):: Public key used to verify traditional long-lived signatures. Must be a valid k8s cosign reference, e.g. k8s://my-space/my-secret where my-secret contains the expected cosign.pub attribute. Required for traditional signing key verification. Will be ignored if any of CERTIFICATE_IDENTITY, CERTIFICATE_IDENTITY_REGEXP, CERTIFICATE_OIDC_ISSUER, or CERTIFICATE_OIDC_ISSUER_REGEXP are provided. *REKOR_HOST* (`string`):: Rekor host for transparency log lookups *CERTIFICATE_IDENTITY* (`string`):: Expected identity in the signing certificate for keyless verification. This should be the email or URI that was used when signing. You should provide both CERTIFICATE_OIDC_ISSUER and CERTIFICATE_IDENTITY for keyless verification. The PUBLIC_KEY param will be ignored if this is provided. *CERTIFICATE_OIDC_ISSUER* (`string`):: Expected OIDC issuer in the signing certificate for keyless verification. This should match the issuer that provided the identity token used for signing. You should provide both CERTIFICATE_OIDC_ISSUER and CERTIFICATE_IDENTITY for keyless verification. The PUBLIC_KEY param will be ignored if this is provided. -*IGNORE_REKOR* (`string`):: Skip Rekor transparency log checks during validation. +*CERTIFICATE_IDENTITY_REGEXP* (`string`):: Similar to CERTIFICATE_IDENTITY but the value is a regexp that will be matched. Note that CERTIFICATE_IDENTITY takes precedence over this if both are present. +*CERTIFICATE_OIDC_ISSUER_REGEXP* (`string`):: Similar to CERTIFICATE_OIDC_ISSUER but a regexp that will be matched. Note that CERTIFICATE_OIDC_ISSUER takes precedence over this if both are present. +*IGNORE_REKOR* (`string`):: Skip Rekor transparency log checks during validation. Compatible with traditional signing secret signature checks only. If any of the CERTIFICATE_* keyless verification params are present, this value is disregarded and Rekor transparency log checks are included. + *Default*: `false` *TUF_MIRROR* (`string`):: TUF mirror URL. Provide a value when NOT using public sigstore deployment. diff --git a/features/__snapshots__/task_validate_image.snap b/features/__snapshots__/task_validate_image.snap index 76541eb8b..294e4927f 100755 --- a/features/__snapshots__/task_validate_image.snap +++ b/features/__snapshots__/task_validate_image.snap @@ -533,3 +533,204 @@ results.rekorUrl: results.tufUrl: --- + +[Keyless signing verification cosign v2 style with regexp params:report-json - 1] +{ + "success": true, + "components": [ + { + "name": "", + "containerImage": "quay.io/conforma/test@sha256:03a10dff06ae364ef9727d562e7077b135b00c7a978e571c4354519e6d0f23b8", + "source": {}, + "successes": [ + { + "msg": "Pass", + "metadata": { + "code": "builtin.attestation.signature_check", + "description": "The attestation signature matches available signing materials.", + "title": "Attestation signature check passed" + } + }, + { + "msg": "Pass", + "metadata": { + "code": "builtin.attestation.syntax_check", + "description": "The attestation has correct syntax.", + "title": "Attestation syntax check passed" + } + }, + { + "msg": "Pass", + "metadata": { + "code": "builtin.image.signature_check", + "description": "The image signature matches available signing materials.", + "title": "Image signature check passed" + } + }, + { + "msg": "Pass", + "metadata": { + "code": "slsa_provenance_available.allowed_predicate_types_provided", + "collections": [ + "minimal", + "slsa3", + "redhat", + "redhat_rpms", + "policy_data" + ], + "description": "Confirm the `allowed_predicate_types` rule data was provided, since it is required by the policy rules in this package.", + "title": "Allowed predicate types provided" + } + }, + { + "msg": "Pass", + "metadata": { + "code": "slsa_provenance_available.attestation_predicate_type_accepted", + "collections": [ + "minimal", + "slsa3", + "redhat", + "redhat_rpms" + ], + "depends_on": [ + "attestation_type.known_attestation_type" + ], + "description": "Verify that the predicateType field of the attestation indicates the in-toto SLSA Provenance format was used to attest the PipelineRun.", + "title": "Expected attestation predicate type found" + } + } + ], + "success": true, + "signatures": [ + { + "keyid": "dc5f3121f1f76f0d687877532ce44ff55aab2050", + "sig": "MEUCIQDV4du9T+vV6dtN1LsCrZgByokRslw43oxscniN3wbaigIgMV+NFgix7ZjqhIpXFIMVFl1CQuya8JQsYP96ByA5iAc=", + "certificate": "-----BEGIN CERTIFICATE-----\nMIIC0zCCAlqgAwIBAgIUfPJP4pJfIr6Pgt2Q2J9hu4DqoJcwCgYIKoZIzj0EAwMw\nNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRl\ncm1lZGlhdGUwHhcNMjYwMzAzMTkxNjUyWhcNMjYwMzAzMTkyNjUyWjAAMFkwEwYH\nKoZIzj0CAQYIKoZIzj0DAQcDQgAEGMk9duvfPU07wcRpBWKXUi8bmr833N3pKhP2\nGCVBlFxZIRcD01FKT4TEMvlRIq8gZJO4eQ/WvEL/NpNmkk+PzaOCAXkwggF1MA4G\nA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQU3F8x\nIfH3bw1oeHdTLORP9VqrIFAwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4Y\nZD8wKQYDVR0RAQH/BB8wHYEbY29uZm9ybWFjb21tdW5pdHlAZ21haWwuY29tMCkG\nCisGAQQBg78wAQEEG2h0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbTArBgorBgEE\nAYO/MAEIBB0MG2h0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbTCBigYKKwYBBAHW\neQIEAgR8BHoAeAB2AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAAB\nnLUhueMAAAQDAEcwRQIgARu6tEmE0vUHU+MhCQB6tzwROaEn4VdlfGBFWQxxcygC\nIQCHm2/lgszmmt2gC6Pl2bfvCRDKewUQDvWjzNqq8WtPczAKBggqhkjOPQQDAwNn\nADBkAjAMnyVwJVMQflB7Iwfte7cuOYYN2uvmEibKwjmmPgZOq43vSH9Y9gtUvyJk\nZ23vTpwCMHKChuWjhTQgxczH7MhKUO2IphbaHeJYmeFa4rrswhv6h9z6v5IIPovF\nsdbKg+sEHw==\n-----END CERTIFICATE-----\n", + "chain": [ + "-----BEGIN CERTIFICATE-----\nMIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMw\nKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0y\nMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3Jl\nLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0C\nAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV7\n7LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS\n0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYB\nBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjp\nKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZI\nzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJR\nnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsP\nmygUY7Ii2zbdCdliiow=\n-----END CERTIFICATE-----\n", + "-----BEGIN CERTIFICATE-----\nMIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMw\nKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0y\nMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3Jl\nLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7\nXeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxex\nX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92j\nYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRY\nwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQ\nKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCM\nWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9\nTNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ\n-----END CERTIFICATE-----\n" + ], + "metadata": { + "Fulcio Issuer": "https://accounts.google.com", + "Fulcio Issuer (V2)": "https://accounts.google.com", + "Issuer": "CN=sigstore-intermediate,O=sigstore.dev", + "Not After": "${TIMESTAMP}", + "Not Before": "${TIMESTAMP}", + "Serial Number": "7cf24fe2925f22be8f82dd90d89f61bb80eaa097", + "Subject Alternative Name": "Email Addresses:conformacommunity@gmail.com" + } + } + ], + "attestations": [ + { + "type": "https://in-toto.io/Statement/v0.1", + "predicateType": "https://slsa.dev/provenance/v1", + "predicateBuildType": "https://example.com/build-type/v1", + "signatures": [ + { + "keyid": "17d7418e0517e21e30f4fe144128b7ca1d1bb2ac", + "sig": "MEUCIBvsTgzJ5DOVIEAH/u5eav7C3QXx6ttR0tZxFQlJe6c4AiEAtIid+gk+EqgxSYNBLquaq2dfdWBL28yR1EOjn/Fi1T8=", + "certificate": "-----BEGIN CERTIFICATE-----\nMIIC1TCCAlqgAwIBAgIUPUQSAPNDQoKF8C3ufUx0Jta8GvEwCgYIKoZIzj0EAwMw\nNzEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MR4wHAYDVQQDExVzaWdzdG9yZS1pbnRl\ncm1lZGlhdGUwHhcNMjYwMzAzMTkxNzA1WhcNMjYwMzAzMTkyNzA1WjAAMFkwEwYH\nKoZIzj0CAQYIKoZIzj0DAQcDQgAE81mfg8hXUQRHdZpbbST2ckHT4YrcRPRvM+tc\nRmcvvexGuwm0yIOBZqIqXeyd/YrJn9MjBdHrmyKIztdR9mdpUaOCAXkwggF1MA4G\nA1UdDwEB/wQEAwIHgDATBgNVHSUEDDAKBggrBgEFBQcDAzAdBgNVHQ4EFgQUF9dB\njgUX4h4w9P4UQSi3yh0bsqwwHwYDVR0jBBgwFoAU39Ppz1YkEZb5qNjpKFWixi4Y\nZD8wKQYDVR0RAQH/BB8wHYEbY29uZm9ybWFjb21tdW5pdHlAZ21haWwuY29tMCkG\nCisGAQQBg78wAQEEG2h0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbTArBgorBgEE\nAYO/MAEIBB0MG2h0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbTCBigYKKwYBBAHW\neQIEAgR8BHoAeAB2AN09MGrGxxEyYxkeHJlnNwKiSl643jyt/4eKcoAvKe6OAAAB\nnLUh7ZUAAAQDAEcwRQIgY5+UpYgU0LsrAiTQSoeLquv9EVJ8lH4rtxQupmSWDWwC\nIQC6zpOJpx/ryldrjdpfycB9wBWIexg+/XC8Avdv9W2D3jAKBggqhkjOPQQDAwNp\nADBmAjEA/LIHzfKog0PwRohtlpLV32CpVyWrTt9jK84quvooFP5dgeegze/A4mrk\n0bO73KdEAjEA94BFoAYPJw1RTmIw5VnZXbYKqhlt0hm4nTx9pVoGQMFEtnIguX7f\nNnaoX2+paxVF\n-----END CERTIFICATE-----\n", + "chain": [ + "-----BEGIN CERTIFICATE-----\nMIICGjCCAaGgAwIBAgIUALnViVfnU0brJasmRkHrn/UnfaQwCgYIKoZIzj0EAwMw\nKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0y\nMjA0MTMyMDA2MTVaFw0zMTEwMDUxMzU2NThaMDcxFTATBgNVBAoTDHNpZ3N0b3Jl\nLmRldjEeMBwGA1UEAxMVc2lnc3RvcmUtaW50ZXJtZWRpYXRlMHYwEAYHKoZIzj0C\nAQYFK4EEACIDYgAE8RVS/ysH+NOvuDZyPIZtilgUF9NlarYpAd9HP1vBBH1U5CV7\n7LSS7s0ZiH4nE7Hv7ptS6LvvR/STk798LVgMzLlJ4HeIfF3tHSaexLcYpSASr1kS\n0N/RgBJz/9jWCiXno3sweTAOBgNVHQ8BAf8EBAMCAQYwEwYDVR0lBAwwCgYIKwYB\nBQUHAwMwEgYDVR0TAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQU39Ppz1YkEZb5qNjp\nKFWixi4YZD8wHwYDVR0jBBgwFoAUWMAeX5FFpWapesyQoZMi0CrFxfowCgYIKoZI\nzj0EAwMDZwAwZAIwPCsQK4DYiZYDPIaDi5HFKnfxXx6ASSVmERfsynYBiX2X6SJR\nnZU84/9DZdnFvvxmAjBOt6QpBlc4J/0DxvkTCqpclvziL6BCCPnjdlIB3Pu3BxsP\nmygUY7Ii2zbdCdliiow=\n-----END CERTIFICATE-----\n", + "-----BEGIN CERTIFICATE-----\nMIIB9zCCAXygAwIBAgIUALZNAPFdxHPwjeDloDwyYChAO/4wCgYIKoZIzj0EAwMw\nKjEVMBMGA1UEChMMc2lnc3RvcmUuZGV2MREwDwYDVQQDEwhzaWdzdG9yZTAeFw0y\nMTEwMDcxMzU2NTlaFw0zMTEwMDUxMzU2NThaMCoxFTATBgNVBAoTDHNpZ3N0b3Jl\nLmRldjERMA8GA1UEAxMIc2lnc3RvcmUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAT7\nXeFT4rb3PQGwS4IajtLk3/OlnpgangaBclYpsYBr5i+4ynB07ceb3LP0OIOZdxex\nX69c5iVuyJRQ+Hz05yi+UF3uBWAlHpiS5sh0+H2GHE7SXrk1EC5m1Tr19L9gg92j\nYzBhMA4GA1UdDwEB/wQEAwIBBjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRY\nwB5fkUWlZql6zJChkyLQKsXF+jAfBgNVHSMEGDAWgBRYwB5fkUWlZql6zJChkyLQ\nKsXF+jAKBggqhkjOPQQDAwNpADBmAjEAj1nHeXZp+13NWBNa+EDsDP8G1WWg1tCM\nWP/WHPqpaVo0jhsweNFZgSs0eE7wYI4qAjEA2WB9ot98sIkoF3vZYdd3/VtWB5b9\nTNMea7Ix/stJ5TfcLLeABLE4BNJOsQ4vnBHJ\n-----END CERTIFICATE-----\n" + ], + "metadata": { + "Fulcio Issuer": "https://accounts.google.com", + "Fulcio Issuer (V2)": "https://accounts.google.com", + "Issuer": "CN=sigstore-intermediate,O=sigstore.dev", + "Not After": "${TIMESTAMP}", + "Not Before": "${TIMESTAMP}", + "Serial Number": "3d441200f343428285f02dee7d4c7426d6bc1af1", + "Subject Alternative Name": "Email Addresses:conformacommunity@gmail.com" + } + } + ] + } + ] + } + ], + "key": "", + "policy": { + "sources": [ + { + "policy": [ + "git::github.com/conforma/policy//policy/release?ref=0de5461c14413484575e63e96ddb514d8ab954b5", + "git::github.com/conforma/policy//policy/lib?ref=0de5461c14413484575e63e96ddb514d8ab954b5" + ], + "config": { + "include": [ + "slsa_provenance_available" + ] + } + } + ], + "rekorUrl": "https://rekor.sigstore.dev" + }, + "ec-version": "${EC_VERSION}", + "effective-time": "${TIMESTAMP}" +} +--- + +[Keyless signing verification cosign v2 style with regexp params:results - 1] +{ + "TEST_OUTPUT": "{\"timestamp\":\"${TIMESTAMP}\",\"namespace\":\"\",\"successes\":5,\"failures\":0,\"warnings\":0,\"result\":\"SUCCESS\"}\n" +} +--- + +[Keyless signing verification cosign v3 style with regexp params:report-json - 1] +{ + "success": false, + "components": [ + { + "name": "", + "containerImage": "quay.io/conforma/test@sha256:712ca3a7fcd41fe6b3e6f434a31f738743b6c31f1d81ad458502d6b0239a8903", + "source": {}, + "violations": [ + { + "msg": "Image attestation check failed: no matching attestations: failed to verify certificate identity: no matching CertificateIdentity found, last error: expected SAN value to match regex \"^konformakommunity@\", got \"conformacommunity@gmail.com\"\nfailed to verify certificate identity: no matching CertificateIdentity found, last error: expected SAN value to match regex \"^konformakommunity@\", got \"conformacommunity@gmail.com\"", + "metadata": { + "code": "builtin.attestation.signature_check", + "description": "The attestation signature matches available signing materials.", + "title": "Attestation signature check passed" + } + }, + { + "msg": "Image signature check failed: no matching attestations: failed to verify certificate identity: no matching CertificateIdentity found, last error: expected SAN value to match regex \"^konformakommunity@\", got \"conformacommunity@gmail.com\"\nfailed to verify certificate identity: no matching CertificateIdentity found, last error: expected SAN value to match regex \"^konformakommunity@\", got \"conformacommunity@gmail.com\"", + "metadata": { + "code": "builtin.image.signature_check", + "description": "The image signature matches available signing materials.", + "title": "Image signature check passed" + } + } + ], + "success": false + } + ], + "key": "", + "policy": { + "sources": [ + { + "policy": [ + "git::github.com/conforma/policy//policy/release?ref=0de5461c14413484575e63e96ddb514d8ab954b5", + "git::github.com/conforma/policy//policy/lib?ref=0de5461c14413484575e63e96ddb514d8ab954b5" + ], + "config": { + "include": [ + "slsa_provenance_available" + ] + } + } + ], + "rekorUrl": "https://rekor.sigstore.dev" + }, + "ec-version": "${EC_VERSION}", + "effective-time": "${TIMESTAMP}" +} +--- + +[Keyless signing verification cosign v3 style with regexp params:results - 1] +{ + "TEST_OUTPUT": "{\"timestamp\":\"${TIMESTAMP}\",\"namespace\":\"\",\"successes\":0,\"failures\":2,\"warnings\":0,\"result\":\"FAILURE\"}\n" +} +--- diff --git a/features/task_validate_image.feature b/features/task_validate_image.feature index d5204e1a0..631506535 100644 --- a/features/task_validate_image.feature +++ b/features/task_validate_image.feature @@ -374,12 +374,42 @@ Feature: Verify Enterprise Contract Tekton Tasks | CERTIFICATE_IDENTITY | conformacommunity@gmail.com | | CERTIFICATE_OIDC_ISSUER | https://accounts.google.com | | REKOR_HOST | https://rekor.sigstore.dev | - | IGNORE_REKOR | false | | STRICT | true | Then the task should succeed And the task logs for step "report-json" should match the snapshot And the task results should match the snapshot + Scenario: Keyless signing verification cosign v2 style with regexp params + Given a working namespace + Given a cluster policy with content: + ``` + { + "sources": [ + { + "policy": [ + "github.com/conforma/policy//policy/release?ref=0de5461c14413484575e63e96ddb514d8ab954b5", + "github.com/conforma/policy//policy/lib?ref=0de5461c14413484575e63e96ddb514d8ab954b5" + ], + "config": { + "include": [ + "slsa_provenance_available" + ] + } + } + ] + } + ``` + When version 0.1 of the task named "verify-enterprise-contract" is run with parameters: + | IMAGES | {"components": [{"containerImage": "quay.io/conforma/test:keyless_v2@sha256:03a10dff06ae364ef9727d562e7077b135b00c7a978e571c4354519e6d0f23b8"}]} | + | POLICY_CONFIGURATION | ${NAMESPACE}/${POLICY_NAME} | + | CERTIFICATE_IDENTITY_REGEXP | ^conformacommunity@ | + | CERTIFICATE_OIDC_ISSUER_REGEXP | https://.*\.google\.com | + | REKOR_HOST | https://rekor.sigstore.dev | + | STRICT | true | + Then the task should succeed + And the task logs for step "report-json" should match the snapshot + And the task results should match the snapshot + # Confirm we can verify the signatures on a keylessly signed image signed with cosign v3 Scenario: Keyless signing verification cosign v3 style Given a working namespace @@ -413,6 +443,39 @@ Feature: Verify Enterprise Contract Tekton Tasks And the task logs for step "report-json" should match the snapshot And the task results should match the snapshot + # Confirm we can verify the signatures on a keylessly signed image signed with cosign v3 + Scenario: Keyless signing verification cosign v3 style with regexp params + Given a working namespace + Given a cluster policy with content: + ``` + { + "sources": [ + { + "policy": [ + "github.com/conforma/policy//policy/release?ref=0de5461c14413484575e63e96ddb514d8ab954b5", + "github.com/conforma/policy//policy/lib?ref=0de5461c14413484575e63e96ddb514d8ab954b5" + ], + "config": { + "include": [ + "slsa_provenance_available" + ] + } + } + ] + } + ``` + When version 0.1 of the task named "verify-enterprise-contract" is run with parameters: + | IMAGES | {"components": [{"containerImage": "quay.io/conforma/test:keyless_v3@sha256:712ca3a7fcd41fe6b3e6f434a31f738743b6c31f1d81ad458502d6b0239a8903"}]} | + | POLICY_CONFIGURATION | ${NAMESPACE}/${POLICY_NAME} | + # Let's make this one fail: + | CERTIFICATE_IDENTITY_REGEXP | ^konformakommunity@ | + | CERTIFICATE_OIDC_ISSUER_REGEXP | https://.*\.google\.com | + | REKOR_HOST | https://rekor.sigstore.dev | + | STRICT | true | + Then the task should fail + And the task logs for step "report-json" should match the snapshot + And the task results should match the snapshot + Scenario: Collect keyless signing parameters from ConfigMap Given a working namespace And a namespace named "konflux-info" exists diff --git a/tasks/verify-conforma-konflux-ta/0.1/verify-conforma-konflux-ta.yaml b/tasks/verify-conforma-konflux-ta/0.1/verify-conforma-konflux-ta.yaml index 59d290db5..6ea237378 100644 --- a/tasks/verify-conforma-konflux-ta/0.1/verify-conforma-konflux-ta.yaml +++ b/tasks/verify-conforma-konflux-ta/0.1/verify-conforma-konflux-ta.yaml @@ -55,8 +55,9 @@ spec: Public key used to verify traditional long-lived signatures. Must be a valid k8s cosign reference, e.g. k8s://my-space/my-secret where my-secret contains the expected cosign.pub attribute. Required for - traditional signing key verification. Will be ignored if either of - CERTIFICATE_IDENTITY or CERTIFICATE_OIDC_ISSUER are provided. + traditional signing key verification. Will be ignored if any of + CERTIFICATE_IDENTITY, CERTIFICATE_IDENTITY_REGEXP, + CERTIFICATE_OIDC_ISSUER, or CERTIFICATE_OIDC_ISSUER_REGEXP are provided. default: "" - name: REKOR_HOST @@ -83,10 +84,27 @@ spec: keyless verification. The PUBLIC_KEY param will be ignored if this is provided. default: "" + - name: CERTIFICATE_IDENTITY_REGEXP + type: string + description: >- + Similar to CERTIFICATE_IDENTITY but the value is a regexp that will be matched. + Note that CERTIFICATE_IDENTITY takes precedence over this if both are present. + default: "" + + - name: CERTIFICATE_OIDC_ISSUER_REGEXP + type: string + description: >- + Similar to CERTIFICATE_OIDC_ISSUER but a regexp that will be matched. Note that + CERTIFICATE_OIDC_ISSUER takes precedence over this if both are present. + default: "" + - name: IGNORE_REKOR type: string description: >- - Skip Rekor transparency log checks during validation. + Skip Rekor transparency log checks during validation. Compatible with traditional + signing secret signature checks only. If any of the CERTIFICATE_* keyless verification + params are present, this value is disregarded and Rekor transparency log checks are + included. default: "false" - name: TUF_MIRROR @@ -325,20 +343,48 @@ spec: # To keep bash logic as thin as possible we deliberately don't sanitize # these params. If something is wrong or missing let Conforma handle it. - if [ -n "${CERTIFICATE_IDENTITY}" ] || [ -n "${CERTIFICATE_OIDC_ISSUER}" ]; then + + if [ -n "${CERTIFICATE_IDENTITY}" ] || \ + [ -n "${CERTIFICATE_OIDC_ISSUER}" ] || \ + [ -n "${CERTIFICATE_IDENTITY_REGEXP}" ] || \ + [ -n "${CERTIFICATE_OIDC_ISSUER_REGEXP}" ]; then + # If *any* of the above are non-empty assume the intention is to + # try keyless verification + + if [ -n "${CERTIFICATE_IDENTITY}" ]; then + cmd_args+=( + --certificate-identity="${CERTIFICATE_IDENTITY}" + ) + elif [ -n "${CERTIFICATE_IDENTITY_REGEXP}" ]; then + cmd_args+=( + --certificate-identity-regexp="${CERTIFICATE_IDENTITY_REGEXP}" + ) + fi + + if [ -n "${CERTIFICATE_OIDC_ISSUER}" ]; then + cmd_args+=( + --certificate-oidc-issuer="${CERTIFICATE_OIDC_ISSUER}" + ) + elif [ -n "${CERTIFICATE_OIDC_ISSUER_REGEXP}" ]; then + cmd_args+=( + --certificate-oidc-issuer-regexp="${CERTIFICATE_OIDC_ISSUER_REGEXP}" + ) + fi + + # Force --ignore-rekor to false since we need rekor cmd_args+=( - --certificate-identity="${CERTIFICATE_IDENTITY}" - --certificate-oidc-issuer="${CERTIFICATE_OIDC_ISSUER}" + --ignore-rekor=false ) - elif [ -n "${PUBLIC_KEY}" ]; then + else + # Assume traditional signing secret verification cmd_args+=( --public-key="${PUBLIC_KEY}" + --ignore-rekor="${IGNORE_REKOR}" ) fi cmd_args+=( --rekor-url="${REKOR_HOST}" - --ignore-rekor="${IGNORE_REKOR}" --workers="${WORKERS}" --info="${INFO}" --timeout=0 @@ -414,6 +460,10 @@ spec: value: "$(params.CERTIFICATE_IDENTITY)" - name: CERTIFICATE_OIDC_ISSUER value: "$(params.CERTIFICATE_OIDC_ISSUER)" + - name: CERTIFICATE_IDENTITY_REGEXP + value: "$(params.CERTIFICATE_IDENTITY_REGEXP)" + - name: CERTIFICATE_OIDC_ISSUER_REGEXP + value: "$(params.CERTIFICATE_OIDC_ISSUER_REGEXP)" - name: REKOR_HOST value: "$(params.REKOR_HOST)" - name: IGNORE_REKOR diff --git a/tasks/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml b/tasks/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml index f30b43485..c4edae2f7 100644 --- a/tasks/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml +++ b/tasks/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml @@ -62,8 +62,9 @@ spec: Public key used to verify traditional long-lived signatures. Must be a valid k8s cosign reference, e.g. k8s://my-space/my-secret where my-secret contains the expected cosign.pub attribute. Required for - traditional signing key verification. Will be ignored if either of - CERTIFICATE_IDENTITY or CERTIFICATE_OIDC_ISSUER are provided. + traditional signing key verification. Will be ignored if any of + CERTIFICATE_IDENTITY, CERTIFICATE_IDENTITY_REGEXP, + CERTIFICATE_OIDC_ISSUER, or CERTIFICATE_OIDC_ISSUER_REGEXP are provided. default: "" - name: REKOR_HOST @@ -90,10 +91,27 @@ spec: keyless verification. The PUBLIC_KEY param will be ignored if this is provided. default: "" + - name: CERTIFICATE_IDENTITY_REGEXP + type: string + description: >- + Similar to CERTIFICATE_IDENTITY but the value is a regexp that will be matched. + Note that CERTIFICATE_IDENTITY takes precedence over this if both are present. + default: "" + + - name: CERTIFICATE_OIDC_ISSUER_REGEXP + type: string + description: >- + Similar to CERTIFICATE_OIDC_ISSUER but a regexp that will be matched. Note that + CERTIFICATE_OIDC_ISSUER takes precedence over this if both are present. + default: "" + - name: IGNORE_REKOR type: string description: >- - Skip Rekor transparency log checks during validation. + Skip Rekor transparency log checks during validation. Compatible with traditional + signing secret signature checks only. If any of the CERTIFICATE_* keyless verification + params are present, this value is disregarded and Rekor transparency log checks are + included. default: "false" - name: TUF_MIRROR @@ -270,20 +288,48 @@ spec: # To keep bash logic as thin as possible we deliberately don't sanitize # these params. If something is wrong or missing let Conforma handle it. - if [ -n "${CERTIFICATE_IDENTITY}" ] || [ -n "${CERTIFICATE_OIDC_ISSUER}" ]; then + + if [ -n "${CERTIFICATE_IDENTITY}" ] || \ + [ -n "${CERTIFICATE_OIDC_ISSUER}" ] || \ + [ -n "${CERTIFICATE_IDENTITY_REGEXP}" ] || \ + [ -n "${CERTIFICATE_OIDC_ISSUER_REGEXP}" ]; then + # If *any* of the above are non-empty assume the intention is to + # try keyless verification + + if [ -n "${CERTIFICATE_IDENTITY}" ]; then + cmd_args+=( + --certificate-identity="${CERTIFICATE_IDENTITY}" + ) + elif [ -n "${CERTIFICATE_IDENTITY_REGEXP}" ]; then + cmd_args+=( + --certificate-identity-regexp="${CERTIFICATE_IDENTITY_REGEXP}" + ) + fi + + if [ -n "${CERTIFICATE_OIDC_ISSUER}" ]; then + cmd_args+=( + --certificate-oidc-issuer="${CERTIFICATE_OIDC_ISSUER}" + ) + elif [ -n "${CERTIFICATE_OIDC_ISSUER_REGEXP}" ]; then + cmd_args+=( + --certificate-oidc-issuer-regexp="${CERTIFICATE_OIDC_ISSUER_REGEXP}" + ) + fi + + # Force --ignore-rekor to false since we need rekor cmd_args+=( - --certificate-identity="${CERTIFICATE_IDENTITY}" - --certificate-oidc-issuer="${CERTIFICATE_OIDC_ISSUER}" + --ignore-rekor=false ) - elif [ -n "${PUBLIC_KEY}" ]; then + else + # Assume traditional signing secret verification cmd_args+=( --public-key="${PUBLIC_KEY}" + --ignore-rekor="${IGNORE_REKOR}" ) fi cmd_args+=( --rekor-url="${REKOR_HOST}" - --ignore-rekor="${IGNORE_REKOR}" --workers="${WORKERS}" --info="${INFO}" --timeout=0 @@ -313,6 +359,10 @@ spec: value: "$(params.CERTIFICATE_IDENTITY)" - name: CERTIFICATE_OIDC_ISSUER value: "$(params.CERTIFICATE_OIDC_ISSUER)" + - name: CERTIFICATE_IDENTITY_REGEXP + value: "$(params.CERTIFICATE_IDENTITY_REGEXP)" + - name: CERTIFICATE_OIDC_ISSUER_REGEXP + value: "$(params.CERTIFICATE_OIDC_ISSUER_REGEXP)" - name: REKOR_HOST value: "$(params.REKOR_HOST)" - name: IGNORE_REKOR