prole/mock_val/init_knoe_auth.sh
chrisfu 3f96f66a78 feat(mock_val): rewrite init scripts; add new service init scripts
Rewrites (updated for knoe namespace, GKE support, and current service configs):
  init_gitlab.sh, init_kong.sh, init_cnpg_backup.sh, init_monitoring.sh,
  init_garage_store.sh, init_gitea.sh, init_kdc.sh, init_openbao.sh,
  init_argocd.sh, init_certmgr.sh, init_common_services.sh, init_db_manager.sh,
  init_forgejo.sh, init_k3s_registry.sh, init_kerberos.sh, init_nginx_ingress.sh,
  init_port_forwards.sh, init_registry.sh, init_service_layer.sh

Deleted: init_cloudnative_pg.sh (superseded by init_cnpg_gke.sh)

New scripts:
  init_cnpg_gke.sh      — CNPG setup for GKE with Workload Identity
  init_knoe_auth.sh     — knoe-auth OIDC service init
  init_knoe_users.sh    — user provisioning
  init_redis.sh         — Redis init
  init_oauth2_proxy.sh / init_oauth2_proxy_prole.sh — OAuth2 proxy setup
  init_grafana_oauth.sh / init_grafana_oauth_prole.sh — Grafana OAuth wiring
  init_1password.sh     — 1Password Connect init
  init_min.sh           — minimal bootstrap

Co-authored-by: Junie <junie@jetbrains.com>
2026-05-23 21:31:27 -07:00

574 lines
22 KiB
Bash
Executable File

