#!/usr/bin/env bash set -euo pipefail # init_kerberos_test.sh # Purpose: # - Run Kerberos authentication checks inside a Kubernetes pod # - Uses the prole-krb5-conf ConfigMap for krb5.conf # Initialize SCRIPT_DIR SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) ORIG_ARGS=("$@") # Load env if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then set -- # shellcheck disable=SC1090 source "$PROLE_HOME/env.sh" set -- "${ORIG_ARGS[@]}" elif [[ -f "$HOME/.prole/env.sh" ]]; then set -- # shellcheck disable=SC1090 source "$HOME/.prole/env.sh" set -- "${ORIG_ARGS[@]}" fi # Load config values from prole.cfg before applying defaults/CLI overrides. # shellcheck disable=SC1090 source "$SCRIPT_DIR/prole_cfg.sh" if [[ "${1:-}" == "--mode" || "${1:-}" == "-m" ]]; then prole_set_mode "${2:-}" shift 2 elif [[ "${1:-}" == --mode=* || "${1:-}" == -m=* ]]; then prole_set_mode "${1#*=}" shift fi ACTION=${1:-test} NAMESPACE=${NAMESPACE:-default} KRB5_REALM=${KRB5_REALM:-${REALM:-}} KRB5_KDC=${KRB5_KDC:-} KRB5_USER=${KRB5_USER:-${KRB5_USERNAME:-}} KRB5_PASSWORD=${KRB5_PASSWORD:-} KRB5_TEST_IMAGE=${KRB5_TEST_IMAGE:-${PROLE_KRB_TEST_IMAGE:-ubuntu:24.04}} KRB5_TEST_PACKAGES=${KRB5_TEST_PACKAGES:-"krb5-user krb5-config libpam-krb5 libnss-krb5 adcli samba-common-bin dnsutils ca-certificates"} REALM_JOIN=${REALM_JOIN:-1} KEEP_POD=${KEEP_POD:-0} KRB5_TEST_HOST_NETWORK=${KRB5_TEST_HOST_NETWORK:-0} KRB5_TEST_DNS_POLICY=${KRB5_TEST_DNS_POLICY:-} ensure_tools() { for t in kubectl curl jq; do command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; } done } ensure_namespace() { if ! kubectl get namespace "$NAMESPACE" >/dev/null 2>&1; then echo "Creating namespace '$NAMESPACE' ..." kubectl create namespace "$NAMESPACE" >/dev/null 2>&1 || true fi } openbao_url() { if [[ -n "${PROLE_OPENBAO_URL:-}" ]]; then echo "$PROLE_OPENBAO_URL" elif curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then echo "http://127.0.0.1:18200" else echo "http://openbao.${NAMESPACE}.svc.cluster.local:8200" fi } openbao_token() { if [[ -f "$PROLE_SERVICE/secrets/openbao-root-token" ]]; then cat "$PROLE_SERVICE/secrets/openbao-root-token" else echo "${OPENBAO_ROOT_TOKEN:-}" fi } fetch_openbao_secret() { local path="$1" local key="$2" local token url token=$(openbao_token) url=$(openbao_url) if [[ -z "$token" ]]; then echo "" return 0 fi curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/$path" | jq -r ".data.data.\"$key\"" || echo "" } resolve_krb5_password() { if [[ -z "${KRB5_PASSWORD}" || "${KRB5_PASSWORD}" == '${OPENBAO:'* || "${KRB5_PASSWORD}" == '${PROLE_SECRET:'* ]]; then local path="prole/${NAMESPACE:-default}/kerberos" local fetched fetched=$(fetch_openbao_secret "$path" "password") if [[ -n "$fetched" && "$fetched" != "null" ]]; then KRB5_PASSWORD="$fetched" fi fi } detect_image() { if [[ -n "$KRB5_TEST_IMAGE" ]]; then echo "$KRB5_TEST_IMAGE" return fi echo "ubuntu:24.04" } ensure_configmap() { if kubectl -n "$NAMESPACE" get configmap prole-krb5-conf >/dev/null 2>&1; then return fi echo "ConfigMap prole-krb5-conf not found. Running init_kerberos.sh update..." if [[ -x "$SCRIPT_DIR/init_kerberos.sh" ]]; then KRB5_REALM="$KRB5_REALM" KRB5_KDC="$KRB5_KDC" "$SCRIPT_DIR/init_kerberos.sh" update || true fi } create_test_pod() { local pod_name="$1" local image="$2" local host_net_block="" if [[ "$KRB5_TEST_HOST_NETWORK" == "1" ]]; then local dns_policy dns_policy=${KRB5_TEST_DNS_POLICY:-Default} host_net_block=$' hostNetwork: true\n dnsPolicy: '"$dns_policy"$'\n' fi cat < /tmp/ubuntu.sources mv /tmp/ubuntu.sources /etc/apt/sources.list.d/ubuntu.sources fi elif [ -f /etc/apt/sources.list ]; then if ! grep -q "universe" /etc/apt/sources.list; then sed -i "s/ main$/ main universe/; s/ main restricted$/ main restricted universe/; s/ main restricted multiverse$/ main restricted universe multiverse/" /etc/apt/sources.list fi fi apt-get update available="" missing="" for pkg in $KRB5_TEST_PACKAGES; do if apt-cache show "$pkg" >/dev/null 2>&1; then available="$available $pkg" else missing="$missing $pkg" fi done if [ -n "$missing" ]; then echo "WARN: skipping missing packages:$missing" >&2 fi if [ -z "$available" ]; then echo "ERROR: none of the requested packages are available." >&2 exit 1 fi DEBIAN_FRONTEND=noninteractive apt-get install -y --no-install-recommends $available ' } resolve_domain() { local domain="${DOMAIN:-}" if [[ -z "$domain" ]]; then domain=$(printf '%s' "$KRB5_REALM" | tr '[:upper:]' '[:lower:]') fi printf '%s' "$domain" } resolve_host_fqdn() { local domain="$1" local host_name="${KRB5_TEST_HOSTNAME:-$2}" if [[ "$host_name" == *.* ]]; then printf '%s' "$host_name" else printf '%s.%s' "$host_name" "$domain" fi } join_realm() { local pod_name="$1" local domain domain=$(resolve_domain) local kdc_host kdc_host=$(printf '%s' "$KRB5_KDC" | awk -F',' '{print $1}' | xargs) local host_fqdn host_fqdn=$(resolve_host_fqdn "$domain" "$pod_name") echo "Registering new Kerberos client as ${host_fqdn} in ${domain} ..." if kubectl -n "$NAMESPACE" exec "$pod_name" -- sh -c 'command -v adcli >/dev/null 2>&1'; then printf '%s\n' "$KRB5_PASSWORD" | kubectl -n "$NAMESPACE" exec -i "$pod_name" -- \ adcli join --domain="$domain" --domain-controller "$kdc_host" --login-user "$KRB5_USER" --stdin-password --host-fqdn "$host_fqdn" --show-details return $? fi if kubectl -n "$NAMESPACE" exec "$pod_name" -- sh -c 'command -v realm >/dev/null 2>&1'; then printf '%s\n' "$KRB5_PASSWORD" | kubectl -n "$NAMESPACE" exec -i "$pod_name" -- \ realm join --user "$KRB5_USER" "$domain" return $? fi echo "ERROR: Neither adcli nor realm found in image; cannot register client." >&2 return 1 } run_test() { ensure_tools ensure_namespace resolve_krb5_password # Support both names for the toggle from prole.cfg local enabled="${KERBEROS_ENABLED:-${ENABLED:-false}}" if [[ "$enabled" == "false" || "$enabled" == "0" || "$enabled" == "False" ]]; then echo "Kerberos is disabled (KERBEROS_ENABLED=$enabled). Skipping test." return 0 fi if [[ -z "$KRB5_REALM" || -z "$KRB5_USER" || -z "$KRB5_PASSWORD" || -z "$KRB5_KDC" ]]; then echo "ERROR: Missing Kerberos configuration. Ensure KRB5_REALM, KRB5_KDC, KRB5_USER, KRB5_PASSWORD are set." >&2 exit 1 fi ensure_configmap local image pod_name image=$(detect_image) pod_name="prole-krb-test-$(date +%s)" echo "Creating Kerberos test pod '$pod_name' in namespace '$NAMESPACE' using image '$image'..." create_test_pod "$pod_name" "$image" echo "Waiting for pod to become ready..." if ! kubectl -n "$NAMESPACE" wait --for=condition=Ready pod/"$pod_name" --timeout=90s; then echo "Pod did not become ready. Describing pod:" kubectl -n "$NAMESPACE" describe pod "$pod_name" || true if [[ "$KEEP_POD" != "1" ]]; then kubectl -n "$NAMESPACE" delete pod "$pod_name" --ignore-not-found fi exit 1 fi install_kerberos_packages "$pod_name" echo "Updating /etc/krb5.conf in pod $pod_name from ConfigMap..." local conf conf=$(kubectl -n "$NAMESPACE" get configmap prole-krb5-conf -o jsonpath='{.data.krb5\.conf}' 2>/dev/null || true) if [[ -n "$conf" ]]; then printf '%s' "$conf" | kubectl -n "$NAMESPACE" exec -i "$pod_name" -- sh -c 'cat > /etc/krb5.conf' else echo "WARN: ConfigMap prole-krb5-conf not found; /etc/krb5.conf may be missing or default." fi echo "Checking for kinit in pod..." if ! kubectl -n "$NAMESPACE" exec "$pod_name" -- sh -c 'command -v kinit >/dev/null 2>&1'; then echo "ERROR: kinit not found after package install in test pod image '$image'." >&2 if [[ "$KEEP_POD" != "1" ]]; then kubectl -n "$NAMESPACE" delete pod "$pod_name" --ignore-not-found fi exit 1 fi echo "Running kinit for ${KRB5_USER}@${KRB5_REALM} ..." if ! printf '%s\n' "$KRB5_PASSWORD" | kubectl -n "$NAMESPACE" exec -i "$pod_name" -- kinit "${KRB5_USER}@${KRB5_REALM}"; then echo "ERROR: kinit failed for ${KRB5_USER}@${KRB5_REALM}." >&2 if [[ "$KEEP_POD" != "1" ]]; then kubectl -n "$NAMESPACE" delete pod "$pod_name" --ignore-not-found fi exit 1 fi echo "Kerberos ticket cache:" kubectl -n "$NAMESPACE" exec "$pod_name" -- klist || true if [[ "${REALM_JOIN:-0}" == "1" ]]; then if ! join_realm "$pod_name"; then echo "ERROR: realm join failed." >&2 if [[ "$KEEP_POD" != "1" ]]; then kubectl -n "$NAMESPACE" delete pod "$pod_name" --ignore-not-found fi exit 1 fi fi if [[ "$KEEP_POD" != "1" ]]; then kubectl -n "$NAMESPACE" delete pod "$pod_name" --ignore-not-found else echo "KEEP_POD=1 set; leaving test pod running: $pod_name" fi } case "$ACTION" in test) run_test ;; cleanup) ensure_tools echo "Deleting kerberos test pods in namespace '$NAMESPACE'..." kubectl -n "$NAMESPACE" delete pod -l app=prole-kerberos-test --ignore-not-found ;; *) echo "Usage: $0 {test|cleanup}" >&2 exit 2 ;; esac