Skip to content

Commit fd529a3

Browse files
committed
fix(ci): verify GHCR mirror platform digests
1 parent e487df4 commit fd529a3

11 files changed

Lines changed: 33 additions & 24 deletions

File tree

.github/scripts/mirror-dhi-image.sh

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
#!/usr/bin/env bash
22
set -euo pipefail
33

4-
if [ "$#" -ne 5 ]; then
5-
echo "usage: $0 <name> <source-ref> <target-ref> <expected-index-digest> <required-arches>" >&2
4+
if [ "$#" -ne 6 ]; then
5+
echo "usage: $0 <name> <source-ref> <target-ref> <expected-source-index-digest> <expected-target-index-digest|auto> <required-arches>" >&2
66
exit 2
77
fi
88

99
name="$1"
1010
source_ref="$2"
1111
target_ref="$3"
12-
expected_digest="$4"
13-
required_arches_csv="$5"
12+
expected_source_digest="$4"
13+
expected_target_digest="$5"
14+
required_arches_csv="$6"
1415

1516
tmpdir="$(mktemp -d)"
1617
trap 'rm -rf "$tmpdir"' EXIT
@@ -25,8 +26,8 @@ target_raw="$tmpdir/target.json"
2526
echo "::group::inspect source ${name}"
2627
skopeo inspect --raw "docker://${source_ref}" >"$source_raw"
2728
source_digest="$(digest_file "$source_raw")"
28-
if [ "$source_digest" != "$expected_digest" ]; then
29-
echo "source digest mismatch for ${name}: expected ${expected_digest}, got ${source_digest}" >&2
29+
if [ "$source_digest" != "$expected_source_digest" ]; then
30+
echo "source digest mismatch for ${name}: expected ${expected_source_digest}, got ${source_digest}" >&2
3031
exit 1
3132
fi
3233

@@ -46,14 +47,15 @@ echo "source ${name} digest ${source_digest} contains ${required_arches_csv}"
4647
echo "::endgroup::"
4748

4849
echo "::group::copy ${name}"
49-
skopeo copy --retry-times 3 --all --preserve-digests "docker://${source_ref}" "docker://${target_ref}"
50+
skopeo copy --retry-times 3 --all "docker://${source_ref}" "docker://${target_ref}"
5051
echo "::endgroup::"
5152

5253
echo "::group::verify target ${name}"
5354
skopeo inspect --raw "docker://${target_ref}" >"$target_raw"
5455
target_digest="$(digest_file "$target_raw")"
55-
if [ "$target_digest" != "$expected_digest" ]; then
56-
echo "target digest mismatch for ${name}: expected ${expected_digest}, got ${target_digest}" >&2
56+
echo "target ${name} index digest ${target_digest}"
57+
if [ "$expected_target_digest" != "auto" ] && [ "$target_digest" != "$expected_target_digest" ]; then
58+
echo "target digest mismatch for ${name}: expected ${expected_target_digest}, got ${target_digest}" >&2
5759
exit 1
5860
fi
5961

