From 4eac31e1837dec0aeafccc41a466f9706060ca7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alexis=20M=C3=A9taireau?= Date: Tue, 22 Apr 2025 17:45:53 +0200 Subject: [PATCH] CI: Add an option to attach container signatures to the registry The `build-push-image.yml` reusable workflow can generate keypairs and sign the container images with them. This is only used by the CI, to test that a valid signature is actually detected as such. --- .github/workflows/build-push-image.yml | 101 +++++++++++++++++- .github/workflows/ci.yml | 70 +++++------- .github/workflows/release-container-image.yml | 1 + 3 files changed, 124 insertions(+), 48 deletions(-) diff --git a/.github/workflows/build-push-image.yml b/.github/workflows/build-push-image.yml index e6a7892..3326097 100644 --- a/.github/workflows/build-push-image.yml +++ b/.github/workflows/build-push-image.yml @@ -15,10 +15,25 @@ on: reproduce: required: true type: boolean + sign: + required: true + type: boolean + key_name: + required: false + type: string + default: "dangerzone-tests" + key_cache: + required: false + type: string + # cosign keys are unique for the ref + default: "v1-keypair-${{ github.ref_name }}" secrets: registry_token: required: true - + outputs: + image_uri: + description: "The published container image location, with the tag and checksum" + value: ${{ jobs.merge.outputs.image_uri }} jobs: lint: @@ -44,6 +59,7 @@ jobs: debian_archive_date: ${{ steps.params.outputs.debian_archive_date }} source_date_epoch: ${{ steps.params.outputs.source_date_epoch }} image: ${{ steps.params.outputs.full_image_name }} + tag: ${{ steps.params.outputs.tag }} steps: - uses: actions/checkout@v4 with: @@ -57,10 +73,9 @@ jobs: SOURCE_DATE_EPOCH=$(date -u -d ${DEBIAN_ARCHIVE_DATE} +"%s") TAG=${DEBIAN_ARCHIVE_DATE}-$(git describe --long --first-parent | tail -c +2) FULL_IMAGE_NAME=${{ inputs.registry }}/${{ inputs.image_name }}:${TAG} - echo "debian_archive_date=${DEBIAN_ARCHIVE_DATE}" >> $GITHUB_OUTPUT echo "source_date_epoch=${SOURCE_DATE_EPOCH}" >> $GITHUB_OUTPUT - echo "tag=${DEBIAN_ARCHIVE_DATE}-${TAG}" >> $GITHUB_OUTPUT + echo "tag=${TAG}" >> $GITHUB_OUTPUT echo "full_image_name=${FULL_IMAGE_NAME}" >> $GITHUB_OUTPUT echo "buildkit_image=${BUILDKIT_IMAGE}" >> $GITHUB_OUTPUT @@ -73,6 +88,7 @@ jobs: debian_archive_date: ${{ needs.prepare.outputs.debian_archive_date }} source_date_epoch: ${{ needs.prepare.outputs.source_date_epoch }} image: ${{ needs.prepare.outputs.image }} + tag: ${{ needs.prepare.outputs.tag }} strategy: fail-fast: false matrix: @@ -140,6 +156,8 @@ jobs: debian_archive_date: ${{ needs.build.outputs.debian_archive_date }} source_date_epoch: ${{ needs.build.outputs.source_date_epoch }} image: ${{ needs.build.outputs.image }} + image_uri: ${{ needs.build.output.image }}@${{ steps.image.outputs.digest_root }}" + tag: ${{ needs.build.outputs.tag }} digest_root: ${{ steps.image.outputs.digest_root }} digest_amd64: ${{ steps.image.outputs.digest_amd64 }} digest_arm64: ${{ steps.image.outputs.digest_arm64 }} @@ -246,3 +264,80 @@ jobs: --platform \ linux/${{ matrix.platform.name }} \ ${{ needs.merge.outputs[format('digest_{0}', matrix.platform.name)] }} + + sign: + if: ${{ inputs.sign }} + runs-on: "ubuntu-latest" + env: + COSIGN_PASSWORD: "password" + COSIGN_YES: true + needs: + - merge + outputs: + image_uri: ${{ steps.params.outputs.image_uri }} + steps: + - name: Prepare env + run: |- + echo "IMAGE_URI=${{ inputs.registry }}/${{ inputs.image_name }}:${{ needs.merge.outputs.tag }}@${{ needs.merge.outputs.digest_root }}" >> $GITHUB_ENV + + - name: Install Cosign + uses: sigstore/cosign-installer@d7d6bc7722e3daa8354c50bcb52f4837da5e9b6a + with: + cosign-release: 'v2.5.0' + - name: Check install + run: cosign version + - name: Cache keypair + id: cache-keypair + uses: actions/cache@v4 + with: + path: "${{ inputs.key_name }}.*" + key: ${{ inputs.key_cache }} + enableCrossOsArchive: true + - if: ${{ steps.cache-keypair.outputs.cache-hit != 'true' }} + continue-on-error: true + name: Generate keypair + run: |- + cosign generate-key-pair --output-key-prefix="${{ inputs.key_name }}" + - name: Cache keypair + uses: actions/cache@v4 + with: + path: "${{ inputs.key_name }}.*" + key: ${{ inputs.key_cache }} + enableCrossOsArchive: true + + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ inputs.registry_user }} + password: ${{ secrets.registry_token }} + + - name: Sign container + run: |- + cosign sign -d --yes --key=${{ inputs.key_name }}.key "${{ env.IMAGE_URI }}" + shell: bash + + - name: Get the container image from the registry + # It contains the image and the signatures + run: |- + cosign save ${{ env.IMAGE_URI }} --dir dz-container + mkdir share + cd dz-container + tar -cvf ../share/container.tar . + cd .. + + - name: Cache container image + id: cache-container-image + uses: actions/cache@v4 + with: + key: v6-container-${{ env.IMAGE_URI }} + path: |- + share/container.tar + + - name: Upload pubkey + uses: actions/upload-artifact@v4 + with: + name: pubkey + path: "${{inputs.key_name}}.pub" + if-no-files-found: error + compression-level: 0 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee7bd73..1b0cf76 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,11 +11,10 @@ on: permissions: packages: write + actions: read # for detecting the Github Actions environment. + id-token: write # for creating OIDC tokens for signing. env: - REGISTRY_USER: ${{ github.actor }} - REGISTRY_PASSWORD: ${{ github.token }} - IMAGE_REGISTRY: ghcr.io/${{ github.repository_owner }} QT_SELECT: "qt6" # Disable multiple concurrent runs on the same branch @@ -45,35 +44,18 @@ jobs: # This is already built daily by the "build.yml" file # But we also want to include this in the checks that run on each push. build-container-image: - runs-on: ubuntu-24.04 - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - name: Get current date - id: date - run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT - - - name: Cache container image - id: cache-container-image - uses: actions/cache@v4 - with: - key: v5-${{ steps.date.outputs.date }}-${{ hashFiles('Dockerfile', 'dangerzone/conversion/*.py', 'dangerzone/container_helpers/*', 'install/common/build-image.py') }} - path: |- - share/container.tar - share/image-id.txt - - - name: Build Dangerzone container image - if: ${{ steps.cache-container-image.outputs.cache-hit != 'true' }} - run: | - python3 ./install/common/build-image.py - - - name: Upload container image - uses: actions/upload-artifact@v4 - with: - name: container.tar - path: share/container.tar + name: Build, push and sign container image + uses: ./.github/workflows/build-push-image.yml + with: + registry: "ghcr.io/${{ github.repository_owner }}" + registry_user: ${{ github.actor }} + image_name: "dangerzone/dangerzone-staging" + reproduce: false + sign: true + key_name: "dangerzone-tests" + key_cache: "v1-test-keypair-${{ github.ref_name }}" + secrets: + registry_token: ${{ secrets.GITHUB_TOKEN }} download-tessdata: name: Download and cache Tesseract data @@ -223,14 +205,14 @@ jobs: id: date run: echo "date=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT - - name: Restore container cache + + - name: Restore container image uses: actions/cache/restore@v4 with: - key: v5-${{ steps.date.outputs.date }}-${{ hashFiles('Dockerfile', 'dangerzone/conversion/*.py', 'dangerzone/container_helpers/*', 'install/common/build-image.py') }} - path: |- - share/container.tar - share/image-id.txt + path: share/container.tar + enableCrossOsArchive: true fail-on-cache-miss: true + key: v6-container-${{ needs.build-container-image.outputs.image_uri }} - name: Build Dangerzone .deb run: | @@ -333,11 +315,10 @@ jobs: - name: Restore container image uses: actions/cache/restore@v4 with: - key: v5-${{ steps.date.outputs.date }}-${{ hashFiles('Dockerfile', 'dangerzone/conversion/*.py', 'dangerzone/container_helpers/*', 'install/common/build-image.py') }} - path: |- - share/container.tar - share/image-id.txt + path: share/container.tar + enableCrossOsArchive: true fail-on-cache-miss: true + key: v6-container-${{ needs.build-container-image.outputs.image_uri }} - name: Build Dangerzone .rpm run: | @@ -430,11 +411,10 @@ jobs: - name: Restore container image uses: actions/cache/restore@v4 with: - key: v5-${{ steps.date.outputs.date }}-${{ hashFiles('Dockerfile', 'dangerzone/conversion/*.py', 'dangerzone/container_helpers/*', 'install/common/build-image.py') }} - path: |- - share/container.tar - share/image-id.txt + path: share/container.tar + enableCrossOsArchive: true fail-on-cache-miss: true + key: v6-container-${{ needs.build-container-image.outputs.image_uri }} - name: Restore cached tessdata uses: actions/cache/restore@v4 diff --git a/.github/workflows/release-container-image.yml b/.github/workflows/release-container-image.yml index da63204..98ff66e 100644 --- a/.github/workflows/release-container-image.yml +++ b/.github/workflows/release-container-image.yml @@ -18,5 +18,6 @@ jobs: registry_user: ${{ github.actor }} image_name: dangerzone/dangerzone reproduce: true + sign: false secrets: registry_token: ${{ secrets.GITHUB_TOKEN }}