1
0
forked from baron/baron-sso
Files
baron-sso/test/ory_v26_compose_policy_test.sh

412 lines
17 KiB
Bash

#!/usr/bin/env bash
set -euo pipefail
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
root_config="$(
docker compose --env-file "$repo_root/.env" -f "$repo_root/compose.ory.yaml" config
)"
docker_config="$(
docker compose --env-file "$repo_root/.env" -f "$repo_root/docker/compose.ory.yaml" config
)"
override_env="$(mktemp)"
grep -Ev '^(USERFRONT_URL|HYDRA_PUBLIC_URL|KRATOS_UI_URL|KRATOS_BROWSER_URL|ADMINFRONT_CALLBACK_URLS|DEVFRONT_CALLBACK_URLS|ORGFRONT_CALLBACK_URLS)=' "$repo_root/.env" >"$override_env"
cat >> "$override_env" <<'EOF'
USERFRONT_URL=https://compose-policy.example.test/sso
HYDRA_PUBLIC_URL=https://compose-policy.example.test/sso/oidc
KRATOS_UI_URL=https://compose-policy.example.test/ui
KRATOS_BROWSER_URL=https://compose-policy.example.test/auth
ADMINFRONT_CALLBACK_URLS=https://compose-policy.example.test/admin/callback
DEVFRONT_CALLBACK_URLS=https://compose-policy.example.test/dev/callback
ORGFRONT_CALLBACK_URLS=https://compose-policy.example.test/org/callback
EOF
trap 'rm -f "$override_env"' EXIT
override_config="$(
docker compose --env-file "$override_env" -f "$repo_root/compose.ory.yaml" config
)"
override_docker_config="$(
docker compose --env-file "$override_env" -f "$repo_root/docker/compose.ory.yaml" config
)"
for service in kratos hydra keto oathkeeper; do
version_key="$(tr '[:lower:]' '[:upper:]' <<<"$service")_VERSION"
expected_version="$(grep -E "^${version_key}=" "$repo_root/.env" | cut -d= -f2-)"
if [[ -z "$expected_version" ]]; then
echo "ERROR: $version_key must be set in .env" >&2
exit 1
fi
if ! grep -q "image: oryd/${service}:${expected_version}" <<<"$root_config"; then
echo "ERROR: compose.ory.yaml must render oryd/${service}:${expected_version}" >&2
exit 1
fi
done
if grep -q "oryd/hydra:v25.4.0" <<<"$root_config"; then
echo "ERROR: compose.ory.yaml must not hard-code init-rp to hydra v25.4.0." >&2
exit 1
fi
for compose_file in "$repo_root/compose.ory.yaml" "$repo_root/docker/compose.ory.yaml"; do
if grep -Eq 'redirect-uri .*:-.*https?://' "$compose_file"; then
echo "ERROR: $compose_file must not hard-code external redirect URI fallbacks; use .env variables." >&2
exit 1
fi
if grep -Eq 'KRATOS_SELFSERVICE_ALLOWED_RETURN_URLS=.*https?://localhost' "$compose_file"; then
echo "ERROR: $compose_file must not hard-code Kratos allowed return URL fallbacks; use .env variables." >&2
exit 1
fi
if awk 'in_block && /^ [A-Za-z0-9_-]+:/ { exit } /^ init-rp:/ { in_block=1 } in_block { print }' "$compose_file" | grep -q -- '--endpoint http://hydra:4445'; then
echo "ERROR: $compose_file init-rp must use HYDRA_ADMIN_URL instead of hard-coded Hydra admin endpoint." >&2
exit 1
fi
if awk 'in_block && /^ [A-Za-z0-9_-]+:/ { exit } /^[[:space:]]+oathkeeper:/ { in_block=1 } in_block { print }' "$compose_file" | grep -q "command: serve proxy --config /etc/config/oathkeeper/oathkeeper.yml"; then
echo "ERROR: $compose_file Oathkeeper must use entrypoint.sh instead of bypassing rules.active.json generation." >&2
exit 1
fi
done
for stack_check_file in \
"$repo_root/compose.ory.yaml" \
"$repo_root/docker/compose.ory.yaml" \
"$repo_root/docker/staging_pull_compose.template.yaml" \
"$repo_root/deploy/templates/docker-compose.yaml"
do
if grep -q 'until curl -s http://' "$stack_check_file"; then
echo "ERROR: Ory stack check must not wait forever; use bounded readiness checks in $stack_check_file." >&2
exit 1
fi
if ! grep -q 'ORY_STACK_CHECK_MAX_ATTEMPTS' "$stack_check_file"; then
echo "ERROR: Ory stack check must expose ORY_STACK_CHECK_MAX_ATTEMPTS in $stack_check_file." >&2
exit 1
fi
if ! grep -q 'ERROR: Ory service not ready' "$stack_check_file"; then
echo "ERROR: Ory stack check must report the failed service name in $stack_check_file." >&2
exit 1
fi
if ! grep -q 'check_ready kratos .* || exit 1' "$stack_check_file"; then
echo "ERROR: Ory stack check must raise a non-zero exit when Kratos is not ready in $stack_check_file." >&2
exit 1
fi
done
for expected_url in \
"https://compose-policy.example.test/sso/oidc" \
"https://compose-policy.example.test/sso/login" \
"https://compose-policy.example.test/sso/consent" \
"https://compose-policy.example.test/sso/error" \
"https://compose-policy.example.test/admin/callback" \
"https://compose-policy.example.test/dev/callback" \
"https://compose-policy.example.test/org/callback"
do
if ! grep -q "$expected_url" <<<"$override_config$override_docker_config"; then
echo "ERROR: Ory compose config must render env override URL: $expected_url" >&2
exit 1
fi
done
root_init_rp="$(
awk 'in_block && /^ [A-Za-z0-9_-]+:/ { exit } /^ init-rp:/ { in_block=1 } in_block { print }' "$repo_root/compose.ory.yaml"
)"
docker_init_rp="$(
awk 'in_block && /^ [A-Za-z0-9_-]+:/ { exit } /^ init-rp:/ { in_block=1 } in_block { print }' "$repo_root/docker/compose.ory.yaml"
)"
for init_rp_file in \
"$repo_root/compose.ory.yaml" \
"$repo_root/docker/compose.ory.yaml" \
"$repo_root/docker/staging_pull_compose.template.yaml" \
"$repo_root/deploy/templates/docker-compose.yaml"
do
if ! grep -Fq 'image: oryd/hydra:${HYDRA_CLI_VERSION:-v26.2.0}' "$init_rp_file"; then
echo "ERROR: init-rp must use the official Hydra CLI image with HYDRA_CLI_VERSION in $init_rp_file." >&2
exit 1
fi
if ! grep -Fq 'entrypoint: ["/bin/sh", "-ec"]' "$init_rp_file"; then
echo "ERROR: init-rp must override the Hydra image entrypoint with /bin/sh -ec in $init_rp_file." >&2
exit 1
fi
if grep -Fq 'HYDRA_CLI_ARCHIVE_VERSION' "$init_rp_file" || grep -Fq 'hydra.tar.gz' "$init_rp_file"; then
echo "ERROR: init-rp must not download Hydra CLI tarballs at runtime in $init_rp_file." >&2
exit 1
fi
done
if grep -q "image: alpine:latest" <<<"$root_init_rp$docker_init_rp"; then
echo "ERROR: init-rp must not use alpine plus runtime Hydra CLI download." >&2
exit 1
fi
if ! grep -q "migrate sql up" "$repo_root/compose.ory.yaml"; then
echo "ERROR: compose.ory.yaml Kratos migration must use migrate sql up." >&2
exit 1
fi
if ! grep -q "keto-migrate:" <<<"$docker_config"; then
echo "ERROR: docker/compose.ory.yaml must include keto-migrate for clean Ory installs." >&2
exit 1
fi
if grep -q "releases/download/v25.4.0" "$repo_root/docker/staging_pull_compose.template.yaml"; then
echo "ERROR: staging pull compose must not download a hard-coded Hydra v25.4.0 CLI." >&2
exit 1
fi
staging_pull_template="$repo_root/docker/staging_pull_compose.template.yaml"
if ! grep -q 'entrypoint: \["/etc/config/oathkeeper/entrypoint.sh"\]' "$staging_pull_template"; then
echo "ERROR: staging pull Oathkeeper must use the env-aware entrypoint." >&2
exit 1
fi
if grep -q "command: serve proxy --config /etc/config/oathkeeper/oathkeeper.yml" "$staging_pull_template"; then
echo "ERROR: staging pull Oathkeeper must not bypass entrypoint.sh with a direct command." >&2
exit 1
fi
if ! grep -q "URLS_SELF_ISSUER=\${HYDRA_PUBLIC_URL}" "$staging_pull_template"; then
echo "ERROR: staging pull Hydra issuer must use HYDRA_PUBLIC_URL." >&2
exit 1
fi
if grep -Eq '(KRATOS_(SERVE|SELFSERVICE|UI|BROWSER|PUBLIC|ADMIN).*:-http://localhost|URLS_.*:-http://localhost)' "$staging_pull_template"; then
echo "ERROR: staging pull Ory browser URLs must not fall back to localhost." >&2
exit 1
fi
if ! grep -q 'KRATOS_SELFSERVICE_ALLOWED_RETURN_URLS=${KRATOS_ALLOWED_RETURN_URLS_JSON' "$staging_pull_template"; then
echo "ERROR: staging pull Kratos allowed_return_urls must be driven by KRATOS_ALLOWED_RETURN_URLS_JSON." >&2
exit 1
fi
for return_path in '/ko' '/en' '/auth/callback' '/ko/auth/callback' '/en/auth/callback'; do
if ! grep -q "$return_path" "$staging_pull_template" "$repo_root/deploy/templates/.env.template" "$repo_root/.gitea/workflows/staging_code_pull.yml"; then
echo "ERROR: staging/prod allowed_return_urls must include locale/callback path: $return_path" >&2
exit 1
fi
done
if grep -Eq 'ORGFRONT_CALLBACK_URLS=.*(172\.16\.10\.176|baron-orgchart\.hmac\.kr|, https?://)' "$staging_pull_template" "$repo_root/.gitea/workflows/staging_code_pull.yml"; then
echo "ERROR: staging pull OrgFront callbacks must not keep private IP, legacy orgchart domain, or comma-space URI entries." >&2
exit 1
fi
if grep -q "rewrite \\^/oidc" "$repo_root/gateway/nginx.conf"; then
echo "ERROR: gateway must preserve the /oidc prefix and let Oathkeeper strip it." >&2
exit 1
fi
for rules_file in \
"$repo_root/docker/ory/oathkeeper/rules.json" \
"$repo_root/docker/ory/oathkeeper/rules.stage.json" \
"$repo_root/docker/ory/oathkeeper/rules.prod.json"
do
for rule_id in hydra-well-known hydra-well-known-oidc hydra-oauth2 hydra-oauth2-oidc hydra-userinfo hydra-userinfo-oidc; do
if ! grep -q "\"id\": \"$rule_id\"" "$rules_file"; then
echo "ERROR: Oathkeeper rules must expose Hydra public route in $rules_file: $rule_id" >&2
exit 1
fi
done
for prefixed_rule in hydra-well-known-oidc hydra-oauth2-oidc hydra-userinfo-oidc; do
if ! awk -v id="\"id\": \"$prefixed_rule\"" '
$0 ~ id { in_rule = 1 }
in_rule && /strip_path/ && /\/oidc/ { found = 1 }
in_rule && /^ }[,]?$/ { in_rule = 0 }
END { exit found ? 0 : 1 }
' "$rules_file"; then
echo "ERROR: prefixed Oathkeeper route must strip /oidc in $rules_file: $prefixed_rule" >&2
exit 1
fi
done
done
for wildcard_rules_file in \
"$repo_root/docker/ory/oathkeeper/rules.json" \
"$repo_root/docker/ory/oathkeeper/rules.stage.json"
do
if grep -q "<\\.\\*>://<\\.\\*>/" "$wildcard_rules_file"; then
echo "ERROR: wildcard Oathkeeper host must not swallow path segments in $wildcard_rules_file." >&2
exit 1
fi
done
deploy_template="$repo_root/deploy/templates/docker-compose.yaml"
deploy_env_template="$repo_root/deploy/templates/.env.template"
deploy_gateway_template="$repo_root/deploy/templates/gateway/nginx.conf"
deploy_kratos_template="$repo_root/deploy/templates/ory/kratos/kratos.yml.template"
deploy_oathkeeper_rules_template="$repo_root/deploy/templates/ory/oathkeeper/rules.json"
for required_template in \
"$repo_root/deploy/templates/orgfront/vite.config.ts" \
"$repo_root/deploy/templates/orgfront/auth.ts" \
"$repo_root/docker/ory/init-db/01_create_dbs.sh" \
"$repo_root/docker/ory/hydra/hydra.yml.template" \
"$repo_root/docker/ory/keto/keto.yml.template" \
"$repo_root/docker/ory/oathkeeper/entrypoint.sh" \
"$repo_root/docker/ory/oathkeeper/oathkeeper.yml.template"
do
if [[ ! -f "$required_template" ]]; then
echo "ERROR: deploy instance generation requires missing source file: $required_template" >&2
exit 1
fi
done
if grep -Eq "oryd/(kratos|hydra|keto|oathkeeper):v25\\.4\\.0" "$deploy_template"; then
echo "ERROR: deploy template Ory stack must not hard-code v25.4.0 images." >&2
exit 1
fi
for prod_sensitive_file in \
"$repo_root/docker/ory/oathkeeper/rules.prod.json" \
"$repo_root/docker/ory/kratos/kratos.yml.template" \
"$repo_root/deploy/templates/ory/kratos/kratos.yml.template"
do
if grep -q "app\\.brsw\\.kr" "$prod_sensitive_file"; then
echo "ERROR: Ory production-sensitive config must not hard-code app.brsw.kr: $prod_sensitive_file" >&2
exit 1
fi
done
for compose_file in "$repo_root/compose.ory.yaml" "$repo_root/docker/compose.ory.yaml" "$repo_root/docker/staging_pull_compose.template.yaml"; do
if grep -Eq './docker/ory/(kratos|hydra|keto|oathkeeper):/etc/config/' "$compose_file"; then
echo "ERROR: Ory compose must mount rendered config/.generated/ory config, not source templates: $compose_file" >&2
exit 1
fi
done
if grep -Eq '\./ory/(kratos|hydra|keto|oathkeeper):/etc/config/' "$deploy_template"; then
echo "ERROR: deploy template must mount rendered config/.generated/ory config, not source templates." >&2
exit 1
fi
if grep -q 'ory/generated' "$deploy_template" "$repo_root/deploy/create-instance.sh"; then
echo "ERROR: deploy template must use config/.generated/ory, not ory/generated." >&2
exit 1
fi
if ! grep -q '^render-ory-config:' "$repo_root/Makefile"; then
echo "ERROR: Makefile must render Ory config before starting Ory services." >&2
exit 1
fi
if ! awk '/^ensure-ory:/ { in_target=1 } in_target && /^[^[:space:]].*:/ && $0 !~ /^ensure-ory:/ { exit } in_target { print }' "$repo_root/Makefile" | grep -q 'restart kratos'; then
echo "ERROR: make up-dev must restart Kratos when Ory is already running so rendered dev config is applied." >&2
exit 1
fi
if ! awk '/^up-all:/ { in_target=1 } in_target && /^[^[:space:]].*:/ && $0 !~ /^up-all:/ { exit } in_target { print }' "$repo_root/Makefile" | grep -q 'restart kratos'; then
echo "ERROR: make up must restart Kratos after rendering Ory config." >&2
exit 1
fi
if ! awk '/^up-ory:/ { in_target=1 } in_target && /^[^[:space:]].*:/ && $0 !~ /^up-ory:/ { exit } in_target { print }' "$repo_root/Makefile" | grep -q 'restart kratos'; then
echo "ERROR: make up-ory must restart Kratos after rendering Ory config." >&2
exit 1
fi
if ! grep -q 'scripts/render_ory_config.sh' "$repo_root/.gitea/workflows/staging_code_pull.yml"; then
echo "ERROR: staging code pull must render Ory config before docker compose up." >&2
exit 1
fi
if ! grep -q 'up -d --force-recreate kratos hydra keto oathkeeper' "$repo_root/.gitea/workflows/staging_code_pull.yml"; then
echo "ERROR: staging code pull must restart Ory services after rendering static config." >&2
exit 1
fi
if grep -Eq '^[[:space:]]*rm -rf "?\$OUTPUT_DIR"?[[:space:]]*$' "$repo_root/scripts/render_ory_config.sh"; then
echo "ERROR: Ory renderer must preserve config/.generated/ory service directories so live bind mounts stay valid." >&2
exit 1
fi
"$repo_root/scripts/render_ory_config.sh" >/dev/null
local_rendered_kratos="$repo_root/config/.generated/ory/kratos/kratos.yml"
if ! awk '/session:/ { in_session=1 } in_session && /domain:/ { print; exit }' "$local_rendered_kratos" | grep -q 'domain: localhost'; then
echo "ERROR: rendered local Kratos config must use localhost as session.cookie.domain for dev runs." >&2
exit 1
fi
stage_render_dir="$(mktemp -d)"
stage_render_env="$(mktemp)"
cat > "$stage_render_env" <<'EOF'
USERFRONT_URL=https://sso.hmac.kr
ADMINFRONT_URL=https://sadmin.hmac.kr
DEVFRONT_URL=https://sdev.hmac.kr
ORGFRONT_URL=https://sorg.hmac.kr
KRATOS_UI_URL=https://sso.hmac.kr
KRATOS_BROWSER_URL=https://sso.hmac.kr/auth
KRATOS_ADMIN_URL=http://kratos:4434
ORY_POSTGRES_PASSWORD=policy-test
KRATOS_ALLOWED_RETURN_URLS_JSON=
KRATOS_ALLOWED_RETURN_URLS_EXTRA=
EOF
ORY_CONFIG_ENV_FILES="$stage_render_env" ORY_CONFIG_OUTPUT_DIR="$stage_render_dir/ory" "$repo_root/scripts/render_ory_config.sh" >/dev/null
stage_rendered_kratos="$stage_render_dir/ory/kratos/kratos.yml"
if ! awk '/allowed_return_urls:/ { in_block=1; next } in_block && /^[[:space:]]+methods:/ { exit } in_block { print }' "$stage_rendered_kratos" | grep -q 'https://sso.hmac.kr'; then
echo "ERROR: rendered stage Kratos config must include the public userfront URL in allowed_return_urls." >&2
exit 1
fi
if awk '/allowed_return_urls:/ { in_block=1; next } in_block && /^[[:space:]]+methods:/ { exit } in_block { print }' "$stage_rendered_kratos" | grep -q 'http://localhost:5000'; then
echo "ERROR: rendered stage Kratos allowed_return_urls must not fall back to localhost." >&2
exit 1
fi
if ! awk '/session:/ { in_session=1 } in_session && /domain:/ { print; exit }' "$stage_rendered_kratos" | grep -q 'domain: hmac.kr'; then
echo "ERROR: rendered stage Kratos config must derive hmac.kr as session.cookie.domain." >&2
exit 1
fi
rm -rf "$stage_render_dir" "$stage_render_env"
for generated_config in \
"$repo_root/config/.generated/ory/kratos/kratos.yml" \
"$repo_root/config/.generated/ory/hydra/hydra.yml" \
"$repo_root/config/.generated/ory/keto/keto.yml" \
"$repo_root/config/.generated/ory/oathkeeper/oathkeeper.yml"
do
if [[ ! -f "$generated_config" ]]; then
echo "ERROR: Ory rendered config is missing: $generated_config" >&2
exit 1
fi
if grep -q '\${' "$generated_config"; then
echo "ERROR: Ory rendered config must not contain placeholders: $generated_config" >&2
exit 1
fi
done
for service in kratos-migrate kratos hydra-migrate hydra keto-migrate keto oathkeeper_logs_init oathkeeper; do
if ! grep -q "^ $service:" "$deploy_template"; then
echo "ERROR: deploy template Ory stack must include service: $service" >&2
exit 1
fi
done
for version_key in KRATOS_VERSION HYDRA_VERSION KETO_VERSION OATHKEEPER_VERSION; do
if ! grep -q "^$version_key=v26\\.2\\.0$" "$deploy_env_template"; then
echo "ERROR: deploy env template must define $version_key=v26.2.0." >&2
exit 1
fi
done
if ! grep -q 'entrypoint: \["/etc/config/oathkeeper/entrypoint.sh"\]' "$deploy_template"; then
echo "ERROR: deploy template Oathkeeper must use the env-aware entrypoint." >&2
exit 1
fi
if grep -q "rewrite \\^/oidc" "$deploy_gateway_template"; then
echo "ERROR: deploy template gateway must preserve the /oidc prefix." >&2
exit 1
fi
if ! grep -q '^version: v26.2.0$' "$deploy_kratos_template"; then
echo "ERROR: deploy Kratos template config version must match v26.2.0." >&2
exit 1
fi
for rule_id in hydra-well-known hydra-well-known-oidc hydra-oauth2 hydra-oauth2-oidc hydra-userinfo hydra-userinfo-oidc; do
if ! grep -q "\"id\": \"$rule_id\"" "$deploy_oathkeeper_rules_template"; then
echo "ERROR: deploy Oathkeeper rules must expose Hydra public route: $rule_id" >&2
exit 1
fi
done
if ! grep -q '"strip_path": "/oidc"' "$deploy_oathkeeper_rules_template"; then
echo "ERROR: deploy Oathkeeper prefixed routes must strip /oidc with strip_path." >&2
exit 1
fi