prole/mock_val/init_kdc.sh

847 lines
28 KiB
Bash
Executable File
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
set -euo pipefail
# init_kdc.sh
# Purpose:
# - Provision the in-cluster Prole KDC (MIT Kerberos) in the common services namespace
# - Optionally configure cross-realm trust to an external realm when credentials are provided
#
# Usage:
# ./init_kdc.sh initialize|update # create/update KDC resources
# ./init_kdc.sh status # show KDC status
# ./init_kdc.sh cleanup # remove KDC resources
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# 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
prole_ensure_kubeconfig >/dev/null 2>&1 || true
ACTION=${1:-initialize}
KDC_NAMESPACE=${PROLE_KDC_NAMESPACE:-${SERVICE_NAMESPACE:-${NAMESPACE:-default}}}
PROLE_KDC_ENABLED=${PROLE_KDC_ENABLED:-1}
PROLE_KDC_NAME=${PROLE_KDC_NAME:-auth}
PROLE_KDC_SERVICE=${PROLE_KDC_SERVICE:-auth}
PROLE_KDC_IMAGE=${PROLE_KDC_IMAGE:-}
PROLE_KDC_IMAGE_NAME=${PROLE_KDC_IMAGE_NAME:-prole-authority}
PROLE_KDC_IMAGE_TAG=${PROLE_KDC_IMAGE_TAG:-latest}
PROLE_KDC_REGISTRY_HOST=${PROLE_KDC_REGISTRY_HOST:-${LOCAL_REGISTRY:-localhost:5000}}
PROLE_KDC_REGISTRY_INTERNAL=${PROLE_KDC_REGISTRY_INTERNAL:-${LOCAL_REGISTRY_INTERNAL:-}}
PROLE_KDC_BUILD_IMAGE=${PROLE_KDC_BUILD_IMAGE:-1}
PROLE_KDC_ROLLOUT_TIMEOUT=${PROLE_KDC_ROLLOUT_TIMEOUT:-5}
PROLE_KDC_DEPLOY_TIMEOUT=${PROLE_KDC_DEPLOY_TIMEOUT:-60}
PROLE_KDC_HOST_NETWORK=${PROLE_KDC_HOST_NETWORK:-}
PROLE_KDC_REALM=${PROLE_KDC_REALM:-}
PROLE_KDC_DOMAIN=${PROLE_KDC_DOMAIN:-}
PROLE_KDC_ADMIN_PRINCIPAL=${PROLE_KDC_ADMIN_PRINCIPAL:-admin/admin}
PROLE_KDC_ADMIN_PASSWORD=${PROLE_KDC_ADMIN_PASSWORD:-}
PROLE_KDC_MASTER_PASSWORD=${PROLE_KDC_MASTER_PASSWORD:-}
PROLE_KDC_TRUST_REALM=${PROLE_KDC_TRUST_REALM:-${KRB5_REALM:-}}
PROLE_KDC_TRUST_ADMIN=${PROLE_KDC_TRUST_ADMIN:-${KRB5_USER:-}}
PROLE_KDC_TRUST_PASSWORD=${PROLE_KDC_TRUST_PASSWORD:-${KRB5_PASSWORD:-}}
PROLE_KDC_TRUST_SHARED_PASSWORD=${PROLE_KDC_TRUST_SHARED_PASSWORD:-}
KRB5_KDC=${KRB5_KDC:-}
KRB5_ADMIN=${KRB5_ADMIN:-}
local_registry_enabled() {
case "${PROLE_ENABLE_LOCAL_REGISTRY:-${ENABLE_LOCAL_REGISTRY:-}}" in
1|true|TRUE|True|yes|YES|Yes|on|ON|On) return 0 ;;
esac
return 1
}
if ! local_registry_enabled; then
PROLE_KDC_REGISTRY_HOST=""
PROLE_KDC_REGISTRY_INTERNAL=""
fi
log() { printf '%s\n' "$*"; }
err() { printf '%s\n' "$*" >&2; }
ensure_tools() {
for t in kubectl; do
command -v "$t" >/dev/null || { err "Missing required tool: $t"; exit 1; }
done
}
ensure_docker() {
command -v docker >/dev/null || { err "Missing required tool: docker"; exit 1; }
if ! docker info >/dev/null 2>&1; then
err "Docker is not running or not accessible."
exit 1
fi
}
ensure_namespace() {
if ! kubectl get namespace "$KDC_NAMESPACE" >/dev/null 2>&1; then
log "Creating namespace '$KDC_NAMESPACE' ..."
kubectl create namespace "$KDC_NAMESPACE" >/dev/null 2>&1 || true
fi
}
normalized_mode() {
if command -v prole_normalize_mode >/dev/null 2>&1; then
prole_normalize_mode "${PROLE_MODE:-}"
return 0
fi
printf '%s' "${PROLE_MODE:-}"
}
is_k3d_mode() {
[[ "$(normalized_mode)" == "k3d" ]]
}
get_kdc_pods_with_containers() {
kubectl -n "$KDC_NAMESPACE" get pods -l "app=${PROLE_KDC_NAME}" \
-o jsonpath='{range .items[*]}{.metadata.name}{"|"}{range .spec.containers[*]}{.name}{" "}{end}{"\n"}{end}' 2>/dev/null || true
}
kdc_pods_missing_container() {
local rows line containers found c
rows=$(get_kdc_pods_with_containers)
[[ -z "$rows" ]] && return 1
while IFS= read -r line; do
[[ -z "$line" ]] && continue
containers="${line#*|}"
found=0
for c in $containers; do
if [[ "$c" == "kdc" ]]; then
found=1
break
fi
done
if [[ "$found" -eq 0 ]]; then
return 0
fi
done <<< "$rows"
return 1
}
kdc_multiple_active_replicasets() {
local rows line spec status count
rows=$(kubectl -n "$KDC_NAMESPACE" get rs -l "app=${PROLE_KDC_NAME}" \
-o jsonpath='{range .items[*]}{.metadata.name}{"|"}{.spec.replicas}{"|"}{.status.replicas}{"\n"}{end}' 2>/dev/null || true)
[[ -z "$rows" ]] && return 1
count=0
while IFS= read -r line; do
[[ -z "$line" ]] && continue
spec=$(printf '%s' "$line" | awk -F'|' '{print $2}')
status=$(printf '%s' "$line" | awk -F'|' '{print $3}')
spec=${spec:-0}
status=${status:-0}
if [[ "$spec" -gt 0 || "$status" -gt 0 ]]; then
count=$((count+1))
fi
done <<< "$rows"
(( count > 1 ))
}
kdc_deployment_not_ready() {
local desired ready updated
desired=$(kubectl -n "$KDC_NAMESPACE" get deploy "$PROLE_KDC_NAME" -o jsonpath='{.spec.replicas}' 2>/dev/null || true)
ready=$(kubectl -n "$KDC_NAMESPACE" get deploy "$PROLE_KDC_NAME" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || true)
updated=$(kubectl -n "$KDC_NAMESPACE" get deploy "$PROLE_KDC_NAME" -o jsonpath='{.status.updatedReplicas}' 2>/dev/null || true)
desired=${desired:-0}
ready=${ready:-0}
updated=${updated:-0}
if [[ "$ready" -lt "$desired" || "$updated" -lt "$desired" ]]; then
return 0
fi
return 1
}
kdc_stale_deployment() {
if ! kdc_deployment_not_ready; then
return 1
fi
kdc_pods_missing_container && return 0
kdc_multiple_active_replicasets && return 0
return 1
}
maybe_cleanup_stale_kdc() {
local reason="${1:-stale}"
local policy="${2:-broad}"
if ! is_k3d_mode; then
return 1
fi
if ! kubectl -n "$KDC_NAMESPACE" get deploy "$PROLE_KDC_NAME" >/dev/null 2>&1; then
return 1
fi
if [[ "$policy" == "strict" ]]; then
if ! kdc_deployment_not_ready; then
return 1
fi
if ! kdc_pods_missing_container; then
return 1
fi
elif ! kdc_stale_deployment; then
return 1
fi
err "WARN: Detected stale Prole KDC deployment (${reason}). Cleaning up..."
cleanup_prole_kdc
return 0
}
lowercase() {
printf '%s' "${1:-}" | tr '[:upper:]' '[:lower:]'
}
registry_host_from_url() {
local value="${1:-}"
value="${value#http://}"
value="${value#https://}"
value="${value%%/*}"
value="${value%%:*}"
printf '%s' "$value"
}
gen_password() {
if command -v openssl >/dev/null 2>&1; then
openssl rand -base64 18
return 0
fi
if command -v python3 >/dev/null 2>&1; then
python3 - <<'PY'
import secrets, string
alphabet = string.ascii_letters + string.digits
print(''.join(secrets.choice(alphabet) for _ in range(24)))
PY
return 0
fi
date +%s
}
b64_decode() {
if base64 --decode </dev/null >/dev/null 2>&1; then
base64 --decode
return 0
fi
if base64 -d </dev/null >/dev/null 2>&1; then
base64 -d
return 0
fi
base64 -D
}
get_secret_value() {
local secret="$1"
local key="$2"
kubectl -n "$KDC_NAMESPACE" get secret "$secret" -o "jsonpath={.data.${key}}" 2>/dev/null | b64_decode 2>/dev/null || true
}
resolve_prole_kdc_defaults() {
if [[ -z "$PROLE_KDC_REALM" ]]; then
PROLE_KDC_REALM="PROLE.LOCAL"
fi
if [[ -z "$PROLE_KDC_DOMAIN" ]]; then
PROLE_KDC_DOMAIN=$(lowercase "$PROLE_KDC_REALM")
fi
local mode=""
if command -v prole_normalize_mode >/dev/null 2>&1; then
mode=$(prole_normalize_mode "${PROLE_MODE:-}")
else
mode="${PROLE_MODE:-}"
fi
local k3s_host=""
if local_registry_enabled; then
if [[ "$mode" == "k3s" ]]; then
k3s_host=$(registry_host_from_url "${PROLE_K3S_SERVER:-${K3S_SERVER_URL:-}}")
if [[ -n "$k3s_host" ]] && ([[ -z "${PROLE_KDC_REGISTRY_HOST:-}" ]] || [[ "$PROLE_KDC_REGISTRY_HOST" == "localhost:5000" ]] || [[ "$PROLE_KDC_REGISTRY_HOST" == *"k3d"* ]]); then
PROLE_KDC_REGISTRY_HOST="${k3s_host}:5000"
fi
fi
if [[ -z "$PROLE_KDC_REGISTRY_INTERNAL" ]]; then
if [[ -n "${KDC_NAMESPACE:-}" ]]; then
PROLE_KDC_REGISTRY_INTERNAL="registry.${KDC_NAMESPACE}.svc.cluster.local:5000"
elif [[ -n "${SERVICE_NAMESPACE:-}" ]]; then
PROLE_KDC_REGISTRY_INTERNAL="registry.${SERVICE_NAMESPACE}.svc.cluster.local:5000"
elif [[ -n "${NAMESPACE:-}" ]]; then
PROLE_KDC_REGISTRY_INTERNAL="registry.${NAMESPACE}.svc.cluster.local:5000"
fi
fi
if [[ "$mode" == "k3s" ]]; then
if [[ -n "$k3s_host" ]] && ([[ -z "$PROLE_KDC_REGISTRY_INTERNAL" ]] || [[ "$PROLE_KDC_REGISTRY_INTERNAL" == *"k3d"* ]] || [[ "$PROLE_KDC_REGISTRY_INTERNAL" == *".svc.cluster.local"* ]]); then
PROLE_KDC_REGISTRY_INTERNAL="${k3s_host}:5000"
fi
fi
else
PROLE_KDC_REGISTRY_INTERNAL=""
fi
if [[ -z "$PROLE_KDC_IMAGE" ]]; then
if [[ -n "$PROLE_KDC_REGISTRY_INTERNAL" ]]; then
PROLE_KDC_IMAGE="${PROLE_KDC_REGISTRY_INTERNAL}/${PROLE_KDC_IMAGE_NAME}:${PROLE_KDC_IMAGE_TAG}"
else
PROLE_KDC_IMAGE="${PROLE_KDC_IMAGE_NAME}:${PROLE_KDC_IMAGE_TAG}"
fi
fi
}
resolve_kdc_docker_dir() {
if [[ -n "${PROLE_KDC_DOCKER_DIR:-}" && -d "$PROLE_KDC_DOCKER_DIR" ]]; then
echo "$PROLE_KDC_DOCKER_DIR"
return 0
fi
if [[ -n "${PROLE_HOME:-}" && -d "$PROLE_HOME/prole/authority" ]]; then
echo "$PROLE_HOME/prole/authority"
return 0
fi
if [[ -n "${PROLE_HOME:-}" && -d "$PROLE_HOME/authority" ]]; then
echo "$PROLE_HOME/authority"
return 0
fi
if [[ -d "$SCRIPT_DIR/../prole/authority" ]]; then
echo "$SCRIPT_DIR/../prole/authority"
return 0
fi
if [[ -d "$SCRIPT_DIR/../authority" ]]; then
echo "$SCRIPT_DIR/../authority"
return 0
fi
return 1
}
is_truthy() {
case "${1:-}" in
1|true|TRUE|True|yes|YES|Yes|on|ON|On) return 0 ;;
esac
return 1
}
build_prole_kdc_image() {
if ! is_truthy "${PROLE_KDC_BUILD_IMAGE:-1}"; then
return 0
fi
local docker_dir
docker_dir=$(resolve_kdc_docker_dir || true)
if [[ -z "$docker_dir" ]]; then
err "ERROR: prole/authority Docker context not found."
exit 1
fi
ensure_docker
local local_tag="${PROLE_KDC_IMAGE_NAME}:${PROLE_KDC_IMAGE_TAG}"
log "Building authority image: ${local_tag} (context: ${docker_dir}) ..."
docker build -t "$local_tag" "$docker_dir"
if [[ -n "$PROLE_KDC_REGISTRY_HOST" ]]; then
local remote_tag="${PROLE_KDC_REGISTRY_HOST}/${PROLE_KDC_IMAGE_NAME}:${PROLE_KDC_IMAGE_TAG}"
log "Tagging authority image for registry: ${remote_tag}"
docker tag "$local_tag" "$remote_tag"
log "Pushing authority image to registry: ${remote_tag}"
if ! docker push "$remote_tag"; then
if command -v skopeo >/dev/null 2>&1; then
log "Docker push failed; retrying with skopeo (insecure registry) ..."
skopeo copy --dest-tls-verify=false "docker-daemon:${local_tag}" "docker://${remote_tag}"
else
err "ERROR: docker push failed and skopeo is not available."
exit 1
fi
fi
fi
if [[ -z "$PROLE_KDC_REGISTRY_HOST" && "$(normalized_mode)" == "k3d" ]]; then
if command -v k3d >/dev/null 2>&1; then
local cluster_name="${K3D_CLUSTER_NAME:-prole-dev-cluster}"
log "Importing authority image into k3d cluster: ${cluster_name}"
k3d image import "$local_tag" -c "$cluster_name" >/dev/null 2>&1 || true
fi
fi
if [[ -z "$PROLE_KDC_REGISTRY_HOST" && "$(normalized_mode)" == "k3s" ]]; then
if command -v ansible >/dev/null 2>&1; then
local export_dir="${DOCKER_IMPORT_DIR:-/tmp}"
local safe_name="${PROLE_KDC_IMAGE_NAME}_${PROLE_KDC_IMAGE_TAG}"
local tar_path="${export_dir%/}/${safe_name}.tar"
local ansible_cfg="${SCRIPT_DIR}/../ansible.cfg"
local vault_file="${ANSIBLE_VAULT_PASSWORD_FILE:-}"
local dest="/tmp/$(basename "$tar_path")"
local ansible_args=("ansible" "k3s_hosts")
mkdir -p "$export_dir"
log "Exporting authority image to ${tar_path}"
docker save -o "$tar_path" "$local_tag"
if [[ -z "$vault_file" && -f "${SCRIPT_DIR}/../.vault_pass" ]]; then
vault_file="${SCRIPT_DIR}/../.vault_pass"
fi
if [[ -n "$vault_file" ]]; then
ansible_args+=("--vault-password-file" "$vault_file")
fi
log "Importing authority image into k3s nodes (group: k3s_hosts)"
if [[ -f "$ansible_cfg" ]]; then
ANSIBLE_CONFIG="$ansible_cfg" "${ansible_args[@]}" -m copy -a "src=$tar_path dest=$dest mode=0644" || { err "ERROR: Failed to copy authority image to k3s nodes."; exit 1; }
ANSIBLE_CONFIG="$ansible_cfg" "${ansible_args[@]}" -m shell -a "ctr -n k8s.io -a /run/k3s/containerd/containerd.sock images import --no-unpack $dest" || { err "ERROR: Failed to import authority image into k3s nodes."; exit 1; }
else
"${ansible_args[@]}" -m copy -a "src=$tar_path dest=$dest mode=0644" || { err "ERROR: Failed to copy authority image to k3s nodes."; exit 1; }
"${ansible_args[@]}" -m shell -a "ctr -n k8s.io -a /run/k3s/containerd/containerd.sock images import --no-unpack $dest" || { err "ERROR: Failed to import authority image into k3s nodes."; exit 1; }
fi
else
err "ERROR: ansible not available; cannot import authority image to k3s nodes."
exit 1
fi
fi
if [[ -n "$PROLE_KDC_REGISTRY_INTERNAL" ]]; then
PROLE_KDC_IMAGE="${PROLE_KDC_REGISTRY_INTERNAL}/${PROLE_KDC_IMAGE_NAME}:${PROLE_KDC_IMAGE_TAG}"
elif [[ -n "$PROLE_KDC_REGISTRY_HOST" ]]; then
PROLE_KDC_IMAGE="${PROLE_KDC_REGISTRY_HOST}/${PROLE_KDC_IMAGE_NAME}:${PROLE_KDC_IMAGE_TAG}"
else
PROLE_KDC_IMAGE="${local_tag}"
fi
}
ensure_prole_kdc_secrets() {
local secret_name="prole-kdc-secrets"
if [[ -z "$PROLE_KDC_ADMIN_PASSWORD" ]]; then
PROLE_KDC_ADMIN_PASSWORD=$(get_secret_value "$secret_name" "admin_password")
fi
if [[ -z "$PROLE_KDC_MASTER_PASSWORD" ]]; then
PROLE_KDC_MASTER_PASSWORD=$(get_secret_value "$secret_name" "master_password")
fi
if [[ -z "$PROLE_KDC_TRUST_SHARED_PASSWORD" ]]; then
PROLE_KDC_TRUST_SHARED_PASSWORD=$(get_secret_value "$secret_name" "trust_shared_password")
fi
if [[ -z "$PROLE_KDC_TRUST_PASSWORD" ]]; then
PROLE_KDC_TRUST_PASSWORD=$(get_secret_value "$secret_name" "trust_password")
fi
if [[ -z "$PROLE_KDC_ADMIN_PASSWORD" ]]; then
PROLE_KDC_ADMIN_PASSWORD=$(gen_password)
fi
if [[ -z "$PROLE_KDC_MASTER_PASSWORD" ]]; then
PROLE_KDC_MASTER_PASSWORD=$(gen_password)
fi
if [[ -z "$PROLE_KDC_TRUST_SHARED_PASSWORD" ]]; then
PROLE_KDC_TRUST_SHARED_PASSWORD="$PROLE_KDC_MASTER_PASSWORD"
fi
kubectl -n "$KDC_NAMESPACE" create secret generic "$secret_name" \
--from-literal=admin_password="$PROLE_KDC_ADMIN_PASSWORD" \
--from-literal=master_password="$PROLE_KDC_MASTER_PASSWORD" \
--from-literal=trust_shared_password="$PROLE_KDC_TRUST_SHARED_PASSWORD" \
--from-literal=trust_password="$PROLE_KDC_TRUST_PASSWORD" \
--dry-run=client -o yaml | kubectl apply -f - >/dev/null
}
ensure_prole_kdc() {
if [[ "$PROLE_KDC_ENABLED" == "0" || "$PROLE_KDC_ENABLED" == "false" || "$PROLE_KDC_ENABLED" == "False" ]]; then
log "Prole KDC disabled (PROLE_KDC_ENABLED=$PROLE_KDC_ENABLED)."
return 0
fi
if [[ -z "${PROLE_KDC_HOST_NETWORK}" && "$(normalized_mode)" == "k3d" ]]; then
PROLE_KDC_HOST_NETWORK=1
fi
resolve_prole_kdc_defaults
build_prole_kdc_image
ensure_prole_kdc_secrets
local deployment_present=0
if kubectl -n "$KDC_NAMESPACE" get deploy "$PROLE_KDC_NAME" >/dev/null 2>&1; then
deployment_present=1
fi
# Clean up legacy names if we're switching to the new auth naming.
if [[ "$PROLE_KDC_NAME" != "dog" ]]; then
kubectl -n "$KDC_NAMESPACE" delete deployment dog --ignore-not-found >/dev/null 2>&1 || true
fi
if [[ "$PROLE_KDC_SERVICE" != "authority" ]]; then
kubectl -n "$KDC_NAMESPACE" delete service authority --ignore-not-found >/dev/null 2>&1 || true
fi
local admin_acl_principal="$PROLE_KDC_ADMIN_PRINCIPAL"
if [[ "$admin_acl_principal" != *"@"* ]]; then
admin_acl_principal="${admin_acl_principal}@${PROLE_KDC_REALM}"
fi
local trust_block=""
if [[ -n "$PROLE_KDC_TRUST_REALM" && "$PROLE_KDC_TRUST_REALM" != "$PROLE_KDC_REALM" && -n "$KRB5_KDC" ]]; then
local trust_admin_server="${KRB5_ADMIN:-$KRB5_KDC}"
trust_block=$(cat <<EOF
${PROLE_KDC_TRUST_REALM} = {
kdc = ${KRB5_KDC}
admin_server = ${trust_admin_server}
}
[capaths]
${PROLE_KDC_REALM} = {
${PROLE_KDC_TRUST_REALM} = .
}
${PROLE_KDC_TRUST_REALM} = {
${PROLE_KDC_REALM} = .
}
EOF
)
fi
apply_kdc_manifest() {
log "Applying Prole KDC '${PROLE_KDC_NAME}' in namespace '${KDC_NAMESPACE}' (realm ${PROLE_KDC_REALM}) ..."
local host_net_block=""
local dns_policy_block=""
if is_truthy "${PROLE_KDC_HOST_NETWORK:-0}"; then
host_net_block=" hostNetwork: true"
dns_policy_block=" dnsPolicy: ClusterFirstWithHostNet"
fi
cat <<EOF | kubectl apply -n "$KDC_NAMESPACE" -f -
apiVersion: v1
kind: ConfigMap
metadata:
name: prole-kdc-config
namespace: ${KDC_NAMESPACE}
data:
krb5.conf: |
[libdefaults]
default_realm = ${PROLE_KDC_REALM}
dns_lookup_realm = false
dns_lookup_kdc = false
[realms]
${PROLE_KDC_REALM} = {
kdc = 127.0.0.1
admin_server = 127.0.0.1
}${trust_block}
kdc.conf: |
[kdcdefaults]
kdc_ports = 88
kdc_tcp_ports = 88
[realms]
${PROLE_KDC_REALM} = {
database_name = /var/lib/krb5kdc/principal
admin_keytab = FILE:/etc/krb5kdc/kadm5.keytab
acl_file = /etc/krb5kdc/kadm5.acl
key_stash_file = /etc/krb5kdc/stash
max_life = 10h 0m 0s
max_renewable_life = 7d 0h 0m 0s
default_principal_flags = +preauth
}
kadm5.acl: |
${admin_acl_principal} *
entrypoint.sh: |
#!/usr/bin/env bash
set -euo pipefail
# Enable verbose debug if requested
if [[ "${PROLE_KDC_DEBUG:-}" == "1" ]]; then
set -x
echo "[DEBUG] Environment snapshot:" >&2
env | sed -E 's/(PASSWORD|TOKEN|SECRET)=.*/\1=****/g' >&2 || true
fi
export DEBIAN_FRONTEND=noninteractive
if ! command -v krb5kdc >/dev/null 2>&1; then
echo "Installing Kerberos packages..."
echo "krb5-config krb5-config/default_realm string ${PROLE_KDC_REALM}" | debconf-set-selections || true
echo "krb5-config krb5-config/kerberos_servers string 127.0.0.1" | debconf-set-selections || true
echo "krb5-config krb5-config/admin_server string 127.0.0.1" | debconf-set-selections || true
apt-get update
apt-get install -y --no-install-recommends krb5-kdc krb5-admin-server krb5-user dnsutils ca-certificates
rm -rf /var/lib/apt/lists/*
fi
mkdir -p /etc/krb5kdc /var/lib/krb5kdc
if [[ -f /opt/prole-kdc/krb5.conf ]]; then
cp /opt/prole-kdc/krb5.conf /etc/krb5.conf
fi
if [[ -f /opt/prole-kdc/kdc.conf ]]; then
cp /opt/prole-kdc/kdc.conf /etc/krb5kdc/kdc.conf
fi
if [[ -f /opt/prole-kdc/kadm5.acl ]]; then
cp /opt/prole-kdc/kadm5.acl /etc/krb5kdc/kadm5.acl
fi
# Validate required secrets early to avoid silent crashes
if [[ -z "${PROLE_KDC_MASTER_PASSWORD:-}" ]]; then
echo "ERROR: Missing required env PROLE_KDC_MASTER_PASSWORD (secret 'prole-kdc-secrets/master_password')." >&2
exit 1
fi
if [[ -z "${PROLE_KDC_ADMIN_PASSWORD:-}" ]]; then
echo "ERROR: Missing required env PROLE_KDC_ADMIN_PASSWORD (secret 'prole-kdc-secrets/admin_password')." >&2
exit 1
fi
# Optionally generate a minimal Samba configuration if a child realm is provided
realm="\${PROLE_CHILD_REALM:-}"
if [[ -z "\$realm" ]]; then
realm="\${PROLE_KDC_REALM}"
fi
if [[ -n "\$realm" ]]; then
workgroup="\${PROLE_CHILD_WORKGROUP:-}"
if [[ -z "\$workgroup" ]]; then
workgroup="\${realm%%.*}"
fi
netbios="\${PROLE_CHILD_NETBIOS_NAME:-}"
if [[ -z "\$netbios" ]]; then
netbios="\$workgroup"
fi
server_string="\${PROLE_CHILD_SERVER_STRING:-}"
if [[ -z "\$server_string" ]]; then
server_string="\${realm} AD DC"
fi
server_role="\${PROLE_SAMBA_SERVER_ROLE:-}"
if [[ -z "\$server_role" ]]; then
server_role='active directory domain controller'
fi
mkdir -p /etc/samba
# Write minimal Samba config without using a heredoc to avoid YAML indentation issues
# when this script is embedded in a ConfigMap. Variables are expanded at container runtime.
{
printf '%s\n' "[global]"
printf '%s\n' " workgroup = \${workgroup}"
printf '%s\n' " realm = \${realm}"
printf '%s\n' " netbios name = \${netbios}"
printf '%s\n' " server string = \${server_string}"
printf '%s\n' " server role = \${server_role}"
} > /etc/samba/smb.conf
fi
realm="\${PROLE_KDC_REALM}"
admin_principal="\${PROLE_KDC_ADMIN_PRINCIPAL}"
if [[ "\${admin_principal}" != *"@"* ]]; then
admin_principal="\${admin_principal}@\${PROLE_KDC_REALM}"
fi
if [[ ! -f /var/lib/krb5kdc/principal ]]; then
echo "Initializing realm database for \${PROLE_KDC_REALM}..."
kdb5_util create -s -r "\${realm}" -P "\${PROLE_KDC_MASTER_PASSWORD}"
fi
if ! kadmin.local -q "get_principal \${admin_principal}" >/dev/null 2>&1; then
echo "Creating admin principal \${admin_principal}..."
kadmin.local -q "addprinc -pw \${PROLE_KDC_ADMIN_PASSWORD} \${admin_principal}"
fi
if [[ -n "\${PROLE_KDC_TRUST_REALM:-}" && "\${PROLE_KDC_TRUST_REALM}" != "\${PROLE_KDC_REALM}" ]]; then
shared_pw="\${PROLE_KDC_TRUST_SHARED_PASSWORD:-\${PROLE_KDC_MASTER_PASSWORD}}"
if ! kadmin.local -q "get_principal krbtgt/\${PROLE_KDC_TRUST_REALM}@\${PROLE_KDC_REALM}" >/dev/null 2>&1; then
echo "Creating trust principal krbtgt/\${PROLE_KDC_TRUST_REALM}@\${PROLE_KDC_REALM}..."
kadmin.local -q "addprinc -pw \${shared_pw} krbtgt/\${PROLE_KDC_TRUST_REALM}@\${PROLE_KDC_REALM}"
fi
if [[ -n "\${PROLE_KDC_TRUST_ADMIN:-}" && -n "\${PROLE_KDC_TRUST_PASSWORD:-}" ]]; then
echo "Creating reciprocal trust principal in \${PROLE_KDC_TRUST_REALM}..."
kadmin -r "\${PROLE_KDC_TRUST_REALM}" -p "\${PROLE_KDC_TRUST_ADMIN}" -w "\${PROLE_KDC_TRUST_PASSWORD}" \
-q "addprinc -pw \${shared_pw} krbtgt/\${PROLE_KDC_REALM}@\${PROLE_KDC_TRUST_REALM}" || true
else
echo "WARN: Missing PROLE_KDC_TRUST_ADMIN/PROLE_KDC_TRUST_PASSWORD; skipping external trust principal."
fi
fi
# Start daemons. Keep kadmind in PID 1; run krb5kdc in background and verify it binds.
echo "Starting krb5kdc and kadmind ..."
krb5kdc -n &
sleep 0.5
if ! pgrep -x krb5kdc >/dev/null 2>&1; then
echo "ERROR: krb5kdc failed to start. Check /var/log/ (syslog) for details." >&2
exit 1
fi
exec kadmind -nofork
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: ${PROLE_KDC_NAME}
namespace: ${KDC_NAMESPACE}
spec:
replicas: 1
selector:
matchLabels:
app: ${PROLE_KDC_NAME}
template:
metadata:
labels:
app: ${PROLE_KDC_NAME}
spec:
${host_net_block}
${dns_policy_block}
containers:
- name: kdc
image: ${PROLE_KDC_IMAGE}
imagePullPolicy: IfNotPresent
command: ["/bin/bash", "/opt/prole-kdc/entrypoint.sh"]
env:
- name: PROLE_KDC_REALM
value: "${PROLE_KDC_REALM}"
- name: PROLE_KDC_ADMIN_PRINCIPAL
value: "${PROLE_KDC_ADMIN_PRINCIPAL}"
- name: PROLE_KDC_MASTER_PASSWORD
valueFrom:
secretKeyRef:
name: prole-kdc-secrets
key: master_password
- name: PROLE_KDC_ADMIN_PASSWORD
valueFrom:
secretKeyRef:
name: prole-kdc-secrets
key: admin_password
- name: PROLE_KDC_TRUST_SHARED_PASSWORD
valueFrom:
secretKeyRef:
name: prole-kdc-secrets
key: trust_shared_password
- name: PROLE_KDC_TRUST_REALM
value: "${PROLE_KDC_TRUST_REALM}"
- name: PROLE_KDC_TRUST_ADMIN
value: "${PROLE_KDC_TRUST_ADMIN}"
- name: PROLE_KDC_TRUST_PASSWORD
valueFrom:
secretKeyRef:
name: prole-kdc-secrets
key: trust_password
- name: PROLE_CHILD_REALM
value: "${PROLE_CHILD_REALM:-}"
- name: PROLE_CHILD_WORKGROUP
value: "${PROLE_CHILD_WORKGROUP:-}"
- name: PROLE_CHILD_NETBIOS_NAME
value: "${PROLE_CHILD_NETBIOS_NAME:-}"
- name: PROLE_CHILD_SERVER_STRING
value: "${PROLE_CHILD_SERVER_STRING:-}"
- name: PROLE_SAMBA_SERVER_ROLE
value: "${PROLE_SAMBA_SERVER_ROLE:-}"
ports:
- name: krb5-udp
containerPort: 88
protocol: UDP
- name: krb5-tcp
containerPort: 88
protocol: TCP
- name: kpasswd-udp
containerPort: 464
protocol: UDP
- name: kpasswd-tcp
containerPort: 464
protocol: TCP
- name: kadmin
containerPort: 749
protocol: TCP
volumeMounts:
- name: prole-kdc-config
mountPath: /opt/prole-kdc
- name: prole-kdc-data
mountPath: /var/lib/krb5kdc
- name: prole-kdc-data
mountPath: /etc/krb5kdc
volumes:
- name: prole-kdc-config
configMap:
name: prole-kdc-config
- name: prole-kdc-data
emptyDir: {}
---
apiVersion: v1
kind: Service
metadata:
name: ${PROLE_KDC_SERVICE}
namespace: ${KDC_NAMESPACE}
spec:
selector:
app: ${PROLE_KDC_NAME}
ports:
- name: krb5-udp
port: 88
targetPort: 88
protocol: UDP
- name: krb5-tcp
port: 88
targetPort: 88
protocol: TCP
- name: kpasswd-udp
port: 464
targetPort: 464
protocol: UDP
- name: kpasswd-tcp
port: 464
targetPort: 464
protocol: TCP
- name: kadmin
port: 749
targetPort: 749
protocol: TCP
EOF
}
apply_kdc_manifest
local rollout_timeout="$PROLE_KDC_ROLLOUT_TIMEOUT"
if [[ "$deployment_present" -eq 0 ]]; then
rollout_timeout="$PROLE_KDC_DEPLOY_TIMEOUT"
fi
if ! kubectl -n "$KDC_NAMESPACE" rollout status deploy/${PROLE_KDC_NAME} --timeout="${rollout_timeout}s"; then
err "WARN: Prole KDC rollout did not complete within ${rollout_timeout}s. Collecting diagnostics..."
# Dump a quick describe and last logs from the newest pod (if any) to aid troubleshooting
latest_pod=$(kubectl -n "$KDC_NAMESPACE" get pod -l "app=${PROLE_KDC_NAME}" --sort-by=.metadata.creationTimestamp -o jsonpath='{.items[-1].metadata.name}' 2>/dev/null || true)
if [[ -n "${latest_pod:-}" ]]; then
err "--- describe pod ${latest_pod} ---"
kubectl -n "$KDC_NAMESPACE" describe pod "$latest_pod" 1>&2 || true
err "--- last 200 log lines from ${latest_pod} ---"
kubectl -n "$KDC_NAMESPACE" logs "$latest_pod" --tail=200 1>&2 || true
else
err "[WARN] No pods found for app=${PROLE_KDC_NAME} to collect logs from."
fi
if maybe_cleanup_stale_kdc "rollout timeout"; then
log "Re-applying Prole KDC after cleanup..."
apply_kdc_manifest
kubectl -n "$KDC_NAMESPACE" rollout status deploy/${PROLE_KDC_NAME} --timeout="${PROLE_KDC_DEPLOY_TIMEOUT}s" || true
fi
fi
}
cleanup_prole_kdc() {
log "Removing Prole KDC resources (if present)..."
kubectl -n "$KDC_NAMESPACE" delete service "$PROLE_KDC_SERVICE" --ignore-not-found
kubectl -n "$KDC_NAMESPACE" delete deployment "$PROLE_KDC_NAME" --ignore-not-found
kubectl -n "$KDC_NAMESPACE" delete configmap prole-kdc-config --ignore-not-found
}
status() {
log "--- init_kdc status ---"
log "Namespace: $KDC_NAMESPACE"
log "Prole KDC enabled: ${PROLE_KDC_ENABLED:-0}"
log "Prole KDC realm: ${PROLE_KDC_REALM:-<unset>}"
log "Prole KDC service: ${PROLE_KDC_SERVICE:-<unset>}"
log "Prole KDC image: ${PROLE_KDC_IMAGE:-<unset>}"
if kubectl -n "$KDC_NAMESPACE" get deploy "$PROLE_KDC_NAME" >/dev/null 2>&1; then
log "[OK] Prole KDC deployment present"
if maybe_cleanup_stale_kdc "status check" "strict"; then
log "[INFO] Stale Prole KDC deployment removed. Re-run init_kdc.sh update to recreate."
fi
else
log "[INFO] Prole KDC deployment not present"
fi
}
case "$ACTION" in
initialize|init|update|deploy|start)
ensure_tools
ensure_namespace
ensure_prole_kdc
;;
status)
ensure_tools
status
;;
cleanup|delete|remove)
ensure_tools
ensure_namespace
cleanup_prole_kdc
;;
*)
err "Usage: $0 {initialize|update|status|cleanup}"
exit 2
;;
esac