diff --git a/.gitlab/ci/pipelines/before_merging.yml b/.gitlab/ci/pipelines/before_merging.yml index 020492c98c3392a3d9387057fd7d432cf7cb802e..5c6b02741e78464a72a1ffb03c2d45a4ff0b6766 100644 --- a/.gitlab/ci/pipelines/before_merging.yml +++ b/.gitlab/ci/pipelines/before_merging.yml @@ -3991,3 +3991,47 @@ trigger:debian_repository_partial: needs: [] trigger: include: .gitlab/ci/pipelines/debian_repository_partial.yml + +oc.script.docker_verify_image_arm64: + image: ${GCP_REGISTRY}/tezos/docker-images/ci-docker:v1.12.0 + stage: manual + tags: + - gcp + rules: + - when: manual + allow_failure: true + needs: + - oc.docker:arm64 + dependencies: [] + timeout: 60 minutes + before_script: + - ./scripts/ci/docker_initialize.sh + script: + - ./scripts/ci/docker_verify_signature.sh + services: + - docker:${DOCKER_VERSION}-dind + variables: + DOCKER_VERSION: 24.0.7 + IMAGE_ARCH_PREFIX: arm64_ + +oc.script.docker_verify_image_amd64: + image: ${GCP_REGISTRY}/tezos/docker-images/ci-docker:v1.12.0 + stage: manual + tags: + - gcp + rules: + - when: manual + allow_failure: true + needs: + - oc.docker:amd64 + dependencies: [] + timeout: 60 minutes + before_script: + - ./scripts/ci/docker_initialize.sh + script: + - ./scripts/ci/docker_verify_signature.sh + services: + - docker:${DOCKER_VERSION}-dind + variables: + DOCKER_VERSION: 24.0.7 + IMAGE_ARCH_PREFIX: amd64_ diff --git a/.gitlab/ci/pipelines/merge_train.yml b/.gitlab/ci/pipelines/merge_train.yml index 020492c98c3392a3d9387057fd7d432cf7cb802e..5c6b02741e78464a72a1ffb03c2d45a4ff0b6766 100644 --- a/.gitlab/ci/pipelines/merge_train.yml +++ b/.gitlab/ci/pipelines/merge_train.yml @@ -3991,3 +3991,47 @@ trigger:debian_repository_partial: needs: [] trigger: include: .gitlab/ci/pipelines/debian_repository_partial.yml + +oc.script.docker_verify_image_arm64: + image: ${GCP_REGISTRY}/tezos/docker-images/ci-docker:v1.12.0 + stage: manual + tags: + - gcp + rules: + - when: manual + allow_failure: true + needs: + - oc.docker:arm64 + dependencies: [] + timeout: 60 minutes + before_script: + - ./scripts/ci/docker_initialize.sh + script: + - ./scripts/ci/docker_verify_signature.sh + services: + - docker:${DOCKER_VERSION}-dind + variables: + DOCKER_VERSION: 24.0.7 + IMAGE_ARCH_PREFIX: arm64_ + +oc.script.docker_verify_image_amd64: + image: ${GCP_REGISTRY}/tezos/docker-images/ci-docker:v1.12.0 + stage: manual + tags: + - gcp + rules: + - when: manual + allow_failure: true + needs: + - oc.docker:amd64 + dependencies: [] + timeout: 60 minutes + before_script: + - ./scripts/ci/docker_initialize.sh + script: + - ./scripts/ci/docker_verify_signature.sh + services: + - docker:${DOCKER_VERSION}-dind + variables: + DOCKER_VERSION: 24.0.7 + IMAGE_ARCH_PREFIX: amd64_ diff --git a/ci/bin/code_verification.ml b/ci/bin/code_verification.ml index 291a4242ec861c9faee99140c778781d7b9a06a2..7928ca49e2468f66d749aba36b52823e331c97f7 100644 --- a/ci/bin/code_verification.ml +++ b/ci/bin/code_verification.ml @@ -1694,6 +1694,27 @@ let jobs pipeline_type = ~stage:Stages.manual () in + let job_docker_verify_test_amd64 : tezos_job = + job_docker_authenticated + ~__POS__ + ~name:"oc.script.docker_verify_image_amd64" + ~stage:Stages.manual + ~variables:[("IMAGE_ARCH_PREFIX", "amd64_")] + ~rules:(make_rules ~manual:Yes ()) + ~dependencies:(Dependent [Job job_docker_amd64_test_manual]) + ["./scripts/ci/docker_verify_signature.sh"] + in + let job_docker_verify_test_arm64 : tezos_job = + job_docker_authenticated + ~__POS__ + ~name:"oc.script.docker_verify_image_arm64" + ~stage:Stages.manual + ~variables:[("IMAGE_ARCH_PREFIX", "arm64_")] + ~rules:(make_rules ~manual:Yes ()) + ~dependencies:(Dependent [Job job_docker_arm64_test_manual]) + ["./scripts/ci/docker_verify_signature.sh"] + in + [ job_docker_amd64_test_manual; job_docker_arm64_test_manual; @@ -1701,6 +1722,7 @@ let jobs pipeline_type = job_build_homebrew_manual; job_debian_repository_trigger; ] + @ [job_docker_verify_test_arm64; job_docker_verify_test_amd64] (* No manual jobs on the scheduled pipeline *) | Schedule_extended_test -> [job_debian_repository_trigger] in diff --git a/scripts/ci/docker_release.sh b/scripts/ci/docker_release.sh index aeab7f7331605ae0f434e8ca0faa051e303ca0d3..e33569659f9dcc6914f24eee7c42ceaf33e18a69 100755 --- a/scripts/ci/docker_release.sh +++ b/scripts/ci/docker_release.sh @@ -46,3 +46,6 @@ OCTEZ_EXECUTABLES="$(cat $EXECUTABLE_FILES)" # Push minimal, bare and debug images ./scripts/ci/docker_push_all.sh + +# Sign image signatures +./scripts/ci/docker_sign.sh diff --git a/scripts/ci/docker_sign.sh b/scripts/ci/docker_sign.sh new file mode 100755 index 0000000000000000000000000000000000000000..53121cffc983132e50636bc44d4f5e308c6bdf4c --- /dev/null +++ b/scripts/ci/docker_sign.sh @@ -0,0 +1,46 @@ +#!/bin/sh +# +# Sign the releasing docker images using Cosign +# +# Reads the following environment variables: +# - 'GCP_SIGNER_SERVICE_ACCOUNT': signer service account key file encoded +# in base64, set by GitLab CI +# - 'GCP_SIGN_KEY': key used for signing, format: +# `gcpkms://projects//locations//keyRings//cryptoKeys//versions/`, +# set by GitLab CI + +set -eu + +current_dir=$(cd "$(dirname "${0}")" && pwd) + +# shellcheck source=./scripts/ci/docker.sh +. "${current_dir}/docker.sh" + +# Auth signer service account +echo "${GCP_SIGNER_SERVICE_ACCOUNT}" | base64 -d > signer_sa.json +gcloud auth activate-service-account --key-file=signer_sa.json +gcloud auth configure-docker us-central1-docker.pkg.dev +gcloud auth print-access-token | docker login -u oauth2accesstoken --password-stdin https://us-central1-docker.pkg.dev +export GOOGLE_APPLICATION_CREDENTIALS=signer_sa.json + +# Install cosign +apk add --update cosign +cosign version + +# Loop over images +for docker_image in ${docker_images}; do + + # Get image digest + IMAGE_DIGEST="${docker_image}@$(docker buildx imagetools inspect "${docker_image}:${DOCKER_IMAGE_TAG}" --format "{{json .Manifest}}" | jq -r '.digest')" + echo "Image digest: ${IMAGE_DIGEST}" + + # Sign image with cosign + cosign sign --key "${GCP_SIGN_KEY}" "${IMAGE_DIGEST}" -y + + # Get the location of image signature as reference + IMAGE_SIGNATURE_LOCATION=$(cosign triangulate "${IMAGE_DIGEST}") + echo "Image signature location: ${IMAGE_SIGNATURE_LOCATION}" +done + +# Remove credentials +rm signer_sa.json diff --git a/scripts/ci/docker_verify_signature.sh b/scripts/ci/docker_verify_signature.sh new file mode 100755 index 0000000000000000000000000000000000000000..d36b3be54a57337d7f828618a715f80eb6101c2f --- /dev/null +++ b/scripts/ci/docker_verify_signature.sh @@ -0,0 +1,43 @@ +#!/bin/sh +# +# Verify the signature of the releasing docker images using Cosign +# +# Reads the following environment variables: +# - 'GCP_SIGN_KEY_URL': key URL used to verify released Docker image signatures, set by GitLab CI + +set -eu + +# Read environment variables written by 'docker_registry_auth.sh' in +# 'before_script'. +. scripts/ci/docker.env + +current_dir=$(cd "$(dirname "${0}")" && pwd) + +# shellcheck source=./scripts/ci/docker.sh +. "${current_dir}/docker.sh" + +# Install cosign +apk add --update cosign +cosign version + +# Get public key +wget -O publickey.pem "${GCP_SIGN_KEY_URL}" +cat publickey.pem + +# Loop over images +for docker_image in ${docker_images}; do + + # Pull images + docker pull "${docker_image}:${DOCKER_IMAGE_TAG}" + + # Get image digest + IMAGE_DIGEST="$(docker image inspect "${docker_image}:${DOCKER_IMAGE_TAG}" --format="{{index .RepoDigests 0}}")" + echo "Image digest: ${IMAGE_DIGEST}" + + # Get the location of image signature as reference + IMAGE_SIGNATURE_LOCATION=$(cosign triangulate "${IMAGE_DIGEST}") + echo "Image signature location: ${IMAGE_SIGNATURE_LOCATION}" + + # Verify the signature + cosign verify --key publickey.pem "${IMAGE_DIGEST}" | jq +done