Files
Mrunal Patel b799fccb8b fix(auth): harden OIDC trust root retrieval (#3332)
* fix(auth): harden OIDC trust root retrieval

Signed-off-by: Mrunal Patel <mrunalp@gmail.com>

* fix(e2e): pass OIDC HTTP acknowledgement value

Signed-off-by: Mrunal Patel <mrunalp@gmail.com>

---------

Signed-off-by: Mrunal Patel <mrunalp@gmail.com>
2026-09-15 17:00:45 +00:00

176 lines
6.0 KiB
Bash
Executable File

#!/usr/bin/env bash
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
#
# Start/stop a Keycloak dev instance for OIDC testing.
# Usage:
# ./scripts/keycloak-dev.sh start # start Keycloak on port 8180
# ./scripts/keycloak-dev.sh stop # stop and remove the container
# ./scripts/keycloak-dev.sh status # check if Keycloak is running
set -euo pipefail
CONTAINER_NAME="openshell-keycloak"
KEYCLOAK_IMAGE="quay.io/keycloak/keycloak:24.0"
KEYCLOAK_PORT="${KEYCLOAK_PORT:-8180}"
REALM_FILE="$(cd "$(dirname "$0")" && pwd)/keycloak-realm.json"
HEALTH_TIMEOUT=90
# Container runtime: honour CONTAINER_RUNTIME, else prefer docker, fall back to podman.
if [ -n "${CONTAINER_RUNTIME:-}" ]; then
CTR="$CONTAINER_RUNTIME"
elif command -v docker &>/dev/null; then
CTR=docker
elif command -v podman &>/dev/null; then
CTR=podman
else
echo "Error: neither docker nor podman found in PATH" >&2
exit 1
fi
cmd_start() {
# Idempotent: if the container is already running, just print info.
if $CTR inspect "$CONTAINER_NAME" &>/dev/null; then
if $CTR inspect --format '{{.State.Running}}' "$CONTAINER_NAME" 2>/dev/null | grep -q true; then
echo "Keycloak is already running on port $KEYCLOAK_PORT"
print_info
return 0
fi
echo "Removing stopped container $CONTAINER_NAME..."
$CTR rm "$CONTAINER_NAME" >/dev/null
fi
if [ ! -f "$REALM_FILE" ]; then
echo "Error: realm file not found: $REALM_FILE" >&2
exit 1
fi
echo "Starting Keycloak ($KEYCLOAK_IMAGE) on port $KEYCLOAK_PORT..."
local port_args=(-p "${KEYCLOAK_PORT}:8080")
local network_args=()
local keycloak_args=(start-dev --import-realm)
local mount_args=(-v "${REALM_FILE}:/opt/keycloak/data/import/realm.json:ro,z")
# In containerized CI (GitHub Actions with a job container), the Docker
# CLI talks to the host daemon via a mounted socket. Port publishing
# lands on the host, not inside this container. Share the job
# container's network namespace so Keycloak is reachable on localhost.
if [ "${GITHUB_ACTIONS:-}" = "true" ] &&
[ -f /.dockerenv ] &&
[ "$CTR" = "docker" ] &&
$CTR inspect "$(hostname)" >/dev/null 2>&1; then
port_args=()
network_args=(--network "container:$(hostname)" --cap-drop ALL --security-opt no-new-privileges)
keycloak_args=(start-dev --http-host=127.0.0.1 --http-port="${KEYCLOAK_PORT}" --import-realm)
# The Docker daemon runs on the runner host. /__w exists only
# inside the job container; /home/runner/_work is mounted at the
# same path in both namespaces.
case "$REALM_FILE" in
/__w/*)
local host_realm_file="/home/runner/_work/${REALM_FILE#/__w/}"
;;
*)
echo "Error: unexpected GitHub workspace path: $REALM_FILE" >&2
exit 1
;;
esac
if [ ! -f "$host_realm_file" ]; then
echo "Error: host-visible realm file not found: $host_realm_file" >&2
exit 1
fi
# --mount fails when the source is absent; -v would silently create
# a directory and let Keycloak start without importing the realm.
mount_args=(
--mount
"type=bind,src=${host_realm_file},dst=/opt/keycloak/data/import/realm.json,readonly"
)
fi
$CTR run -d \
--name "$CONTAINER_NAME" \
"${network_args[@]}" \
"${port_args[@]}" \
-e KEYCLOAK_ADMIN=admin \
-e KEYCLOAK_ADMIN_PASSWORD=admin \
"${mount_args[@]}" \
"$KEYCLOAK_IMAGE" \
"${keycloak_args[@]}"
echo "Waiting for Keycloak to become healthy (up to ${HEALTH_TIMEOUT}s)..."
local elapsed=0
while [ $elapsed -lt $HEALTH_TIMEOUT ]; do
if curl -sf \
"http://127.0.0.1:${KEYCLOAK_PORT}/realms/openshell/.well-known/openid-configuration" \
>/dev/null 2>&1; then
echo "Keycloak is ready."
print_info
return 0
fi
sleep 2
elapsed=$((elapsed + 2))
done
echo "Error: Keycloak did not become healthy within ${HEALTH_TIMEOUT}s" >&2
echo "Logs:"
$CTR logs --tail 30 "$CONTAINER_NAME"
exit 1
}
cmd_stop() {
if $CTR inspect "$CONTAINER_NAME" &>/dev/null; then
echo "Stopping and removing $CONTAINER_NAME..."
$CTR stop "$CONTAINER_NAME" 2>/dev/null || true
$CTR rm "$CONTAINER_NAME" 2>/dev/null || true
echo "Done."
else
echo "Container $CONTAINER_NAME is not running."
fi
}
cmd_status() {
if $CTR inspect "$CONTAINER_NAME" &>/dev/null; then
if $CTR inspect --format '{{.State.Running}}' "$CONTAINER_NAME" 2>/dev/null | grep -q true; then
echo "Keycloak is running on port $KEYCLOAK_PORT"
print_info
return 0
fi
echo "Container $CONTAINER_NAME exists but is not running."
return 1
fi
echo "Container $CONTAINER_NAME does not exist."
return 1
}
print_info() {
local issuer="http://127.0.0.1:${KEYCLOAK_PORT}/realms/openshell"
echo ""
echo " Issuer URL: $issuer"
echo " Discovery: ${issuer}/.well-known/openid-configuration"
echo " Admin console: http://localhost:${KEYCLOAK_PORT}/admin (admin/admin)"
echo ""
echo " Test users:"
echo " admin@test / admin (role: openshell-admin)"
echo " user@test / user (role: openshell-user)"
echo " user-b@test / user-b (role: openshell-user)"
echo ""
echo " Get a token:"
echo " curl -s -X POST ${issuer}/protocol/openid-connect/token \\"
echo " -d 'grant_type=password&client_id=openshell-cli&username=admin@test&password=admin' \\"
echo " | jq -r .access_token"
echo ""
}
case "${1:-help}" in
start) cmd_start ;;
stop) cmd_stop ;;
status) cmd_status ;;
*)
echo "Usage: $0 {start|stop|status}"
exit 1
;;
esac