diff --git a/authbridge/authproxy/entrypoint-authbridge.sh b/authbridge/authproxy/entrypoint-authbridge.sh index 416a4b72b..96ba7a879 100644 --- a/authbridge/authproxy/entrypoint-authbridge.sh +++ b/authbridge/authproxy/entrypoint-authbridge.sh @@ -1,28 +1,40 @@ -#!/bin/sh +#!/bin/bash set -eu -# AuthBridge combined entrypoint +# AuthBridge combined entrypoint with process supervision. # Manages: spiffe-helper, client-registration, go-processor, envoy # # Startup order preserves current multi-container timing: # 1. spiffe-helper (background, long-running) -- writes JWT SVID # 2. go-processor (background) -- handles missing credentials via waitForCredentials # 3. client-registration (background one-shot) -- writes credentials when ready -# 4. envoy (exec, foreground) -- inbound JWT validation works immediately +# 4. envoy (background) -- inbound JWT validation works immediately # # Runs as UID 1337 (Envoy UID, excluded from iptables redirect). # -# Process management: Envoy (PID 1 via exec) is the liveness-critical process. -# Background subprocesses (spiffe-helper, go-processor) are expected to be -# long-running but their failures are not monitored here — Kubernetes liveness -# probes on the Envoy admin port detect the overall container health. -# Client-registration failures are non-fatal (go-processor handles missing -# credentials gracefully via passthrough mode). +# Process management: The shell stays as PID 1 and monitors critical long-running +# processes (envoy, go-processor, spiffe-helper). If any critical process exits, +# all others are killed and the container exits non-zero so Kubernetes restarts it. +# Client-registration is a one-shot process and is NOT monitored -- go-processor +# handles missing credentials gracefully via passthrough mode. + +# PIDs of critical long-running processes to monitor. +CRITICAL_PIDS="" + +cleanup() { + echo "[AuthBridge] Received signal, shutting down..." + # shellcheck disable=SC2086 + kill $CRITICAL_PIDS 2>/dev/null || true + wait + exit 0 +} +trap cleanup TERM INT # --- Phase 1: Start spiffe-helper (if enabled) --- -if [ "${SPIRE_ENABLED}" = "true" ]; then +if [ "${SPIRE_ENABLED:-}" = "true" ]; then echo "[AuthBridge] Starting spiffe-helper..." /usr/local/bin/spiffe-helper -config=/etc/spiffe-helper/helper.conf run & + CRITICAL_PIDS="$CRITICAL_PIDS $!" fi # --- Phase 2: Start go-processor --- @@ -30,13 +42,15 @@ fi # Inbound JWT validation works immediately (doesn't need credentials). echo "[AuthBridge] Starting go-processor..." /usr/local/bin/go-processor & +CRITICAL_PIDS="$CRITICAL_PIDS $!" sleep 2 # --- Phase 3: Start client-registration (background, non-blocking) --- # This runs asynchronously so Envoy starts immediately. # Failures are non-fatal: go-processor handles missing credentials gracefully. +# NOT added to CRITICAL_PIDS -- this is a one-shot process. ( - if [ "${SPIRE_ENABLED}" = "true" ]; then + if [ "${SPIRE_ENABLED:-}" = "true" ]; then echo "[AuthBridge] Waiting for SPIFFE credentials..." while [ ! -f /opt/jwt_svid.token ]; do sleep 1; done echo "[AuthBridge] SPIFFE credentials ready" @@ -59,7 +73,7 @@ sleep 2 echo "[AuthBridge] Client ID: $CLIENT_NAME" fi - if [ "${CLIENT_REGISTRATION_ENABLED}" != "false" ]; then + if [ "${CLIENT_REGISTRATION_ENABLED:-}" != "false" ]; then echo "[AuthBridge] Starting client registration..." python3 /app/client_registration.py || \ echo "[AuthBridge] WARNING: Client registration failed, continuing without" @@ -67,9 +81,21 @@ sleep 2 fi ) & -# --- Phase 4: Start Envoy (foreground) --- -# exec replaces this shell with Envoy (becomes PID 1). -# Kubernetes sends SIGTERM to PID 1 on termination. +# --- Phase 4: Start Envoy (background) --- +# Envoy runs in the background so the shell can monitor all critical processes. +# Kubernetes sends SIGTERM to PID 1 (this shell), which forwards via trap. echo "[AuthBridge] Starting Envoy..." -exec /usr/local/bin/envoy -c /etc/envoy/envoy.yaml \ - --service-cluster auth-proxy --service-node auth-proxy +/usr/local/bin/envoy -c /etc/envoy/envoy.yaml \ + --service-cluster auth-proxy --service-node auth-proxy & +CRITICAL_PIDS="$CRITICAL_PIDS $!" + +# Wait for the first critical process to exit. +# If any critical process dies, restart the container. +# shellcheck disable=SC2086 +wait -n $CRITICAL_PIDS +EXIT_CODE=$? +echo "[AuthBridge] A critical process exited unexpectedly (exit code $EXIT_CODE), terminating container" +# shellcheck disable=SC2086 +kill $CRITICAL_PIDS 2>/dev/null || true +wait +exit 1 diff --git a/authbridge/authproxy/entrypoint-envoy.sh b/authbridge/authproxy/entrypoint-envoy.sh index 04e6ed7d9..b4fe6295b 100644 --- a/authbridge/authproxy/entrypoint-envoy.sh +++ b/authbridge/authproxy/entrypoint-envoy.sh @@ -1,6 +1,20 @@ -#!/bin/sh +#!/bin/bash +set -eu -# Start the go-processor in the background +# Envoy + go-processor entrypoint with process supervision. +# Both processes run in the background; the shell stays as PID 1. +# If either process exits, the other is killed and the container exits +# non-zero so Kubernetes restarts it. + +cleanup() { + echo "Received signal, shutting down..." + kill "$GO_PROCESSOR_PID" "$ENVOY_PID" 2>/dev/null || true + wait + exit 0 +} +trap cleanup TERM INT + +# Start go-processor in the background echo "Starting go-processor..." /usr/local/bin/go-processor & GO_PROCESSOR_PID=$! @@ -8,6 +22,16 @@ GO_PROCESSOR_PID=$! # Give go-processor a moment to start sleep 2 -# Start Envoy in the foreground +# Start Envoy in the background (not exec — shell must stay PID 1 for supervision) echo "Starting Envoy..." -exec /usr/local/bin/envoy -c /etc/envoy/envoy.yaml --service-cluster auth-proxy --service-node auth-proxy --log-level debug +/usr/local/bin/envoy -c /etc/envoy/envoy.yaml \ + --service-cluster auth-proxy --service-node auth-proxy --log-level debug & +ENVOY_PID=$! + +# Wait for the first child to exit. If either process dies, restart the container. +wait -n "$GO_PROCESSOR_PID" "$ENVOY_PID" +EXIT_CODE=$? +echo "A process exited unexpectedly (exit code $EXIT_CODE), terminating container" +kill "$GO_PROCESSOR_PID" "$ENVOY_PID" 2>/dev/null || true +wait +exit 1