prole/etc/init_kerberos.sh
chrisfu 96f594fd3c Refactor initialization scripts and add new service components
- Consolidated and split initialization scripts in etc/:
    - Removed init_prole-db.sh and init_authority.sh.
    - Added init_kdc.sh for in-cluster MIT Kerberos KDC (prole-authority).
    - Added init_ollama.sh for Ollama AI service integration.
    - Added init_service_layer.sh for high-level service orchestration.
    - Added init_k3s_registry.sh for private registry management.
- Major updates to install.py:
    - Support for new Ollama and KDC configuration.
    - Improved prole.cfg rendering and namespace handling.
    - Updated unattended install flags.
- Infrastructure and Deployment:
    - Updated K3s Ansible role with private registry support (registries.yaml template).
    - Added prole-authority Dockerfile.
    - Updated OpenBao Kerberos ConfigMap and other K8s manifests.
- Configuration:
    - Updated prole.cfg with new sections for Ollama and Monitoring.
    - Refined environment variable exports in env.sh and prole_cfg.sh.
2026-02-11 13:09:31 -08:00

729 lines
21 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:-}
KRB5_ADMIN=${KRB5_ADMIN:-}
KRB5_USER=${KRB5_USER:-${KRB5_USERNAME:-}}
KRB5_PASSWORD=${KRB5_PASSWORD:-}
# AD DC forwarding (socat proxy) options
KRB5_AD_PORT_FORWARD=${KRB5_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}}
KRB5_AD_NAMESPACE=${KRB5_AD_NAMESPACE:-${SERVICE_NAMESPACE}}
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_pod_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.podTemplate' >/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
}
is_ip_address() {
[[ "$1" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]
}
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
}
resolve_krb5_defaults() {
local svc_ns kdc_app kdc_svc
svc_ns="${SERVICE_NAMESPACE:-${NAMESPACE:-default}}"
kdc_app="${PROLE_KDC_NAME:-dog}"
kdc_svc="${PROLE_KDC_SERVICE:-authority}"
# If the KDC pod is available in the service namespace, default to its service endpoint.
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
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)
if [[ -n "$kdc_host" ]] && is_private_ip "$kdc_host"; 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="${SERVICE_NAMESPACE:-${NAMESPACE:-default}}"
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 = true
dns_lookup_kdc = true
[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 -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 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() {
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 "/etc/prole/krb5.conf:/etc/krb5.conf" '
map(select(.name != $name)) + [{"name": $name, "value": $value}]
')
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_pod_template; then
err "CNPG CRD does not support spec.podTemplate; falling back to in-pod krb5.conf update."
return 1
fi
local conf conf_hash
conf=$(get_krb5_conf)
if [[ -z "$conf" ]]; then
err "Missing krb5.conf data in ConfigMap prole-krb5-conf."
return 1
fi
conf_hash=$(printf '%s' "$conf" | sha256_stdin)
log "Patching CNPG Cluster $CNPG_CLUSTER_NAME to mount krb5.conf (rolling update) ..."
if ! cat <<EOF | kubectl apply --server-side --field-manager=prole-kerberos -f -
apiVersion: postgresql.cnpg.io/v1
kind: Cluster
metadata:
name: ${CNPG_CLUSTER_NAME}
namespace: ${NAMESPACE}
spec:
podTemplate:
metadata:
annotations:
prole.dev/krb5-conf-hash: "${conf_hash}"
spec:
volumes:
- name: krb5-conf
configMap:
name: prole-krb5-conf
items:
- key: krb5.conf
path: krb5.conf
containers:
- name: postgres
env:
- name: KRB5_CONFIG
value: /etc/prole/krb5.conf:/etc/krb5.conf
volumeMounts:
- name: krb5-conf
mountPath: /etc/prole/krb5.conf
subPath: krb5.conf
readOnly: true
EOF
then
err "Note: unable to apply krb5.conf mount via server-side apply."
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 /etc/prole/krb5.conf in pod $pod ..."
if printf '%s' "$conf" | kubectl -n "$NAMESPACE" exec -i "$pod" -c postgres -- sh -c 'mkdir -p /etc/prole && tee /etc/prole/krb5.conf /etc/krb5.conf >/dev/null'; then
log "[OK] Updated /etc/prole/krb5.conf in $pod"
else
err "[WARN] Unable to write /etc/prole/krb5.conf in $pod (permission?)."
err " Consider mounting ConfigMap prole-krb5-conf into the CNPG pods or updating the image."
fi
done
}
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
tcp_cmd+="socat -d -d TCP-LISTEN:${port},fork,reuseaddr TCP:${kdc_host}:${port} & "
done
for port in $KRB5_AD_UDP_PORTS; do
udp_cmd+="socat -d -d UDP-LISTEN:${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 -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 printf " - containerPort: %s\n protocol: TCP\n" "$port"; done)
$(for port in $KRB5_AD_UDP_PORTS; do printf " - containerPort: %s\n protocol: UDP\n" "$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
name="tcp-${port}"
printf " - name: %s\n port: %s\n targetPort: %s\n protocol: TCP\n" "$name" "$port" "$port"
done
for port in $KRB5_AD_UDP_PORTS; do
name="udp-${port}"
printf " - name: %s\n port: %s\n targetPort: %s\n protocol: UDP\n" "$name" "$port" "$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
return 0
fi
if ! is_ip_address "$kdc_host"; then
log "Skipping KDC egress policy (KRB5_KDC is not an IP: $kdc_host)."
return 0
fi
cidr="${kdc_host}/32"
ns="${SERVICE_NAMESPACE:-${NAMESPACE:-default}}"
ensure_namespace_named "$ns"
log "Ensuring KDC egress policy in namespace '${ns}' for ${cidr} ..."
cat <<EOF | kubectl apply -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)
EOF
}
initialize() {
ensure_tools
ensure_namespace
resolve_krb5_defaults
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
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
fi
patch_cnpg_cluster_for_auth || true
reload_cnpg_cluster || true
log "Kerberos initialization complete."
}
run_test() {
ensure_tools
ensure_namespace
resolve_krb5_defaults
resolve_krb5_password
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 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
if [[ "$mode" == "k3s" ]]; then
ensure_kdc_network_allow || true
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"
ensure_krb5_conf_configmap
if [[ -x "$SCRIPT_DIR/init_kerberos_test.sh" ]]; then
KRB5_REALM="$KRB5_REALM" KRB5_KDC="$effective_kdc" KRB5_ADMIN="$KRB5_ADMIN" \
KRB5_USER="$KRB5_USER" KRB5_PASSWORD="$KRB5_PASSWORD" \
"$SCRIPT_DIR/init_kerberos_test.sh" test
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