Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions .github/workflows/verify-keycloak-cohort-restores.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ on:
workflow_dispatch:
inputs:
keycloak_release_sha:
description: Exact protected Keycloak main SHA whose pinned 26.7.3 runtime must start all five restored databases
description: Exact protected Keycloak main SHA whose reviewed pinned runtime must start all five restored databases
required: true
type: string

Expand Down Expand Up @@ -66,9 +66,7 @@ jobs:
set -euo pipefail
[[ "$(git rev-parse HEAD)" == "${GITHUB_SHA}" ]]
[[ "$(git -C _keycloak rev-parse HEAD)" == "${KEYCLOAK_RELEASE_SHA}" ]]
grep -Fxq 'KEYCLOAK_UPSTREAM_VERSION=26.7.3' _keycloak/envs/production/.env.keycloak
grep -Fxq 'KEYCLOAK_IMAGE=dhi.io/keycloak:26-debian13@sha256:fab1484b1762fd1269e63a40f068ec73ea75b498eaaa5d02f62f022a5d00ff0f' \
_keycloak/envs/production/.env.keycloak
python3 scripts/read-keycloak-runtime.py _keycloak/envs/production/.env.keycloak --field github-env >> "$GITHUB_ENV"

- name: Ensure interrupted cohort material expires on the release host
shell: bash
Expand Down Expand Up @@ -140,7 +138,7 @@ jobs:
run: |
set -euo pipefail
export DOCKER_CONFIG="/tmp/postgres-keycloak-cohort-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}/docker"
base='dhi.io/keycloak:26-debian13@sha256:fab1484b1762fd1269e63a40f068ec73ea75b498eaaa5d02f62f022a5d00ff0f'
base="${COHORT_KEYCLOAK_IMAGE}"
tag="makepad/keycloak-catwlk-cohort:${KEYCLOAK_RELEASE_SHA}"
manifest_runtime=$(python3 - <<'PY'
import json
Expand All @@ -167,7 +165,7 @@ jobs:
set -euo pipefail
job_root="/tmp/postgres-keycloak-cohort-${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}"
export DOCKER_CONFIG="${job_root}/docker"
docker pull 'dhi.io/keycloak:26-debian13@sha256:fab1484b1762fd1269e63a40f068ec73ea75b498eaaa5d02f62f022a5d00ff0f' >/dev/null
docker pull "${COHORT_KEYCLOAK_IMAGE}" >/dev/null
docker pull 'postgres:16-alpine@sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777' >/dev/null
catwlk_image=$(<"${job_root}/catwlk-image-tag")
scripts/restore-keycloak-cohort-backups.sh "${job_root}/backups" "${job_root}/results" _keycloak "${catwlk_image}"
Expand All @@ -182,7 +180,7 @@ jobs:
evidence_dir="${job_root}/evidence"
install -d -m 0700 "${evidence_dir}"
python3 - "${job_root}/results/instances.json" "${evidence_dir}/keycloak-cohort-restore-evidence.json" \
"${GITHUB_RUN_ID}" "${GITHUB_RUN_ATTEMPT}" "${GITHUB_SHA}" "${KEYCLOAK_RELEASE_SHA}" <<'PY'
"${GITHUB_RUN_ID}" "${GITHUB_RUN_ATTEMPT}" "${GITHUB_SHA}" "${KEYCLOAK_RELEASE_SHA}" "${COHORT_KEYCLOAK_IMAGE}" "${COHORT_KEYCLOAK_VERSION}" <<'PY'
import json
import pathlib
import sys
Expand All @@ -197,10 +195,10 @@ jobs:
"postgres_head_sha": sys.argv[5],
"postgres_ref": "refs/heads/main",
"keycloak_release_sha": sys.argv[6],
"keycloak_base_image": "dhi.io/keycloak:26-debian13@sha256:fab1484b1762fd1269e63a40f068ec73ea75b498eaaa5d02f62f022a5d00ff0f",
"keycloak_base_image": sys.argv[7],
"catwlk_runtime_image_id": results["catwlk_runtime_image_id"],
"fingerprint_schema": "makepad.keycloak-config-fingerprint.v2",
"keycloak_upstream_version": "26.7.3",
"keycloak_upstream_version": sys.argv[8],
"result": "restored-databases-compatible",
"instances": results["instances"],
}
Expand Down
7 changes: 5 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -364,15 +364,18 @@ Pull-request and protected-main checks run through the repository's native
`pull_request_target`, never grants write permissions, and never exposes a
deployment environment to pull-request code.