.github/workflows/mirror-hardened-images.yml

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,15 @@ jobs:
2929
source: dhi.io/oauth2-proxy@sha256:8f4e89762735e7ec7c3f1bbdd5da4dcd55358db8c3278bfbc2e46a7f86ab7d9e
3030
target: ghcr.io/terion-name/terrarium-dhi-oauth2-proxy:7.15.2-debian13
3131
package: terrarium-dhi-oauth2-proxy
32-
digest: sha256:8f4e89762735e7ec7c3f1bbdd5da4dcd55358db8c3278bfbc2e46a7f86ab7d9e
32+
source_digest: sha256:8f4e89762735e7ec7c3f1bbdd5da4dcd55358db8c3278bfbc2e46a7f86ab7d9e
33+
target_digest: auto
3334
arches: amd64,arm64
3435
- name: postgres
3536
source: dhi.io/postgres@sha256:ae0f0ac1f942ff7898bb217e599cc488b5c7a2611a0957daae44c00584a59714
3637
target: ghcr.io/terion-name/terrarium-dhi-postgres:17.9-alpine3.22-fips
3738
package: terrarium-dhi-postgres
38-
digest: sha256:ae0f0ac1f942ff7898bb217e599cc488b5c7a2611a0957daae44c00584a59714
39+
source_digest: sha256:ae0f0ac1f942ff7898bb217e599cc488b5c7a2611a0957daae44c00584a59714
40+
target_digest: auto
3941
arches: amd64,arm64
4042
env:
4143
REGISTRY_AUTH_FILE: /tmp/terrarium-containers-auth.json
@@ -83,7 +85,8 @@ jobs:
8385
"${{ matrix.name }}"
8486
"${{ matrix.source }}"
8587
"${{ matrix.target }}"
86-
"${{ matrix.digest }}"
88+
"${{ matrix.source_digest }}"
89+
"${{ matrix.target_digest }}"
8790
"${{ matrix.arches }}"
8891
8992
- name: Make GHCR package public when permitted

ansible/roles/idp_zitadel/defaults/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ terrarium_docker_hardened_images: auto
1515
terrarium_docker_hardened_image_mirrors: true
1616
terrarium_zitadel_postgres_image: ""
1717
terrarium_zitadel_postgres_image_hardened: "dhi.io/postgres:17.9-alpine3.22-fips@sha256:ae0f0ac1f942ff7898bb217e599cc488b5c7a2611a0957daae44c00584a59714"
18-
terrarium_zitadel_postgres_image_mirror: "ghcr.io/terion-name/terrarium-dhi-postgres:17.9-alpine3.22-fips@sha256:ae0f0ac1f942ff7898bb217e599cc488b5c7a2611a0957daae44c00584a59714"
18+
terrarium_zitadel_postgres_image_mirror: "ghcr.io/terion-name/terrarium-dhi-postgres:17.9-alpine3.22-fips"
1919
terrarium_zitadel_postgres_image_fallback: "postgres:17.9-alpine3.22@sha256:034839bd88128360cda25496ebdb1471e24a4aa09b937160c73df2bb51126308"
2020
terrarium_zitadel_pat_expiration: "2099-01-01T00:00:00Z"
2121
terrarium_zitadel_project_name: Terrarium

ansible/roles/oauth2_proxy/defaults/main.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ terrarium_docker_hardened_images: auto
55
terrarium_docker_hardened_image_mirrors: true
66
terrarium_oauth2_proxy_image: ""
77
terrarium_oauth2_proxy_image_hardened: "dhi.io/oauth2-proxy:7.15.2-debian13@sha256:8f4e89762735e7ec7c3f1bbdd5da4dcd55358db8c3278bfbc2e46a7f86ab7d9e"
8-
terrarium_oauth2_proxy_image_mirror: "ghcr.io/terion-name/terrarium-dhi-oauth2-proxy:7.15.2-debian13@sha256:8f4e89762735e7ec7c3f1bbdd5da4dcd55358db8c3278bfbc2e46a7f86ab7d9e"
8+
terrarium_oauth2_proxy_image_mirror: "ghcr.io/terion-name/terrarium-dhi-oauth2-proxy:7.15.2-debian13"
99
terrarium_oauth2_proxy_image_fallback: "quay.io/oauth2-proxy/oauth2-proxy:v7.15.2@sha256:aa0bd8dd5ab0c78e4c91c92755ad573a5f92241f88138b4141b8ec803463b4fd"
1010
terrarium_oauth2_proxy_uid: "65532"
1111
terrarium_oauth2_proxy_gid: "65532"

