forked from baron/baron-sso
366 lines
15 KiB
Bash
366 lines
15 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)"
|
|
cp "$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"
|
|
)"
|
|
if grep -q "image: oryd/hydra" <<<"$root_init_rp$docker_init_rp"; then
|
|
echo "ERROR: init-rp must not use the Hydra service image because distroless tags do not provide /bin/sh." >&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 ! 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 -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
|
|
|
|
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
|
|
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
|