security: 🛡️ Harden GitHub Actions workflows (#349)

## Summary

This PR addresses several security vulnerabilities and applies hardening
measures to the GitHub Actions workflows:

- **Replace `secrets: inherit` with explicit secret passing** - Prevents
unnecessary exposure of all repository secrets to called workflows
- **Add SHA256 checksum verification for downloaded binaries** -
Protects against supply chain attacks via compromised upstream releases
- **Add GitHub Environment protections for release workflows** -
Requires approval before publishing to Docker Hub or creating releases
- **Add explicit minimal permissions to all workflows** - Follows
principle of least privilege, removes unnecessary `packages: write` from
CI.yml

## Changes by Category

### 1. Explicit Secret Passing
| Workflow | Before | After |
|----------|--------|-------|
| CI.yml → docker-build-ci | `secrets: inherit` | No secrets
(GITHUB_TOKEN is automatic) |
| CI.yml → docker-build-release | `secrets: inherit` | Explicit
`DOCKERHUB_USERNAME`, `DOCKERHUB_TOKEN` |
| CI.yml → e2e-tests | `secrets: inherit` | No secrets (GITHUB_TOKEN is
automatic) |

### 2. Binary Checksum Verification
| Workflow | Binary | SHA256 |
|----------|--------|--------|
| task-rust-lint.yml | taplo 0.8.1 | `c62baa73c9d7c1572...` |
| task-e2e.yml | kurtosis 1.11.99 | `5e88e98c1b255362...` |

### 3. Environment Protections
| Workflow | Job | Environment |
|----------|-----|-------------|
| task-docker-release.yml | build-test-push | `production` |
| task-publish-binary.yml | publish-draft-release | `releases` |
| task-publish-binary.yml | docker-release-candidate | `production` |
| task-publish-runtime.yml | publish-draft-release | `releases` |

### 4. Explicit Permissions
All 14 workflow files now have explicit `permissions:` blocks with
minimal required access.

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Ahmad Kaouk <56095276+ahmadkaouk@users.noreply.github.com>
This commit is contained in:
Steve Degosserie 2025-12-12 10:52:50 +01:00 committed by GitHub
parent 0cd52a0937
commit 746fce9328
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
14 changed files with 81 additions and 4 deletions

View file

@ -11,9 +11,10 @@ on:
pull_request:
branches: [main]
# Explicit minimal permissions
# Note: Reusable workflows define their own permissions
permissions:
contents: read
packages: write
concurrency:
group: pr-checks-${{ github.workflow }}-${{ github.head_ref || github.run_id }}
@ -46,14 +47,16 @@ jobs:
docker-build-ci:
needs: [build-operator]
uses: ./.github/workflows/task-docker-ci.yml
secrets: inherit
# Note: GITHUB_TOKEN is automatically available to reusable workflows
with:
binary-hash: ${{ needs.build-operator.outputs.binary-hash }}
docker-build-release:
if: github.ref == 'refs/heads/main'
uses: ./.github/workflows/task-docker-release.yml
secrets: inherit
secrets:
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
DOCKERHUB_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
moonwall-tests:
needs: [build-operator]
@ -65,6 +68,6 @@ jobs:
e2e-tests:
needs: [docker-build-ci]
uses: ./.github/workflows/task-e2e.yml
secrets: inherit
# Note: GITHUB_TOKEN is automatically available to reusable workflows
with:
image-tag: ${{ needs.docker-build-ci.outputs.image-tag }}

View file

@ -10,6 +10,11 @@ on:
description: "The hash of the operator binary"
value: ${{ jobs.build-node.outputs.binary-hash }}
# Explicit minimal permissions
permissions:
contents: read
actions: write # Required for uploading artifacts
jobs:
build-node:
outputs:

View file

@ -14,6 +14,10 @@ on:
required: true
type: string
# Explicit minimal permissions
permissions:
contents: read
defaults:
run:
shell: bash

View file

@ -17,6 +17,13 @@ on:
type: boolean
default: false
workflow_call:
secrets:
DOCKERHUB_USERNAME:
description: "Docker Hub username"
required: true
DOCKERHUB_TOKEN:
description: "Docker Hub access token"
required: true
outputs:
image-tag:
description: "The tag portion of the docker image (without registry)"
@ -33,6 +40,8 @@ concurrency:
jobs:
build-test-push:
runs-on: ubuntu-latest
# Require approval before publishing to Docker Hub
environment: production
outputs:
image-tag: ${{ steps.extract_tag.outputs.image-tag }}

View file

@ -60,6 +60,9 @@ jobs:
- name: Install Kurtosis
run: |
# Install Kurtosis locally without sudo
# SHA256 checksum for patched kurtosis binary (stiiifff fork v1.11.99)
KURTOSIS_SHA256="5e88e98c1b255362268b4c385cdb6bbba7e82b333c4b2c05bc0bff7de0560b2a"
if ! command -v kurtosis &> /dev/null; then
echo "Installing Kurtosis $KURTOSIS_VERSION locally"
mkdir -p ~/.local/bin
@ -68,6 +71,12 @@ jobs:
# rm kurtosis-cli.tar.gz
# For now, we use a patched version of Kurtosis CLI & Engine that supports Podman properly
wget -q -O ~/.local/bin/kurtosis https://github.com/stiiifff/kurtosis/releases/download/1.11.99/kurtosis
# Verify checksum before making executable
echo "Verifying kurtosis checksum..."
echo "${KURTOSIS_SHA256} $HOME/.local/bin/kurtosis" | sha256sum -c -
chmod +x ~/.local/bin/kurtosis
echo "$HOME/.local/bin" >> $GITHUB_PATH
export PATH="$HOME/.local/bin:$PATH"
else

View file

@ -9,6 +9,10 @@ on:
workflow_dispatch:
workflow_call:
# Explicit minimal permissions
permissions:
contents: read
env:
FOUNDRY_PROFILE: ci

View file

@ -9,6 +9,11 @@ on:
required: true
type: string
# Explicit minimal permissions
permissions:
contents: read
actions: write # Required for uploading artifacts
jobs:
moonwall:
runs-on: ubuntu-latest

View file

@ -16,6 +16,7 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: read
actions: write # Required for uploading artifacts
steps:
- name: Checkout
uses: actions/checkout@v5
@ -35,6 +36,7 @@ jobs:
needs: ["prepare-sources"]
permissions:
contents: read
actions: write # Required for uploading artifacts
strategy:
matrix:
cpu: ["x86-64", "skylake", "znver3"]
@ -52,6 +54,8 @@ jobs:
publish-draft-release:
runs-on: ubuntu-latest
# Require approval before creating release drafts
environment: releases
permissions:
contents: write
needs: ["build-binary"]
@ -108,6 +112,8 @@ jobs:
docker-release-candidate:
runs-on: ubuntu-latest
# Require approval before publishing RC images to Docker Hub
environment: production
needs: ["build-binary"]
steps:
- name: Checkout

View file

@ -18,6 +18,7 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: read
actions: write # Required for uploading artifacts
steps:
- uses: actions/checkout@v5
- name: Upload scripts
@ -49,6 +50,7 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: read
actions: write # Required for uploading artifacts
strategy:
matrix:
chain: ["stagenet", "testnet", "mainnet"]
@ -104,6 +106,8 @@ jobs:
####### Prepare the release draft #######
publish-draft-release:
runs-on: ubuntu-latest
# Require approval before creating release drafts
environment: releases
permissions:
contents: write
needs: ["setup-scripts", "build-srtool-runtimes"]

View file

@ -14,6 +14,10 @@ on:
description: set to pull_request number to execute on external pr
required: false
# Explicit minimal permissions
permissions:
contents: read
env:
CARGO_TERM_COLOR: always
WORKING_DIR: operator
@ -77,11 +81,19 @@ jobs:
run: |
# Install taplo in user space without sudo
TAPLO_VERSION="0.8.1"
# SHA256 checksum for taplo-full-linux-x86_64.gz (decompressed binary)
TAPLO_SHA256="c62baa73c9d7c1572047b1a502ca805e1372e5db7c9a935532f702f12d6115ce"
if ! command -v taplo &> /dev/null || [[ $(taplo --version | grep -oP '\d+\.\d+\.\d+' | head -1) != "$TAPLO_VERSION" ]]; then
echo "Installing taplo $TAPLO_VERSION in ~/.local/bin"
mkdir -p ~/.local/bin
curl -Ls "https://github.com/tamasfe/taplo/releases/download/${TAPLO_VERSION}/taplo-full-linux-x86_64.gz" | \
gzip -d > ~/.local/bin/taplo
# Verify checksum before making executable
echo "Verifying taplo checksum..."
echo "${TAPLO_SHA256} $HOME/.local/bin/taplo" | sha256sum -c -
chmod +x ~/.local/bin/taplo
echo "$HOME/.local/bin" >> $GITHUB_PATH
export PATH="$HOME/.local/bin:$PATH"

View file

@ -10,6 +10,10 @@ on:
workflow_dispatch:
workflow_call:
# Explicit minimal permissions
permissions:
contents: read
jobs:
all-rust-tests:
name: Run all Operator Rust tests (/w partitioning)

View file

@ -4,6 +4,10 @@ on:
workflow_dispatch:
workflow_call:
# Explicit minimal permissions
permissions:
contents: read
jobs:
generate-wagmi:
runs-on: ubuntu-latest

View file

@ -4,6 +4,10 @@ on:
workflow_dispatch:
workflow_call:
# Explicit minimal permissions
permissions:
contents: read
jobs:
typecheck:
runs-on: ubuntu-latest

View file

@ -14,6 +14,10 @@ on:
- 'Cargo.toml'
- 'Cargo.lock'
# Explicit minimal permissions
permissions:
contents: read
jobs:
audit:
runs-on: ubuntu-latest