ansible/site.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,11 @@
5454
terrarium_docker_hardened_image_mirrors: true
5555
terrarium_oauth2_proxy_image: ""
5656
terrarium_oauth2_proxy_image_hardened: "dhi.io/oauth2-proxy:7.15.2-debian13@sha256:8f4e89762735e7ec7c3f1bbdd5da4dcd55358db8c3278bfbc2e46a7f86ab7d9e"
57-
terrarium_oauth2_proxy_image_mirror: "ghcr.io/terion-name/terrarium-dhi-oauth2-proxy:7.15.2-debian13@sha256:8f4e89762735e7ec7c3f1bbdd5da4dcd55358db8c3278bfbc2e46a7f86ab7d9e"
57+
terrarium_oauth2_proxy_image_mirror: "ghcr.io/terion-name/terrarium-dhi-oauth2-proxy:7.15.2-debian13"
5858
terrarium_oauth2_proxy_image_fallback: "quay.io/oauth2-proxy/oauth2-proxy:v7.15.2@sha256:aa0bd8dd5ab0c78e4c91c92755ad573a5f92241f88138b4141b8ec803463b4fd"
5959
terrarium_zitadel_postgres_image: ""
6060
terrarium_zitadel_postgres_image_hardened: "dhi.io/postgres:17.9-alpine3.22-fips@sha256:ae0f0ac1f942ff7898bb217e599cc488b5c7a2611a0957daae44c00584a59714"
61-
terrarium_zitadel_postgres_image_mirror: "ghcr.io/terion-name/terrarium-dhi-postgres:17.9-alpine3.22-fips@sha256:ae0f0ac1f942ff7898bb217e599cc488b5c7a2611a0957daae44c00584a59714"
61+
terrarium_zitadel_postgres_image_mirror: "ghcr.io/terion-name/terrarium-dhi-postgres:17.9-alpine3.22-fips"
6262
terrarium_zitadel_postgres_image_fallback: "postgres:17.9-alpine3.22@sha256:034839bd88128360cda25496ebdb1471e24a4aa09b937160c73df2bb51126308"
6363
terrarium_email: ""
6464
terrarium_acme_email: "{{ terrarium_email }}"

docs/getting-started/installation.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ Terrarium also verifies the most failure-prone integrations while you configure
9696

9797
## Container Image Sources
9898

99-
Terrarium pins the oauth2-proxy and local ZITADEL Postgres images by digest. The default source order is:
99+
Terrarium pins the upstream oauth2-proxy and local ZITADEL Postgres image sources by digest. The default source order is:
100100

101101
- upstream Docker Hardened Images from `dhi.io` when Docker registry credentials exist on the host
102102
- Terrarium's GHCR mirror of those same DHI multi-arch indexes when upstream DHI credentials are not present

scripts/terrarium-traefik-sync.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const ROUTE_AUTH_DIR = "/var/lib/terrarium/oauth2-proxy-routes";
1414
const ROUTE_AUTH_COMPOSE_PATH = `${ROUTE_AUTH_DIR}/docker-compose.yml`;
1515
const ROUTE_AUTH_BASE_PORT = 4181;
1616
const DEFAULT_OAUTH2_PROXY_IMAGE =
17-
"ghcr.io/terion-name/terrarium-dhi-oauth2-proxy:7.15.2-debian13@sha256:8f4e89762735e7ec7c3f1bbdd5da4dcd55358db8c3278bfbc2e46a7f86ab7d9e";
17+
"ghcr.io/terion-name/terrarium-dhi-oauth2-proxy:7.15.2-debian13";
1818
const OAUTH2_PROXY_UID = 65532;
1919
const OAUTH2_PROXY_GID = 65532;
2020
const ROUTE_AUTH_READY_ATTEMPTS = 12;

tests/ansible-bootstrap-cert.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ const repoRoot = join(import.meta.dir, "..");
66
const POSTGRES_DHI_IMAGE =
77
"dhi.io/postgres:17.9-alpine3.22-fips@sha256:ae0f0ac1f942ff7898bb217e599cc488b5c7a2611a0957daae44c00584a59714";
88
const POSTGRES_MIRROR_IMAGE =
9-
"ghcr.io/terion-name/terrarium-dhi-postgres:17.9-alpine3.22-fips@sha256:ae0f0ac1f942ff7898bb217e599cc488b5c7a2611a0957daae44c00584a59714";
9+
"ghcr.io/terion-name/terrarium-dhi-postgres:17.9-alpine3.22-fips";
1010
const POSTGRES_FALLBACK_IMAGE =
1111
"postgres:17.9-alpine3.22@sha256:034839bd88128360cda25496ebdb1471e24a4aa09b937160c73df2bb51126308";
1212

tests/hardened-image-mirror.test.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,21 @@ describe("Docker Hardened Image mirror workflow", () => {
2424
expect(workflow).toContain(`dhi.io/oauth2-proxy@${OAUTH2_PROXY_DIGEST}`);
2525
expect(workflow).not.toContain(`dhi.io/oauth2-proxy:7.15.2-debian13@${OAUTH2_PROXY_DIGEST}`);
2626
expect(workflow).toContain(`ghcr.io/terion-name/terrarium-dhi-oauth2-proxy:7.15.2-debian13`);
27-
expect(workflow).toContain(`digest: ${OAUTH2_PROXY_DIGEST}`);
27+
expect(workflow).toContain(`source_digest: ${OAUTH2_PROXY_DIGEST}`);
2828
expect(workflow).toContain(`dhi.io/postgres@${POSTGRES_DIGEST}`);
2929
expect(workflow).not.toContain(`dhi.io/postgres:17.9-alpine3.22-fips@${POSTGRES_DIGEST}`);
3030
expect(workflow).toContain(`ghcr.io/terion-name/terrarium-dhi-postgres:17.9-alpine3.22-fips`);
31-
expect(workflow).toContain(`digest: ${POSTGRES_DIGEST}`);
31+
expect(workflow).toContain(`source_digest: ${POSTGRES_DIGEST}`);
32+
expect(workflow.match(/target_digest: auto/g)).toHaveLength(2);
3233
expect(workflow.match(/arches: amd64,arm64/g)).toHaveLength(2);
3334

3435
expect(script).toContain("skopeo inspect --raw");
35-
expect(script).toContain("skopeo copy --retry-times 3 --all --preserve-digests");
36+
expect(script).toContain("skopeo copy --retry-times 3 --all");
37+
expect(script).not.toContain("--preserve-digests");
3638
expect(script).toContain("sha256sum");
3739
expect(script).toContain("target digest mismatch");
40+
expect(script).toContain("source_arch_digest");
41+
expect(script).toContain("target_arch_digest");
3842
expect(script).toContain('(.platform.os // "") == "linux"');
3943
expect(script).toContain('(.platform.architecture // "") == $arch');
4044
});

tests/oauth2-proxy-template.test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { describe, expect, test } from "bun:test";
55
const OAUTH2_PROXY_DHI_IMAGE =
66
"dhi.io/oauth2-proxy:7.15.2-debian13@sha256:8f4e89762735e7ec7c3f1bbdd5da4dcd55358db8c3278bfbc2e46a7f86ab7d9e";
77
const OAUTH2_PROXY_MIRROR_IMAGE =
8-
"ghcr.io/terion-name/terrarium-dhi-oauth2-proxy:7.15.2-debian13@sha256:8f4e89762735e7ec7c3f1bbdd5da4dcd55358db8c3278bfbc2e46a7f86ab7d9e";
8+
"ghcr.io/terion-name/terrarium-dhi-oauth2-proxy:7.15.2-debian13";
99
const OAUTH2_PROXY_FALLBACK_IMAGE =
1010
"quay.io/oauth2-proxy/oauth2-proxy:v7.15.2@sha256:aa0bd8dd5ab0c78e4c91c92755ad573a5f92241f88138b4141b8ec803463b4fd";
1111

0 commit comments

Comments
 (0)