diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml index 9ba9cc2a..066445fa 100644 --- a/.github/workflows/release.yaml +++ b/.github/workflows/release.yaml @@ -164,6 +164,9 @@ jobs: needs: - test - check_version + permissions: + contents: read + id-token: write # IMPORTANT: mandatory for keyless signing with cosign steps: - name: Checkout uses: actions/checkout@v4 @@ -195,6 +198,7 @@ jobs: username: ${{ secrets.DOCKERHUB_USERNAME }} password: ${{ secrets.DOCKERHUB_TOKEN }} - name: Build and push + id: build uses: docker/build-push-action@v5 with: context: . @@ -207,14 +211,23 @@ jobs: labels: ${{ steps.meta.outputs.labels }} sbom: true provenance: mode=max + - name: Install cosign + if: ${{ github.event_name != 'pull_request' }} + uses: sigstore/cosign-installer@v3 + - name: Sign the image with cosign (keyless) + if: ${{ github.event_name != 'pull_request' }} + env: + DIGEST: ${{ steps.build.outputs.digest }} + # Sign the immutable digest of the multi-arch index, not the mutable tags. + run: cosign sign --yes "datacontract/cli@${DIGEST}" push-to-ecr: runs-on: ubuntu-latest needs: - docker steps: - - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + - name: Install cosign + uses: sigstore/cosign-installer@v3 - name: Set up AWS CLI uses: aws-actions/configure-aws-credentials@v4 @@ -234,10 +247,13 @@ jobs: aws ecr-public get-login-password --region us-east-1 | docker login --username AWS --password-stdin public.ecr.aws/s4e5k7s9 - name: Copy Multi-Arch Image from Docker Hub to AWS ECR + # `cosign copy` carries the signature and the SBOM/provenance attestations + # along with the image; `docker buildx imagetools create` would copy the + # manifest only and leave the signature behind on Docker Hub. run: | VERSION="${{ github.ref_name }}" VERSION="${VERSION#v}" # Strip 'v' prefix to match Docker Hub tag - docker buildx imagetools create \ - --tag public.ecr.aws/s4e5k7s9/datacontract-cli:latest \ - --tag public.ecr.aws/s4e5k7s9/datacontract-cli:${VERSION} \ - docker.io/datacontract/cli:${VERSION} + SOURCE="docker.io/datacontract/cli:${VERSION}" + TARGET="public.ecr.aws/s4e5k7s9/datacontract-cli" + cosign copy --force "${SOURCE}" "${TARGET}:${VERSION}" + cosign copy --force "${SOURCE}" "${TARGET}:latest" diff --git a/CHANGELOG.md b/CHANGELOG.md index 063123cc..370498c5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ This removes the JVM dependency, makes the images much, much smaller (Docker ima ### Added - `DATACONTRACT_KAFKA_MAX_MESSAGES` limits how many messages `datacontract test` reads from a topic, and `DATACONTRACT_KAFKA_TIMEOUT` how long it waits for one +- Released Docker images are signed with cosign keyless signing, on Docker Hub and the Amazon ECR Public mirror; see [Installation](https://docs.datacontract.com/installation#verifying-the-image) for how to verify them ### Changed - `datacontract test` for Kafka no longer needs PySpark or a Java runtime: `datacontract-cli[kafka]` now installs confluent-kafka, fastavro, and DuckDB instead diff --git a/README.md b/README.md index a3b82a38..7ab4eb08 100644 --- a/README.md +++ b/README.md @@ -246,6 +246,8 @@ alias datacontract='docker run --rm -v "${PWD}:/home/datacontract" datacontract/ _Note:_ The output of Docker command line messages is limited to 80 columns and may include line breaks. Don't pipe docker output to files if you want to export code. Use the `--output` option instead. +Released images are signed with [cosign](https://docs.sigstore.dev/cosign/signing/overview/) keyless signing. See [Verifying the image](https://docs.datacontract.com/installation#verifying-the-image) to check a signature before you run it. + ## Optional Dependencies (Extras) diff --git a/docs/docs/installation.md b/docs/docs/installation.md index 177ab04b..362ce28e 100644 --- a/docs/docs/installation.md +++ b/docs/docs/installation.md @@ -74,6 +74,43 @@ alias datacontract='docker run --rm -v "${PWD}:/home/datacontract" datacontract/ The output of Docker command line messages is limited to 80 columns and may include line breaks. Don't pipe Docker output to files if you want to export code — use the `--output` option instead. ::: +The image is also mirrored to Amazon ECR Public: + +```bash +docker pull public.ecr.aws/s4e5k7s9/datacontract-cli +``` + +### Verifying the image + +Released images are signed with [cosign](https://docs.sigstore.dev/cosign/signing/overview/) keyless signing, using the GitHub Actions OIDC identity of the release workflow. There is no public key to distribute — verification checks that the image was built and signed by that workflow, in this repository, from a release tag. + +Install [cosign](https://docs.sigstore.dev/cosign/system_config/installation/), then: + +```bash +cosign verify datacontract/cli:latest \ + --certificate-identity-regexp '^https://github\.com/datacontract/datacontract-cli/\.github/workflows/release\.yaml@refs/tags/v' \ + --certificate-oidc-issuer https://token.actions.githubusercontent.com +``` + +Both `--certificate-identity-regexp` and `--certificate-oidc-issuer` are required. Without them, `cosign verify` accepts any valid Sigstore signature — including one produced by someone else. + +The same command works for the ECR mirror, which carries the signature and attestations along with the image: + +```bash +cosign verify public.ecr.aws/s4e5k7s9/datacontract-cli:latest \ + --certificate-identity-regexp '^https://github\.com/datacontract/datacontract-cli/\.github/workflows/release\.yaml@refs/tags/v' \ + --certificate-oidc-issuer https://token.actions.githubusercontent.com +``` + +Each image also ships an SBOM and SLSA provenance attestation, which you can inspect with: + +```bash +docker buildx imagetools inspect datacontract/cli:latest --format '{{ json .SBOM }}' +docker buildx imagetools inspect datacontract/cli:latest --format '{{ json .Provenance }}' +``` + +Signatures are attached to images released from version 1.1.0 onwards. Older tags are unsigned. + ## Optional dependencies (extras) The CLI defines several optional dependencies (extras) for specific server types. With `all`, every server dependency is included. diff --git a/docs/docs/release-notes.md b/docs/docs/release-notes.md index 2dee6b8b..a108207c 100644 --- a/docs/docs/release-notes.md +++ b/docs/docs/release-notes.md @@ -24,10 +24,14 @@ marked as such in the entry. - Install or upgrade with `uv tool install --upgrade datacontract-cli` or `pip install --upgrade datacontract-cli` — see [Installation](./installation.md). - Packages: [PyPI](https://pypi.org/project/datacontract-cli/#history) · [Docker Hub](https://hub.docker.com/r/datacontract/cli/tags) · [GitHub releases](https://github.com/datacontract/datacontract-cli/releases) -## Unreleased {#unreleased} +## Unreleased - targeting for 1.1.0 {#vUnreleased - targeting for 1-1-0} + +This release drops the pyspark compile-time dependency. The server types `dataframe` and `databricks` still work with a provided Spark session. +This removes the JVM dependency, makes the images much, much smaller (Docker image from 777 MB to 277 MB), and many CVEs are resolved. ### Added - `DATACONTRACT_KAFKA_MAX_MESSAGES` limits how many messages `datacontract test` reads from a topic, and `DATACONTRACT_KAFKA_TIMEOUT` how long it waits for one +- Released Docker images are signed with cosign keyless signing, on Docker Hub and the Amazon ECR Public mirror; see [Installation](https://docs.datacontract.com/installation#verifying-the-image) for how to verify them ### Changed - `datacontract test` for Kafka no longer needs PySpark or a Java runtime: `datacontract-cli[kafka]` now installs confluent-kafka, fastavro, and DuckDB instead