## Keycloak 26.7.3 cohort restore evidence
## Keycloak cohort restore evidence

Before the five-realm Keycloak release, dispatch protected workflow `Verify
Keycloak Cohort Restore Compatibility` with the exact lowercase current
Keycloak protected-main SHA. There is no mutable rollout repository variable.
The workflow resolves `Makepad-fr/keycloak` main independently, checks out that
exact release, and verifies its pinned
`dhi.io/keycloak:26-debian13@sha256:fab1484b1762fd1269e63a40f068ec73ea75b498eaaa5d02f62f022a5d00ff0f`
runtime and upstream version `26.7.3`.
runtime and upstream version `26.7.3`, or the reviewed `26.8.1` pair
`dhi.io/keycloak:26.8.1-debian13@sha256:e32c433741f6c253fd410ad209f548cb962746b76f7bc19a2fe34466cbd8cc12`.
`scripts/read-keycloak-runtime.py` rejects unknown, duplicate, missing, or
mismatched pins before capture. Build, restore and receipt use that same pair.

The protected release runner captures fresh custom-format, no-owner,
no-privilege dumps of exactly `keycloak_catwlk`, `keycloak_makepad`,
Expand Down
40 changes: 40 additions & 0 deletions scripts/read-keycloak-runtime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
#!/usr/bin/env python3
"""Resolve only reviewed immutable runtime pins from an exact Keycloak checkout."""
import argparse
import importlib.util
from pathlib import Path

spec = importlib.util.spec_from_file_location("cohort_evidence", Path(__file__).with_name("verify-keycloak-cohort-evidence.py"))
contract = importlib.util.module_from_spec(spec)
spec.loader.exec_module(contract)


def read_runtime(path):
fields = {}
for line in Path(path).read_text().splitlines():
key, sep, value = line.partition("=")
if key in ("KEYCLOAK_IMAGE", "KEYCLOAK_UPSTREAM_VERSION"):
if key in fields or not sep:
raise ValueError("Runtime pin is duplicated or malformed")
fields[key] = value
image = fields.get("KEYCLOAK_IMAGE")
version = fields.get("KEYCLOAK_UPSTREAM_VERSION")
if image not in contract.REVIEWED_RUNTIMES or contract.REVIEWED_RUNTIMES[image] != version:
raise ValueError("Runtime image/version pair has not been reviewed")
return image, version