#!/usr/bin/env bash
# init_knoe_auth.sh
# Provision knoe-auth (Kerberos KDC + Spring Boot enrollment service).
#
# Usage:
# ./etc/init_knoe_auth.sh [--context KUBECONTEXT] [--namespace NAMESPACE] [--project PROJECT_ID] [--mode MODE]
# ./etc/init_knoe_auth.sh initialize # full setup
# ./etc/init_knoe_auth.sh schema # schema only (idempotent)
# ./etc/init_knoe_auth.sh invite EMAIL # create first admin invite
# ./etc/init_knoe_auth.sh status # check pod + principal state
#
# Modes:
# (default / gke) GKE deploy — uses 1Password for secrets, GCP Workload Identity
# k3d Local k3d dev loop — uses hardcoded dev passwords, skips GCP steps
#
# Prerequisites:
# kubectl, op (1Password CLI — GKE mode only), psql (or kubectl exec fallback)
#
# Env vars (override args):
# APP_CLUSTER_KUBECONTEXT, KNOE_NAMESPACE, GCP_PROJECT_ID,
# KNOE_DB_HOST, KNOE_DB_PORT, KNOE_DB_NAME, KNOE_DB_SUPERUSER,
# KNOE_MODE (set to 'k3d' as alternative to --mode k3d)
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
GKE_DIR="$REPO_ROOT/deploy/gcp/gke"
# ── Defaults ────────────────────────────────────────────────────────────────
MODE="${KNOE_MODE:-gke}" # gke (default) or k3d
APP_CTX="${APP_CLUSTER_KUBECONTEXT:-gke_plenary-truck-485623-p7_us-west3_knoe-dev-0}"
NAMESPACE="${KNOE_NAMESPACE:-knoe-system}"
GCP_PROJECT="${GCP_PROJECT_ID:-plenary-truck-485623-p7}"
DB_HOST="${KNOE_DB_HOST:-}" # resolved from CNPG svc if blank
DB_PORT="${KNOE_DB_PORT:-5432}"
DB_NAME="${KNOE_DB_NAME:-knoe}"
DB_SUPERUSER="${KNOE_DB_SUPERUSER:-postgres}"
REALM="KNOE.DEV"
AUTH_HOST="${KNOE_AUTH_HOST:-https://auth.knoe.dev}"
# k3d-specific defaults (overridden when --mode k3d is active)
K3D_CLUSTER_NAME="${K3D_CLUSTER_NAME:-k3d-knoe}"
K3D_CTX="k3d-${K3D_CLUSTER_NAME}"
K3D_DB_NS="${K3D_DB_NS:-knoe-db-0}"
# Dev-only passwords — NOT used in GKE mode; safe to commit
K3D_KDC_MASTER_PASSWORD="${K3D_KDC_MASTER_PASSWORD:-knoe-local-master-dev}"
K3D_KDC_ADMIN_PASSWORD="${K3D_KDC_ADMIN_PASSWORD:-knoe-local-admin-dev}"
log() { echo "[init_knoe_auth] $*"; }
info() { log "INFO $*"; }
warn() { log "WARN $*" >&2; }
die() { log "ERROR $*" >&2; exit 1; }
kube() { kubectl --context="$APP_CTX" "$@"; }
# ── Argument parsing ─────────────────────────────────────────────────────────
COMMAND="${1:-initialize}"
shift || true
while [[ $# -gt 0 ]]; do
case "$1" in
--context) APP_CTX="$2"; shift 2 ;;
--namespace) NAMESPACE="$2"; shift 2 ;;
--project) GCP_PROJECT="$2"; shift 2 ;;
--db-host) DB_HOST="$2"; shift 2 ;;
--mode) MODE="$2"; shift 2 ;;
*) break ;;
esac
done
# Apply k3d mode overrides after argument parsing
if [[ "$MODE" == "k3d" ]]; then
APP_CTX="${APP_CLUSTER_KUBECONTEXT:-${K3D_CTX}}"
REALM="KNOE.LOCAL"
AUTH_HOST="${KNOE_AUTH_HOST:-http://localhost:8080}"
# DB is accessed via port-forward (localhost:5432) in k3d mode
DB_HOST="${KNOE_DB_HOST:-localhost}"
fi
# ── Helpers ──────────────────────────────────────────────────────────────────
require_tool() {
command -v "$1" >/dev/null 2>&1 || die "Required tool not found: $1 — install it and retry."
}
wait_for_pods() {
local label="$1"
local timeout="${2:-180}"
info "Waiting up to ${timeout}s for pods with label ${label} in ${NAMESPACE}..."
kube -n "$NAMESPACE" wait pod \
-l "$label" \
--for=condition=Ready \
--timeout="${timeout}s"
}
op_secret() {
# Retrieve a 1Password secret; fall back to prompting if op isn't authed.
local item="$1" field="${2:-password}"
if command -v op >/dev/null 2>&1; then
op item get "$item" --fields "$field" 2>/dev/null || {
warn "1Password: could not read $item/$field — prompting."
read -rsp "Enter value for $item/$field: " val; echo
printf '%s' "$val"
}
else
read -rsp "Enter value for $item/$field: " val; echo
printf '%s' "$val"
fi
}
resolve_db_host() {
if [[ -n "$DB_HOST" ]]; then return; fi
if [[ "$MODE" == "k3d" ]]; then
# In k3d mode the engineer runs make k3d-knoe-pf first; DB is at localhost:5432
DB_HOST="localhost"
info "k3d mode: using DB host localhost (port-forward expected on :5432)"
return
fi
# Try to resolve CNPG primary service from the DB cluster context
DB_CTX="${DB_CLUSTER_KUBECONTEXT:-gke_plenary-truck-485623-p7_us-west3_knoe-cnpg-0}"
DB_HOST=$(kubectl --context="$DB_CTX" -n knoe-db-0 \
get svc knoe-db-rw -o jsonpath='{.spec.clusterIP}' 2>/dev/null || echo "")
[[ -z "$DB_HOST" ]] && die "Cannot resolve KNOE DB host. Set KNOE_DB_HOST or ensure knoe-db-rw svc exists."
info "Resolved DB host: $DB_HOST"
}
psql_file() {
local file="$1"
if [[ "$MODE" == "k3d" ]]; then
# In k3d mode: exec into the CNPG primary pod directly (no port-forward needed for schema)
local pod
pod=$(kubectl --context="$APP_CTX" -n "$K3D_DB_NS" \
get pod -l cnpg.io/cluster=knoe-db,role=primary \
-o jsonpath='{.items[0].metadata.name}' 2>/dev/null)
[[ -z "$pod" ]] && die "Cannot find CNPG primary pod in k3d. Is 'make k3d-knoe-up' complete?"
kubectl --context="$APP_CTX" -n "$K3D_DB_NS" cp "$file" "${pod}:/tmp/knoe_auth_schema.sql"
kubectl --context="$APP_CTX" -n "$K3D_DB_NS" exec "$pod" -- \
psql -U "$DB_SUPERUSER" -d "$DB_NAME" -f /tmp/knoe_auth_schema.sql
return
fi
if command -v psql >/dev/null 2>&1 && [[ -n "${PGPASSWORD:-}" ]]; then
psql -h "$DB_HOST" -p "$DB_PORT" -U "$DB_SUPERUSER" -d "$DB_NAME" -f "$file"
else
# Fallback: exec into CNPG primary pod
DB_CTX="${DB_CLUSTER_KUBECONTEXT:-gke_plenary-truck-485623-p7_us-west3_knoe-cnpg-0}"
local pod
pod=$(kubectl --context="$DB_CTX" -n knoe-db-0 \
get pod -l cnpg.io/cluster=knoe-db,role=primary \
-o jsonpath='{.items[0].metadata.name}' 2>/dev/null)
[[ -z "$pod" ]] && die "Cannot find CNPG primary pod. Set PGPASSWORD and KNOE_DB_HOST for direct psql."
kubectl --context="$DB_CTX" -n knoe-db-0 cp "$file" "${pod}:/tmp/knoe_auth_schema.sql"
kubectl --context="$DB_CTX" -n knoe-db-0 exec "$pod" -- \
psql -U "$DB_SUPERUSER" -d "$DB_NAME" -f /tmp/knoe_auth_schema.sql
fi
}
# ── Schema ───────────────────────────────────────────────────────────────────
run_schema() {
info "Applying knoe-auth schema additions..."
resolve_db_host
local tmpfile
tmpfile=$(mktemp /tmp/knoe_auth_schema_XXXX.sql)
cat > "$tmpfile" <<'ENDSQL'
-- ── knoe-auth Round 1 schema additions ───────────────────────────────────────
-- Idempotent: all CREATE TABLE ... IF NOT EXISTS
-- Invite tokens (admin creates, single-use)
-- contact is the email/phone the invite was sent to — the OTP trust anchor.
-- knoe.dev starts with ZERO pre-knowledge of the developer's home org.
CREATE TABLE IF NOT EXISTS knoe.invitation (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
token TEXT NOT NULL UNIQUE,
contact TEXT NOT NULL,
contact_type TEXT NOT NULL DEFAULT 'email',
name_hint TEXT,
otp_hash TEXT NOT NULL,
otp_expires_at TIMESTAMPTZ NOT NULL,
otp_attempts INT NOT NULL DEFAULT 0,
otp_verified_at TIMESTAMPTZ,
created_by TEXT NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
expires_at TIMESTAMPTZ NOT NULL,
used_at TIMESTAMPTZ,
used_by TEXT
);
CREATE INDEX IF NOT EXISTS idx_invitation_token ON knoe.invitation(token);
CREATE INDEX IF NOT EXISTS idx_invitation_contact ON knoe.invitation(contact);
-- External identity corroborations (Google sub → knoe user)
-- provider_hd records the developer's home domain (prole.org, gmail.com, etc.)
-- for audit purposes only — it is NOT used for access control.
CREATE TABLE IF NOT EXISTS knoe.identity (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES knoe.user(id) ON DELETE CASCADE,
provider TEXT NOT NULL,
provider_sub TEXT NOT NULL,
provider_email TEXT,
provider_hd TEXT,
verified_at TIMESTAMPTZ NOT NULL,
UNIQUE(provider, provider_sub)
);
CREATE INDEX IF NOT EXISTS idx_identity_user ON knoe.identity(user_id);
-- TOTP 2FA credentials (encrypted secret, backup codes)
CREATE TABLE IF NOT EXISTS knoe.totp_credential (
user_id INT PRIMARY KEY REFERENCES knoe.user(id) ON DELETE CASCADE,
secret TEXT NOT NULL,
verified_at TIMESTAMPTZ,
backup_codes TEXT[],
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
-- Platform-managed resources (repos, db roles, policies, etc.)
CREATE TABLE IF NOT EXISTS knoe.knobject (
id SERIAL PRIMARY KEY,
type TEXT NOT NULL,
name TEXT NOT NULL,
platform_id TEXT,
metadata JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
UNIQUE(type, name)
);
-- Access grants (user → knobject with role)
CREATE TABLE IF NOT EXISTS knoe.access_grant (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES knoe.user(id),
knobject_id INT NOT NULL REFERENCES knoe.knobject(id),
role TEXT NOT NULL,
granted_by TEXT NOT NULL,
granted_at TIMESTAMPTZ NOT NULL DEFAULT now(),
revoked_at TIMESTAMPTZ,
UNIQUE(user_id, knobject_id)
);
-- Async provisioning job queue (GitLab user, Gitea user, CNPG role, etc.)
CREATE TABLE IF NOT EXISTS knoe.provisioning_job (
id SERIAL PRIMARY KEY,
user_id INT NOT NULL REFERENCES knoe.user(id),
job_type TEXT NOT NULL,
status TEXT NOT NULL DEFAULT 'pending',
payload JSONB,
result JSONB,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
updated_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX IF NOT EXISTS idx_provisioning_job_status
ON knoe.provisioning_job(status, created_at);
-- Seed well-known knobjects
INSERT INTO knoe.knobject (type, name, metadata) VALUES
('gitea_org', 'knoey.com', '{"description": "Knoey.com Gitea organisation"}'),
('gitlab_group','knoey.com', '{"description": "Knoey.com GitLab group"}')
ON CONFLICT (type, name) DO NOTHING;
SELECT 'knoe-auth schema v1 applied.' AS status;
ENDSQL
psql_file "$tmpfile"
rm -f "$tmpfile"
info "Schema applied."
}
# ── Dev-user seed (k3d only) ─────────────────────────────────────────────────
#
# Creates the `knoe_developer` group role (the production GKE deploy uses it
# too via pg_hba.conf `+knoe_developer` rules; on GKE it was hand-rolled per
# 2026-04-30 onboarding work, never baked into postInitTemplateSQL — see
# docs/db-access.md). Then creates a `chrisfu` LOGIN role with a dev
# password and grants `knoe_developer` to it, so the engineer can connect
# from the host as chrisfu@knoey.com via the port-forward.
#
# Idempotent: re-runs on every `make k3d-knoe-up` and either creates or
# updates the role's password. This makes the rebuild loop deterministic —
# after `down && up`, chrisfu's password is always `chrisfu-dev`.
seed_dev_users_k3d() {
info "Seeding dev users (knoe_developer + chrisfu) for k3d ..."
local tmpfile
tmpfile=$(mktemp /tmp/knoe_auth_seed_XXXX.sql)
cat > "$tmpfile" <<'ENDSQL'
-- knoe_developer group role: R/W on knoe + public, R/O on auth/storage/extensions.
-- Mirrors the GKE production layout (docs/db-access.md). NOLOGIN — group only.
DO $do$ BEGIN
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'knoe_developer') THEN
CREATE ROLE knoe_developer NOLOGIN;
END IF;
END $do$;
-- knoe + public — full R/W
GRANT USAGE ON SCHEMA knoe TO knoe_developer;
GRANT USAGE ON SCHEMA public TO knoe_developer;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA knoe TO knoe_developer;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO knoe_developer;
GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA knoe TO knoe_developer;
GRANT USAGE, SELECT, UPDATE ON ALL SEQUENCES IN SCHEMA public TO knoe_developer;
ALTER DEFAULT PRIVILEGES IN SCHEMA knoe GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO knoe_developer;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT SELECT, INSERT, UPDATE, DELETE ON TABLES TO knoe_developer;
ALTER DEFAULT PRIVILEGES IN SCHEMA knoe GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO knoe_developer;
ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT USAGE, SELECT, UPDATE ON SEQUENCES TO knoe_developer;
-- Per-engineer LOGIN role for chrisfu (local-dev only; password is fixed dev value).
-- Creates if missing, otherwise resets the password — guarantees the rebuild loop
-- always produces the same credentials.
DO $do$ BEGIN
IF NOT EXISTS (SELECT FROM pg_roles WHERE rolname = 'chrisfu') THEN
CREATE ROLE chrisfu LOGIN INHERIT PASSWORD 'chrisfu-dev';
ELSE
ALTER ROLE chrisfu WITH LOGIN INHERIT PASSWORD 'chrisfu-dev';
END IF;
END $do$;
GRANT knoe_developer TO chrisfu;
-- Sanity check (visible in psql_file output).
SELECT 'chrisfu' AS role,
pg_has_role('chrisfu', 'knoe_developer', 'MEMBER') AS is_developer;
ENDSQL
psql_file "$tmpfile"
rm -f "$tmpfile"
info "Dev-user seed applied (chrisfu / chrisfu-dev, member of knoe_developer)."
}
# ── KDC secrets ──────────────────────────────────────────────────────────────
create_kdc_secrets() {
if kube -n "$NAMESPACE" get secret knoe-kdc-secrets >/dev/null 2>&1; then
info "knoe-kdc-secrets already exists — skipping."
return
fi
if [[ "$MODE" == "k3d" ]]; then
info "k3d mode: creating knoe-kdc-secrets with dev passwords (no 1Password)..."
kube -n "$NAMESPACE" create secret generic knoe-kdc-secrets \
--from-literal=master_password="$K3D_KDC_MASTER_PASSWORD" \
--from-literal=admin_password="$K3D_KDC_ADMIN_PASSWORD"
info "knoe-kdc-secrets created (dev passwords)."
return
fi
info "Creating knoe-kdc-secrets from 1Password..."
local master admin
master=$(op_secret "knoe-kdc-master" "password")
admin=$(op_secret "knoe-kdc-admin" "password")
kube -n "$NAMESPACE" create secret generic knoe-kdc-secrets \
--from-literal=master_password="$master" \
--from-literal=admin_password="$admin"
info "knoe-kdc-secrets created."
}
create_google_oidc_secret() {
if kube -n "$NAMESPACE" get secret knoe-auth-google-oidc >/dev/null 2>&1; then
info "knoe-auth-google-oidc already exists — skipping."
return
fi
info "Creating knoe-auth-google-oidc secret..."
local client_id client_secret
client_id=$(op_secret "knoe-google-oidc" "client_id")
client_secret=$(op_secret "knoe-google-oidc" "client_secret")
kube -n "$NAMESPACE" create secret generic knoe-auth-google-oidc \
--from-literal=client_id="$client_id" \
--from-literal=client_secret="$client_secret"
info "knoe-auth-google-oidc created."
}
create_session_secret() {
if kube -n "$NAMESPACE" get secret knoe-auth-secrets >/dev/null 2>&1; then
info "knoe-auth-secrets already exists — skipping."
return
fi
info "Creating knoe-auth-secrets (session HMAC key)..."
local session_secret
session_secret=$(op_secret "knoe-auth-session" "password")
kube -n "$NAMESPACE" create secret generic knoe-auth-secrets \
--from-literal=sessionSecret="$session_secret"
info "knoe-auth-secrets created."
}
create_oidc_path_b_secret() {
if kube -n "$NAMESPACE" get secret knoe-auth-oidc >/dev/null 2>&1; then
info "knoe-auth-oidc already exists — skipping."
return
fi
info "Creating knoe-auth-oidc secret for Path B..."
local client_id client_secret signing_key
client_id=$(op_secret "knoe-auth-oidc-gitlab" "client_id")
client_secret=$(op_secret "knoe-auth-oidc-gitlab" "client_secret")
signing_key=$(op_secret "knoe-auth-oidc-signing" "private_key")
kube -n "$NAMESPACE" create secret generic knoe-auth-oidc \
--from-literal=client-id="$client_id" \
--from-literal=client-secret="$client_secret" \
--from-literal=signing-key="$signing_key"
info "knoe-auth-oidc created."
}
# ── Manifests ────────────────────────────────────────────────────────────────
apply_manifests() {
if [[ "$MODE" == "k3d" ]]; then
local k3d_dir="$REPO_ROOT/k8s/knoe"
info "k3d mode: applying KDC manifests from $k3d_dir..."
kube apply -f "$k3d_dir/knoe-kdc-configmap.yaml"
kube apply -f "$k3d_dir/knoe-kdc-pvc.yaml"
kube apply -f "$k3d_dir/knoe-kdc-deployment.yaml"
kube apply -f "$k3d_dir/knoe-kdc-service.yaml"
info "k3d mode: skipping knoe-auth Deployment (runs on host via mvn spring-boot:run)."
return
fi
info "Applying KDC ConfigMap..."
kube apply -f "$GKE_DIR/knoe-kdc-configmap.yaml"
info "Applying knoe-auth Deployment..."
kube apply -f "$GKE_DIR/knoe-auth-deployment.yaml"
}
# ── Invite helper ─────────────────────────────────────────────────────────────
create_first_invite() {
local contact="${1:-}"
[[ -z "$contact" ]] && { read -rp "Invite contact (email or phone): " contact; }
local name_hint=""
read -rp "Display name hint (optional, press Enter to skip): " name_hint || true
info "Creating invite for: $contact"
local admin_token
admin_token=$(op_secret "knoe-admin-token" "credential" 2>/dev/null || \
{ read -rsp "Admin token: " t; echo; printf '%s' "$t"; })
local response
response=$(curl -sf -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer $admin_token" \
-d "{\"contact\":\"$contact\",\"contactType\":\"email\",\"nameHint\":\"$name_hint\"}" \
"${AUTH_HOST}/auth/admin/invites") || {
warn "Admin API call failed. knoe-auth may not be ready yet."
warn "Retry: POST ${AUTH_HOST}/auth/admin/invites"
return 1
}
local invite_url
invite_url=$(printf '%s' "$response" | grep -o '"enrollUrl":"[^"]*"' | sed 's/"enrollUrl":"//;s/"//')
printf '\n\033[1;32mInvite URL:\033[0m %s\n\n' "$invite_url"
info "Send the above URL to: $contact"
}
# ── Status ────────────────────────────────────────────────────────────────────
show_status() {
if [[ "$MODE" == "k3d" ]]; then
info "=== KDC pod status ==="
kube -n "$NAMESPACE" get pods -l app=knoe-kdc 2>/dev/null || true
info "=== KDC secret ==="
kube -n "$NAMESPACE" get secret knoe-kdc-secrets 2>/dev/null || true
info "=== KDC service ==="
kube -n "$NAMESPACE" get svc knoe-kdc 2>/dev/null || true
info "=== knoe-auth runs on host ==="
info " export KRB5_CONFIG=\$PWD/etc/krb5.local.conf"
info " mvn -pl authority spring-boot:run"
return
fi
info "=== Pod status ==="
kube -n "$NAMESPACE" get pods -l app=knoe-auth 2>/dev/null || true
info "=== Secrets ==="
kube -n "$NAMESPACE" get secret \
knoe-kdc-secrets knoe-auth-google-oidc knoe-auth-secrets 2>/dev/null || true
info "=== Services ==="
kube -n "$NAMESPACE" get svc knoe-auth 2>/dev/null || true
info "=== Enrollment endpoint ==="
info "${AUTH_HOST}/auth/enroll"
}
# ── Full initialization ───────────────────────────────────────────────────────
cmd_initialize() {
require_tool kubectl
info "=== knoe-auth initialization ==="
info " Mode: $MODE"
info " Realm: $REALM"
info " Cluster: $APP_CTX"
info " NS: $NAMESPACE"
info ""
if [[ "$MODE" == "k3d" ]]; then
cmd_initialize_k3d
return
fi
# 1. Ensure namespace exists
kube get namespace "$NAMESPACE" >/dev/null 2>&1 || \
kube create namespace "$NAMESPACE"
# 2. Secrets
create_kdc_secrets
create_google_oidc_secret
create_session_secret
create_oidc_path_b_secret
# 3. Apply ConfigMap + Deployment
apply_manifests
# 4. Wait for pods
wait_for_pods "app=knoe-auth" 240
# 5. Schema
run_schema
# 6. Done
info ""
info "=== knoe-auth is ready ==="
info "Enrollment URL: ${AUTH_HOST}/auth/enroll?token=<invite_token>"
info ""
info "Next: create first admin invite:"
info " $0 invite chrisfu@knoey.com"
info ""
show_status
}
cmd_initialize_k3d() {
info "=== k3d mode: provisioning KDC + schema ==="
# 1. Ensure namespace exists
kube get namespace "$NAMESPACE" >/dev/null 2>&1 || \
kube create namespace "$NAMESPACE"
# 2. KDC secret (dev passwords, no 1Password)
create_kdc_secrets
# 3. Apply KDC manifests
apply_manifests
# 4. Wait for KDC pod
info "Waiting for KDC deployment to be ready..."
kube -n "$NAMESPACE" rollout status deployment/knoe-kdc --timeout=120s
# 5. Schema (via kubectl exec into CNPG primary)
run_schema
# 6. Dev-user seed: knoe_developer group + chrisfu role (k3d-only).
seed_dev_users_k3d
info ""
info "=== k3d knoe-auth stack is ready ==="
info "Run: make k3d-knoe-pf"
info "Then in another terminal:"
info " export KRB5_CONFIG=\$PWD/etc/krb5.local.conf"
info " export KNOE_AUTH_OIDC_SIGNING_KEY=\$(cat etc/secrets/knoe-auth-oidc-key.b64)"
info " mvn -pl authority spring-boot:run -Dspring-boot.run.profiles=k3d"
info ""
info "Dev DB access (from host, with port-forward up):"
info " PGPASSWORD=chrisfu-dev psql -h localhost -U chrisfu -d knoe-db"
info ""
show_status
}
# ── Dispatch ──────────────────────────────────────────────────────────────────
case "$COMMAND" in
initialize) cmd_initialize ;;
schema) run_schema ;;
invite) create_first_invite "${1:-}" ;;
status) show_status ;;
*)
echo "Usage: $0 {initialize|schema|invite EMAIL|status} [--context CTX] [--namespace NS]" >&2
exit 1
;;
esac