mirror of
https://github.com/dredx/prole.git
synced 2026-09-23 10:13:58 +00:00
- Refactored installer UI with updated canvas rendering, sidebar navigation, and footer buttons. - Enhanced styling for macOS compatibility and consistent design across controls. - Added Pytest-based unit tests for `screen.py` and `config.py`. - Expanded dependency catalog with new tools like `tshark` and `pyshark`. - Improved error tolerance for background rendering and added placeholders for Kerberos configuration.
473 lines
15 KiB
Bash
Executable File
473 lines
15 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
# init_openbao.sh
|
|
# Purpose:
|
|
# - Deploy OpenBao to Kubernetes (dev mode) and store admin ed25519 key pair
|
|
# Generate and apply a Kerberos krb5.conf ConfigMap for an external realm
|
|
# - Local Docker helpers for OpenBao (optional)
|
|
|
|
# Initialize SCRIPT_DIR
|
|
SCRIPT_DIR=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
|
|
|
|
# Preserve positional args while sourcing env
|
|
__PROLE_SAVED_ARGS=("$@")
|
|
if [[ -n "${PROLE_HOME:-}" && -f "$PROLE_HOME/env.sh" ]]; then
|
|
set --
|
|
# shellcheck disable=SC1090
|
|
source "$PROLE_HOME/env.sh"
|
|
elif [[ -f "$HOME/.prole/env.sh" ]]; then
|
|
set --
|
|
# shellcheck disable=SC1090
|
|
source "$HOME/.prole/env.sh"
|
|
fi
|
|
set -- "${__PROLE_SAVED_ARGS[@]}"
|
|
unset __PROLE_SAVED_ARGS
|
|
|
|
if [[ -z "${PROLE_SERVICE:-}" ]]; then
|
|
echo "ERROR: PROLE_SERVICE is not defined in env. Provide PROLE_HOME/env.sh or ~/.prole/env.sh" >&2
|
|
exit 1
|
|
fi
|
|
|
|
ACTION=${1:-}
|
|
|
|
# Defaults
|
|
NAMESPACE=${NAMESPACE:-prole}
|
|
OPENBAO_NAME=${OPENBAO_NAME:-openbao}
|
|
OPENBAO_MANIFEST_DIR="$SCRIPT_DIR/../k8s/openbao"
|
|
|
|
# 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"
|
|
|
|
ensure_tools() {
|
|
for t in kubectl curl openssl base64; do
|
|
command -v "$t" >/dev/null || { echo "Missing required tool: $t" >&2; exit 1; }
|
|
done
|
|
}
|
|
|
|
ensure_docker() {
|
|
command -v docker >/dev/null || { echo "Missing required tool: docker" >&2; exit 1; }
|
|
}
|
|
|
|
ensure_admin_keypair() {
|
|
# Ensure admin ed25519 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 ed25519 keypair in $SECRETS_DIR ..."
|
|
openssl genpkey -algorithm ED25519 -out "$admin_key_priv"
|
|
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
|
|
openssl rand -hex 24 >"$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: $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: $NAMESPACE
|
|
spec:
|
|
selector:
|
|
app: $OPENBAO_NAME
|
|
ports:
|
|
- name: http
|
|
port: 8200
|
|
targetPort: 8200
|
|
EOF
|
|
fi
|
|
}
|
|
|
|
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: $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
|
|
}
|
|
|
|
apply_k8s() {
|
|
echo "Applying OpenBao manifest to namespace '$NAMESPACE' ..."
|
|
if [[ -f "$SCRIPT_DIR/../k8s/prole/openbao-statefulset.yaml" ]]; then
|
|
kubectl apply -n "$NAMESPACE" -f "$SCRIPT_DIR/../k8s/prole/openbao-statefulset.yaml"
|
|
kubectl apply -n "$NAMESPACE" -f "$SCRIPT_DIR/../k8s/prole/openbao-service.yaml"
|
|
else
|
|
kubectl apply -n "$NAMESPACE" -f "$OPENBAO_MANIFEST_DIR/deployment.yaml"
|
|
fi
|
|
echo "Applying Kerberos ConfigMap (external realm) to namespace '$NAMESPACE' ..."
|
|
kubectl apply -n "$NAMESPACE" -f "$OPENBAO_MANIFEST_DIR/kerberos-configmap.yaml"
|
|
}
|
|
|
|
wait_for_openbao() {
|
|
echo "Waiting for OpenBao to become ready ..."
|
|
if kubectl get statefulset/$OPENBAO_NAME -n "$NAMESPACE" >/dev/null 2>&1; then
|
|
kubectl rollout status statefulset/$OPENBAO_NAME -n "$NAMESPACE" --timeout=120s
|
|
else
|
|
kubectl rollout status deploy/$OPENBAO_NAME -n "$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="http://127.0.0.1:18200"
|
|
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/prole/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/prole/admin is already up to date."
|
|
else
|
|
echo "Writing admin key pair to kv/prole/admin ..."
|
|
curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \
|
|
-X POST "$svc/v1/kv/data/prole/admin" \
|
|
-d "{\"data\":{\"admin_private_key_b64\":\"$priv\",\"admin_public_key_b64\":\"$pub\"}}" >/dev/null
|
|
echo "Stored admin key pair in OpenBao kv/prole/admin."
|
|
fi
|
|
else
|
|
echo "Writing admin key pair to kv/prole/admin ..."
|
|
curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \
|
|
-X POST "$svc/v1/kv/data/prole/admin" \
|
|
-d "{\"data\":{\"admin_private_key_b64\":\"$priv\",\"admin_public_key_b64\":\"$pub\"}}" >/dev/null
|
|
echo "Stored admin key pair in OpenBao kv/prole/admin."
|
|
fi
|
|
|
|
if [[ -n "$db_pass" ]]; then
|
|
echo "Writing database user password to kv/prole/db ..."
|
|
curl -sS -H "X-Vault-Token: $token" -H 'Content-Type: application/json' \
|
|
-X POST "$svc/v1/kv/data/prole/db" \
|
|
-d "{\"data\":{\"username\":\"prole\",\"password\":\"$db_pass\"}}" >/dev/null
|
|
echo "Stored database password in OpenBao kv/prole/db."
|
|
fi
|
|
}
|
|
|
|
# Removed: prompt_admin_password_and_apply_secret (no AD admin secret required)
|
|
|
|
docker_start() {
|
|
echo "Starting local Docker container for OpenBao ..."
|
|
ensure_docker
|
|
docker rm -f "$OPENBAO_NAME" >/dev/null 2>&1 || true
|
|
# 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
|
|
docker run -d --name "$OPENBAO_NAME" -p 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: $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
|
|
if kubectl -n "$NAMESPACE" get statefulset "$OPENBAO_NAME" >/dev/null 2>&1; then
|
|
local ready desired
|
|
ready=$(kubectl -n "$NAMESPACE" get statefulset "$OPENBAO_NAME" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo "0")
|
|
desired=$(kubectl -n "$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 "$NAMESPACE" get deploy "$OPENBAO_NAME" >/dev/null 2>&1; then
|
|
# Get readiness
|
|
local ready desired
|
|
ready=$(kubectl -n "$NAMESPACE" get deploy "$OPENBAO_NAME" -o jsonpath='{.status.readyReplicas}' 2>/dev/null || echo "0")
|
|
desired=$(kubectl -n "$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
|
|
else
|
|
echo "[MISSING] OpenBao Deployment or StatefulSet '$OPENBAO_NAME' in namespace '$NAMESPACE'"
|
|
ok=1
|
|
fi
|
|
|
|
if kubectl -n "$NAMESPACE" get configmap prole-krb5-conf >/dev/null 2>&1; then
|
|
echo "[OK] Kerberos ConfigMap 'prole-krb5-conf' present"
|
|
else
|
|
echo "[MISSING] Kerberos ConfigMap 'prole-krb5-conf' in namespace '$NAMESPACE'"
|
|
ok=1
|
|
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 ed25519 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
|
|
if [[ -n "${PROLE_OPENBAO_URL:-}" ]]; then
|
|
svc_url="$PROLE_OPENBAO_URL"
|
|
elif curl -sS "http://127.0.0.1:18200/v1/sys/health" >/dev/null 2>&1; then
|
|
svc_url="http://127.0.0.1:18200"
|
|
else
|
|
svc_url="http://$OPENBAO_NAME.$NAMESPACE.svc.cluster.local:8200"
|
|
fi
|
|
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
|
|
# Read password from stdin if provided
|
|
db_pass=""
|
|
if [[ ! -t 0 ]]; then
|
|
read -r db_pass
|
|
fi
|
|
|
|
ensure_admin_keypair
|
|
generate_openbao_manifests
|
|
generate_kerberos_configmap
|
|
apply_k8s
|
|
|
|
if [[ -n "$db_pass" ]]; then
|
|
echo "Creating database user secret 'prole-db-user' ..."
|
|
kubectl create secret generic prole-db-user -n "$NAMESPACE" \
|
|
--from-literal=username=prole \
|
|
--from-literal=password="$db_pass" \
|
|
--dry-run=client -o yaml | kubectl apply -n "$NAMESPACE" -f -
|
|
|
|
echo "Creating database superuser secret 'prole-db-superuser' ..."
|
|
kubectl create secret generic prole-db-superuser -n "$NAMESPACE" \
|
|
--from-literal=username=postgres \
|
|
--from-literal=password="$db_pass" \
|
|
--dry-run=client -o yaml | kubectl apply -n "$NAMESPACE" -f -
|
|
fi
|
|
|
|
wait_for_openbao
|
|
|
|
# Ensure port-forward is running for OpenBao
|
|
echo "Ensuring port-forward for OpenBao is active ..."
|
|
"$SCRIPT_DIR/init_port_forwards.sh" restart openbao
|
|
|
|
if ! curl -sS http://127.0.0.1:18200/v1/sys/health >/dev/null 2>&1; then
|
|
echo "ERROR: OpenBao not reachable at http://127.0.0.1:18200." >&2
|
|
echo "Please start port-forwards: $SCRIPT_DIR/init_port_forwards.sh start openbao" >&2
|
|
exit 1
|
|
fi
|
|
init_openbao_kv_and_store_admin_key
|
|
echo "Initialization complete. Manifests in: $OPENBAO_MANIFEST_DIR"
|
|
;;
|
|
update|reload)
|
|
generate_openbao_manifests
|
|
generate_kerberos_configmap
|
|
apply_k8s
|
|
echo "Re-applied manifests."
|
|
;;
|
|
*)
|
|
echo "Usage: $0 {start|stop|status|restart|initialize|update|reload}" >&2
|
|
exit 2
|
|
;;
|
|
esac
|