Skip to content

Commit dbd77ce

Browse files
[patch] Harden verified image publication (#17)
1 parent 481df51 commit dbd77ce

4 files changed

Lines changed: 120 additions & 3 deletions

File tree

.github/workflows/actionlint.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ on:
55
paths:
66
- ".github/actions/**"
77
- ".github/workflows/**"
8+
- "scripts/test-image-workflow.sh"
89
push:
910
branches:
1011
- main
1112
paths:
1213
- ".github/actions/**"
1314
- ".github/workflows/**"
15+
- "scripts/test-image-workflow.sh"
1416

1517
permissions:
1618
contents: read
@@ -33,3 +35,6 @@ jobs:
3335

3436
- name: Validate workflows
3537
run: go run github.com/rhysd/actionlint/cmd/actionlint@v1.7.12
38+
39+
- name: Validate image publication contract
40+
run: bash scripts/test-image-workflow.sh

.github/workflows/build-push.yaml

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,73 @@ name: build-push
22

33
on:
44
workflow_dispatch:
5+
pull_request:
6+
paths:
7+
- Dockerfile
8+
- .github/workflows/build-push.yaml
9+
- scripts/test-image-workflow.sh
510
push:
611
branches:
712
- main
813
paths:
914
- Dockerfile
1015
- .github/workflows/build-push.yaml
16+
- scripts/test-image-workflow.sh
17+
18+
permissions:
19+
contents: read
1120

1221
jobs:
22+
image-check:
23+
if: github.event_name == 'pull_request'
24+
strategy:
25+
fail-fast: false
26+
matrix:
27+
architecture:
28+
- runner: ubuntu-24.04
29+
platform: linux/amd64
30+
suffix: amd64
31+
- runner: ubuntu-24.04-arm
32+
platform: linux/arm64
33+
suffix: arm64
34+
runs-on: ${{ matrix.architecture.runner }}
35+
steps:
36+
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
37+
with:
38+
persist-credentials: false
39+
40+
- uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 # v3
41+
42+
- name: Build native image without credentials
43+
uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 # v6
44+
with:
45+
context: .
46+
load: true
47+
platforms: ${{ matrix.architecture.platform }}
48+
provenance: false
49+
push: false
50+
tags: terraform-linux-packages:ci-${{ matrix.architecture.suffix }}
51+
52+
- name: Scan native image
53+
uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
54+
with:
55+
image-ref: terraform-linux-packages:ci-${{ matrix.architecture.suffix }}
56+
format: table
57+
exit-code: "1"
58+
ignore-unfixed: true
59+
severity: HIGH,CRITICAL
60+
vuln-type: os,library
61+
1362
run:
14-
uses: libops/.github/.github/workflows/build-push.yaml@d7f0ba06b4c8a1f8559892ea65a0b1af4d024dd3 # main
63+
if: github.ref == 'refs/heads/main'
64+
uses: libops/.github/.github/workflows/build-push.yaml@a86300fb8020d0f7141bb9f833d89b5dbd7aa4d7 # guarded-signed-image-publisher
65+
with:
66+
ref: ${{ github.sha }}
67+
expected-main-sha: ${{ github.ref == 'refs/heads/main' && github.sha || '' }}
68+
scan: true
69+
sign: true
70+
certificate-identity: https://github.com/libops/.github/.github/workflows/build-push.yaml@a86300fb8020d0f7141bb9f833d89b5dbd7aa4d7
1571
permissions:
1672
contents: read
1773
packages: write
18-
secrets: inherit
74+
id-token: write

Dockerfile

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,15 @@
1-
FROM gcr.io/google.com/cloudsdktool/google-cloud-cli:slim@sha256:61cdaee8f89a2500c8e93fac2499d98a5b7692fcc3d7d69abbef48200477cb71
1+
FROM gcr.io/google.com/cloudsdktool/google-cloud-cli:slim@sha256:288bee65409ada9168944d1af8050247b556f8c0aeef97a3889ea05ee6294d7c
2+
3+
ENV CLOUDSDK_STORAGE_USE_GCLOUD_CRC32C=false
24

35
RUN apt-get update \
6+
&& apt-get upgrade -y \
47
&& apt-get install -y --no-install-recommends aptly createrepo-c gnupg ca-certificates dpkg-dev rpm \
8+
&& if [ -x /usr/lib/google-cloud-sdk/platform/bundledpythonunix/bin/python3 ]; then \
9+
/usr/lib/google-cloud-sdk/platform/bundledpythonunix/bin/python3 -m pip install \
10+
--no-cache-dir \
11+
--only-binary=:all: \
12+
cryptography==48.0.1; \
13+
fi \
14+
&& rm -f /usr/lib/google-cloud-sdk/bin/gcloud-crc32c \
515
&& rm -rf /var/lib/apt/lists/*

scripts/test-image-workflow.sh

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5+
DOCKERFILE="${ROOT_DIR}/Dockerfile"
6+
WORKFLOW="${ROOT_DIR}/.github/workflows/build-push.yaml"
7+
SHARED_PUBLISHER_SHA="a86300fb8020d0f7141bb9f833d89b5dbd7aa4d7"
8+
9+
require_text() {
10+
local value="$1"
11+
if ! grep -Fq -- "$value" "$WORKFLOW"; then
12+
printf 'image workflow must contain: %s\n' "$value" >&2
13+
return 1
14+
fi
15+
}
16+
17+
forbid_text() {
18+
local value="$1"
19+
if grep -Fq -- "$value" "$WORKFLOW"; then
20+
printf 'image workflow must not contain: %s\n' "$value" >&2
21+
return 1
22+
fi
23+
}
24+
25+
require_text "pull_request:"
26+
require_text "if: github.event_name == 'pull_request'"
27+
require_text "if: github.ref == 'refs/heads/main'"
28+
require_text "Build native image without credentials"
29+
require_text "libops/.github/.github/workflows/build-push.yaml@${SHARED_PUBLISHER_SHA}"
30+
require_text 'ref: ${{ github.sha }}'
31+
require_text "expected-main-sha: \${{ github.ref == 'refs/heads/main' && github.sha || '' }}"
32+
require_text "scan: true"
33+
require_text "sign: true"
34+
require_text "certificate-identity: https://github.com/libops/.github/.github/workflows/build-push.yaml@${SHARED_PUBLISHER_SHA}"
35+
require_text "packages: write"
36+
require_text "id-token: write"
37+
38+
forbid_text "build-push.yaml@main"
39+
forbid_text "build-push-ghcr.yaml"
40+
forbid_text "secrets: inherit"
41+
forbid_text "docker-registry:"
42+
forbid_text "additional-gar-registry:"
43+
44+
grep -Fq 'CLOUDSDK_STORAGE_USE_GCLOUD_CRC32C=false' "$DOCKERFILE"
45+
grep -Fq 'cryptography==48.0.1' "$DOCKERFILE"
46+
grep -Fq 'rm -f /usr/lib/google-cloud-sdk/bin/gcloud-crc32c' "$DOCKERFILE"

0 commit comments

Comments
 (0)