prole/etc/init_openbao.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

732 lines
25 KiB
Bash
Executable File

#!/usr/bin/env bash
set -euo pipefail
# init_openbao.sh
# Purpose:
# - Run OpenBao as a local Docker container (dev mode) and store admin key pair
# - Optionally generate Kerberos krb5.conf ConfigMap for external realm (legacy k8s flow)
# - Store secrets in OpenBao kv (admin keys, user keys, db password, optional TDE keys)
# Initialize SCRIPT_DIR
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 [[ -z "${PROLE_SERVICE:-}" ]]; then
echo "ERROR: PROLE_SERVICE is not defined. Provide PROLE_HOME/env.sh or ~/.prole/env.sh" >&2
exit 1
fi
ACTION=""
NAMESPACE_OVERRIDE=""
# Defaults
OPENBAO_NAME=${OPENBAO_NAME:-openbao}
OPENBAO_IMAGE=${OPENBAO_IMAGE:-ghcr.io/openbao/openbao:latest}
# Support both PROLE_HOME/k8s and sibling k8s directory
if [[ -d "$SCRIPT_DIR/../k8s/openbao" ]]; then
OPENBAO_MANIFEST_DIR="$SCRIPT_DIR/../k8s/openbao"
elif [[ -n "${PROLE_HOME:-}" && -d "$PROLE_HOME/k8s/openbao" ]]; then
OPENBAO_MANIFEST_DIR="$PROLE_HOME/k8s/openbao"
else
OPENBAO_MANIFEST_DIR="$SCRIPT_DIR/../k8s/openbao"
fi
# Kerberos realm defaults
KRB5_REALM=${KRB5_REALM:-PROLE.ORG}
DOMAIN=${DOMAIN:-$(echo "$KRB5_REALM" | tr 'A-Z' 'a-z')}
KRB5_KDC=${KRB5_KDC:-kdc.$DOMAIN}
KRB5_ADMIN=${KRB5_ADMIN:-}
# Secrets and token locations
SECRETS_DIR="$PROLE_SERVICE/secrets"
mkdir -p "$SECRETS_DIR"
admin_key_priv="$SECRETS_DIR/admin_ed25519.key"
admin_key_pub="$SECRETS_DIR/admin_ed25519.pub"
root_token_file="$SECRETS_DIR/openbao-root-token"
while [[ $# -gt 0 ]]; do
case "$1" in
-m|--mode)
shift
prole_set_mode "${1:-}"
;;
-m=*|--mode=*)
prole_set_mode "${1#*=}"
;;
-n|--namespace)
shift
if [[ -z "${1:-}" ]]; then
echo "ERROR: -n/--namespace requires a value" >&2
exit 2
fi
NAMESPACE_OVERRIDE="$1"
;;
-n=*|--namespace=*)
NAMESPACE_OVERRIDE="${1#*=}"
;;
start|stop|status|restart|initialize|update|reload)
ACTION="$1"
;;
--)
shift
break
;;
*)
if [[ -z "$ACTION" && "$1" != -* ]]; then
ACTION="$1"
else
echo "Unknown argument: $1" >&2
exit 2
fi
;;
esac
shift
done
if [[ -n "$NAMESPACE_OVERRIDE" ]]; then
NAMESPACE="$NAMESPACE_OVERRIDE"
export NAMESPACE
fi
if [[ -z "${NAMESPACE:-}" ]]; then
NAMESPACE="default"
export NAMESPACE
fi
OPENBAO_NAMESPACE=${OPENBAO_NAMESPACE:-${SERVICE_NAMESPACE:-${NAMESPACE:-default}}}
OPENBAO_PATH_NAMESPACE=${OPENBAO_PATH_NAMESPACE:-${NAMESPACE:-default}}
OPENBAO_RESOURCE_NAMESPACE="$OPENBAO_NAMESPACE"
BAO_NAMESPACE="${OPENBAO_PATH_NAMESPACE:-default}"
BAO_PATH_PREFIX="prole/${BAO_NAMESPACE}"
BAO_PATH_ADMIN="${BAO_PATH_PREFIX}/admin"
BAO_PATH_USER="${BAO_PATH_PREFIX}/user"
BAO_PATH_DB="${BAO_PATH_PREFIX}/db"
BAO_PATH_TDE="${BAO_PATH_PREFIX}/tde"
BAO_PATH_KERBEROS="${BAO_PATH_PREFIX}/kerberos"
BAO_PATH_MONITORING="${BAO_PATH_PREFIX}/monitoring"
# Default Grafana password to the database root password when not explicitly set.
GRAFANA_ADMIN_PASSWORD=${GRAFANA_ADMIN_PASSWORD:-${DB_PASSWORD:-}}
ensure_tools() {
for t in curl openssl base64 jq; do
command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; }
done
}
ensure_k8s_tools() {
command -v kubectl >/dev/null || { echo "Missing required tool: kubectl" >&2; exit 1; }
}
ensure_namespace() {
if ! kubectl get namespace "$OPENBAO_RESOURCE_NAMESPACE" >/dev/null 2>&1; then
echo "Creating namespace '$OPENBAO_RESOURCE_NAMESPACE' ..."
kubectl create namespace "$OPENBAO_RESOURCE_NAMESPACE" >/dev/null 2>&1 || true
fi
}
ensure_docker() {
command -v docker >/dev/null || { echo "Missing required tool: docker" >&2; exit 1; }
}
ensure_admin_keypair() {
# Ensure admin keypair exists and a root token is available
mkdir -p "$SECRETS_DIR"
if [[ ! -f "$admin_key_priv" || ! -f "$admin_key_pub" ]]; then
echo "Generating admin keypair in $SECRETS_DIR ..."
# Attempt ed25519, fallback to rsa if not available
if ! openssl genpkey -algorithm ED25519 -out "$admin_key_priv" 2>/dev/null; then
echo "ED25519 not supported by openssl, falling back to RSA 4096..."
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out "$admin_key_priv"
fi
openssl pkey -in "$admin_key_priv" -pubout -out "$admin_key_pub"
chmod 0600 "$admin_key_priv"
fi
# Ensure a root token exists for OpenBao dev server interactions
if [[ ! -f "$root_token_file" || ! -s "$root_token_file" ]]; then
local token
token="${OPENBAO_ROOT_TOKEN:-}"
if [[ -z "$token" ]]; then
token=$(detect_openbao_root_token || true)
fi
if [[ -z "$token" ]]; then
token="root"
fi
printf "%s" "$token" >"$root_token_file"
chmod 0600 "$root_token_file"
fi
}
generate_openbao_manifests() {
mkdir -p "$OPENBAO_MANIFEST_DIR"
# Only create a simple deployment if none exists; otherwise respect current file
local deploy="$OPENBAO_MANIFEST_DIR/deployment.yaml"
if [[ ! -f "$deploy" ]]; then
cat >"$deploy" <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
name: $OPENBAO_NAME
namespace: $OPENBAO_RESOURCE_NAMESPACE
spec:
replicas: 1
selector:
matchLabels:
app: $OPENBAO_NAME
template:
metadata:
labels:
app: $OPENBAO_NAME
spec:
containers:
- name: $OPENBAO_NAME
image: $OPENBAO_IMAGE
args: ["server","-dev","-dev-listen-address=0.0.0.0:8200"]
ports:
- containerPort: 8200
---
apiVersion: v1
kind: Service
metadata:
name: $OPENBAO_NAME
namespace: $OPENBAO_RESOURCE_NAMESPACE
spec:
selector:
app: $OPENBAO_NAME
ports:
- name: http
port: 8200
targetPort: 8200
EOF
fi
}
bao_service_url() {
if [[ -n "${PROLE_OPENBAO_URL:-}" ]]; then
echo "$PROLE_OPENBAO_URL"
return 0
fi
echo "http://127.0.0.1:18200"
}
wait_for_openbao_http() {
local svc
svc="$(bao_service_url)"
local max_retries=${1:-20}
local count=0
while ! curl -sS "$svc/v1/sys/health" >/dev/null 2>&1; do
if [[ $count -ge $max_retries ]]; then
echo "ERROR: OpenBao not reachable at $svc after waiting." >&2
return 1
fi
echo "Waiting for OpenBao to be ready... ($count/$max_retries)"
sleep 2
count=$((count + 1))
done
return 0
}
detect_openbao_root_token() {
local token=""
local file
for file in "$SCRIPT_DIR/../k8s/prole/openbao-statefulset.yaml" \
"$OPENBAO_MANIFEST_DIR/deployment.yaml" \
"$OPENBAO_MANIFEST_DIR/statefulset.yaml"; do
if [[ -f "$file" ]]; then
token=$(grep -Eo 'dev-root-token-id=[^" ]+' "$file" 2>/dev/null | head -n1 | cut -d= -f2 || true)
if [[ -z "$token" ]]; then
token=$(grep -n "VAULT_DEV_ROOT_TOKEN_ID" -A2 "$file" 2>/dev/null \
| grep -Eo 'value:[[:space:]]*[^[:space:]]+' | head -n1 | awk '{print $2}' || true)
fi
if [[ -n "$token" ]]; then
echo "$token"
return 0
fi
fi
done
return 1
}
generate_kerberos_configmap() {
mkdir -p "$OPENBAO_MANIFEST_DIR"
local domain_lower
domain_lower=$(echo "$DOMAIN" | tr 'A-Z' 'a-z')
# Split KRB5_KDC on comma into bash array
local IFS=','
read -r -a __kdcs <<< "$KRB5_KDC"
local kdc_lines=""
local kdc
for kdc in "${__kdcs[@]}"; do
kdc_lines+=$' kdc = '"$kdc"$'\n'
done
local admin_server admin_line
admin_server=${KRB5_ADMIN:-"${__kdcs[0]:-}"}
admin_line=""
if [[ -n "$admin_server" ]]; then
# 8 spaces to remain within the YAML literal block indentation
admin_line=$' admin_server = '"$admin_server"$'\n'
fi
cat >"$OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml" <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
name: prole-krb5-conf
namespace: $OPENBAO_RESOURCE_NAMESPACE
data:
krb5.conf: |
[libdefaults]
default_realm = $KRB5_REALM
dns_lookup_realm = true
dns_lookup_kdc = true
[realms]
$KRB5_REALM = {
${kdc_lines}${admin_line} }
[domain_realm]
.$domain_lower = $KRB5_REALM
$domain_lower = $KRB5_REALM
EOF
}
should_apply_kerberos_configmap() {
local flag="${OPENBAO_APPLY_KRB5_CONFIGMAP:-}"
if [[ "$flag" == "1" || "$flag" == "true" || "$flag" == "True" ]]; then
if [[ -n "${KRB5_REALM:-}" && -n "${KRB5_KDC:-}" ]]; then
return 0
fi
echo "WARN: OPENBAO_APPLY_KRB5_CONFIGMAP set but KRB5_REALM/KRB5_KDC missing; skipping Kerberos ConfigMap."
fi
return 1
}
apply_k8s() {
echo "Applying OpenBao manifest to namespace '$OPENBAO_RESOURCE_NAMESPACE' ..."
if [[ -f "$SCRIPT_DIR/../k8s/prole/openbao-statefulset.yaml" ]]; then
prole_render_manifest "$SCRIPT_DIR/../k8s/prole/openbao-statefulset.yaml" | kubectl apply -n "$OPENBAO_RESOURCE_NAMESPACE" -f -
prole_render_manifest "$SCRIPT_DIR/../k8s/prole/openbao-service.yaml" | kubectl apply -n "$OPENBAO_RESOURCE_NAMESPACE" -f -
else
prole_render_manifest "$OPENBAO_MANIFEST_DIR/deployment.yaml" | kubectl apply -n "$OPENBAO_RESOURCE_NAMESPACE" -f -
fi
if should_apply_kerberos_configmap && [[ -f "$OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml" ]]; then
echo "Applying Kerberos ConfigMap (external realm) to namespace '$OPENBAO_RESOURCE_NAMESPACE' ..."
kubectl apply -n "$OPENBAO_RESOURCE_NAMESPACE" -f "$OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml"
fi
}
apply_openbao_host_network_patch() {
if [[ "${OPENBAO_HOST_NETWORK:-0}" != "1" ]]; then
return
fi
echo "Enabling hostNetwork for OpenBao (OPENBAO_HOST_NETWORK=1) ..."
if kubectl get statefulset "$OPENBAO_NAME" -n "$OPENBAO_RESOURCE_NAMESPACE" >/dev/null 2>&1; then
kubectl patch statefulset "$OPENBAO_NAME" -n "$OPENBAO_RESOURCE_NAMESPACE" --type merge \
-p '{"spec":{"template":{"spec":{"hostNetwork":true,"dnsPolicy":"ClusterFirstWithHostNet"}}}}' >/dev/null
elif kubectl get deployment "$OPENBAO_NAME" -n "$OPENBAO_RESOURCE_NAMESPACE" >/dev/null 2>&1; then
kubectl patch deployment "$OPENBAO_NAME" -n "$OPENBAO_RESOURCE_NAMESPACE" --type merge \
-p '{"spec":{"template":{"spec":{"hostNetwork":true,"dnsPolicy":"ClusterFirstWithHostNet"}}}}' >/dev/null
fi
}
wait_for_openbao() {
echo "Waiting for OpenBao to become ready ..."
if kubectl get statefulset/$OPENBAO_NAME -n "$OPENBAO_RESOURCE_NAMESPACE" >/dev/null 2>&1; then
kubectl rollout status statefulset/$OPENBAO_NAME -n "$OPENBAO_RESOURCE_NAMESPACE" --timeout=120s
else
kubectl rollout status deploy/$OPENBAO_NAME -n "$OPENBAO_RESOURCE_NAMESPACE" --timeout=120s
fi
}
init_openbao_kv_and_store_admin_key() {
local token
token=$(cat "$root_token_file")
# Enable kv (if not already) and write keys
local svc
svc="$(bao_service_url)"
echo "Configuring KV at $svc ..."
# Check if kv/ is already mounted
if curl -sS -H "X-Vault-Token: $token" "$svc/v1/sys/mounts" | grep -q '"kv/":'; then
echo "KV path 'kv/' is already enabled."
else
curl -sS -H "X-Vault-Token: $token" -X POST "$svc/v1/sys/mounts/kv" \
-d '{"type":"kv","options":{"version":"2"}}' >/dev/null 2>&1 || true
fi
local priv pub
priv=$(base64 <"$admin_key_priv" | tr -d '\n')
pub=$(base64 <"$admin_key_pub" | tr -d '\n')
# Check if admin key already exists and matches
local existing_data
existing_data=$(curl -sS -H "X-Vault-Token: $token" "$svc/v1/kv/data/$BAO_PATH_ADMIN" 2>/dev/null || true)
if [[ -n "$existing_data" ]]; then
local ex_priv ex_pub
ex_priv=$(echo "$existing_data" | grep -o '"admin_private_key_b64":"[^"]*' | cut -d'"' -f4 || true)
ex_pub=$(echo "$existing_data" | grep -o '"admin_public_key_b64":"[^"]*' | cut -d'"' -f4 || true)
if [[ "$ex_priv" == "$priv" && "$ex_pub" == "$pub" ]]; then
echo "Admin key pair in OpenBao kv/$BAO_PATH_ADMIN is already up to date."
else
echo "Writing admin key pair to kv/$BAO_PATH_ADMIN ..."
# Preserve other fields (like root_password or ansible_public_key_b64)
local current_json
current_json=$(echo "$existing_data" | jq -c ".data.data")
local new_json
new_json=$(echo "$current_json" | jq -c ". + {\"admin_private_key_b64\":\"$priv\",\"admin_public_key_b64\":\"$pub\"}")
curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \
-X POST "$svc/v1/kv/data/$BAO_PATH_ADMIN" \
-d "{\"data\":$new_json}" >/dev/null
echo "Stored admin key pair in OpenBao kv/$BAO_PATH_ADMIN."
fi
else
echo "Writing admin key pair to kv/$BAO_PATH_ADMIN ..."
curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \
-X POST "$svc/v1/kv/data/$BAO_PATH_ADMIN" \
-d "{\"data\":{\"admin_private_key_b64\":\"$priv\",\"admin_public_key_b64\":\"$pub\"}}" >/dev/null
echo "Stored admin key pair in OpenBao kv/$BAO_PATH_ADMIN."
fi
# Store Ansible public key if it exists
local ansible_pub_path="$HOME/.ssh/id_ed25519_ansible.pub"
if [[ -f "$ansible_pub_path" ]]; then
echo "Storing Ansible public key in OpenBao kv/$BAO_PATH_ADMIN ..."
local a_pub
a_pub=$(base64 <"$ansible_pub_path" | tr -d '\n')
local current_admin_data
current_admin_data=$(curl -sS -H "X-Vault-Token: $token" "$svc/v1/kv/data/$BAO_PATH_ADMIN" | jq -c ".data.data" 2>/dev/null || echo "{}")
local updated_admin_data
updated_admin_data=$(echo "$current_admin_data" | jq -c ". + {\"ansible_public_key_b64\":\"$a_pub\"}")
curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \
-X POST "$svc/v1/kv/data/$BAO_PATH_ADMIN" \
-d "{\"data\":$updated_admin_data}" >/dev/null
echo "Ansible public key stored."
fi
# Store User SSH keys if they exist
local user_key_priv="$HOME/.ssh/id_prole_ed25519"
local user_key_pub="$HOME/.ssh/id_prole_ed25519.pub"
if [[ -f "$user_key_priv" && -f "$user_key_pub" ]]; then
local u_priv u_pub
u_priv=$(base64 <"$user_key_priv" | tr -d '\n')
u_pub=$(base64 <"$user_key_pub" | tr -d '\n')
local username=${PROLE_DB_USER:-"prole"}
echo "Writing user SSH keys for '$username' to kv/$BAO_PATH_USER ..."
curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \
-X POST "$svc/v1/kv/data/$BAO_PATH_USER" \
-d "{\"data\":{\"username\":\"$username\",\"private_key_b64\":\"$u_priv\",\"public_key_b64\":\"$u_pub\"}}" >/dev/null
echo "Stored user SSH keys in OpenBao kv/$BAO_PATH_USER."
fi
if [[ -n "$db_pass" ]]; then
local username=${PROLE_DB_USER:-"prole"}
echo "Writing database user password for '$username' to kv/$BAO_PATH_DB ..."
curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \
-X POST "$svc/v1/kv/data/$BAO_PATH_DB" \
-d "{\"data\":{\"username\":\"$username\",\"password\":\"$db_pass\"}}" >/dev/null
echo "Stored database password in OpenBao kv/$BAO_PATH_DB."
fi
# Store TDE keypair when at-rest encryption is enabled
local tde_enabled="${AT_REST_ENCRYPTION_ENABLED:-}"
case "$tde_enabled" in
1|true|TRUE|True|yes|YES|on|ON)
local tde_priv="$SECRETS_DIR/tde_ed25519.key"
local tde_pub="$SECRETS_DIR/tde_ed25519.pub"
if [[ ! -f "$tde_priv" || ! -f "$tde_pub" ]]; then
echo "Generating TDE keypair in $SECRETS_DIR ..."
if ! openssl genpkey -algorithm ED25519 -out "$tde_priv" 2>/dev/null; then
echo "ED25519 not supported by openssl, falling back to RSA 4096..."
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:4096 -out "$tde_priv"
fi
openssl pkey -in "$tde_priv" -pubout -out "$tde_pub"
chmod 0600 "$tde_priv"
fi
local tde_priv_b64 tde_pub_b64
tde_priv_b64=$(base64 <"$tde_priv" | tr -d '\n')
tde_pub_b64=$(base64 <"$tde_pub" | tr -d '\n')
local tde_existing
tde_existing=$(curl -sS -H "X-Vault-Token: $token" "$svc/v1/kv/data/$BAO_PATH_TDE" 2>/dev/null || true)
if [[ -n "$tde_existing" ]]; then
local ex_priv ex_pub
ex_priv=$(echo "$tde_existing" | grep -o '"tde_private_key_b64":"[^"]*' | cut -d'"' -f4 || true)
ex_pub=$(echo "$tde_existing" | grep -o '"tde_public_key_b64":"[^"]*' | cut -d'"' -f4 || true)
if [[ "$ex_priv" == "$tde_priv_b64" && "$ex_pub" == "$tde_pub_b64" ]]; then
echo "TDE keypair in OpenBao kv/$BAO_PATH_TDE is already up to date."
else
echo "Writing TDE keypair to kv/$BAO_PATH_TDE ..."
local tde_json
tde_json=$(echo "$tde_existing" | jq -c ".data.data")
local new_tde_json
new_tde_json=$(echo "$tde_json" | jq -c ". + {\"tde_private_key_b64\":\"$tde_priv_b64\",\"tde_public_key_b64\":\"$tde_pub_b64\"}")
curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \
-X POST "$svc/v1/kv/data/$BAO_PATH_TDE" \
-d "{\"data\":$new_tde_json}" >/dev/null
echo "Stored TDE keypair in OpenBao kv/$BAO_PATH_TDE."
fi
else
echo "Writing TDE keypair to kv/$BAO_PATH_TDE ..."
curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \
-X POST "$svc/v1/kv/data/$BAO_PATH_TDE" \
-d "{\"data\":{\"tde_private_key_b64\":\"$tde_priv_b64\",\"tde_public_key_b64\":\"$tde_pub_b64\"}}" >/dev/null
echo "Stored TDE keypair in OpenBao kv/$BAO_PATH_TDE."
fi
;;
esac
local krb_user
krb_user="${KRB5_USER:-${KRB5_USERNAME:-}}"
if [[ -n "${KRB5_PASSWORD:-}" && -n "${KRB5_REALM:-}" && -n "$krb_user" ]]; then
echo "Writing Kerberos credentials for '$krb_user@${KRB5_REALM}' to kv/$BAO_PATH_KERBEROS ..."
curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \
-X POST "$svc/v1/kv/data/$BAO_PATH_KERBEROS" \
-d "{\"data\":{\"username\":\"$krb_user\",\"realm\":\"$KRB5_REALM\",\"kdc\":\"${KRB5_KDC:-}\",\"password\":\"$KRB5_PASSWORD\"}}" >/dev/null
echo "Stored Kerberos credentials in OpenBao kv/$BAO_PATH_KERBEROS."
fi
if [[ -n "${GRAFANA_ADMIN_PASSWORD:-}" ]]; then
echo "Writing Grafana admin password to kv/$BAO_PATH_MONITORING ..."
curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \
-X POST "$svc/v1/kv/data/$BAO_PATH_MONITORING" \
-d "{\"data\":{\"grafana_admin_password\":\"$GRAFANA_ADMIN_PASSWORD\"}}" >/dev/null
echo "Stored Grafana admin password in OpenBao kv/$BAO_PATH_MONITORING."
fi
}
# Removed: prompt_admin_password_and_apply_secret (no AD admin secret required)
docker_start() {
echo "Starting local Docker container for OpenBao ..."
ensure_docker
# OpenBao dev
local token
token=$(cat "$root_token_file" 2>/dev/null || true)
if [[ -z "$token" ]]; then
token=$(openssl rand -hex 24)
printf "%s" "$token" >"$root_token_file"
chmod 0600 "$root_token_file"
fi
local running existing host_ip
running=$(docker ps --filter "name=^/${OPENBAO_NAME}$" --format '{{.Names}}' | tr -d '\n')
if [[ "$running" == "$OPENBAO_NAME" ]]; then
echo "OpenBao container already running: $OPENBAO_NAME"
return 0
fi
existing=$(docker ps -a --filter "name=^/${OPENBAO_NAME}$" --format '{{.Names}}' | tr -d '\n')
if [[ "$existing" == "$OPENBAO_NAME" ]]; then
host_ip=$(docker inspect -f '{{(index (index .HostConfig.PortBindings "8200/tcp") 0).HostIp}}' "$OPENBAO_NAME" 2>/dev/null || echo "")
if [[ "$host_ip" == "127.0.0.1" || -z "$host_ip" ]]; then
echo "Starting existing OpenBao container: $OPENBAO_NAME"
docker start "$OPENBAO_NAME" >/dev/null
return 0
fi
echo "Existing OpenBao container is bound to $host_ip; recreating with 127.0.0.1..."
docker rm -f "$OPENBAO_NAME" >/dev/null 2>&1 || true
fi
docker run -d --name "$OPENBAO_NAME" \
--restart unless-stopped \
-p 127.0.0.1:18200:8200 \
"$OPENBAO_IMAGE" server -dev -dev-listen-address=0.0.0.0:8200 -dev-root-token-id="$token"
echo "Docker container started: $OPENBAO_NAME"
}
docker_stop() {
docker rm -f "$OPENBAO_NAME" >/dev/null 2>&1 || true
echo "Stopped OpenBao container if it was running."
}
docker_restart() {
docker_stop
docker_start
}
# status command implementation wrapped in a function to avoid top-level 'local'
cmd_status() {
echo "--- init_primary_domain status ---"
echo "Namespace: $OPENBAO_RESOURCE_NAMESPACE"
echo "Secret namespace: $BAO_NAMESPACE"
echo "OpenBao name: $OPENBAO_NAME"
echo "Manifest dir: $OPENBAO_MANIFEST_DIR"
# Files/manifests present
local ok=0
if [[ -f "$OPENBAO_MANIFEST_DIR/deployment.yaml" ]]; then
echo "[OK] OpenBao deployment manifest exists"
else
echo "[MISSING] $OPENBAO_MANIFEST_DIR/deployment.yaml"
ok=1
fi
if [[ -f "$OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml" ]]; then
echo "[OK] Kerberos ConfigMap manifest exists"
else
echo "[MISSING] $OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml"
ok=1
fi
# K8s resources (legacy flow; best-effort only)
if command -v kubectl >/dev/null 2>&1; then
if kubectl -n "$OPENBAO_RESOURCE_NAMESPACE" get statefulset "$OPENBAO_NAME" >/dev/null 2>&1; then
local ready desired
ready=$(kubectl -n "$OPENBAO_RESOURCE_NAMESPACE" get statefulset "$OPENBAO_NAME" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo "0")
desired=$(kubectl -n "$OPENBAO_RESOURCE_NAMESPACE" get statefulset "$OPENBAO_NAME" -o jsonpath='{.status.replicas}' 2>/dev/null || echo "0")
ready=${ready:-0}
desired=${desired:-0}
if [[ "$ready" == "$desired" && "$ready" != "0" ]]; then
echo "[OK] OpenBao StatefulSet running ($ready/$desired ready)"
else
echo "[WARN] OpenBao StatefulSet not fully ready ($ready/$desired)"
ok=1
fi
elif kubectl -n "$OPENBAO_RESOURCE_NAMESPACE" get deploy "$OPENBAO_NAME" >/dev/null 2>&1; then
local ready desired
ready=$(kubectl -n "$OPENBAO_RESOURCE_NAMESPACE" get deploy "$OPENBAO_NAME" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo "0")
desired=$(kubectl -n "$OPENBAO_RESOURCE_NAMESPACE" get deploy "$OPENBAO_NAME" -o jsonpath='{.status.replicas}' 2>/dev/null || echo "0")
ready=${ready:-0}
desired=${desired:-0}
if [[ "$ready" == "$desired" && "$ready" != "0" ]]; then
echo "[OK] OpenBao Deployment running ($ready/$desired ready)"
else
echo "[WARN] OpenBao Deployment not fully ready ($ready/$desired)"
ok=1
fi
fi
if kubectl -n "$OPENBAO_RESOURCE_NAMESPACE" get configmap prole-krb5-conf >/dev/null 2>&1; then
echo "[OK] Kerberos ConfigMap 'prole-krb5-conf' present"
fi
fi
# Docker (optional local mode)
if command -v docker >/dev/null 2>&1; then
if docker ps --format '{{.Names}}' | grep -Fxq "$OPENBAO_NAME"; then
echo "[OK] Docker container '$OPENBAO_NAME' is running"
else
# Not necessarily an error; we primarily use k8s
echo "[INFO] Docker container '$OPENBAO_NAME' not running (k8s mode may be in use)"
fi
fi
# Secrets and tokens
if [[ -f "$admin_key_priv" && -f "$admin_key_pub" ]]; then
echo "[OK] Admin keypair present in $SECRETS_DIR"
else
echo "[MISSING] Admin keypair files in $SECRETS_DIR"
ok=1
fi
if [[ -s "$root_token_file" ]]; then
echo "[OK] OpenBao root token file present"
else
echo "[MISSING] OpenBao root token file ($root_token_file)"
ok=1
fi
# OpenBao reachability and token validity (best-effort)
# Prefer explicit override, then localhost port-forward, then cluster DNS
local svc_url
svc_url="$(bao_service_url)"
if curl -sS "$svc_url/v1/sys/health" >/dev/null 2>&1; then
echo "[OK] OpenBao service reachable"
if [[ -s "$root_token_file" ]]; then
local code
code=$(curl -s -o /dev/null -w "%{http_code}" -H "X-Vault-Token: $(cat "$root_token_file")" "$svc_url/v1/sys/mounts" || echo "000")
if [[ "$code" == "200" ]]; then
echo "[OK] OpenBao root token authenticates"
else
echo "[WARN] OpenBao root token did not authenticate (HTTP $code)"
ok=1
fi
fi
else
echo "[WARN] OpenBao service not reachable at $svc_url"
ok=1
fi
# Service certificate existence (CNPG TLS)
local CNPG_CLUSTER_NAME
CNPG_CLUSTER_NAME=${CNPG_CLUSTER_NAME:-prole-db}
if kubectl -n "$NAMESPACE" get secret "${CNPG_CLUSTER_NAME}-tls" >/dev/null 2>&1; then
echo "[OK] Service certificate secret '${CNPG_CLUSTER_NAME}-tls' exists"
else
echo "[INFO] Service certificate secret '${CNPG_CLUSTER_NAME}-tls' not found"
fi
echo "-------------------------------------"
if [[ $ok -eq 0 ]]; then
echo "Status: OK"
return 0
else
echo "Status: Issues detected"
return 1
fi
}
case "$ACTION" in
start)
ensure_tools
ensure_docker
ensure_admin_keypair
docker_start
;;
status)
ensure_tools
cmd_status
;;
stop)
ensure_docker
docker_stop
;;
restart)
ensure_tools
ensure_docker
ensure_admin_keypair
docker_restart
;;
initialize)
ensure_tools
ensure_docker
# Read password from stdin if provided
db_pass=""
if [[ ! -t 0 ]]; then
read -r db_pass || true
fi
ensure_admin_keypair
docker_start
wait_for_openbao_http 20
init_openbao_kv_and_store_admin_key
echo "Initialization complete. OpenBao available at $(bao_service_url)"
;;
update|reload)
ensure_tools
ensure_k8s_tools
ensure_namespace
generate_openbao_manifests
if should_apply_kerberos_configmap; then
generate_kerberos_configmap
fi
apply_k8s
apply_openbao_host_network_patch
echo "Re-applied manifests."
;;
*)
echo "Usage: $0 [-n|--namespace NAME] {start|stop|status|restart|initialize|update|reload}" >&2
exit 2
;;
esac