mirror of
https://github.com/dredx/prole.git
synced 2026-09-23 10:13:58 +00:00
Summary: Removed the prole-db-manager microservice and simplified deployment to use prole-authority as the internal management and authorization point. Fixed two blocking bugs that prevented silent install from completing on knoe-dev-cluster. Removed: prole-db-manager - Deleted db-manager-deployment.yaml and db-manager-service.yaml from opentofu manifests - Deleted src/db-manager/ (Dockerfile, server.js, package.json, tests) - Removed prole-db-manager port-forward mapping from installer/core/env.py - Removed init_db_manager.sh from Initialization Scripts (milestones.py, actions.py) - Removed init_certmgr.sh and init_db_manager.sh tabs from services screen (services.py) - Removed live k8s Deployment/Service from knoe-dev-cluster Fixed: PostgreSQL version downgrade error (pg17 -> pg18) - Created conf/postgresql/.version with value 18 - Updated k8s/prole/prole-db.yaml and prole-db-recovery.yaml.tpl imageName to prole-db:18-089 - Fixed _init_database_options_state() to restore saved version_type from prole.cfg so db_version_type defaults to v18 (pg18) instead of silently reverting to pg17 - Added database_options.* keys to _collect_input_snapshot() in cfg.py so distribution, version_type, and all extension toggles persist to prole.cfg Fixed: Cluster name inconsistency - Removed stale prole-dev-cluster references; all scripts now use knoe-dev-cluster - Added knoe-dev-cluster to mode-detection case in etc/prole_cfg.sh Config: conf/prole.cfg - Set kerberos_config.enabled = False, KERBEROS_AUTO_ENABLED = False - Added database_options.distribution = percona, version_type = v18 - Added all 13 extension flags set to True (postgis, pgvector, pgcrypto, pgaudit, pg_repack, pg_stat_statements, pg_buffercache, pg_freespacemap, pgrowlocks, postgres_fdw, dblink, pg_stat_monitor, pgbadger) Verification: ./install.py -s -l -v -c conf/prole.cfg completed successfully. CNPG deployed prole-db:18-089 to knoe-dev-cluster; all milestones passed. Co-authored-by: Junie <junie@jetbrains.com>
1124 lines
33 KiB
Bash
Executable File
1124 lines
33 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
# init_kerberos.sh
|
|
# Purpose:
|
|
# - Configure Kerberos realm settings for CloudNative-PG pods
|
|
# - KDC provisioning is handled by init_kdc.sh
|
|
# - Optionally run a Kerberos authentication test inside a Kubernetes pod
|
|
# - Optionally provision an in-cluster port forwarder to a Samba AD DC
|
|
#
|
|
# Usage:
|
|
# ./init_kerberos.sh initialize # apply krb5.conf to CNPG pods (best-effort)
|
|
# ./init_kerberos.sh test # run test pod + kinit (with optional AD port forward)
|
|
# ./init_kerberos.sh status # show detected config + resources
|
|
# ./init_kerberos.sh cleanup # remove AD forwarder resources
|
|
|
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
|
|
# Load environment and config via prole_cfg.sh
|
|
# 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:-initialize}
|
|
|
|
CNPG_CLUSTER_NAME=${CNPG_CLUSTER_NAME:-prole-db}
|
|
|
|
KRB5_REALM=${KRB5_REALM:-${REALM:-}}
|
|
KRB5_KDC=${KRB5_KDC:-${KDC:-}}
|
|
KRB5_ADMIN=${KRB5_ADMIN:-${SERVER:-}}
|
|
KRB5_USER=${KRB5_USER:-${USER:-${KRB5_USERNAME:-}}}
|
|
KRB5_PASSWORD=${KRB5_PASSWORD:-${PASSWORD:-}}
|
|
PROLE_KDC_NAME=${PROLE_KDC_NAME:-auth}
|
|
|
|
# AD DC forwarding (socat proxy) options
|
|
KRB5_AD_PORT_FORWARD=${KRB5_AD_PORT_FORWARD:-${AD_PORT_FORWARD:-}}
|
|
KRB5_AD_PROXY_NAME=${KRB5_AD_PROXY_NAME:-prole-kerberos-ad-forwarder}
|
|
KRB5_AD_SERVICE_NAME=${KRB5_AD_SERVICE_NAME:-prole-kerberos-ad-dc}
|
|
KRB5_AD_PROXY_IMAGE=${KRB5_AD_PROXY_IMAGE:-alpine/socat}
|
|
KRB5_AD_PROXY_HOST_NETWORK=${KRB5_AD_PROXY_HOST_NETWORK:-1}
|
|
KRB5_AD_TCP_PORTS=${KRB5_AD_TCP_PORTS:-"88 389 445 464 636"}
|
|
KRB5_AD_UDP_PORTS=${KRB5_AD_UDP_PORTS:-"88 464"}
|
|
CNPG_WAIT_TIMEOUT=${CNPG_WAIT_TIMEOUT:-300}
|
|
SERVICE_NAMESPACE=${SERVICE_NAMESPACE:-${NAMESPACE:-default}}
|
|
OPENBAO_NAMESPACE=${OPENBAO_NAMESPACE:-${SERVICE_NAMESPACE:-default}}
|
|
KRB5_AD_NAMESPACE=${KRB5_AD_NAMESPACE:-${SERVICE_NAMESPACE}}
|
|
OPENBAO_PORT_FORWARD_LOCAL=${OPENBAO_PORT_FORWARD_LOCAL:-8200}
|
|
|
|
log() { printf '%s\n' "$*"; }
|
|
err() { printf '%s\n' "$*" >&2; }
|
|
|
|
ensure_tools() {
|
|
for t in kubectl curl jq; do
|
|
command -v "$t" >/dev/null || { err "Missing required tool: $t"; exit 1; }
|
|
done
|
|
}
|
|
|
|
cnpg_supports_projected_volume_template() {
|
|
kubectl get crd clusters.postgresql.cnpg.io -o json 2>/dev/null \
|
|
| jq -e '.spec.versions[] | select(.name=="v1") | .schema.openAPIV3Schema.properties.spec.properties.projectedVolumeTemplate' >/dev/null
|
|
}
|
|
|
|
sha256_stdin() {
|
|
if command -v sha256sum >/dev/null 2>&1; then
|
|
sha256sum | awk '{print $1}'
|
|
return 0
|
|
fi
|
|
if command -v shasum >/dev/null 2>&1; then
|
|
shasum -a 256 | awk '{print $1}'
|
|
return 0
|
|
fi
|
|
cat >/dev/null
|
|
echo ""
|
|
return 0
|
|
}
|
|
|
|
ensure_namespace() {
|
|
if ! kubectl get namespace "$NAMESPACE" >/dev/null 2>&1; then
|
|
log "Creating namespace '$NAMESPACE' ..."
|
|
kubectl create namespace "$NAMESPACE" >/dev/null 2>&1 || true
|
|
fi
|
|
}
|
|
|
|
ensure_namespace_named() {
|
|
local ns="$1"
|
|
if [[ -z "$ns" ]]; then
|
|
return 0
|
|
fi
|
|
if ! kubectl get namespace "$ns" >/dev/null 2>&1; then
|
|
log "Creating namespace '$ns' ..."
|
|
kubectl create namespace "$ns" >/dev/null 2>&1 || true
|
|
fi
|
|
}
|
|
|
|
preferred_service_namespace() {
|
|
local ns="${SERVICE_NAMESPACE:-}"
|
|
local primary="${NAMESPACE:-}"
|
|
|
|
if [[ -n "$primary" && "$primary" != "default" ]]; then
|
|
if [[ -z "$ns" || "$ns" == "default" ]]; then
|
|
ns="$primary"
|
|
fi
|
|
fi
|
|
|
|
if [[ -z "$ns" ]]; then
|
|
ns="${primary:-default}"
|
|
fi
|
|
|
|
printf '%s' "$ns"
|
|
}
|
|
|
|
is_ip_address() {
|
|
[[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]
|
|
}
|
|
|
|
detect_prole_cfg() {
|
|
if [[ -n "${PROLE_CONF:-}" && -f "$PROLE_CONF/prole.cfg" ]]; then
|
|
printf '%s' "$PROLE_CONF/prole.cfg"
|
|
elif [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/conf/prole.cfg" ]]; then
|
|
printf '%s' "$PROLE_HOME/conf/prole.cfg"
|
|
elif [[ -f "$SCRIPT_DIR/../conf/prole.cfg" ]]; then
|
|
printf '%s' "$SCRIPT_DIR/../conf/prole.cfg"
|
|
fi
|
|
}
|
|
|
|
cfg_extract_key() {
|
|
local cfg="$1" key="$2"
|
|
awk -F= -v k="$key" '
|
|
/^[[:space:]]*;/ {next}
|
|
/^[[:space:]]*#/ {next}
|
|
/^[[:space:]]*\[/ {next}
|
|
$1 ~ "^[[:space:]]*" k "[[:space:]]*$" {
|
|
v=$2
|
|
sub(/^[[:space:]]+/, "", v)
|
|
sub(/[[:space:]]+$/, "", v)
|
|
val=v
|
|
}
|
|
END { print val }
|
|
' "$cfg" 2>/dev/null || true
|
|
}
|
|
|
|
normalize_child_realm_preference() {
|
|
local cfg val parent child cfg_realm cfg_use_child_set=0
|
|
cfg=$(detect_prole_cfg)
|
|
if [[ -n "$cfg" ]]; then
|
|
val=$(cfg_extract_key "$cfg" "PROLE_USE_CHILD_REALM")
|
|
if [[ -n "$val" ]]; then
|
|
cfg_use_child_set=1
|
|
if _prole_bool_true "$val"; then
|
|
PROLE_USE_CHILD_REALM=1
|
|
else
|
|
PROLE_USE_CHILD_REALM=0
|
|
fi
|
|
fi
|
|
cfg_realm=$(cfg_extract_key "$cfg" "REALM")
|
|
if [[ -z "$cfg_realm" ]]; then
|
|
cfg_realm=$(cfg_extract_key "$cfg" "kerberos_config.realm")
|
|
fi
|
|
fi
|
|
|
|
child="${PROLE_CHILD_REALM:-}"
|
|
if [[ -n "$child" ]]; then
|
|
child=$(printf '%s' "$child" | tr '[:lower:]' '[:upper:]')
|
|
fi
|
|
if [[ -n "$cfg_realm" ]]; then
|
|
cfg_realm=$(printf '%s' "$cfg_realm" | tr '[:lower:]' '[:upper:]')
|
|
fi
|
|
|
|
if [[ $cfg_use_child_set -eq 0 && -n "$cfg_realm" ]]; then
|
|
# If config doesn't explicitly request child realm, prefer the configured realm.
|
|
KRB5_REALM="$cfg_realm"
|
|
REALM="$cfg_realm"
|
|
return 0
|
|
fi
|
|
|
|
if [[ -n "${PROLE_USE_CHILD_REALM:-}" ]] && _prole_bool_true "${PROLE_USE_CHILD_REALM}"; then
|
|
if [[ -n "$child" ]]; then
|
|
KRB5_REALM="$child"
|
|
REALM="$child"
|
|
fi
|
|
return 0
|
|
fi
|
|
|
|
if [[ -n "$cfg_realm" ]]; then
|
|
KRB5_REALM="$cfg_realm"
|
|
REALM="$cfg_realm"
|
|
return 0
|
|
fi
|
|
|
|
if [[ -n "${PROLE_USE_CHILD_REALM:-}" ]] && ! _prole_bool_true "${PROLE_USE_CHILD_REALM}"; then
|
|
parent="${PROLE_PARENT_REALM:-${REALM:-}}"
|
|
if [[ -z "$parent" && -n "$child" && "$child" == *.* ]]; then
|
|
parent="${child#*.}"
|
|
fi
|
|
if [[ -n "$parent" ]]; then
|
|
parent=$(printf '%s' "$parent" | tr '[:lower:]' '[:upper:]')
|
|
KRB5_REALM="$parent"
|
|
REALM="$parent"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
prefer_cfg_realm_for_test() {
|
|
local cfg realm
|
|
cfg=$(detect_prole_cfg)
|
|
if [[ -z "$cfg" ]]; then
|
|
return 0
|
|
fi
|
|
realm=$(cfg_extract_key "$cfg" "REALM")
|
|
if [[ -z "$realm" ]]; then
|
|
realm=$(cfg_extract_key "$cfg" "kerberos_config.realm")
|
|
fi
|
|
if [[ -n "$realm" ]]; then
|
|
realm=$(printf '%s' "$realm" | tr '[:lower:]' '[:upper:]')
|
|
KRB5_REALM="$realm"
|
|
REALM="$realm"
|
|
fi
|
|
}
|
|
|
|
openbao_url() {
|
|
if [[ -n "${PROLE_OPENBAO_URL:-}" ]]; then
|
|
echo "$PROLE_OPENBAO_URL"
|
|
return 0
|
|
fi
|
|
if prole_is_in_cluster; then
|
|
echo "http://openbao.${OPENBAO_NAMESPACE}.svc.cluster.local:8200"
|
|
return 0
|
|
fi
|
|
if curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
|
echo "http://127.0.0.1:8200"
|
|
return 0
|
|
elif curl -sS "http://127.0.0.1:8200/v1/sys/health" >/dev/null 2>&1; then
|
|
echo "http://127.0.0.1:8200"
|
|
return 0
|
|
else
|
|
echo ""
|
|
return 0
|
|
fi
|
|
}
|
|
|
|
_OPENBAO_PF_PID=""
|
|
_OPENBAO_PF_STARTED=0
|
|
|
|
start_openbao_port_forward() {
|
|
local port="${OPENBAO_PORT_FORWARD_LOCAL}"
|
|
local url="http://127.0.0.1:${port}"
|
|
|
|
if curl -sS "$url/v1/sys/health" >/dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
command -v kubectl >/dev/null 2>&1 || return 1
|
|
kubectl -n "${OPENBAO_NAMESPACE:-default}" port-forward "svc/openbao" "${port}:8200" >/dev/null 2>&1 &
|
|
_OPENBAO_PF_PID=$!
|
|
_OPENBAO_PF_STARTED=1
|
|
|
|
for _ in {1..20}; do
|
|
if curl -sS "$url/v1/sys/health" >/dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
if ! kill -0 "$_OPENBAO_PF_PID" >/dev/null 2>&1; then
|
|
break
|
|
fi
|
|
sleep 1
|
|
done
|
|
return 1
|
|
}
|
|
|
|
stop_openbao_port_forward() {
|
|
if [[ "${_OPENBAO_PF_STARTED}" == "1" && -n "${_OPENBAO_PF_PID:-}" ]]; then
|
|
kill "$_OPENBAO_PF_PID" >/dev/null 2>&1 || true
|
|
wait "$_OPENBAO_PF_PID" >/dev/null 2>&1 || true
|
|
fi
|
|
_OPENBAO_PF_STARTED=0
|
|
_OPENBAO_PF_PID=""
|
|
}
|
|
|
|
ensure_openbao_url() {
|
|
local url
|
|
url="$(openbao_url)"
|
|
if [[ -n "$url" ]]; then
|
|
printf '%s' "$url"
|
|
return 0
|
|
fi
|
|
if start_openbao_port_forward; then
|
|
trap stop_openbao_port_forward EXIT
|
|
printf 'http://127.0.0.1:%s' "${OPENBAO_PORT_FORWARD_LOCAL}"
|
|
return 0
|
|
fi
|
|
printf ''
|
|
return 0
|
|
}
|
|
|
|
openbao_token() {
|
|
local token=""
|
|
if [[ -f "$PROLE_SERVICE/secrets/openbao-root-token" ]]; then
|
|
token=$(cat "$PROLE_SERVICE/secrets/openbao-root-token" 2>/dev/null || true)
|
|
fi
|
|
if [[ -z "$token" || "$token" == "root" ]]; then
|
|
token="${OPENBAO_ROOT_TOKEN:-}"
|
|
fi
|
|
if [[ -z "$token" || "$token" == "root" ]]; then
|
|
if command -v kubectl >/dev/null 2>&1; then
|
|
token=$(kubectl -n "${OPENBAO_NAMESPACE:-default}" get secret openbao-root -o jsonpath='{.data.token}' 2>/dev/null | base64 -d 2>/dev/null || true)
|
|
fi
|
|
fi
|
|
printf '%s' "$token"
|
|
}
|
|
|
|
fetch_openbao_secret() {
|
|
local path="$1"
|
|
local key="$2"
|
|
local token url
|
|
token=$(openbao_token)
|
|
url=$(ensure_openbao_url)
|
|
if [[ -n "$token" && -n "$url" ]]; then
|
|
local out=""
|
|
out=$(curl -sS -H "X-Vault-Token: $token" "$url/v1/kv/data/$path" 2>/dev/null || true)
|
|
if [[ -n "$out" ]]; then
|
|
echo "$out" | jq -r ".data.data.\"$key\"" 2>/dev/null || echo ""
|
|
return 0
|
|
fi
|
|
fi
|
|
# Fallback: use in-cluster OpenBao CLI if available
|
|
if [[ -n "$token" ]] && command -v kubectl >/dev/null 2>&1; then
|
|
local bao_out=""
|
|
bao_out=$(kubectl -n "${OPENBAO_NAMESPACE:-default}" exec deploy/openbao -- sh -c \
|
|
"BAO_ADDR=http://127.0.0.1:8200 BAO_TOKEN=${token} bao kv get -field=${key} kv/${path}" 2>/dev/null || true)
|
|
if [[ -n "$bao_out" ]]; then
|
|
printf '%s' "$bao_out"
|
|
return 0
|
|
fi
|
|
fi
|
|
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"
|
|
else
|
|
# Fallback to database password if kerberos secret is missing
|
|
path="prole/${NAMESPACE:-default}/db"
|
|
fetched=$(fetch_openbao_secret "$path" "password")
|
|
if [[ -n "$fetched" && "$fetched" != "null" ]]; then
|
|
KRB5_PASSWORD="$fetched"
|
|
fi
|
|
fi
|
|
fi
|
|
}
|
|
|
|
resolve_krb5_defaults() {
|
|
local svc_ns kdc_app kdc_svc
|
|
svc_ns="${SERVICE_NAMESPACE:-${NAMESPACE:-default}}"
|
|
kdc_app="${PROLE_KDC_NAME:-auth}"
|
|
kdc_svc="${PROLE_KDC_SERVICE:-auth}"
|
|
|
|
# If the KDC pod is available in the service namespace, default to its service endpoint
|
|
# only when no explicit KRB5_KDC was provided.
|
|
if [[ -z "${KRB5_KDC}" ]]; then
|
|
if kubectl get pod -n "$svc_ns" -l "app=${kdc_app}" >/dev/null 2>&1; then
|
|
KRB5_KDC="${kdc_svc}.${svc_ns}.svc.cluster.local"
|
|
KRB5_ADMIN="${kdc_svc}.${svc_ns}.svc.cluster.local"
|
|
if [[ -z "${KRB5_REALM}" ]]; then
|
|
KRB5_REALM="PROLE.ORG"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if [[ -z "${KRB5_REALM}" && -n "${REALM:-}" ]]; then
|
|
KRB5_REALM="$REALM"
|
|
fi
|
|
if [[ -z "${KRB5_KDC}" && -n "${DOMAIN:-}" ]]; then
|
|
KRB5_KDC="kdc.${DOMAIN}"
|
|
fi
|
|
if [[ -z "${KRB5_ADMIN}" ]]; then
|
|
if [[ -n "${KRB5_KDC}" ]]; then
|
|
KRB5_ADMIN=$(printf '%s' "${KRB5_KDC}" | awk -F',' '{print $1}' | xargs)
|
|
elif [[ -n "${DOMAIN:-}" ]]; then
|
|
KRB5_ADMIN="kdc.${DOMAIN}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
primary_kdc() {
|
|
if [[ -z "${KRB5_KDC}" ]]; then
|
|
echo ""
|
|
return 0
|
|
fi
|
|
printf '%s' "${KRB5_KDC}" | awk -F',' '{print $1}' | xargs
|
|
}
|
|
|
|
is_private_ip() {
|
|
local ip="$1"
|
|
if [[ "$ip" =~ ^10\.|^192\.168\.|^172\.(1[6-9]|2[0-9]|3[0-1])\.|^169\.254\. ]]; then
|
|
return 0
|
|
fi
|
|
return 1
|
|
}
|
|
|
|
default_port_forward_if_local() {
|
|
if [[ -n "${KRB5_AD_PORT_FORWARD}" ]]; then
|
|
return
|
|
fi
|
|
local kdc_host
|
|
kdc_host=$(primary_kdc)
|
|
# Only auto-enable for loopback or localhost by default.
|
|
# 10.x.x.x and other private ranges are often reachable directly in K3d/Docker.
|
|
if [[ -n "$kdc_host" ]] && [[ "$kdc_host" == "127.0.0.1" || "$kdc_host" == "localhost" ]]; then
|
|
KRB5_AD_PORT_FORWARD=1
|
|
else
|
|
KRB5_AD_PORT_FORWARD=0
|
|
fi
|
|
}
|
|
|
|
sync_prole_kdc_trust() {
|
|
if [[ ! -x "$SCRIPT_DIR/init_kdc.sh" ]]; then
|
|
return 0
|
|
fi
|
|
local svc_ns
|
|
svc_ns=$(preferred_service_namespace)
|
|
SERVICE_NAMESPACE="$svc_ns" \
|
|
KRB5_REALM="$KRB5_REALM" \
|
|
KRB5_KDC="$KRB5_KDC" \
|
|
KRB5_ADMIN="$KRB5_ADMIN" \
|
|
KRB5_USER="$KRB5_USER" \
|
|
KRB5_PASSWORD="$KRB5_PASSWORD" \
|
|
"$SCRIPT_DIR/init_kdc.sh" update || true
|
|
}
|
|
|
|
ensure_krb5_conf_configmap() {
|
|
log "Creating/updating ConfigMap prole-krb5-conf ..."
|
|
|
|
local kdc_val admin_val realm_val domain_val
|
|
realm_val="$KRB5_REALM"
|
|
domain_val="${DOMAIN:-}"
|
|
if [[ -z "$domain_val" && -n "$realm_val" ]]; then
|
|
domain_val=$(printf '%s' "$realm_val" | tr '[:upper:]' '[:lower:]')
|
|
fi
|
|
kdc_val="$KRB5_KDC"
|
|
admin_val="${KRB5_ADMIN:-}"
|
|
|
|
if [[ -z "$realm_val" || -z "$kdc_val" ]]; then
|
|
err "ERROR: KRB5_REALM and KRB5_KDC must be set to build krb5.conf."
|
|
return 1
|
|
fi
|
|
|
|
if [[ -z "$admin_val" ]]; then
|
|
admin_val=$(printf '%s' "$kdc_val" | awk -F',' '{print $1}' | xargs)
|
|
fi
|
|
|
|
local TMP
|
|
TMP=$(mktemp)
|
|
cat >"$TMP" <<EOF
|
|
[libdefaults]
|
|
default_realm = $realm_val
|
|
dns_lookup_realm = false
|
|
dns_lookup_kdc = false
|
|
|
|
[realms]
|
|
$realm_val = {
|
|
kdc = $kdc_val
|
|
admin_server = $admin_val
|
|
}
|
|
|
|
[domain_realm]
|
|
.$domain_val = $realm_val
|
|
$domain_val = $realm_val
|
|
EOF
|
|
kubectl -n "$NAMESPACE" create configmap prole-krb5-conf --from-file=krb5.conf="$TMP" --dry-run=client -o yaml | kubectl apply --validate=false -f -
|
|
rm -f "$TMP"
|
|
}
|
|
|
|
get_krb5_conf() {
|
|
kubectl -n "$NAMESPACE" get configmap prole-krb5-conf -o jsonpath='{.data.krb5\.conf}' 2>/dev/null || true
|
|
}
|
|
|
|
wait_for_cnpg_pods() {
|
|
local timeout=${1:-$CNPG_WAIT_TIMEOUT}
|
|
local start_time
|
|
start_time=$(date +%s)
|
|
|
|
while true; do
|
|
if kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" >/dev/null 2>&1; then
|
|
if kubectl -n "$NAMESPACE" get pods -l "cnpg.io/cluster=$CNPG_CLUSTER_NAME" --no-headers 2>/dev/null | grep -q .; then
|
|
if kubectl -n "$NAMESPACE" wait --for=condition=Ready pod -l "cnpg.io/cluster=$CNPG_CLUSTER_NAME" --timeout=60s >/dev/null 2>&1; then
|
|
return 0
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
if (( $(date +%s) - start_time > timeout )); then
|
|
err "ERROR: Timed out waiting for CNPG pods for cluster '$CNPG_CLUSTER_NAME' in namespace '$NAMESPACE'."
|
|
return 1
|
|
fi
|
|
log "Waiting for CNPG pods for cluster '$CNPG_CLUSTER_NAME'..."
|
|
sleep 3
|
|
done
|
|
}
|
|
|
|
patch_cnpg_cluster_for_auth() {
|
|
log "Patching CNPG Cluster $CNPG_CLUSTER_NAME to enable GSSAPI (best-effort) ..."
|
|
log "Current Context: $(kubectl config current-context 2>/dev/null || echo 'unknown')"
|
|
log "Target Namespace: $NAMESPACE"
|
|
|
|
if ! kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" >/dev/null 2>&1; then
|
|
err "ERROR: Cluster '$CNPG_CLUSTER_NAME' not found in namespace '$NAMESPACE'."
|
|
return 1
|
|
fi
|
|
|
|
# Best-effort patch; schema may vary with CNPG version.
|
|
# We remove krb_srvname as it is unrecognized in some PG 17 builds.
|
|
# We revert certificates to default to avoid operator TLS issues.
|
|
|
|
local kerberos_enabled="${KERBEROS_ENABLED:-${ENABLED:-false}}"
|
|
local pg_hba
|
|
if [[ "$kerberos_enabled" == "true" || "$kerberos_enabled" == "True" || "$kerberos_enabled" == "1" ]]; then
|
|
pg_hba="[
|
|
\"local all postgres trust\",
|
|
\"host all postgres all scram-sha-256\",
|
|
\"host all authenticator all scram-sha-256\",
|
|
\"host all all all gss include_realm=1 krb_realm=$KRB5_REALM\",
|
|
\"host all all all scram-sha-256\"
|
|
]"
|
|
else
|
|
pg_hba="[
|
|
\"local all postgres trust\",
|
|
\"host all postgres all scram-sha-256\",
|
|
\"host all all all scram-sha-256\"
|
|
]"
|
|
fi
|
|
|
|
if ! kubectl -n "$NAMESPACE" patch cluster "$CNPG_CLUSTER_NAME" --type merge -p "{
|
|
\"spec\": {
|
|
\"postgresql\": {
|
|
\"parameters\": {\"krb_srvname\": null},
|
|
\"pg_hba\": $pg_hba
|
|
},
|
|
\"certificates\": {
|
|
\"serverCASecret\": null,
|
|
\"clientCASecret\": null
|
|
}
|
|
}
|
|
}"; then
|
|
err "Note: patch failed, but cluster exists. It may need adjustment for your CNPG version."
|
|
fi
|
|
}
|
|
|
|
reload_cnpg_cluster() {
|
|
if command -v kubectl >/dev/null 2>&1 && kubectl cnpg >/dev/null 2>&1; then
|
|
log "Reloading CNPG Cluster $CNPG_CLUSTER_NAME with kubectl cnpg ..."
|
|
kubectl cnpg reload -n "$NAMESPACE" "$CNPG_CLUSTER_NAME" || true
|
|
return 0
|
|
fi
|
|
|
|
local primary
|
|
primary=$(kubectl -n "$NAMESPACE" get pods -l "cnpg.io/cluster=$CNPG_CLUSTER_NAME" -o json 2>/dev/null \
|
|
| jq -r '.items[] | select(.metadata.labels["cnpg.io/instanceRole"]=="primary" or .metadata.labels["cnpg.io/role"]=="primary" or .metadata.labels["role"]=="primary") | .metadata.name' \
|
|
| head -n1)
|
|
|
|
if [[ -z "$primary" ]]; then
|
|
err "Unable to detect primary pod for CNPG cluster '$CNPG_CLUSTER_NAME'; skipping reload."
|
|
return 1
|
|
fi
|
|
|
|
log "Reloading PostgreSQL configuration in $primary ..."
|
|
kubectl -n "$NAMESPACE" exec "$primary" -c postgres -- sh -c 'pg_ctl reload -D "${PGDATA:-/var/lib/postgresql/data}"' || true
|
|
}
|
|
|
|
apply_krb5_env_to_cnpg() {
|
|
local conf_path="${1:-/projected/krb5/krb5.conf}"
|
|
local conf_hash=""
|
|
local conf
|
|
conf=$(get_krb5_conf)
|
|
if [[ -n "$conf" ]]; then
|
|
conf_hash=$(printf '%s' "$conf" | sha256_stdin)
|
|
fi
|
|
if ! kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" >/dev/null 2>&1; then
|
|
return 1
|
|
fi
|
|
|
|
local current_env updated_env
|
|
current_env=$(kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" -o json 2>/dev/null | jq -c '.spec.env // []')
|
|
updated_env=$(printf '%s' "$current_env" | jq -c \
|
|
--arg name "KRB5_CONFIG" --arg value "$conf_path" \
|
|
--arg hash_name "PROLE_KRB5_CONF_SHA" --arg hash_value "$conf_hash" '
|
|
map(select(.name != $name and .name != $hash_name)) +
|
|
[{"name": $name, "value": $value}] +
|
|
(if $hash_value == "" then [] else [{"name": $hash_name, "value": $hash_value}] end)
|
|
')
|
|
|
|
kubectl -n "$NAMESPACE" patch cluster "$CNPG_CLUSTER_NAME" --type merge -p "{\"spec\":{\"env\":$updated_env}}" >/dev/null 2>&1 || true
|
|
}
|
|
|
|
apply_krb5_conf_mount_to_cnpg() {
|
|
if ! kubectl -n "$NAMESPACE" get cluster "$CNPG_CLUSTER_NAME" >/dev/null 2>&1; then
|
|
err "ERROR: Cluster '$CNPG_CLUSTER_NAME' not found in namespace '$NAMESPACE'."
|
|
return 1
|
|
fi
|
|
if ! cnpg_supports_projected_volume_template; then
|
|
err "CNPG CRD does not support spec.projectedVolumeTemplate; falling back to in-pod krb5.conf update."
|
|
return 1
|
|
fi
|
|
|
|
local conf
|
|
conf=$(get_krb5_conf)
|
|
if [[ -z "$conf" ]]; then
|
|
err "Missing krb5.conf data in ConfigMap prole-krb5-conf."
|
|
return 1
|
|
fi
|
|
|
|
log "Patching CNPG Cluster $CNPG_CLUSTER_NAME to mount krb5.conf via projected volume ..."
|
|
if ! kubectl -n "$NAMESPACE" patch cluster "$CNPG_CLUSTER_NAME" --type merge -p "{
|
|
\"spec\": {
|
|
\"projectedVolumeTemplate\": {
|
|
\"sources\": [
|
|
{\"configMap\": {\"name\": \"prole-krb5-conf\", \"items\": [{\"key\": \"krb5.conf\", \"path\": \"krb5/krb5.conf\"}]}}
|
|
]
|
|
}
|
|
}
|
|
}" >/dev/null 2>&1; then
|
|
err "Note: unable to apply krb5.conf mount via patch."
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
apply_krb5_conf_to_cnpg_pods() {
|
|
local conf
|
|
conf=$(get_krb5_conf)
|
|
if [[ -z "$conf" ]]; then
|
|
err "Missing krb5.conf data in ConfigMap prole-krb5-conf."
|
|
return 1
|
|
fi
|
|
|
|
local pods
|
|
pods=$(kubectl -n "$NAMESPACE" get pods -l "cnpg.io/cluster=$CNPG_CLUSTER_NAME" -o jsonpath='{.items[*].metadata.name}' 2>/dev/null || true)
|
|
if [[ -z "$pods" ]]; then
|
|
err "No CNPG pods found for cluster '$CNPG_CLUSTER_NAME' in namespace '$NAMESPACE'."
|
|
return 1
|
|
fi
|
|
|
|
for pod in $pods; do
|
|
log "Updating /controller/krb5.conf in pod $pod ..."
|
|
if printf '%s' "$conf" | kubectl -n "$NAMESPACE" exec -i "$pod" -c postgres -- sh -c 'tee /controller/krb5.conf >/dev/null'; then
|
|
log "[OK] Updated /controller/krb5.conf in $pod"
|
|
else
|
|
err "[WARN] Unable to write /controller/krb5.conf in $pod."
|
|
err " Consider mounting ConfigMap prole-krb5-conf into the CNPG pods or updating the image."
|
|
fi
|
|
done
|
|
}
|
|
|
|
apply_krb5_conf_to_test_pod() {
|
|
local conf pod
|
|
conf=$(get_krb5_conf)
|
|
if [[ -z "$conf" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
# Look for the 'auth' pod (MIT KDC or test pod)
|
|
pod=$(kubectl -n "$NAMESPACE" get pods -l "app=auth" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true)
|
|
if [[ -z "$pod" ]]; then
|
|
# Fallback to 'dog' (legacy name)
|
|
pod=$(kubectl -n "$NAMESPACE" get pods -l "app=dog" -o jsonpath='{.items[0].metadata.name}' 2>/dev/null || true)
|
|
fi
|
|
|
|
if [[ -z "$pod" ]]; then
|
|
return 0
|
|
fi
|
|
|
|
log "Updating /etc/krb5.conf in test pod $pod ..."
|
|
if printf '%s' "$conf" | kubectl -n "$NAMESPACE" exec -i "$pod" -- sh -c 'tee /etc/krb5.conf >/dev/null'; then
|
|
log "[OK] Updated /etc/krb5.conf in $pod"
|
|
else
|
|
err "[WARN] Unable to write /etc/krb5.conf in $pod."
|
|
fi
|
|
}
|
|
|
|
apply_ad_forwarder() {
|
|
local kdc_host
|
|
kdc_host=$(primary_kdc)
|
|
if [[ -z "$kdc_host" ]]; then
|
|
err "KRB5_KDC is required to set up the AD port forwarder."
|
|
exit 1
|
|
fi
|
|
|
|
local host_net_block=""
|
|
local dns_policy_block=""
|
|
if [[ "$KRB5_AD_PROXY_HOST_NETWORK" == "1" ]]; then
|
|
host_net_block=" hostNetwork: true"
|
|
dns_policy_block=" dnsPolicy: ClusterFirstWithHostNet"
|
|
fi
|
|
|
|
local tcp_cmd=""
|
|
local udp_cmd=""
|
|
local port
|
|
for port in $KRB5_AD_TCP_PORTS; do
|
|
local target_port=$port
|
|
if [[ "$KRB5_AD_PROXY_HOST_NETWORK" == "1" ]]; then
|
|
target_port=$((port + 10000))
|
|
fi
|
|
tcp_cmd+="socat -d -d TCP-LISTEN:${target_port},fork,reuseaddr TCP:${kdc_host}:${port} & "
|
|
done
|
|
for port in $KRB5_AD_UDP_PORTS; do
|
|
local target_port=$port
|
|
if [[ "$KRB5_AD_PROXY_HOST_NETWORK" == "1" ]]; then
|
|
target_port=$((port + 10000))
|
|
fi
|
|
udp_cmd+="socat -d -d UDP-LISTEN:${target_port},fork,reuseaddr UDP:${kdc_host}:${port} & "
|
|
done
|
|
|
|
local forward_cmd="${tcp_cmd}${udp_cmd}wait"
|
|
|
|
log "Applying AD DC forwarder '${KRB5_AD_PROXY_NAME}' in namespace '${KRB5_AD_NAMESPACE}' (target ${kdc_host}) ..."
|
|
cat <<EOF | kubectl apply --validate=false -n "$KRB5_AD_NAMESPACE" -f -
|
|
apiVersion: apps/v1
|
|
kind: Deployment
|
|
metadata:
|
|
name: ${KRB5_AD_PROXY_NAME}
|
|
namespace: ${KRB5_AD_NAMESPACE}
|
|
spec:
|
|
replicas: 1
|
|
selector:
|
|
matchLabels:
|
|
app: ${KRB5_AD_PROXY_NAME}
|
|
template:
|
|
metadata:
|
|
labels:
|
|
app: ${KRB5_AD_PROXY_NAME}
|
|
spec:
|
|
${host_net_block}
|
|
${dns_policy_block}
|
|
containers:
|
|
- name: ad-forwarder
|
|
image: ${KRB5_AD_PROXY_IMAGE}
|
|
command: ["/bin/sh", "-c"]
|
|
args:
|
|
- >-
|
|
${forward_cmd}
|
|
ports:
|
|
$(
|
|
for port in $KRB5_AD_TCP_PORTS; do
|
|
local target_port=$port
|
|
if [[ "$KRB5_AD_PROXY_HOST_NETWORK" == "1" ]]; then
|
|
target_port=$((port + 10000))
|
|
fi
|
|
printf " - containerPort: %s\n protocol: TCP\n" "$target_port"
|
|
done
|
|
for port in $KRB5_AD_UDP_PORTS; do
|
|
local target_port=$port
|
|
if [[ "$KRB5_AD_PROXY_HOST_NETWORK" == "1" ]]; then
|
|
target_port=$((port + 10000))
|
|
fi
|
|
printf " - containerPort: %s\n protocol: UDP\n" "$target_port"
|
|
done
|
|
)
|
|
---
|
|
apiVersion: v1
|
|
kind: Service
|
|
metadata:
|
|
name: ${KRB5_AD_SERVICE_NAME}
|
|
namespace: ${KRB5_AD_NAMESPACE}
|
|
spec:
|
|
selector:
|
|
app: ${KRB5_AD_PROXY_NAME}
|
|
ports:
|
|
$(
|
|
for port in $KRB5_AD_TCP_PORTS; do
|
|
local target_port=$port
|
|
if [[ "$KRB5_AD_PROXY_HOST_NETWORK" == "1" ]]; then
|
|
target_port=$((port + 10000))
|
|
fi
|
|
name="tcp-${port}"
|
|
printf " - name: %s\n port: %s\n targetPort: %s\n protocol: TCP\n" "$name" "$port" "$target_port"
|
|
done
|
|
for port in $KRB5_AD_UDP_PORTS; do
|
|
local target_port=$port
|
|
if [[ "$KRB5_AD_PROXY_HOST_NETWORK" == "1" ]]; then
|
|
target_port=$((port + 10000))
|
|
fi
|
|
name="udp-${port}"
|
|
printf " - name: %s\n port: %s\n targetPort: %s\n protocol: UDP\n" "$name" "$port" "$target_port"
|
|
done
|
|
)
|
|
EOF
|
|
|
|
kubectl -n "$KRB5_AD_NAMESPACE" rollout status deploy/${KRB5_AD_PROXY_NAME} --timeout=120s || true
|
|
}
|
|
|
|
cleanup_ad_forwarder() {
|
|
log "Removing AD forwarder resources (if present)..."
|
|
kubectl -n "$KRB5_AD_NAMESPACE" delete service "$KRB5_AD_SERVICE_NAME" --ignore-not-found
|
|
kubectl -n "$KRB5_AD_NAMESPACE" delete deployment "$KRB5_AD_PROXY_NAME" --ignore-not-found
|
|
}
|
|
|
|
ensure_kdc_network_allow() {
|
|
local kdc_host cidr ns
|
|
kdc_host=$(primary_kdc)
|
|
if [[ -z "$kdc_host" ]]; then
|
|
kdc_host="${AD_DC_IP:-${KDC_ANSIBLE_DETECTED:-${KDC_AUTO_DETECTED:-}}}"
|
|
fi
|
|
if [[ -z "$kdc_host" ]]; then
|
|
return 0
|
|
fi
|
|
if ! is_ip_address "$kdc_host"; then
|
|
local fallback_ip="${AD_DC_IP:-${KDC_ANSIBLE_DETECTED:-${KDC_AUTO_DETECTED:-}}}"
|
|
if [[ -n "$fallback_ip" ]] && is_ip_address "$fallback_ip"; then
|
|
kdc_host="$fallback_ip"
|
|
log "Using AD DC IP for KDC egress policy: $kdc_host"
|
|
else
|
|
log "Skipping KDC egress policy (no IP for KDC: $kdc_host)."
|
|
return 0
|
|
fi
|
|
fi
|
|
cidr="${kdc_host}/32"
|
|
ns=$(preferred_service_namespace)
|
|
|
|
ensure_namespace_named "$ns"
|
|
log "Ensuring KDC egress policy in namespace '${ns}' ..."
|
|
cat <<EOF | kubectl apply --validate=false -n "$ns" -f -
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: prole-kdc-egress
|
|
namespace: ${ns}
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app: ${PROLE_KDC_NAME:-dog}
|
|
policyTypes:
|
|
- Egress
|
|
egress:
|
|
- to:
|
|
- ipBlock:
|
|
cidr: ${cidr}
|
|
ports:
|
|
$(for port in $KRB5_AD_TCP_PORTS; do printf " - protocol: TCP\n port: %s\n" "$port"; done)
|
|
$(for port in $KRB5_AD_UDP_PORTS; do printf " - protocol: UDP\n port: %s\n" "$port"; done)
|
|
- protocol: TCP
|
|
port: 53
|
|
- protocol: UDP
|
|
port: 53
|
|
- protocol: TCP
|
|
port: 749 # Kadmin
|
|
- protocol: TCP
|
|
port: 636 # LDAPS
|
|
- to:
|
|
- namespaceSelector: {} # Allow internal cluster traffic
|
|
---
|
|
apiVersion: networking.k8s.io/v1
|
|
kind: NetworkPolicy
|
|
metadata:
|
|
name: prole-ad-forwarder-egress
|
|
namespace: ${ns}
|
|
spec:
|
|
podSelector:
|
|
matchLabels:
|
|
app: ${KRB5_AD_PROXY_NAME}
|
|
policyTypes:
|
|
- Egress
|
|
egress:
|
|
- to:
|
|
- ipBlock:
|
|
cidr: ${cidr}
|
|
EOF
|
|
}
|
|
|
|
maybe_apply_k3d_route_fix() {
|
|
local norm mode
|
|
mode="${PROLE_MODE:-}"
|
|
if command -v prole_normalize_mode >/dev/null 2>&1; then
|
|
norm=$(prole_normalize_mode "$mode")
|
|
else
|
|
norm="$mode"
|
|
fi
|
|
if [[ "$norm" != "k3d" ]]; then
|
|
return 0
|
|
fi
|
|
if [[ -x "$SCRIPT_DIR/k3d_route_fix.sh" ]]; then
|
|
log "Applying k3d route fix (best-effort) ..."
|
|
"$SCRIPT_DIR/k3d_route_fix.sh" >/dev/null 2>&1 || err "WARN: k3d route fix failed; continuing."
|
|
fi
|
|
}
|
|
|
|
initialize() {
|
|
ensure_tools
|
|
ensure_namespace
|
|
resolve_krb5_defaults
|
|
normalize_child_realm_preference
|
|
prefer_cfg_realm_for_test
|
|
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
|
|
log "Kerberos is disabled (KERBEROS_ENABLED=$enabled). Skipping initialization."
|
|
# Cleanup any existing Kerberos config if it was previously enabled
|
|
kubectl -n "$NAMESPACE" delete configmap prole-krb5-conf --ignore-not-found
|
|
return 0
|
|
fi
|
|
|
|
if [[ -z "${KRB5_REALM}" || -z "${KRB5_KDC}" ]]; then
|
|
err "ERROR: KRB5_REALM and KRB5_KDC must be set for Kerberos initialization."
|
|
err " Set KRB5_REALM/KRB5_KDC or REALM/DOMAIN in env or config."
|
|
exit 1
|
|
fi
|
|
|
|
sync_prole_kdc_trust
|
|
ensure_krb5_conf_configmap
|
|
if apply_krb5_conf_mount_to_cnpg; then
|
|
apply_krb5_env_to_cnpg "/projected/krb5/krb5.conf"
|
|
wait_for_cnpg_pods "$CNPG_WAIT_TIMEOUT"
|
|
else
|
|
wait_for_cnpg_pods "$CNPG_WAIT_TIMEOUT"
|
|
apply_krb5_conf_to_cnpg_pods
|
|
apply_krb5_env_to_cnpg "/controller/krb5.conf"
|
|
fi
|
|
patch_cnpg_cluster_for_auth || true
|
|
reload_cnpg_cluster || true
|
|
log "Kerberos initialization complete."
|
|
}
|
|
|
|
run_test() {
|
|
ensure_tools
|
|
ensure_namespace
|
|
resolve_krb5_defaults
|
|
normalize_child_realm_preference
|
|
prefer_cfg_realm_for_test
|
|
resolve_krb5_password
|
|
|
|
KRB5_USER="${KERBEROS_TEST_ADMIN_USER:-administrator}"
|
|
|
|
if [[ -z "${KRB5_REALM}" || -z "${KRB5_KDC}" || -z "${KRB5_USER}" || -z "${KRB5_PASSWORD}" ]]; then
|
|
err "ERROR: Missing Kerberos configuration. Ensure KRB5_REALM, KRB5_KDC, KRB5_USER, KRB5_PASSWORD are set."
|
|
exit 1
|
|
fi
|
|
|
|
local mode
|
|
mode="${PROLE_MODE:-}"
|
|
local prefer_parent="${KERBEROS_TEST_PREFER_PARENT_REALM:-1}"
|
|
if _prole_bool_true "$prefer_parent" && [[ -n "${PROLE_PARENT_REALM:-}" ]]; then
|
|
KRB5_REALM="${PROLE_PARENT_REALM}"
|
|
REALM="${PROLE_PARENT_REALM}"
|
|
elif _prole_bool_true "$prefer_parent" && [[ -n "${KRB5_REALM:-}" && "${KRB5_REALM}" == *.* ]]; then
|
|
local derived_parent="${KRB5_REALM#*.}"
|
|
KRB5_REALM="${derived_parent}"
|
|
REALM="${derived_parent}"
|
|
fi
|
|
local test_use_child="${PROLE_USE_CHILD_REALM:-}"
|
|
if _prole_bool_true "$prefer_parent"; then
|
|
test_use_child=0
|
|
fi
|
|
local ad_forward_explicit=0
|
|
if [[ -n "${KRB5_AD_PORT_FORWARD}" ]]; then
|
|
ad_forward_explicit=1
|
|
fi
|
|
local cleanup_after_test=0
|
|
|
|
default_port_forward_if_local
|
|
|
|
if [[ "$mode" == "k3s" && "$ad_forward_explicit" -eq 0 ]]; then
|
|
KRB5_AD_PORT_FORWARD=0
|
|
fi
|
|
|
|
local normalized=""
|
|
if command -v prole_normalize_mode >/dev/null 2>&1; then
|
|
normalized=$(prole_normalize_mode "$mode")
|
|
else
|
|
normalized="$mode"
|
|
fi
|
|
if [[ "$normalized" == "k3s" || "$normalized" == "k3d" ]]; then
|
|
ensure_kdc_network_allow || true
|
|
maybe_apply_k3d_route_fix
|
|
fi
|
|
|
|
local effective_kdc="$KRB5_KDC"
|
|
if [[ "${KRB5_AD_PORT_FORWARD}" == "1" ]]; then
|
|
ensure_namespace_named "$KRB5_AD_NAMESPACE"
|
|
local forwarder_preexisting=0
|
|
if kubectl -n "$KRB5_AD_NAMESPACE" get deploy "$KRB5_AD_PROXY_NAME" >/dev/null 2>&1; then
|
|
forwarder_preexisting=1
|
|
fi
|
|
apply_ad_forwarder
|
|
if [[ "$KRB5_AD_SERVICE_NAME" == *.* ]]; then
|
|
effective_kdc="${KRB5_AD_SERVICE_NAME}"
|
|
else
|
|
effective_kdc="${KRB5_AD_SERVICE_NAME}.${KRB5_AD_NAMESPACE}.svc.cluster.local"
|
|
fi
|
|
if [[ "$mode" == "k3d" && "$forwarder_preexisting" -eq 0 ]]; then
|
|
cleanup_after_test=1
|
|
trap cleanup_ad_forwarder EXIT
|
|
fi
|
|
fi
|
|
|
|
log "Using KDC endpoint for test: ${effective_kdc}"
|
|
|
|
KRB5_KDC="$effective_kdc"
|
|
if [[ "${KRB5_AD_PORT_FORWARD}" == "1" ]]; then
|
|
KRB5_ADMIN="$effective_kdc"
|
|
fi
|
|
ensure_krb5_conf_configmap
|
|
apply_krb5_conf_to_test_pod
|
|
|
|
if [[ -x "$SCRIPT_DIR/init_kerberos_test.sh" ]]; then
|
|
local test_samba_dns="${SAMBA_DNS_SERVER:-}"
|
|
if [[ "${KRB5_AD_PORT_FORWARD}" == "1" ]]; then
|
|
test_samba_dns="$effective_kdc"
|
|
fi
|
|
local kdc_ns
|
|
kdc_ns=$(preferred_service_namespace)
|
|
run_kerberos_test_loop() {
|
|
local test_attempt=1
|
|
local test_max_attempts=2
|
|
while true; do
|
|
if PROLE_USE_CHILD_REALM="$test_use_child" \
|
|
KRB5_REALM="$KRB5_REALM" KRB5_KDC="$effective_kdc" KRB5_ADMIN="$KRB5_ADMIN" \
|
|
KRB5_USER="${KERBEROS_TEST_ADMIN_USER:-administrator}" KRB5_PASSWORD="$KRB5_PASSWORD" \
|
|
SAMBA_ADMIN_USER="${KERBEROS_TEST_ADMIN_USER:-administrator}" SAMBA_ADMIN_PASSWORD="$KRB5_PASSWORD" \
|
|
SAMBA_DNS_SERVER="$test_samba_dns" \
|
|
"$SCRIPT_DIR/init_kerberos_test.sh" test; then
|
|
return 0
|
|
fi
|
|
|
|
if [[ $test_attempt -ge $test_max_attempts ]]; then
|
|
err "Kerberos test failed after ${test_attempt} attempts."
|
|
return 1
|
|
fi
|
|
|
|
err "Kerberos test failed; resetting '${PROLE_KDC_NAME}' pod and retrying..."
|
|
kubectl -n "$kdc_ns" delete pod -l "app=${PROLE_KDC_NAME}" --ignore-not-found >/dev/null 2>&1 || true
|
|
kubectl -n "$kdc_ns" rollout status deploy/"$PROLE_KDC_NAME" --timeout=120s >/dev/null 2>&1 || true
|
|
test_attempt=$((test_attempt + 1))
|
|
done
|
|
}
|
|
|
|
local tried_parent=0
|
|
while true; do
|
|
if run_kerberos_test_loop; then
|
|
break
|
|
fi
|
|
|
|
if [[ $tried_parent -eq 0 && -n "${PROLE_CHILD_REALM:-}" && -n "${PROLE_PARENT_REALM:-}" && "${KRB5_REALM}" == "${PROLE_CHILD_REALM}" ]]; then
|
|
err "Kerberos test failed for child realm '${PROLE_CHILD_REALM}'; retrying with parent realm '${PROLE_PARENT_REALM}'..."
|
|
KRB5_REALM="${PROLE_PARENT_REALM}"
|
|
PROLE_USE_CHILD_REALM=0
|
|
ensure_krb5_conf_configmap || true
|
|
apply_krb5_conf_to_test_pod || true
|
|
tried_parent=1
|
|
continue
|
|
fi
|
|
|
|
exit 1
|
|
done
|
|
else
|
|
err "init_kerberos_test.sh not found."
|
|
exit 1
|
|
fi
|
|
|
|
if [[ "$cleanup_after_test" -eq 1 ]]; then
|
|
trap - EXIT
|
|
cleanup_ad_forwarder || true
|
|
fi
|
|
}
|
|
|
|
status() {
|
|
ensure_tools
|
|
log "--- init_kerberos status ---"
|
|
log "Namespace: $NAMESPACE"
|
|
log "CNPG cluster: $CNPG_CLUSTER_NAME"
|
|
log "KRB5_REALM: ${KRB5_REALM:-<unset>}"
|
|
log "KRB5_KDC: ${KRB5_KDC:-<unset>}"
|
|
log "KRB5_USER: ${KRB5_USER:-<unset>}"
|
|
log "AD forwarder enabled: ${KRB5_AD_PORT_FORWARD:-<auto>}"
|
|
log "AD forwarder namespace: ${KRB5_AD_NAMESPACE:-<unset>}"
|
|
log "AD forwarder deployment: $KRB5_AD_PROXY_NAME"
|
|
log "AD forwarder service: $KRB5_AD_SERVICE_NAME"
|
|
if kubectl -n "$NAMESPACE" get configmap prole-krb5-conf >/dev/null 2>&1; then
|
|
log "[OK] ConfigMap prole-krb5-conf present"
|
|
else
|
|
log "[MISSING] ConfigMap prole-krb5-conf"
|
|
fi
|
|
if kubectl -n "$KRB5_AD_NAMESPACE" get deploy "$KRB5_AD_PROXY_NAME" >/dev/null 2>&1; then
|
|
log "[OK] AD forwarder deployment present"
|
|
else
|
|
log "[INFO] AD forwarder deployment not present"
|
|
fi
|
|
if kubectl -n "$KRB5_AD_NAMESPACE" get svc "$KRB5_AD_SERVICE_NAME" >/dev/null 2>&1; then
|
|
log "[OK] AD forwarder service present"
|
|
else
|
|
log "[INFO] AD forwarder service not present"
|
|
fi
|
|
}
|
|
|
|
case "$ACTION" in
|
|
initialize|configure|update|reload)
|
|
initialize
|
|
;;
|
|
test)
|
|
run_test
|
|
;;
|
|
status)
|
|
status
|
|
;;
|
|
cleanup)
|
|
ensure_tools
|
|
ensure_namespace
|
|
cleanup_ad_forwarder
|
|
;;
|
|
*)
|
|
err "Usage: $0 {initialize|configure|update|reload|test|status|cleanup}" >&2
|
|
exit 2
|
|
;;
|
|
esac
|