if __name__ == "__main__":
parser = argparse.ArgumentParser(description=__doc__)
parser.add_argument("config", type=Path)
parser.add_argument("--field", choices=("image", "version", "github-env"), required=True)
args = parser.parse_args()
try:
image, version = read_runtime(args.config)
except (OSError, ValueError) as error:
parser.exit(1, str(error) + "\n")
if args.field == "github-env":
print("COHORT_KEYCLOAK_IMAGE=" + image)
print("COHORT_KEYCLOAK_VERSION=" + version)
else:
print(image if args.field == "image" else version)
4 changes: 2 additions & 2 deletions scripts/restore-keycloak-cohort-backups.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ backup_dir=$1
result_dir=$2
keycloak_source=$3
catwlk_image=$4
keycloak_image='dhi.io/keycloak:26-debian13@sha256:fab1484b1762fd1269e63a40f068ec73ea75b498eaaa5d02f62f022a5d00ff0f'
keycloak_image=$(python3 "$(dirname "$0")/read-keycloak-runtime.py" "${keycloak_source}/envs/production/.env.keycloak" --field image)
postgres_image='postgres:16-alpine@sha256:57c72fd2a128e416c7fcc499958864df5301e940bca0a56f58fddf30ffc07777'
run_id=${GITHUB_RUN_ID:-1}
run_attempt=${GITHUB_RUN_ATTEMPT:-1}
Expand Down Expand Up @@ -197,7 +197,7 @@ PY
[[ $(docker inspect "${active_keycloak_container}" --format '{{.State.Running}}') == true ]] || break
sleep 1
done
[[ "${ready}" == 1 ]] || { docker logs "${active_keycloak_container}" >&2; echo "Keycloak 26.7.3 did not become ready for restored ${slug}." >&2; exit 1; }
[[ "${ready}" == 1 ]] || { docker logs "${active_keycloak_container}" >&2; echo "Reviewed Keycloak runtime did not become ready for restored ${slug}." >&2; exit 1; }
fingerprint_configuration after
fingerprints=()
for category in $(printf '%s\n' "${!category_tables[@]}" | sort); do
Expand Down
2 changes: 2 additions & 0 deletions scripts/test-keycloak-cohort-evidence.sh
Original file line number Diff line number Diff line change
Expand Up @@ -142,4 +142,6 @@ if python3 "${validator}" "${oversized}" >/dev/null 2>&1; then
exit 1
fi

python3 "$(dirname "$0")/test-read-keycloak-runtime.py"

echo "Keycloak cohort evidence contract tests passed."
38 changes: 38 additions & 0 deletions scripts/test-read-keycloak-runtime.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/usr/bin/env python3
"""Runtime selection must reject arbitrary images before restore work starts."""
import importlib.util
import tempfile
import unittest
from pathlib import Path

spec = importlib.util.spec_from_file_location('runtime', Path(__file__).with_name('read-keycloak-runtime.py'))
runtime = importlib.util.module_from_spec(spec)
spec.loader.exec_module(runtime)


class RuntimeTests(unittest.TestCase):
def read(self, contents):
with tempfile.TemporaryDirectory() as directory:
path = Path(directory) / 'runtime.env'
path.write_text(contents)
return runtime.read_runtime(path)

def test_reviewed_pairs(self):
for image, version in runtime.contract.REVIEWED_RUNTIMES.items():
with self.subTest(version=version):
self.assertEqual((image, version), self.read(f'IGNORED=value\nKEYCLOAK_IMAGE={image}\nKEYCLOAK_UPSTREAM_VERSION={version}\n'))

def test_reject_unreviewed_or_ambiguous_config(self):
image, version = next(iter(runtime.contract.REVIEWED_RUNTIMES.items()))
valid = f'KEYCLOAK_IMAGE={image}\nKEYCLOAK_UPSTREAM_VERSION={version}\n'
for contents in ('', f'KEYCLOAK_IMAGE={image}\n', valid.replace(version, '99.0.0'),
valid.replace(image, 'dhi.io/keycloak:latest'),
valid + f'KEYCLOAK_IMAGE={image}\n',
valid + f'KEYCLOAK_UPSTREAM_VERSION={version}\n',
valid.replace(image, image + ' ')):
with self.subTest(contents=contents), self.assertRaises(ValueError):
self.read(contents)


if __name__ == '__main__':
unittest.main()
4 changes: 3 additions & 1 deletion scripts/validate-postgres-config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -868,7 +868,9 @@ for required in (
"makepad.keycloak-cohort-restore-evidence.v2",
"restored-databases-compatible",
"dhi.io/keycloak:26-debian13@sha256:fab1484b1762fd1269e63a40f068ec73ea75b498eaaa5d02f62f022a5d00ff0f",
"KEYCLOAK_UPSTREAM_VERSION=26.7.3",
"read-keycloak-runtime.py",
"COHORT_KEYCLOAK_IMAGE",
"COHORT_KEYCLOAK_VERSION",
"restore-keycloak-cohort-backups.sh",
"verify-keycloak-cohort-evidence.py",
):
Expand Down