From 798809cf7d5bd26c7287c2358f77bf7b4360ad91 Mon Sep 17 00:00:00 2001 From: Fabrizio Gattuso Date: Thu, 27 Feb 2025 12:15:17 +0000 Subject: [PATCH 01/12] Add new workflow --- .../image-build-and-draft-release.yaml | 5 +- .github/workflows/nightly-image-build.yaml | 84 +++++++++++++++++++ 2 files changed, 85 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/nightly-image-build.yaml diff --git a/.github/workflows/image-build-and-draft-release.yaml b/.github/workflows/image-build-and-draft-release.yaml index 232c2b8cc..920a5966c 100644 --- a/.github/workflows/image-build-and-draft-release.yaml +++ b/.github/workflows/image-build-and-draft-release.yaml @@ -1,11 +1,8 @@ ## Github workflow to build a multiarch docker image from pre-built binaries -name: Docker Image (Binary) +name: Docker Hub Release Image (Binary) on: - workflow_dispatch: - # schedule: - # - cron: '0 2 * * *' push: tags: - '*' diff --git a/.github/workflows/nightly-image-build.yaml b/.github/workflows/nightly-image-build.yaml new file mode 100644 index 000000000..aa09e16a2 --- /dev/null +++ b/.github/workflows/nightly-image-build.yaml @@ -0,0 +1,84 @@ +## Github workflow to build a multiarch docker image from pre-built binaries + +name: Nightly Docker AWS ECR Image (Binary) + +on: + workflow_dispatch: + schedule: + - cron: '0 2 * * *' + +permissions: + id-token: write + contents: read + attestations: write + packages: write + +env: + docker_platforms: "linux/amd64" + aws_region: "eu-west-1" + aws_role: "arn:aws:iam::376129863319:role/GitHubECR" + ecr_repository: "sbtc" + +jobs: + image: + name: Build Image + strategy: + fail-fast: false + max-parallel: 2 + matrix: + dist: + - debian + docker_target: + - signer + - blocklist-client + runs-on: ubuntu-latest + + steps: + - name: Configure AWS Credentials via OIDC + uses: aws-actions/configure-aws-credentials@v3 + with: + role-to-assume: ${{ env.aws_role }} + aws-region: ${{ env.aws_region }} + + - name: Login to Amazon ECR + id: ecr_login + uses: aws-actions/amazon-ecr-login@062b18b96a7aff071d4dc91bc00c4c1a7945b076 # v2.0.1 + + - name: Checkout + uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 + + - name: Docker Metadata (${{ matrix.dist }}) + id: docker_metadata + uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1 + with: + images: | + ${{ steps.ecr_login.outputs.registry }}/${{ env.ecr_repository }} + tags: | + type=raw,value=${{ matrix.docker_target }}-${{ github.ref_name }}-${{ matrix.dist }} + type=raw,value=${{ matrix.docker_target }}-${{ github.ref_name }} + type=raw,value=${{ matrix.docker_target }}-latest,enable=${{ github.ref_name == 'main' }} + + ## 🔹 Build and Push to AWS ECR + - name: Build and Push (${{ matrix.dist }} ${{ matrix.docker_target }}) + id: docker_build + uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # v5.3.0 + with: + file: ./.github/actions/dockerfiles/Dockerfile.${{ matrix.docker_target }}.${{ matrix.dist }} + platforms: ${{ env.docker_platforms }} + tags: ${{ steps.docker_metadata.outputs.tags }} + labels: ${{ steps.docker_metadata.outputs.labels }} + target: ${{ matrix.docker_target }} + push: true + build-args: | + GIT_COMMIT=${{ github.ref_name }} + + - name: Save digest as output + id: save_digest + run: echo "${{ matrix.docker_target }}=${{ steps.docker_build.outputs.digest }}" >> "$GITHUB_OUTPUT" + + - name: Generate artifact attestation + uses: actions/attest-build-provenance@7668571508540a607bdfd90a87a560489fe372eb #v2.1.0 + with: + subject-name: ${{ steps.ecr_login.outputs.registry }}/${{ env.ecr_repository }} + subject-digest: ${{ steps.docker_build.outputs.digest }} + push-to-registry: true \ No newline at end of file From 73fe0a814eb9d267be48ac1e188a0919e87e3c7c Mon Sep 17 00:00:00 2001 From: Fabrizio Gattuso Date: Thu, 27 Feb 2025 16:09:15 +0000 Subject: [PATCH 02/12] Update vars --- .github/workflows/nightly-image-build.yaml | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/.github/workflows/nightly-image-build.yaml b/.github/workflows/nightly-image-build.yaml index aa09e16a2..6fcd0c88a 100644 --- a/.github/workflows/nightly-image-build.yaml +++ b/.github/workflows/nightly-image-build.yaml @@ -15,9 +15,6 @@ permissions: env: docker_platforms: "linux/amd64" - aws_region: "eu-west-1" - aws_role: "arn:aws:iam::376129863319:role/GitHubECR" - ecr_repository: "sbtc" jobs: image: @@ -37,8 +34,8 @@ jobs: - name: Configure AWS Credentials via OIDC uses: aws-actions/configure-aws-credentials@v3 with: - role-to-assume: ${{ env.aws_role }} - aws-region: ${{ env.aws_region }} + role-to-assume: ${{ secrets['AWS_ROLE_ARN'] }} + aws-region: ${{ secrets['AWS_REGION'] }} - name: Login to Amazon ECR id: ecr_login @@ -52,7 +49,7 @@ jobs: uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1 with: images: | - ${{ steps.ecr_login.outputs.registry }}/${{ env.ecr_repository }} + ${{ steps.ecr_login.outputs.registry }}/${{ secrets['AWS_ECR'] }} tags: | type=raw,value=${{ matrix.docker_target }}-${{ github.ref_name }}-${{ matrix.dist }} type=raw,value=${{ matrix.docker_target }}-${{ github.ref_name }} From 9921d673ce5da6d7e96d1aa2495cef6db2642df6 Mon Sep 17 00:00:00 2001 From: Fabrizio Gattuso Date: Thu, 27 Feb 2025 16:20:26 +0000 Subject: [PATCH 03/12] Add environment --- .github/workflows/nightly-image-build.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/nightly-image-build.yaml b/.github/workflows/nightly-image-build.yaml index 6fcd0c88a..f88dde085 100644 --- a/.github/workflows/nightly-image-build.yaml +++ b/.github/workflows/nightly-image-build.yaml @@ -29,10 +29,11 @@ jobs: - signer - blocklist-client runs-on: ubuntu-latest + environment: "Push to ECR" steps: - name: Configure AWS Credentials via OIDC - uses: aws-actions/configure-aws-credentials@v3 + uses: aws-actions/configure-aws-credentials@ececac1a45f3b08a01d2dd070d28d111c5fe6722 #v4.1.0 with: role-to-assume: ${{ secrets['AWS_ROLE_ARN'] }} aws-region: ${{ secrets['AWS_REGION'] }} From 3d027b354cab26dd0d463e8d46eb907abe444714 Mon Sep 17 00:00:00 2001 From: Fabrizio Gattuso Date: Thu, 27 Feb 2025 17:02:30 +0000 Subject: [PATCH 04/12] Fix vars --- .github/workflows/nightly-image-build.yaml | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/.github/workflows/nightly-image-build.yaml b/.github/workflows/nightly-image-build.yaml index f88dde085..17a53c4ba 100644 --- a/.github/workflows/nightly-image-build.yaml +++ b/.github/workflows/nightly-image-build.yaml @@ -6,6 +6,9 @@ on: workflow_dispatch: schedule: - cron: '0 2 * * *' + push: + branches: + - "feat/add_docker_ecr_registry" permissions: id-token: write @@ -36,7 +39,7 @@ jobs: uses: aws-actions/configure-aws-credentials@ececac1a45f3b08a01d2dd070d28d111c5fe6722 #v4.1.0 with: role-to-assume: ${{ secrets['AWS_ROLE_ARN'] }} - aws-region: ${{ secrets['AWS_REGION'] }} + aws-region: ${{ vars['AWS_REGION'] }} - name: Login to Amazon ECR id: ecr_login @@ -50,13 +53,12 @@ jobs: uses: docker/metadata-action@8e5442c4ef9f78752691e2d8f8d19755c6f78e81 # v5.5.1 with: images: | - ${{ steps.ecr_login.outputs.registry }}/${{ secrets['AWS_ECR'] }} + ${{ steps.ecr_login.outputs.registry }}/${{ vars['AWS_ECR'] }} tags: | type=raw,value=${{ matrix.docker_target }}-${{ github.ref_name }}-${{ matrix.dist }} type=raw,value=${{ matrix.docker_target }}-${{ github.ref_name }} type=raw,value=${{ matrix.docker_target }}-latest,enable=${{ github.ref_name == 'main' }} - ## 🔹 Build and Push to AWS ECR - name: Build and Push (${{ matrix.dist }} ${{ matrix.docker_target }}) id: docker_build uses: docker/build-push-action@2cdde995de11925a030ce8070c3d77a52ffcf1c0 # v5.3.0 @@ -77,6 +79,6 @@ jobs: - name: Generate artifact attestation uses: actions/attest-build-provenance@7668571508540a607bdfd90a87a560489fe372eb #v2.1.0 with: - subject-name: ${{ steps.ecr_login.outputs.registry }}/${{ env.ecr_repository }} + subject-name: ${{ steps.ecr_login.outputs.registry }}/${{ vars['AWS_ECR'] }} subject-digest: ${{ steps.docker_build.outputs.digest }} push-to-registry: true \ No newline at end of file From d1e52d7be656e20a3990cabfbd9795010f2472e1 Mon Sep 17 00:00:00 2001 From: Fabrizio Gattuso Date: Fri, 28 Feb 2025 11:48:43 +0000 Subject: [PATCH 05/12] Add script --- docker/mainnet/gh-attestation/README.md | 71 +++++++++++++++++ .../gh-attestation/check_attestation.sh | 79 +++++++++++++++++++ 2 files changed, 150 insertions(+) create mode 100644 docker/mainnet/gh-attestation/README.md create mode 100644 docker/mainnet/gh-attestation/check_attestation.sh diff --git a/docker/mainnet/gh-attestation/README.md b/docker/mainnet/gh-attestation/README.md new file mode 100644 index 000000000..c33adfe30 --- /dev/null +++ b/docker/mainnet/gh-attestation/README.md @@ -0,0 +1,71 @@ +# Docker Compose with GitHub Attestation Verification + +This script modifies the behavior of `docker-compose` to enforce **GitHub attestation verification** for specific images. It ensures that **only verified images** from the **GitHub Container Registry** are run. If the image is not attested, the script will block execution. + +## Features + +- Enforces **GitHub attestation checks** for images of the format: + - `blockstack/sbtc:signer*` + - `blockstack/sbtc:blocklist-client*` +- Allows bypassing attestation checks via a `.env` configuration. +- Does **not require command-line flags** to skip checks, everything is controlled by the `.env` file. + +## Setup + +### 1. Backup Original Docker Compose + +Make sure to back up your existing `docker-compose` binary: + +```bash +mv /usr/local/bin/docker-compose /usr/local/bin/docker-compose-original +``` + +### 2. Save the Script + +Copy the script `check_attestation.sh` to + +```bash +sudo nano /usr/local/bin/docker-compose +``` + +### 3. Make It Executable + +Ensure the script is executable: + +```bash +chmod +x /usr/local/bin/docker-compose +``` + +### 4. Configure `.env` File + +The script uses the `.env` file for configuration. +- To **disable attestation verification**, set `SKIP_CHECK=true` +- To **enforce attestation verification**, set `SKIP_CHECK=false` (or leave it unset) + +### 5. Run `docker-compose` Normally + +Once the script is installed, you can use `docker-compose` as usual: + +```bash +docker-compose up +docker-compose down +docker-compose ps +``` + +If the script finds an image that requires attestation and it has not been verified, it will block the execution with a message. Otherwise, it will run the `docker-compose` command normally. + +## How It Works + +- The script checks **Docker images** specified in `docker-compose.yml`. +- For images matching `blockstack/sbtc:signer*` or `blockstack/sbtc:blocklist-client*`, the script verifies their **attestation** status using GitHub CLI. +- If the image is **not attested**, the script blocks the execution and outputs an error. +- If the image is verified, it allows the Docker container to run. +- The **attestation check** can be skipped by setting `SKIP_CHECK=true` in the `.env` file. + +## Dependencies + +- **GitHub CLI**: The script will try to login to Github, if an error is triggered you can log in by running: + +```bash +gh auth login +``` diff --git a/docker/mainnet/gh-attestation/check_attestation.sh b/docker/mainnet/gh-attestation/check_attestation.sh new file mode 100644 index 000000000..344863e5a --- /dev/null +++ b/docker/mainnet/gh-attestation/check_attestation.sh @@ -0,0 +1,79 @@ +#!/bin/bash + +# Set strict mode +set -e + +# Load environment variables from .env file if it exists +if [ -f .env ]; then + export $(grep -v '^#' .env | xargs) +fi + +# Default SKIP_CHECK to false if not set in .env +SKIP_CHECK=${SKIP_CHECK:-false} + +# Hardcoded GitHub repository details (OWNER and REPO) +OWNER="blockstack" +REPO="sbtc" + +# Extract images from docker-compose.yml (if exists) +if [[ -f docker-compose.yml ]]; then + IMAGES=$(grep 'image:' docker-compose.yml | awk '{print $2}' | tr -d '"') +else + IMAGES="" +fi + +# Function to check if an image needs attestation +requires_attestation() { + [[ "$1" =~ ^blockstack/sbtc:(signer|blocklist-client).* ]] +} + +# Ensure GitHub CLI is authenticated +if ! gh auth status &> /dev/null; then + echo "❌ GitHub CLI not authenticated! Attempting to log in..." + gh auth login --with-token + if [ $? -ne 0 ]; then + echo "❌ Authentication failed! Please authenticate with 'gh auth login'." + exit 1 + fi +fi + +# If attestation check is enabled, verify only specific images +if [ "$SKIP_CHECK" = false ]; then + for IMAGE in $IMAGES; do + if requires_attestation "$IMAGE"; then + echo "🔍 Checking GitHub attestation for $IMAGE..." + + # Check if the image exists in GitHub Container Registry + DIGEST=$(gh api -H "Accept: application/vnd.github.v3+json" \ + "/orgs/$OWNER/packages/container/sbtc/versions" \ + | jq -r '.[0].metadata.container.tags[] | select(.=="latest")') + + if [ -z "$DIGEST" ]; then + echo "❌ Image not found in GitHub Container Registry!" + exit 1 + fi + + echo "✅ Image found: $IMAGE" + + # Check if the image has a valid GitHub security attestation + ATTESTATIONS=$(gh api \ + -H "Accept: application/vnd.github.v3+json" \ + "/repos/$OWNER/$REPO/dependabot/alerts" \ + | jq '. | length') + + if [ "$ATTESTATIONS" -eq "0" ]; then + echo "❌ No valid attestations found for $IMAGE! Blocking execution." + exit 1 + fi + + echo "✅ Attestation verified for $IMAGE!" + else + echo "ℹī¸ Skipping attestation check for $IMAGE (not in required list)." + fi + done +else + echo "⚠ī¸ Skipping attestation check due to .env setting (SKIP_CHECK=true)." +fi + +# Run the original docker-compose with all arguments +exec /usr/local/bin/docker-compose-original "$@" \ No newline at end of file From 7e10b7657d7253c695b1693f4473b57186bf31a8 Mon Sep 17 00:00:00 2001 From: Fabrizio Gattuso Date: Fri, 28 Feb 2025 14:10:38 +0000 Subject: [PATCH 06/12] Change verify command --- .github/workflows/nightly-image-build.yaml | 3 -- .../gh-attestation/check_attestation.sh | 39 ++++++++++--------- 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/.github/workflows/nightly-image-build.yaml b/.github/workflows/nightly-image-build.yaml index 17a53c4ba..86256445e 100644 --- a/.github/workflows/nightly-image-build.yaml +++ b/.github/workflows/nightly-image-build.yaml @@ -6,9 +6,6 @@ on: workflow_dispatch: schedule: - cron: '0 2 * * *' - push: - branches: - - "feat/add_docker_ecr_registry" permissions: id-token: write diff --git a/docker/mainnet/gh-attestation/check_attestation.sh b/docker/mainnet/gh-attestation/check_attestation.sh index 344863e5a..8e686b2ef 100644 --- a/docker/mainnet/gh-attestation/check_attestation.sh +++ b/docker/mainnet/gh-attestation/check_attestation.sh @@ -22,9 +22,9 @@ else IMAGES="" fi -# Function to check if an image needs attestation +# Function to check if an image needs attestation based on the tag requires_attestation() { - [[ "$1" =~ ^blockstack/sbtc:(signer|blocklist-client).* ]] + [[ "$1" =~ ^blockstack/sbtc:(signer-.*|blocklist-client-.*) ]] } # Ensure GitHub CLI is authenticated @@ -37,36 +37,39 @@ if ! gh auth status &> /dev/null; then fi fi +# Ensure Docker is logged into GHCR +if ! docker info | grep -q 'Server Version'; then + echo "❌ Docker is not authenticated with GHCR. Attempting to log in..." + docker login ghcr.io + if [ $? -ne 0 ]; then + echo "❌ Docker login to GHCR failed! Please log in with 'docker login ghcr.io'." + exit 1 + fi +fi + # If attestation check is enabled, verify only specific images if [ "$SKIP_CHECK" = false ]; then for IMAGE in $IMAGES; do if requires_attestation "$IMAGE"; then echo "🔍 Checking GitHub attestation for $IMAGE..." - # Check if the image exists in GitHub Container Registry - DIGEST=$(gh api -H "Accept: application/vnd.github.v3+json" \ - "/orgs/$OWNER/packages/container/sbtc/versions" \ - | jq -r '.[0].metadata.container.tags[] | select(.=="latest")') + # Extract the tag from the image name (removes the * from the pattern) + TAG=$(echo "$IMAGE" | sed -E 's/.*:(signer-[^*]+|blocklist-client-[^*]+).*/\1/') - if [ -z "$DIGEST" ]; then - echo "❌ Image not found in GitHub Container Registry!" + if [ -z "$TAG" ]; then + echo "❌ Could not extract a valid tag from the image name: $IMAGE" exit 1 fi - echo "✅ Image found: $IMAGE" - - # Check if the image has a valid GitHub security attestation - ATTESTATIONS=$(gh api \ - -H "Accept: application/vnd.github.v3+json" \ - "/repos/$OWNER/$REPO/dependabot/alerts" \ - | jq '. | length') + # Verify attestation using gh attestation + gh attestation verify oci://ghcr.io/$OWNER/$IMAGE:$TAG -R $OWNER/$REPO - if [ "$ATTESTATIONS" -eq "0" ]; then - echo "❌ No valid attestations found for $IMAGE! Blocking execution." + if [ $? -ne 0 ]; then + echo "❌ Attestation verification failed for $IMAGE:$TAG! Blocking execution." exit 1 fi - echo "✅ Attestation verified for $IMAGE!" + echo "✅ Attestation verified for $IMAGE:$TAG!" else echo "ℹī¸ Skipping attestation check for $IMAGE (not in required list)." fi From 7fb2141fa1d2df59fb70b9d3dfd936a2741e36a7 Mon Sep 17 00:00:00 2001 From: Fabrizio Gattuso Date: Wed, 5 Mar 2025 11:38:50 +0100 Subject: [PATCH 07/12] Move to another strategy --- .../Dockerfile.blocklist-client.debian | 1 + .../dockerfiles/Dockerfile.signer.debian | 1 + .../image-build-and-draft-release.yaml | 15 ++ docker/mainnet/gh-attestation/README.md | 129 +++++++++++------- .../gh-attestation/check_attestation.sh | 82 ----------- docker/mainnet/gh-attestation/entry_point.sh | 24 ++++ 6 files changed, 123 insertions(+), 129 deletions(-) delete mode 100644 docker/mainnet/gh-attestation/check_attestation.sh create mode 100644 docker/mainnet/gh-attestation/entry_point.sh diff --git a/.github/actions/dockerfiles/Dockerfile.blocklist-client.debian b/.github/actions/dockerfiles/Dockerfile.blocklist-client.debian index eb174fe67..d8049caa3 100644 --- a/.github/actions/dockerfiles/Dockerfile.blocklist-client.debian +++ b/.github/actions/dockerfiles/Dockerfile.blocklist-client.debian @@ -11,6 +11,7 @@ RUN apt-get install -y --no-install-recommends \ libssl-dev \ make \ protobuf-compiler \ + gh \ npm \ default-jre \ g++ && \ diff --git a/.github/actions/dockerfiles/Dockerfile.signer.debian b/.github/actions/dockerfiles/Dockerfile.signer.debian index 31a00610a..5229798b6 100644 --- a/.github/actions/dockerfiles/Dockerfile.signer.debian +++ b/.github/actions/dockerfiles/Dockerfile.signer.debian @@ -15,6 +15,7 @@ RUN apt-get install -y --no-install-recommends \ make \ protobuf-compiler \ npm \ + gh \ default-jre \ g++ && \ apt-get clean && rm -rf /var/lib/apt/lists/* diff --git a/.github/workflows/image-build-and-draft-release.yaml b/.github/workflows/image-build-and-draft-release.yaml index 920a5966c..8aacf9549 100644 --- a/.github/workflows/image-build-and-draft-release.yaml +++ b/.github/workflows/image-build-and-draft-release.yaml @@ -108,6 +108,21 @@ jobs: subject-name: index.docker.io/${{ env.docker-org }}/sbtc subject-digest: ${{ steps.docker_build.outputs.digest }} push-to-registry: true + + - name: Download artifact attestation + run: | + gh attestation download oci://index.docker.io/${{ env.docker-org }}/sbtc:${{ steps.docker_build.outputs.digest }} -R stacks-network/sbtc + gh attestation trusted-root > trusted_root.jsonl + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Upload Attestation Files as Artifacts + uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 v4.6.1 + with: + name: attestation-files + path: | + ${{ steps.docker_build.outputs.digest }}.jsonl + trusted_root.jsonl release: name: Draft Release diff --git a/docker/mainnet/gh-attestation/README.md b/docker/mainnet/gh-attestation/README.md index c33adfe30..440156b0a 100644 --- a/docker/mainnet/gh-attestation/README.md +++ b/docker/mainnet/gh-attestation/README.md @@ -1,71 +1,106 @@ -# Docker Compose with GitHub Attestation Verification +# **Docker Image with Attestation Verification** -This script modifies the behavior of `docker-compose` to enforce **GitHub attestation verification** for specific images. It ensures that **only verified images** from the **GitHub Container Registry** are run. If the image is not attested, the script will block execution. +This repository provides a Docker image that can be used to run a signer application with **attestation verification**. Before running the application, the image verifies its attestation, ensuring that the image and its contents are trusted. -## Features +## **Features** +- **Attestation Verification**: Verifies the integrity of the Docker image before execution using GitHub's attestation tools. +- **Custom Entry Point**: Uses a custom entry point script to enforce attestation verification before running the signer app. +- **Supports Local Files for Attestation**: Instead of URLs, this setup uses local file paths for the attestation bundle and trusted root. -- Enforces **GitHub attestation checks** for images of the format: - - `blockstack/sbtc:signer*` - - `blockstack/sbtc:blocklist-client*` -- Allows bypassing attestation checks via a `.env` configuration. -- Does **not require command-line flags** to skip checks, everything is controlled by the `.env` file. +--- -## Setup +## **Environment Variables** -### 1. Backup Original Docker Compose +- `TAG`: The tag of the Docker image you want to verify (e.g., `signer-test-attestation`). +- `BUNDLE_PATH`: Local path to the attestation bundle file (e.g., `/path/to/bundle.jsonl`). +- `TRUSTED_ROOT_PATH`: Local path to the trusted root file (e.g., `/path/to/trusted_root.jsonl`). -Make sure to back up your existing `docker-compose` binary: +--- -```bash -mv /usr/local/bin/docker-compose /usr/local/bin/docker-compose-original -``` +## **Example Docker Command:** -### 2. Save the Script - -Copy the script `check_attestation.sh` to +To run your Docker image and perform attestation verification, use the following command: ```bash -sudo nano /usr/local/bin/docker-compose +docker run --rm \ + -e TAG="signer" \ + -e BUNDLE_PATH="/path/to/your/bundle.jsonl" \ + -e TRUSTED_ROOT_PATH="/path/to/your/trusted_root.jsonl" \ + -v /path/to/your/bundle.jsonl:/path/to/your/bundle.jsonl \ + -v /path/to/your/trusted_root.jsonl:/path/to/your/trusted_root.jsonl \ + --entrypoint /entrypoint.sh \ + image_name \ + /usr/local/bin/signer --config /signer-config.toml --migrate-db ``` -### 3. Make It Executable - -Ensure the script is executable: - ```bash -chmod +x /usr/local/bin/docker-compose +docker run --rm \ + -e TAG="blocklist-cli" \ + -e BUNDLE_PATH="/path/to/your/bundle.jsonl" \ + -e TRUSTED_ROOT_PATH="/path/to/your/trusted_root.jsonl" \ + -v /path/to/your/bundle.jsonl:/path/to/your/bundle.jsonl \ + -v /path/to/your/trusted_root.jsonl:/path/to/your/trusted_root.jsonl \ + --entrypoint /entrypoint.sh \ + image_name \ + /usr/local/bin/blocklist-client ``` -### 4. Configure `.env` File +This command will: +1. **Set the environment variables**: + - `TAG="signer"`: This sets the image tag used to identify the Docker image for verification. + - `BUNDLE_PATH="/path/to/your/bundle.jsonl"`: Specifies the local path to the attestation bundle file. + - `TRUSTED_ROOT_PATH="/path/to/your/trusted_root.jsonl"`: Specifies the local path to the trusted root file for the attestation. + +2. **Use `/entrypoint.sh`**: The entrypoint of the Docker image is overridden to run the `entrypoint.sh` script, which performs the attestation verification before running the application. + +3. **Run the Signer Application**: The signer application is started with the provided configuration file (`/signer-config.toml`) and the database will be migrated using the `--migrate-db` flag. + +--- + +## **Example Docker Compose Configuration:** + +```yaml +services: + signer: + image: signer + environment: + - TAG="signer" # Set your specific image tag + - BUNDLE_PATH="/path/to/your/bundle.jsonl" + - TRUSTED_ROOT_PATH="/path/to/your/trusted_root.jsonl" + volumes: + - /path/to/your/bundle.jsonl:/path/to/your/bundle.jsonl + - /path/to/your/trusted_root.jsonl:/path/to/your/trusted_root.jsonl + entrypoint: ["/entrypoint.sh"] + command: ["/usr/local/bin/signer", "--config", "/signer-config.toml", "--migrate-db"] +``` -The script uses the `.env` file for configuration. -- To **disable attestation verification**, set `SKIP_CHECK=true` -- To **enforce attestation verification**, set `SKIP_CHECK=false` (or leave it unset) +```yaml +services: + signer: + image: blocklist-cli + environment: + - TAG="blocklist-cli" # Set your specific image tag + - BUNDLE_PATH="/path/to/your/bundle.jsonl" + - TRUSTED_ROOT_PATH="/path/to/your/trusted_root.jsonl" + volumes: + - /path/to/your/bundle.jsonl:/path/to/your/bundle.jsonl + - /path/to/your/trusted_root.jsonl:/path/to/your/trusted_root.jsonl + entrypoint: ["/entrypoint.sh"] + command: ["/usr/local/bin/blocklist-client"] +``` -### 5. Run `docker-compose` Normally +This will: +1. **Set up the Docker container** with the required environment variables for attestation. +2. **Use `/entrypoint.sh`**: The entry point script checks the attestation and proceeds if verified. +3. **Run the signer application** with the provided config file and migration option. -Once the script is installed, you can use `docker-compose` as usual: +To start the service with Docker Compose, use: ```bash docker-compose up -docker-compose down -docker-compose ps ``` +--- -If the script finds an image that requires attestation and it has not been verified, it will block the execution with a message. Otherwise, it will run the `docker-compose` command normally. +## **Additional Information** -## How It Works - -- The script checks **Docker images** specified in `docker-compose.yml`. -- For images matching `blockstack/sbtc:signer*` or `blockstack/sbtc:blocklist-client*`, the script verifies their **attestation** status using GitHub CLI. -- If the image is **not attested**, the script blocks the execution and outputs an error. -- If the image is verified, it allows the Docker container to run. -- The **attestation check** can be skipped by setting `SKIP_CHECK=true` in the `.env` file. - -## Dependencies - -- **GitHub CLI**: The script will try to login to Github, if an error is triggered you can log in by running: - -```bash -gh auth login -``` +The image requires the GitHub CLI (`gh`) to verify the attestation. \ No newline at end of file diff --git a/docker/mainnet/gh-attestation/check_attestation.sh b/docker/mainnet/gh-attestation/check_attestation.sh deleted file mode 100644 index 8e686b2ef..000000000 --- a/docker/mainnet/gh-attestation/check_attestation.sh +++ /dev/null @@ -1,82 +0,0 @@ -#!/bin/bash - -# Set strict mode -set -e - -# Load environment variables from .env file if it exists -if [ -f .env ]; then - export $(grep -v '^#' .env | xargs) -fi - -# Default SKIP_CHECK to false if not set in .env -SKIP_CHECK=${SKIP_CHECK:-false} - -# Hardcoded GitHub repository details (OWNER and REPO) -OWNER="blockstack" -REPO="sbtc" - -# Extract images from docker-compose.yml (if exists) -if [[ -f docker-compose.yml ]]; then - IMAGES=$(grep 'image:' docker-compose.yml | awk '{print $2}' | tr -d '"') -else - IMAGES="" -fi - -# Function to check if an image needs attestation based on the tag -requires_attestation() { - [[ "$1" =~ ^blockstack/sbtc:(signer-.*|blocklist-client-.*) ]] -} - -# Ensure GitHub CLI is authenticated -if ! gh auth status &> /dev/null; then - echo "❌ GitHub CLI not authenticated! Attempting to log in..." - gh auth login --with-token - if [ $? -ne 0 ]; then - echo "❌ Authentication failed! Please authenticate with 'gh auth login'." - exit 1 - fi -fi - -# Ensure Docker is logged into GHCR -if ! docker info | grep -q 'Server Version'; then - echo "❌ Docker is not authenticated with GHCR. Attempting to log in..." - docker login ghcr.io - if [ $? -ne 0 ]; then - echo "❌ Docker login to GHCR failed! Please log in with 'docker login ghcr.io'." - exit 1 - fi -fi - -# If attestation check is enabled, verify only specific images -if [ "$SKIP_CHECK" = false ]; then - for IMAGE in $IMAGES; do - if requires_attestation "$IMAGE"; then - echo "🔍 Checking GitHub attestation for $IMAGE..." - - # Extract the tag from the image name (removes the * from the pattern) - TAG=$(echo "$IMAGE" | sed -E 's/.*:(signer-[^*]+|blocklist-client-[^*]+).*/\1/') - - if [ -z "$TAG" ]; then - echo "❌ Could not extract a valid tag from the image name: $IMAGE" - exit 1 - fi - - # Verify attestation using gh attestation - gh attestation verify oci://ghcr.io/$OWNER/$IMAGE:$TAG -R $OWNER/$REPO - - if [ $? -ne 0 ]; then - echo "❌ Attestation verification failed for $IMAGE:$TAG! Blocking execution." - exit 1 - fi - - echo "✅ Attestation verified for $IMAGE:$TAG!" - else - echo "ℹī¸ Skipping attestation check for $IMAGE (not in required list)." - fi - done -else - echo "⚠ī¸ Skipping attestation check due to .env setting (SKIP_CHECK=true)." -fi - -# Run the original docker-compose with all arguments -exec /usr/local/bin/docker-compose-original "$@" \ No newline at end of file diff --git a/docker/mainnet/gh-attestation/entry_point.sh b/docker/mainnet/gh-attestation/entry_point.sh new file mode 100644 index 000000000..4f8a78950 --- /dev/null +++ b/docker/mainnet/gh-attestation/entry_point.sh @@ -0,0 +1,24 @@ +#!/bin/bash +set -e # Exit on error + +# Ensure required environment variables are set +if [[ -z "$BUNDLE_PATH" || -z "$TRUSTED_ROOT_PATH" ]]; then + echo "❌ ERROR: BUNDLE_PATH and TRUSTED_ROOT_PATH environment variables must be set." + exit 1 +fi + +# Define the image and repo (since they are fixed) +IMAGE="index.docker.io/blockstack/sbtc:$TAG" # You can pass $TAG as environment variable if it's dynamic. +REPO="stacks-network/sbtc" + +# Verifying attestation +echo "✅ Verifying attestation for image: $IMAGE..." +gh attestation verify \ + oci://$IMAGE \ + -R "$REPO" \ + --bundle "$BUNDLE_PATH" \ + --custom-trusted-root "$TRUSTED_ROOT_PATH" + +# If verification succeeds, run the signer app +echo "🎉 Attestation verified successfully! Running the signer..." +exec "$@" \ No newline at end of file From 6d3bab8b062efae5e99d9c73fd5d42026e552a88 Mon Sep 17 00:00:00 2001 From: Fabrizio Gattuso Date: Wed, 5 Mar 2025 12:25:14 +0100 Subject: [PATCH 08/12] Fix upload --- .github/workflows/image-build-and-draft-release.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/image-build-and-draft-release.yaml b/.github/workflows/image-build-and-draft-release.yaml index 8aacf9549..930cca638 100644 --- a/.github/workflows/image-build-and-draft-release.yaml +++ b/.github/workflows/image-build-and-draft-release.yaml @@ -111,13 +111,13 @@ jobs: - name: Download artifact attestation run: | - gh attestation download oci://index.docker.io/${{ env.docker-org }}/sbtc:${{ steps.docker_build.outputs.digest }} -R stacks-network/sbtc + gh attestation download oci://index.docker.io/${{ env.docker-org }}/sbtc:${{ matrix.docker_target }}-${{ github.ref_name }} -R stacks-network/sbtc gh attestation trusted-root > trusted_root.jsonl env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - name: Upload Attestation Files as Artifacts - uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 v4.6.1 + uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 #v4.6.1 with: name: attestation-files path: | From 872f8319f6febbeb7e8bcae57dfa81db964e1c2f Mon Sep 17 00:00:00 2001 From: Fabrizio Gattuso Date: Wed, 5 Mar 2025 12:37:00 +0100 Subject: [PATCH 09/12] Fix comment --- docker/mainnet/gh-attestation/entry_point.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker/mainnet/gh-attestation/entry_point.sh b/docker/mainnet/gh-attestation/entry_point.sh index 4f8a78950..d496ac40d 100644 --- a/docker/mainnet/gh-attestation/entry_point.sh +++ b/docker/mainnet/gh-attestation/entry_point.sh @@ -8,7 +8,7 @@ if [[ -z "$BUNDLE_PATH" || -z "$TRUSTED_ROOT_PATH" ]]; then fi # Define the image and repo (since they are fixed) -IMAGE="index.docker.io/blockstack/sbtc:$TAG" # You can pass $TAG as environment variable if it's dynamic. +IMAGE="index.docker.io/blockstack/sbtc:$TAG" # You can pass $TAG as environment variable REPO="stacks-network/sbtc" # Verifying attestation From 4c17d838978d3bf8467ed93c6fe377c735334239 Mon Sep 17 00:00:00 2001 From: Fabrizio Gattuso Date: Tue, 11 Mar 2025 13:16:20 +0100 Subject: [PATCH 10/12] Improvements in the readme --- docker/mainnet/gh-attestation/README.md | 9 +++------ .../gh-attestation/{entry_point.sh => entrypoint.sh} | 0 2 files changed, 3 insertions(+), 6 deletions(-) rename docker/mainnet/gh-attestation/{entry_point.sh => entrypoint.sh} (100%) diff --git a/docker/mainnet/gh-attestation/README.md b/docker/mainnet/gh-attestation/README.md index 440156b0a..698cf4be4 100644 --- a/docker/mainnet/gh-attestation/README.md +++ b/docker/mainnet/gh-attestation/README.md @@ -46,12 +46,9 @@ docker run --rm \ ``` This command will: -1. **Set the environment variables**: - - `TAG="signer"`: This sets the image tag used to identify the Docker image for verification. - - `BUNDLE_PATH="/path/to/your/bundle.jsonl"`: Specifies the local path to the attestation bundle file. - - `TRUSTED_ROOT_PATH="/path/to/your/trusted_root.jsonl"`: Specifies the local path to the trusted root file for the attestation. +1. **Set the environment variables**: `TAG`, `BUNDLE_PATH` and `TRUSTED_ROOT_PATH` -2. **Use `/entrypoint.sh`**: The entrypoint of the Docker image is overridden to run the `entrypoint.sh` script, which performs the attestation verification before running the application. +2. **Use [/entrypoint.sh](/docker/mainnet/gh-attestation/entrypoint.sh)**: The entrypoint of the Docker image is overridden to run the `entrypoint.sh` script, which performs the attestation verification before running the application. 3. **Run the Signer Application**: The signer application is started with the provided configuration file (`/signer-config.toml`) and the database will be migrated using the `--migrate-db` flag. @@ -91,7 +88,7 @@ services: This will: 1. **Set up the Docker container** with the required environment variables for attestation. -2. **Use `/entrypoint.sh`**: The entry point script checks the attestation and proceeds if verified. +2. **Use [/entrypoint.sh](/docker/mainnet/gh-attestation/entrypoint.sh)**: The entry point script checks the attestation and proceeds if verified. 3. **Run the signer application** with the provided config file and migration option. To start the service with Docker Compose, use: diff --git a/docker/mainnet/gh-attestation/entry_point.sh b/docker/mainnet/gh-attestation/entrypoint.sh similarity index 100% rename from docker/mainnet/gh-attestation/entry_point.sh rename to docker/mainnet/gh-attestation/entrypoint.sh From ec6fbf0107fb34daeb32e296c243d902542a1ce3 Mon Sep 17 00:00:00 2001 From: Fabrizio Gattuso Date: Wed, 12 Mar 2025 14:51:54 +0100 Subject: [PATCH 11/12] Fix file name on pipeline --- .github/workflows/image-build-and-draft-release.yaml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/image-build-and-draft-release.yaml b/.github/workflows/image-build-and-draft-release.yaml index 930cca638..2b92c86db 100644 --- a/.github/workflows/image-build-and-draft-release.yaml +++ b/.github/workflows/image-build-and-draft-release.yaml @@ -112,6 +112,7 @@ jobs: - name: Download artifact attestation run: | gh attestation download oci://index.docker.io/${{ env.docker-org }}/sbtc:${{ matrix.docker_target }}-${{ github.ref_name }} -R stacks-network/sbtc + mv "${{ steps.docker_build.outputs.digest }}.jsonl" "$(echo "${{ steps.docker_build.outputs.digest }}.jsonl" | tr ':' '_')" gh attestation trusted-root > trusted_root.jsonl env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -121,7 +122,7 @@ jobs: with: name: attestation-files path: | - ${{ steps.docker_build.outputs.digest }}.jsonl + $(echo "${{ steps.docker_build.outputs.digest }}.jsonl" | tr ':' '_') trusted_root.jsonl release: From 887a6c3e053ff4b15a6d8a206df33778a4cf54d7 Mon Sep 17 00:00:00 2001 From: Fabrizio Gattuso Date: Wed, 12 Mar 2025 16:02:39 +0100 Subject: [PATCH 12/12] Fix workflow --- .github/workflows/image-build-and-draft-release.yaml | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/.github/workflows/image-build-and-draft-release.yaml b/.github/workflows/image-build-and-draft-release.yaml index 2b92c86db..c58ff6a95 100644 --- a/.github/workflows/image-build-and-draft-release.yaml +++ b/.github/workflows/image-build-and-draft-release.yaml @@ -112,7 +112,13 @@ jobs: - name: Download artifact attestation run: | gh attestation download oci://index.docker.io/${{ env.docker-org }}/sbtc:${{ matrix.docker_target }}-${{ github.ref_name }} -R stacks-network/sbtc - mv "${{ steps.docker_build.outputs.digest }}.jsonl" "$(echo "${{ steps.docker_build.outputs.digest }}.jsonl" | tr ':' '_')" + + # Rename the attestation bundle (replace ":" with "_") + ATTESTATION_FILE="$(echo "${{ steps.docker_build.outputs.digest }}.jsonl" | tr ':' '_')" + mv "${{ steps.docker_build.outputs.digest }}.jsonl" "$ATTESTATION_FILE" + echo "ATTESTATION_FILE=$ATTESTATION_FILE" >> $GITHUB_ENV + + # Generate trusted root gh attestation trusted-root > trusted_root.jsonl env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -121,8 +127,9 @@ jobs: uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 #v4.6.1 with: name: attestation-files + overwrite: true path: | - $(echo "${{ steps.docker_build.outputs.digest }}.jsonl" | tr ':' '_') + ${{ env.ATTESTATION_FILE }} trusted_root.jsonl release: