diff --git a/.github/docker-image-mirror-config.yaml b/.github/docker-image-mirror-config.yaml new file mode 100644 index 0000000000..e4d5746a9f --- /dev/null +++ b/.github/docker-image-mirror-config.yaml @@ -0,0 +1,27 @@ +# Mirror configuration with SHA256 digests for immutability +# Format: source → destination with SHA digest + +images: + # Flyway images (Docker Hub) + - source: flyway/flyway:10-alpine + destination: ghcr.io/loculus-project/flyway:10-alpine + digest: "sha256:f736d2d71e8a120d3950a67c1b4cdf155cf3a6d8be149b4ec784f24dbf22f50f" + + - source: flyway/flyway:11.10.0-alpine-mongo + destination: ghcr.io/loculus-project/flyway:11.10.0-alpine-mongo + digest: "sha256:1398d6a9223ae0f2164d15282c029614d60df826c500f6b78d86498422837945" + + # MinIO (Quay.io) + - source: quay.io/minio/minio:latest + destination: ghcr.io/loculus-project/minio:latest + digest: "sha256:9966a92a734f9411e32f4f41d7d9d826fcdc0f68c4e20b70295bd4e7c11f8a2f" + + # Keycloak (Quay.io) + - source: quay.io/keycloak/keycloak:23.0 + destination: ghcr.io/loculus-project/keycloak:23.0 + digest: "sha256:461bdabba2bd2de667b19b0c1831754cb0f1c6c1d1c85cd35ccfa65335078962" + + # Busybox (K8s registry) + - source: registry.k8s.io/busybox:latest + destination: ghcr.io/loculus-project/busybox:latest + digest: "sha256:d8d3bc2c183ed2f9f10e7258f84971202325ee6011ba137112e01e30f206de67" diff --git a/.github/workflows/ena-submission-workflow-tests.yml b/.github/workflows/ena-submission-workflow-tests.yml index f039ae5717..e9558c11b0 100644 --- a/.github/workflows/ena-submission-workflow-tests.yml +++ b/.github/workflows/ena-submission-workflow-tests.yml @@ -17,7 +17,7 @@ jobs: ENA_PASSWORD: ${{ secrets.ENA_SUBMISSION_PASSWORD }} services: postgres: - image: postgres:15 + image: ghcr.io/cloudnative-pg/postgresql@sha256:8e08e8d876545a0ce1cd5aa3c13923181f1d51d419cb618bc52832f4c89fda06 env: POSTGRES_USER: postgres POSTGRES_PASSWORD: unsecure diff --git a/.github/workflows/mirror-docker-images.yml b/.github/workflows/mirror-docker-images.yml new file mode 100644 index 0000000000..64f9f67979 --- /dev/null +++ b/.github/workflows/mirror-docker-images.yml @@ -0,0 +1,79 @@ +name: Mirror Images to GHCR + +on: + schedule: + - cron: '0 3 * * *' # Daily at 3 AM UTC + workflow_dispatch: + inputs: + force_mirror: + description: 'Force mirror all images' + type: boolean + default: false + +permissions: + packages: write + contents: write # Need write to update config with digests + +jobs: + mirror: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + + - name: Log in to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Log in to Docker Hub (if secrets available) + if: ${{ secrets.DOCKERHUB_USERNAME != '' }} + uses: docker/login-action@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN_READ_PUBLIC }} + + - name: Mirror images and update digests + env: + FORCE: ${{ inputs.force_mirror || 'false' }} + run: | + set -euo pipefail + + images=$(yq eval '.images | length' .github/docker-image-mirror-config.yaml) + updated=false + + for i in $(seq 0 $((images - 1))); do + source=$(yq eval ".images[$i].source" .github/docker-image-mirror-config.yaml) + dest=$(yq eval ".images[$i].destination" .github/docker-image-mirror-config.yaml) + current_digest=$(yq eval ".images[$i].digest" .github/docker-image-mirror-config.yaml) + + # Get source digest + source_digest=$(docker manifest inspect "$source" 2>/dev/null | jq -r 'if .manifests then .manifests[0].digest else .config.digest // "unknown" end') + + # Skip if digest matches and not forced + if [ "$FORCE" != "true" ] && [ "$current_digest" = "$source_digest" ]; then + echo "$source: skipping (unchanged)" + continue + fi + + echo "$source → $dest" + docker pull "$source" >/dev/null 2>&1 + docker tag "$source" "$dest" + docker push "$dest" >/dev/null 2>&1 + + # Update digest in config + yq eval ".images[$i].digest = \"$source_digest\"" -i .github/docker-image-mirror-config.yaml + updated=true + done + + echo "updated=$updated" >> $GITHUB_ENV + + - name: Commit digest updates + if: env.updated == 'true' + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add .github/docker-image-mirror-config.yaml + git commit -m "chore: Update image digests after mirroring" + git push diff --git a/.github/workflows/schema-dump.yml b/.github/workflows/schema-dump.yml index a49e61fbbb..653c32c05e 100644 --- a/.github/workflows/schema-dump.yml +++ b/.github/workflows/schema-dump.yml @@ -19,7 +19,7 @@ jobs: services: postgres: - image: postgres:15 + image: ghcr.io/cloudnative-pg/postgresql@sha256:8e08e8d876545a0ce1cd5aa3c13923181f1d51d419cb618bc52832f4c89fda06 env: POSTGRES_PASSWORD: postgres POSTGRES_DB: postgres @@ -40,7 +40,7 @@ jobs: fetch-depth: 0 - name: Run Flyway migrations - uses: docker://flyway/flyway:10-alpine + uses: docker://ghcr.io/loculus-project/flyway@sha256:f736d2d71e8a120d3950a67c1b4cdf155cf3a6d8be149b4ec784f24dbf22f50f env: FLYWAY_URL: jdbc:postgresql://postgres:5432/postgres FLYWAY_USER: postgres @@ -49,7 +49,7 @@ jobs: args: -locations=filesystem:./backend/src/main/resources/db/migration/ migrate - name: Generate new schema - uses: docker://postgres:16 + uses: docker://ghcr.io/cloudnative-pg/postgresql@sha256:8e08e8d876545a0ce1cd5aa3c13923181f1d51d419cb618bc52832f4c89fda06 with: args: > bash -c "PGPASSWORD=postgres pg_dump diff --git a/ena-submission/flyway/Dockerfile b/ena-submission/flyway/Dockerfile index a0ab7a695b..613b998cf7 100644 --- a/ena-submission/flyway/Dockerfile +++ b/ena-submission/flyway/Dockerfile @@ -1,6 +1,6 @@ # The version number is also used in the ena-submission-workflow-tests.yml file to extract the Flyway version dynamically. # We thus require a specific major.minor.patch version here -FROM flyway/flyway:11.10.0-alpine-mongo +FROM ghcr.io/loculus-project/flyway@sha256:1398d6a9223ae0f2164d15282c029614d60df826c500f6b78d86498422837945 # Copy configuration and SQL files COPY conf /flyway/conf diff --git a/kubernetes/loculus/templates/ingest-deployment.yaml b/kubernetes/loculus/templates/ingest-deployment.yaml index 4beb7cb35d..99d75d3f95 100644 --- a/kubernetes/loculus/templates/ingest-deployment.yaml +++ b/kubernetes/loculus/templates/ingest-deployment.yaml @@ -28,7 +28,7 @@ spec: {{- include "possiblePriorityClassName" $ | nindent 6 }} initContainers: - name: version-check - image: busybox + image: ghcr.io/loculus-project/busybox@sha256:d8d3bc2c183ed2f9f10e7258f84971202325ee6011ba137112e01e30f206de67 {{- include "loculus.resources" (list "ingest-init" $.Values) | nindent 10 }} command: ['sh', '-c', ' CONFIG_VERSION=$(grep "verify_loculus_version_is:" /package/config/config.yaml | sed "s/verify_loculus_version_is: //;"); diff --git a/kubernetes/loculus/templates/keycloak-database-standin.yaml b/kubernetes/loculus/templates/keycloak-database-standin.yaml index 0856b47227..4f31156000 100644 --- a/kubernetes/loculus/templates/keycloak-database-standin.yaml +++ b/kubernetes/loculus/templates/keycloak-database-standin.yaml @@ -24,7 +24,7 @@ spec: spec: containers: - name: loculus-keycloak-database - image: postgres:15.12 + image: ghcr.io/cloudnative-pg/postgresql@sha256:8e08e8d876545a0ce1cd5aa3c13923181f1d51d419cb618bc52832f4c89fda06 resources: requests: memory: "30Mi" diff --git a/kubernetes/loculus/templates/keycloak-deployment.yaml b/kubernetes/loculus/templates/keycloak-deployment.yaml index 78bd59233f..f629ca612e 100644 --- a/kubernetes/loculus/templates/keycloak-deployment.yaml +++ b/kubernetes/loculus/templates/keycloak-deployment.yaml @@ -36,7 +36,7 @@ spec: containers: - name: keycloak # TODO #1221 - image: quay.io/keycloak/keycloak:23.0 + image: ghcr.io/loculus-project/keycloak@sha256:461bdabba2bd2de667b19b0c1831754cb0f1c6c1d1c85cd35ccfa65335078962 {{- include "loculus.resources" (list "keycloak" $.Values) | nindent 10 }} env: - name: REGISTRATION_TERMS_MESSAGE diff --git a/kubernetes/loculus/templates/loculus-database-standin.yaml b/kubernetes/loculus/templates/loculus-database-standin.yaml index 1324d2827a..8343c23148 100644 --- a/kubernetes/loculus/templates/loculus-database-standin.yaml +++ b/kubernetes/loculus/templates/loculus-database-standin.yaml @@ -22,7 +22,7 @@ spec: spec: containers: - name: database - image: postgres:15.12 + image: ghcr.io/cloudnative-pg/postgresql@sha256:8e08e8d876545a0ce1cd5aa3c13923181f1d51d419cb618bc52832f4c89fda06 args: - "-c" - "shared_preload_libraries=pg_stat_statements" diff --git a/kubernetes/loculus/templates/minio-deployment.yaml b/kubernetes/loculus/templates/minio-deployment.yaml index e18364dd85..87548d1770 100644 --- a/kubernetes/loculus/templates/minio-deployment.yaml +++ b/kubernetes/loculus/templates/minio-deployment.yaml @@ -47,7 +47,7 @@ spec: name: minio-policies containers: - name: minio - image: minio/minio:latest + image: ghcr.io/loculus-project/minio@sha256:9966a92a734f9411e32f4f41d7d9d826fcdc0f68c4e20b70295bd4e7c11f8a2f {{- include "loculus.resources" (list "minio" $.Values) | nindent 10 }} args: ["server", "/data"] ports: