diff --git a/docs/modules/ROOT/pages/verify-enterprise-contract.adoc b/docs/modules/ROOT/pages/verify-enterprise-contract.adoc index 8af4427e3..fbf48ced0 100644 --- a/docs/modules/ROOT/pages/verify-enterprise-contract.adoc +++ b/docs/modules/ROOT/pages/verify-enterprise-contract.adoc @@ -34,6 +34,8 @@ 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 signatures. Must be a valid k8s cosign reference, e.g. k8s://my-space/my-secret where my-secret contains the expected cosign.pub attribute. *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. +*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. *IGNORE_REKOR* (`string`):: Skip Rekor transparency log checks during validation. + *Default*: `false` diff --git a/features/__snapshots__/task_validate_image.snap b/features/__snapshots__/task_validate_image.snap index 13b2bcb53..91a7a9e69 100755 --- a/features/__snapshots__/task_validate_image.snap +++ b/features/__snapshots__/task_validate_image.snap @@ -180,3 +180,276 @@ true "TEST_OUTPUT": "{\"timestamp\":\"${TIMESTAMP}\",\"namespace\":\"\",\"successes\":3,\"failures\":0,\"warnings\":0,\"result\":\"SUCCESS\"}\n" } --- + +[Keyless signing verification cosign v3 style:report-json - 1] +{ + "success": true, + "components": [ + { + "name": "", + "containerImage": "quay.io/conforma/test@sha256:712ca3a7fcd41fe6b3e6f434a31f738743b6c31f1d81ad458502d6b0239a8903", + "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": "", + "sig": "" + }, + { + "keyid": "", + "sig": "" + } + ], + "attestations": [ + { + "type": "https://in-toto.io/Statement/v0.1", + "predicateType": "https://slsa.dev/provenance/v1", + "signatures": [ + { + "keyid": "", + "sig": "MEUCIQC5bGm4zzbExXBMrZCmqZ98iqUhi8TV/maq/8dJ/c3POAIgCNw+RkeO7PAkT6JDWIvISZ2AjILu9YuPQ0qqfNwCqug=" + } + ] + }, + { + "type": "https://in-toto.io/Statement/v0.1", + "predicateType": "https://sigstore.dev/cosign/sign/v1", + "signatures": [ + { + "keyid": "", + "sig": "MEUCID1cJkxyk1oGvXcoAVkDST9A1vfX2gxPEz+LUzN10nDmAiEAxh9rp79yr4fZmAWWOit0dZ5QWK+uYIU8fQVb0/rLIyM=" + } + ] + } + ] + } + ], + "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:results - 1] +{ + "TEST_OUTPUT": "{\"timestamp\":\"${TIMESTAMP}\",\"namespace\":\"\",\"successes\":5,\"failures\":0,\"warnings\":0,\"result\":\"SUCCESS\"}\n" +} +--- + +[Keyless signing verification cosign v2 style: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:results - 1] +{ + "TEST_OUTPUT": "{\"timestamp\":\"${TIMESTAMP}\",\"namespace\":\"\",\"successes\":5,\"failures\":0,\"warnings\":0,\"result\":\"SUCCESS\"}\n" +} +--- diff --git a/features/task_validate_image.feature b/features/task_validate_image.feature index 1a63f289b..de205d33b 100644 --- a/features/task_validate_image.feature +++ b/features/task_validate_image.feature @@ -337,3 +337,78 @@ Feature: Verify Enterprise Contract Tekton Tasks Then the task should succeed And the task logs for step "report" should match the snapshot And the task results should match the snapshot + + # See hack/keyless-test-image for how the quay.io/conforma/test:keyless_v2 + # and quay.io/conforma/test:keyless_v3 test images where created. It's not + # ideal that this test requires an external image, but we already do this + # elsewhere, so I guess one more is okay. + + # Todo: We should be able test this also with an internally built image + # similar to how it's done in the "happy day with keyless" scenario in the + # validate_image feature. + + # Confirm we can verify the signatures on a keylessly signed image signed with cosign v2 + Scenario: Keyless signing verification cosign v2 style + 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 | 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 + + # 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 + 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} | + | 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 diff --git a/hack/keyless-test-image/create.sh b/hack/keyless-test-image/create.sh new file mode 100755 index 000000000..abe23f733 --- /dev/null +++ b/hack/keyless-test-image/create.sh @@ -0,0 +1,89 @@ +#!/usr/bin/env bash +# Copyright The Conforma Contributors +# +# 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. +# +# SPDX-License-Identifier: Apache-2.0 + +set -euo pipefail + +source "$(dirname ${BASH_SOURCE[0]})/helpers.sh" + +# This script creates two keylessly signed images that we use in our acceptance +# tests. One created with cosign v2 and one with cosign v3 using the newer +# sigstore bundle and OCI referrers + +# Prereqs if you want to recreate these images: +# - A working push credential for quay.io/conforma/test +# - The ability to authenticate as the "conformacommunity@gmail.com" Google account + +# Note: Ideally we would not rely on external images in the tests, but this is +# the quickest way to get some meaningful acceptance tests for the keyless +# image verification in the Tekton task. Also, we already have some other +# external images used in the tests, so I figure adding one more isn't such a +# big deal. + +REPO=quay.io/conforma/test + +# Todo: Maybe we can we specify these explicitly when signing +# CERT_IDENITY="conformacommunity@gmail.com" +# CERT_OIDC_ISSUER="https://accounts.google.com" + +# Todo maybe: Pin the versions of cosign, (perhaps with a go.mod file?) instead +# of using @latest. + +for ver in v2 v3; do + LABEL="keyless_$ver" + COSIGN="go run github.com/sigstore/cosign/$ver/cmd/cosign@latest" + GIT_VER=$($COSIGN version --json | jq -r .gitVersion) + DATE_STR=$(date) + + + h1 "Creating image ($ver)" + podman build -t "$REPO:$LABEL" -f - . < /hello.txt +CMD ["cat", "/hello.txt"] +EOF + + h1 "Pushing image ($ver)" + podman push "$REPO:$LABEL" + + h1 "Signing image ($ver)" + # Use the digest otherwise cosign complains + DIGEST=$(skopeo inspect "docker://quay.io/conforma/test:keyless_$ver" | jq -r .Digest) + $COSIGN sign -y "$REPO@$DIGEST" + + h1 "Creating a signed attestation ($ver)" + # Push a minimal attestation + $COSIGN attest -y \ + --predicate - \ + --type "https://slsa.dev/provenance/v1" \ + $REPO@$DIGEST < /dev/null +$COSIGN_V3 verify-attestation "$IMAGE_V2" --type "$SLSA" "${CERT_ARGS[@]}" > /dev/null +pause + +h1 "Forwards compatibility does not work (v3 sig with v2 cosign)" +set +e +$COSIGN_V2 verify "$IMAGE_V3" "${CERT_ARGS[@]}" +$COSIGN_V2 verify-attestation "$IMAGE_V3" --type "$SLSA" "${CERT_ARGS[@]}" +set -e +pause 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 dd53433d7..d7a4e1624 100644 --- a/tasks/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml +++ b/tasks/verify-enterprise-contract/0.1/verify-enterprise-contract.yaml @@ -69,6 +69,20 @@ spec: description: Rekor host for transparency log lookups default: "" + - name: CERTIFICATE_IDENTITY + type: string + description: >- + Expected identity in the signing certificate for keyless verification. + This should be the email or URI that was used when signing. + default: "" + + - name: CERTIFICATE_OIDC_ISSUER + type: string + description: >- + Expected OIDC issuer in the signing certificate for keyless verification. + This should match the issuer that provided the identity token used for signing. + default: "" + - name: IGNORE_REKOR type: string description: >- @@ -236,49 +250,89 @@ spec: - name: validate image: quay.io/conforma/cli:latest onError: continue # progress even if the step fails so we can see the debug logs - command: [ec] - args: - - validate - - image - - "--images" - - "/tekton/home/snapshot.json" - - "--policy" - - "$(params.POLICY_CONFIGURATION)" - - "--public-key" - - "$(params.PUBLIC_KEY)" - - "--rekor-url" - - "$(params.REKOR_HOST)" - - "--ignore-rekor=$(params.IGNORE_REKOR)" - - "--workers" - - "$(params.WORKERS)" - # NOTE: The syntax below is required to negate boolean parameters - - "--info=$(params.INFO)" - # Fresh versions of ec support "--timeout=0" to indicate no timeout, but this would break - # the task if it's used with an older version of ec. In an abundance of caution, let's set - # an arbitrary high value instead of using 0 here. In future we can change it to 0. - # (The reason to not use an explicit timeout for ec is so Tekton can handle the timeouts). - - "--timeout=100h" - - "--strict=false" - - "--show-successes" - - "--effective-time=$(params.EFFECTIVE_TIME)" - - "--extra-rule-data=$(params.EXTRA_RULE_DATA)" - - "--retry-max-wait" - - "$(params.RETRY_MAX_WAIT)" - - "--retry-max-retry" - - "$(params.RETRY_MAX_RETRY)" - - "--retry-duration" - - "$(params.RETRY_DURATION)" - - "--retry-factor" - - "$(params.RETRY_FACTOR)" - - "--retry-jitter" - - "$(params.RETRY_JITTER)" - - "--output" - - "text=$(params.HOMEDIR)/text-report.txt?show-successes=false" - - "--output" - - "appstudio=$(results.TEST_OUTPUT.path)" - - "--output" - - "json=$(params.HOMEDIR)/report-json.json" + script: | + #!/bin/bash + set -euo pipefail + + cmd_args=( + "validate" + "image" + "--images" "/tekton/home/snapshot.json" + "--policy" "${POLICY_CONFIGURATION}" + ) + + # 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 + cmd_args+=( + "--certificate-identity" "${CERTIFICATE_IDENTITY}" + "--certificate-oidc-issuer" "${CERTIFICATE_OIDC_ISSUER}" + ) + elif [ -n "${PUBLIC_KEY}" ]; then + cmd_args+=( + "--public-key" "${PUBLIC_KEY}" + ) + fi + + cmd_args+=( + "--rekor-url" "${REKOR_HOST}" + "--ignore-rekor=${IGNORE_REKOR}" + "--workers" "${WORKERS}" + # NOTE: The syntax below is required to negate boolean parameters + "--info=${INFO}" + # Fresh versions of ec support "--timeout=0" to indicate no timeout, but this would break + # the task if it's used with an older version of ec. In an abundance of caution, let's set + # an arbitrary high value instead of using 0 here. In future we can change it to 0. + # (The reason to not use an explicit timeout for ec is so Tekton can handle the timeouts). + "--timeout=100h" + "--strict=false" + "--show-successes" + "--effective-time=${EFFECTIVE_TIME}" + "--extra-rule-data=${EXTRA_RULE_DATA}" + "--retry-max-wait" "${RETRY_MAX_WAIT}" + "--retry-max-retry" "${RETRY_MAX_RETRY}" + "--retry-duration" "${RETRY_DURATION}" + "--retry-factor" "${RETRY_FACTOR}" + "--retry-jitter" "${RETRY_JITTER}" + "--output" "text=${HOMEDIR}/text-report.txt?show-successes=false" + "--output" "appstudio=$(results.TEST_OUTPUT.path)" + "--output" "json=${HOMEDIR}/report-json.json" + ) + + exec ec "${cmd_args[@]}" env: + - name: POLICY_CONFIGURATION + value: "$(params.POLICY_CONFIGURATION)" + - name: PUBLIC_KEY + value: "$(params.PUBLIC_KEY)" + - name: CERTIFICATE_IDENTITY + value: "$(params.CERTIFICATE_IDENTITY)" + - name: CERTIFICATE_OIDC_ISSUER + value: "$(params.CERTIFICATE_OIDC_ISSUER)" + - name: REKOR_HOST + value: "$(params.REKOR_HOST)" + - name: IGNORE_REKOR + value: "$(params.IGNORE_REKOR)" + - name: WORKERS + value: "$(params.WORKERS)" + - name: INFO + value: "$(params.INFO)" + - name: EFFECTIVE_TIME + value: "$(params.EFFECTIVE_TIME)" + - name: EXTRA_RULE_DATA + value: "$(params.EXTRA_RULE_DATA)" + - name: RETRY_MAX_WAIT + value: "$(params.RETRY_MAX_WAIT)" + - name: RETRY_MAX_RETRY + value: "$(params.RETRY_MAX_RETRY)" + - name: RETRY_DURATION + value: "$(params.RETRY_DURATION)" + - name: RETRY_FACTOR + value: "$(params.RETRY_FACTOR)" + - name: RETRY_JITTER + value: "$(params.RETRY_JITTER)" + - name: HOMEDIR + value: "$(params.HOMEDIR)" - name: SSL_CERT_DIR # The Tekton Operator automatically sets the SSL_CERT_DIR env to the value below but, # of course, without the $(param.SSL_CERT_DIR) bit. When a Task Step sets it to a