mirror of
https://github.com/dredx/prole.git
synced 2026-09-23 12:03:59 +00:00
Mirrors the knoe-db commit `ff7546d` patches into the prole copy of `etc/init_kdc.sh` so a re-run of `install.sh --mode k3s --reset` from this repo produces a working cross-realm trust without manual cluster surgery. The k3s cluster is provisioned from this repo, so the source fix must live here (knoe-db remains canonical for GKE). Changes to etc/init_kdc.sh: 1. Create BOTH cross-realm krbtgts in MIT, not just the outbound one. The inbound `krbtgt/<REALM>@<TRUST_REALM>` (issued by Samba, decrypted here) was missing entirely; without it, MIT cannot decrypt inbound TGTs and the trust never carries traffic. 2. Pin both cross-realm krbtgts to RC4 (`arcfour-hmac:normal`). AES keys depend on salt, and Samba's `<remote_realm>+UPN` salt does not match MIT's `<local_realm>+<principal-no-realm>`; RC4 has no salt so both sides converge from the password alone. Matches the already-pinned Samba side (commit `ad1eced`). 3. Replace the broken "remote kadmin to Samba" reciprocal-trust block with a documented no-op pointing at `infrastructure/playbooks/kerberos_trust_setup.yml`. Samba AD does not accept additions over MIT's kadmin protocol; the block always failed with "Missing parameters in krb5.conf required for kadmin client". 4. Switch the KDC data volume from emptyDir to a PVC (claimName `knoe-kdc-data`, parameterized by `$PROLE_KDC_STORAGE_SIZE` and `$PROLE_KDC_STORAGE_CLASS`). State now survives pod restarts. Adds Junie brief `docs/plans/junie/kdc-trust-reset-repeatable.md` with four TDD acceptance criteria for an end-to-end --reset run on the prole k3s cluster. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1071 lines
36 KiB
Bash
Executable File
1071 lines
36 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
|
||
set -euo pipefail
|
||
|
||
# init_kdc.sh
|
||
# Purpose:
|
||
# - Provision the in-cluster Knoe 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/knoe_cfg.sh"
|
||
|
||
if [[ "${1:-}" == "--mode" || "${1:-}" == "-m" ]]; then
|
||
knoe_set_mode "${2:-}"
|
||
shift 2
|
||
elif [[ "${1:-}" == --mode=* || "${1:-}" == -m=* ]]; then
|
||
knoe_set_mode "${1#*=}"
|
||
shift
|
||
fi
|
||
|
||
knoe_ensure_kubeconfig >/dev/null 2>&1 || true
|
||
ensure_kube_context || exit 1
|
||
|
||
ACTION=${1:-initialize}
|
||
|
||
resolve_kdc_mode_hint() {
|
||
local mode_hint="${KNOE_MODE:-${DEPLOYMENT_MODE:-${CLUSTER_ENV:-k3s}}}"
|
||
if command -v knoe_normalize_mode >/dev/null 2>&1; then
|
||
knoe_normalize_mode "$mode_hint"
|
||
return 0
|
||
fi
|
||
mode_hint=$(printf '%s' "$mode_hint" | tr 'A-Z' 'a-z')
|
||
case "$mode_hint" in
|
||
prod|production)
|
||
printf 'k8s'
|
||
;;
|
||
*)
|
||
printf '%s' "$mode_hint"
|
||
;;
|
||
esac
|
||
}
|
||
|
||
default_knoe_kdc_name() {
|
||
case "$(resolve_kdc_mode_hint)" in
|
||
k8s)
|
||
printf 'authority-gcp-auth'
|
||
;;
|
||
*)
|
||
printf 'authority-knoe-auth'
|
||
;;
|
||
esac
|
||
}
|
||
|
||
KDC_NAMESPACE=${PROLE_KDC_NAMESPACE:-${SERVICE_NAMESPACE:-${NAMESPACE:-default}}}
|
||
# Namespace where knoe-auth runs and expects the `knoe-kdc-config` ConfigMap.
|
||
# Defaults to PROLE_NAMESPACE (from knoe.cfg) when present.
|
||
PROLE_AUTH_NAMESPACE=${PROLE_AUTH_NAMESPACE:-${PROLE_NAMESPACE:-}}
|
||
PROLE_KDC_ENABLED=${PROLE_KDC_ENABLED:-1}
|
||
PROLE_KDC_NAME=${PROLE_KDC_NAME:-$(default_knoe_kdc_name)}
|
||
PROLE_KDC_SERVICE=${PROLE_KDC_SERVICE:-auth}
|
||
PROLE_KDC_IMAGE=${PROLE_KDC_IMAGE:-}
|
||
# If the user provided an explicit image, we should not require Docker unless
|
||
# they explicitly opt into building.
|
||
PROLE_KDC_IMAGE_EXPLICIT=0
|
||
if [[ -n "${PROLE_KDC_IMAGE:-}" ]]; then
|
||
PROLE_KDC_IMAGE_EXPLICIT=1
|
||
fi
|
||
PROLE_KDC_IMAGE_NAME=${PROLE_KDC_IMAGE_NAME:-knoe-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_SET=0
|
||
if [[ -n "${PROLE_KDC_BUILD_IMAGE+x}" ]]; then
|
||
PROLE_KDC_BUILD_IMAGE_SET=1
|
||
fi
|
||
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() {
|
||
local raw="${PROLE_ENABLE_LOCAL_REGISTRY:-${ENABLE_LOCAL_REGISTRY:-}}"
|
||
if [[ -n "$raw" ]]; then
|
||
case "$raw" in
|
||
1|true|TRUE|True|yes|YES|Yes|on|ON|On) return 0 ;;
|
||
esac
|
||
return 1
|
||
fi
|
||
|
||
local mode=""
|
||
if command -v knoe_normalize_mode >/dev/null 2>&1; then
|
||
mode=$(knoe_normalize_mode "${KNOE_MODE:-${DEPLOYMENT_MODE:-${CLUSTER_ENV:-}}}")
|
||
else
|
||
mode="${KNOE_MODE:-${DEPLOYMENT_MODE:-${CLUSTER_ENV:-}}}"
|
||
fi
|
||
[[ "$mode" == "k3d" || "$mode" == "k3s" ]]
|
||
}
|
||
|
||
if ! local_registry_enabled; then
|
||
# Only clear default local-registry settings; preserve explicit registry host values.
|
||
_default_host="${LOCAL_REGISTRY:-localhost:5000}"
|
||
if [[ -z "${PROLE_KDC_REGISTRY_HOST:-}" || "${PROLE_KDC_REGISTRY_HOST:-}" == "$_default_host" ]]; then
|
||
PROLE_KDC_REGISTRY_HOST=""
|
||
fi
|
||
PROLE_KDC_REGISTRY_INTERNAL=""
|
||
unset _default_host
|
||
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."
|
||
err "This is required to build/push the Knoe KDC image."
|
||
err "Start Docker (e.g. Docker Desktop), or set PROLE_KDC_IMAGE to a prebuilt image and/or set PROLE_KDC_BUILD_IMAGE=0."
|
||
exit 1
|
||
fi
|
||
}
|
||
|
||
docker_available() {
|
||
command -v docker >/dev/null 2>&1 || return 1
|
||
docker info >/dev/null 2>&1
|
||
}
|
||
|
||
resolve_knoe_kdc_image() {
|
||
# Ensure we have a deterministic image reference even when skipping local builds.
|
||
if [[ -n "${PROLE_KDC_IMAGE:-}" ]]; then
|
||
return 0
|
||
fi
|
||
if [[ -n "${PROLE_KDC_REGISTRY_INTERNAL:-}" ]]; then
|
||
PROLE_KDC_IMAGE="${PROLE_KDC_REGISTRY_INTERNAL}/${PROLE_KDC_IMAGE_NAME}:${PROLE_KDC_IMAGE_TAG}"
|
||
return 0
|
||
fi
|
||
if [[ -n "${PROLE_KDC_REGISTRY_HOST:-}" ]]; then
|
||
PROLE_KDC_IMAGE="${PROLE_KDC_REGISTRY_HOST}/${PROLE_KDC_IMAGE_NAME}:${PROLE_KDC_IMAGE_TAG}"
|
||
return 0
|
||
fi
|
||
PROLE_KDC_IMAGE="${PROLE_KDC_IMAGE_NAME}:${PROLE_KDC_IMAGE_TAG}"
|
||
}
|
||
|
||
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 knoe_normalize_mode >/dev/null 2>&1; then
|
||
knoe_normalize_mode "${KNOE_MODE:-}"
|
||
return 0
|
||
fi
|
||
printf '%s' "${KNOE_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 Knoe KDC deployment (${reason}). Cleaning up..."
|
||
cleanup_knoe_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_knoe_kdc_defaults() {
|
||
if [[ -z "$PROLE_KDC_REALM" ]]; then
|
||
# Post-rebrand default — must match knoe-db/etc/init_kdc.sh. The old
|
||
# "PROLE.LOCAL" string seeded a stale KDC config on the prole k3s
|
||
# cluster that took an afternoon of cross-realm trust debugging to
|
||
# find — see ~/.claude/plans/chrisfu-myrddin-dev-prole-git-pull-*.md.
|
||
PROLE_KDC_REALM="KNOE.LOCAL"
|
||
fi
|
||
if [[ -z "$PROLE_KDC_DOMAIN" ]]; then
|
||
PROLE_KDC_DOMAIN=$(lowercase "$PROLE_KDC_REALM")
|
||
fi
|
||
local mode=""
|
||
if command -v knoe_normalize_mode >/dev/null 2>&1; then
|
||
mode=$(knoe_normalize_mode "${KNOE_MODE:-}")
|
||
else
|
||
mode="${KNOE_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
|
||
local reg_ns="${REGISTRY_NAMESPACE:-${SERVICE_NAMESPACE:-${NAMESPACE:-default}}}"
|
||
PROLE_KDC_REGISTRY_INTERNAL="registry.${reg_ns}.svc.cluster.local:5000"
|
||
unset reg_ns
|
||
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 "${KNOE_HOME:-}" && -d "$KNOE_HOME/knoe/authority" ]]; then
|
||
echo "$KNOE_HOME/knoe/authority"
|
||
return 0
|
||
fi
|
||
if [[ -n "${KNOE_HOME:-}" && -d "$KNOE_HOME/authority" ]]; then
|
||
echo "$KNOE_HOME/authority"
|
||
return 0
|
||
fi
|
||
if [[ -d "$SCRIPT_DIR/../knoe/authority" ]]; then
|
||
echo "$SCRIPT_DIR/../knoe/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_knoe_kdc_image() {
|
||
# If the user explicitly sets PROLE_KDC_BUILD_IMAGE, honor it.
|
||
# Otherwise, skip building when an explicit image was provided.
|
||
if [[ "$PROLE_KDC_BUILD_IMAGE_SET" == "1" ]]; then
|
||
if ! is_truthy "${PROLE_KDC_BUILD_IMAGE:-0}"; then
|
||
resolve_knoe_kdc_image
|
||
return 0
|
||
fi
|
||
else
|
||
if [[ "$PROLE_KDC_IMAGE_EXPLICIT" == "1" ]]; then
|
||
log "Using preconfigured PROLE_KDC_IMAGE='${PROLE_KDC_IMAGE}'; skipping local image build."
|
||
return 0
|
||
fi
|
||
fi
|
||
|
||
if ! docker_available; then
|
||
# If the user forced a build, we must fail with a clear error.
|
||
if [[ "$PROLE_KDC_BUILD_IMAGE_SET" == "1" ]]; then
|
||
ensure_docker
|
||
fi
|
||
|
||
log "[WARN] Docker is not available; skipping Knoe KDC image build."
|
||
log " Set PROLE_KDC_BUILD_IMAGE=1 to force building (requires Docker), or set PROLE_KDC_IMAGE to a prebuilt image."
|
||
resolve_knoe_kdc_image
|
||
return 0
|
||
fi
|
||
|
||
local docker_dir
|
||
docker_dir=$(resolve_kdc_docker_dir || true)
|
||
if [[ -z "$docker_dir" ]]; then
|
||
err "ERROR: knoe/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:-knoe-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
|
||
err "ERROR: No registry host configured for k3s; cannot publish authority image."
|
||
err " Set PROLE_K3S_SERVER (so the script can infer <host>:5000) or set PROLE_KDC_REGISTRY_HOST explicitly."
|
||
exit 1
|
||
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_knoe_kdc_secrets() {
|
||
local secret_name="knoe-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_knoe_kdc() {
|
||
if [[ "$PROLE_KDC_ENABLED" == "0" || "$PROLE_KDC_ENABLED" == "false" || "$PROLE_KDC_ENABLED" == "False" ]]; then
|
||
log "Knoe 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_knoe_kdc_defaults
|
||
build_knoe_kdc_image
|
||
ensure_knoe_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 Knoe 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
|
||
# PVC storage class: explicit when $PROLE_KDC_STORAGE_CLASS is set;
|
||
# otherwise leave blank so the cluster's default StorageClass picks
|
||
# the binder (k3s "local-path", GKE "standard", etc.).
|
||
local storage_class_block=""
|
||
if [[ -n "${PROLE_KDC_STORAGE_CLASS:-}" ]]; then
|
||
storage_class_block=" storageClassName: ${PROLE_KDC_STORAGE_CLASS}"
|
||
fi
|
||
cat <<EOF | kubectl apply -n "$KDC_NAMESPACE" -f -
|
||
apiVersion: v1
|
||
kind: ConfigMap
|
||
metadata:
|
||
name: knoe-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/knoe-kdc/krb5.conf ]]; then
|
||
cp /opt/knoe-kdc/krb5.conf /etc/krb5.conf
|
||
fi
|
||
if [[ -f /opt/knoe-kdc/kdc.conf ]]; then
|
||
cp /opt/knoe-kdc/kdc.conf /etc/krb5kdc/kdc.conf
|
||
fi
|
||
if [[ -f /opt/knoe-kdc/kadm5.acl ]]; then
|
||
cp /opt/knoe-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 'knoe-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 'knoe-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 here‑doc 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}}"
|
||
# ------------------------------------------------------------------
|
||
# Cross-realm krbtgt principals — RC4 only.
|
||
#
|
||
# Both directions of the trust live as their own krbtgt principal,
|
||
# each keyed to the same shared password. We pin RC4 (arcfour-hmac)
|
||
# because AES key derivation requires a salt, and Samba's salt
|
||
# convention (<remote_realm> + UPN) does not match MIT's
|
||
# (<local_realm> + <principal-no-realm>). RC4 derives keys from
|
||
# the password alone, so both sides converge with no salt fight.
|
||
# ------------------------------------------------------------------
|
||
|
||
# Outbound: KNOE.LOCAL → PROLE.ORG (issued here, decrypted by Samba)
|
||
if ! kadmin.local -q "get_principal krbtgt/\${PROLE_KDC_TRUST_REALM}@\${PROLE_KDC_REALM}" >/dev/null 2>&1; then
|
||
echo "Creating outbound trust principal krbtgt/\${PROLE_KDC_TRUST_REALM}@\${PROLE_KDC_REALM}..."
|
||
kadmin.local -q "addprinc -pw \${shared_pw} -e arcfour-hmac:normal krbtgt/\${PROLE_KDC_TRUST_REALM}@\${PROLE_KDC_REALM}"
|
||
fi
|
||
|
||
# Inbound: PROLE.ORG → KNOE.LOCAL (issued by Samba, decrypted here)
|
||
if ! kadmin.local -q "get_principal krbtgt/\${PROLE_KDC_REALM}@\${PROLE_KDC_TRUST_REALM}" >/dev/null 2>&1; then
|
||
echo "Creating inbound trust principal krbtgt/\${PROLE_KDC_REALM}@\${PROLE_KDC_TRUST_REALM}..."
|
||
kadmin.local -q "addprinc -pw \${shared_pw} -e arcfour-hmac:normal krbtgt/\${PROLE_KDC_REALM}@\${PROLE_KDC_TRUST_REALM}"
|
||
fi
|
||
|
||
# NOTE: The Samba-side trust account (user "krbtgt_\${PROLE_KDC_REALM}"
|
||
# in PROLE.ORG with UPN/SPN krbtgt/\${PROLE_KDC_REALM}) is provisioned
|
||
# OUT-OF-BAND by this repo's Ansible playbook:
|
||
# infrastructure/playbooks/kerberos_trust_setup.yml
|
||
# Earlier versions of this script tried to use a remote "kadmin"
|
||
# client to write that principal into Samba, but Samba AD does not
|
||
# accept additions over MIT's kadmin protocol — it always failed
|
||
# with "Missing parameters in krb5.conf required for kadmin client".
|
||
# Run the playbook once after this KDC comes up:
|
||
# ANSIBLE_VAULT_PASSWORD_FILE=\$PWD/.vault_pass \\
|
||
# ansible-playbook infrastructure/playbooks/kerberos_trust_setup.yml
|
||
echo "Note: Samba-side trust account is provisioned out-of-band by"
|
||
echo " infrastructure/playbooks/kerberos_trust_setup.yml"
|
||
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/knoe-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: knoe-kdc-secrets
|
||
key: master_password
|
||
- name: PROLE_KDC_ADMIN_PASSWORD
|
||
valueFrom:
|
||
secretKeyRef:
|
||
name: knoe-kdc-secrets
|
||
key: admin_password
|
||
- name: PROLE_KDC_TRUST_SHARED_PASSWORD
|
||
valueFrom:
|
||
secretKeyRef:
|
||
name: knoe-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: knoe-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: knoe-kdc-config
|
||
mountPath: /opt/knoe-kdc
|
||
- name: knoe-kdc-data
|
||
mountPath: /var/lib/krb5kdc
|
||
- name: knoe-kdc-data
|
||
mountPath: /etc/krb5kdc
|
||
volumes:
|
||
- name: knoe-kdc-config
|
||
configMap:
|
||
name: knoe-kdc-config
|
||
- name: knoe-kdc-data
|
||
persistentVolumeClaim:
|
||
claimName: knoe-kdc-data
|
||
---
|
||
apiVersion: v1
|
||
kind: PersistentVolumeClaim
|
||
metadata:
|
||
name: knoe-kdc-data
|
||
namespace: ${KDC_NAMESPACE}
|
||
spec:
|
||
accessModes:
|
||
- ReadWriteOnce
|
||
resources:
|
||
requests:
|
||
storage: ${PROLE_KDC_STORAGE_SIZE:-1Gi}
|
||
${storage_class_block}
|
||
---
|
||
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
|
||
|
||
# knoe-auth runs in PROLE_AUTH_NAMESPACE (default: PROLE_NAMESPACE) and mounts
|
||
# ConfigMap `knoe-kdc-config` for its embedded KDC sidecar. When the KDC itself
|
||
# is deployed into a different namespace (default: SERVICE_NAMESPACE), ensure
|
||
# the configmap also exists in the knoe-auth namespace to prevent FailedMount.
|
||
if [[ -n "${PROLE_AUTH_NAMESPACE:-}" && "${PROLE_AUTH_NAMESPACE}" != "${KDC_NAMESPACE}" ]]; then
|
||
if ! kubectl get namespace "$PROLE_AUTH_NAMESPACE" >/dev/null 2>&1; then
|
||
log "Creating namespace '$PROLE_AUTH_NAMESPACE' ..."
|
||
kubectl create namespace "$PROLE_AUTH_NAMESPACE" >/dev/null 2>&1 || true
|
||
fi
|
||
log "Ensuring ConfigMap 'knoe-kdc-config' exists in namespace '${PROLE_AUTH_NAMESPACE}' for knoe-auth ..."
|
||
cat <<EOF | kubectl apply -n "$PROLE_AUTH_NAMESPACE" -f -
|
||
apiVersion: v1
|
||
kind: ConfigMap
|
||
metadata:
|
||
name: knoe-kdc-config
|
||
namespace: ${PROLE_AUTH_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
|
||
export DEBIAN_FRONTEND=noninteractive
|
||
|
||
realm="\${PROLE_KDC_REALM:-PROLE.ORG}"
|
||
admin_principal="\${PROLE_KDC_ADMIN_PRINCIPAL:-admin/admin}"
|
||
if [[ "\${admin_principal}" != *"@"* ]]; then
|
||
admin_principal="\${admin_principal}@\${realm}"
|
||
fi
|
||
|
||
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/knoe-kdc/krb5.conf ]]; then
|
||
cp /opt/knoe-kdc/krb5.conf /etc/krb5.conf
|
||
fi
|
||
if [[ -f /opt/knoe-kdc/kdc.conf ]]; then
|
||
cp /opt/knoe-kdc/kdc.conf /etc/krb5kdc/kdc.conf
|
||
fi
|
||
if [[ -f /opt/knoe-kdc/kadm5.acl ]]; then
|
||
cp /opt/knoe-kdc/kadm5.acl /etc/krb5kdc/kadm5.acl
|
||
fi
|
||
|
||
if [[ -z "\${PROLE_KDC_MASTER_PASSWORD:-}" ]]; then
|
||
echo "ERROR: Missing required env PROLE_KDC_MASTER_PASSWORD (secret 'knoe-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 'knoe-kdc-secrets/admin_password')." >&2
|
||
exit 1
|
||
fi
|
||
|
||
if [[ ! -f /var/lib/krb5kdc/principal ]]; then
|
||
echo "Initializing realm database for \${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
|
||
|
||
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
|
||
EOF
|
||
fi
|
||
|
||
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: Knoe 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 Knoe 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_knoe_kdc() {
|
||
log "Removing Knoe 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 knoe-kdc-config --ignore-not-found
|
||
|
||
if [[ -n "${PROLE_AUTH_NAMESPACE:-}" && "${PROLE_AUTH_NAMESPACE}" != "${KDC_NAMESPACE}" ]]; then
|
||
kubectl -n "$PROLE_AUTH_NAMESPACE" delete configmap knoe-kdc-config --ignore-not-found
|
||
fi
|
||
}
|
||
|
||
status() {
|
||
log "--- init_kdc status ---"
|
||
log "Namespace: $KDC_NAMESPACE"
|
||
log "Knoe KDC enabled: ${PROLE_KDC_ENABLED:-0}"
|
||
log "Knoe KDC realm: ${PROLE_KDC_REALM:-<unset>}"
|
||
log "Knoe KDC service: ${PROLE_KDC_SERVICE:-<unset>}"
|
||
log "Knoe KDC image: ${PROLE_KDC_IMAGE:-<unset>}"
|
||
if kubectl -n "$KDC_NAMESPACE" get deploy "$PROLE_KDC_NAME" >/dev/null 2>&1; then
|
||
log "[OK] Knoe KDC deployment present"
|
||
if maybe_cleanup_stale_kdc "status check" "strict"; then
|
||
log "[INFO] Stale Knoe KDC deployment removed. Re-run init_kdc.sh update to recreate."
|
||
fi
|
||
else
|
||
log "[INFO] Knoe KDC deployment not present"
|
||
fi
|
||
}
|
||
|
||
case "$ACTION" in
|
||
initialize|init|update|deploy|start)
|
||
ensure_tools
|
||
ensure_namespace
|
||
ensure_knoe_kdc
|
||
;;
|
||
status)
|
||
ensure_tools
|
||
status
|
||
;;
|
||
cleanup|delete|remove)
|
||
ensure_tools
|
||
ensure_namespace
|
||
cleanup_knoe_kdc
|
||
;;
|
||
*)
|
||
err "Usage: $0 {initialize|update|status|cleanup}"
|
||
exit 2
|
||
;;
|
||
esac
|