Files
proxmox-scripts/ct/nexus-db.sh
T
l.kirchner 0ee7ceae55 feat(nexus-db): PostgreSQL 16 + pgvector LXC template (nexus-hub K-102)
webapp-pattern ct/install pair: PGDG repo, database nexus with
least-privilege owner role, pg_hba allowlist restricted to the nexus
app LXC (explicit reject for everything else), pgvector created by the
installer, credentials/DSN summary in /root/nexus-db.credentials.
Idempotent re-runs keep role/db and do not rotate the password.
2026-06-11 15:51:04 +02:00

94 lines
3.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# nexus-db — PostgreSQL 16 (+pgvector) for nexus (Family Knowledge Hub)
#
# Creates an unprivileged Debian 12 LXC that:
# - runs PostgreSQL 16 from the PGDG repo with the pgvector extension
# - hosts database `nexus` owned by a least-privilege role `nexus`
# - accepts connections ONLY from the nexus app LXC (pg_hba allowlist);
# every other host is rejected
#
# Companion card: nexus-hub K-102. Run on a Proxmox VE host:
# bash -c "$(curl -fsSL https://gitea.luki-net.org/luki-net/proxmox-scripts/raw/branch/main/ct/nexus-db.sh)"
set -euo pipefail
APP="nexus-db"
APP_DESCRIPTION="PostgreSQL 16 + pgvector for nexus (access restricted to the nexus LXC)"
LIB_URL="${LIB_URL:-https://gitea.luki-net.org/luki-net/proxmox-scripts/raw/branch/main/lib}"
INSTALL_SCRIPT_URL="${INSTALL_SCRIPT_URL:-https://gitea.luki-net.org/luki-net/proxmox-scripts/raw/branch/main/install/nexus-db-install.sh}"
# LXC defaults (DB only: small CPU, RAM matters for shared_buffers/cache)
DEFAULT_HOSTNAME="nexus-db"
DEFAULT_DISK="16"
DEFAULT_CORES="2"
DEFAULT_RAM="4096"
DEFAULT_DB_NAME="nexus"
DEFAULT_DB_USER="nexus"
DEFAULT_DB_PORT="5432"
# ── app-specific prompts (host TTY; each skipped if the var is preset) ───────
prompt_app_config() {
echo
echo "── nexus-db configuration ───────────────────────────────────"
# The ONLY host that may connect (pg_hba allowlist) — the nexus app LXC.
if [[ -z "${NEXUS_APP_IP:-}" ]]; then
read -rp "IP of the nexus app LXC (sole allowed client): " NEXUS_APP_IP
fi
[[ -n "${NEXUS_APP_IP:-}" ]] || { msg_err "NEXUS_APP_IP is required (pg_hba allowlist)"; exit 1; }
DB_NAME="${DB_NAME:-$DEFAULT_DB_NAME}"
DB_USER="${DB_USER:-$DEFAULT_DB_USER}"
DB_PORT="${DB_PORT:-$DEFAULT_DB_PORT}"
echo " → database: $DB_NAME role: $DB_USER port: $DB_PORT"
echo " → allowed client: $NEXUS_APP_IP/32 (everything else is rejected)"
}
# ── push gathered config into the container for the installer to consume ─────
push_app_config() {
msg_info "Pushing db config into container..."
local tmpf; tmpf=$(mktemp)
cat >"$tmpf" <<EOF
NEXUS_APP_IP='$NEXUS_APP_IP'
DB_NAME='$DB_NAME'
DB_USER='$DB_USER'
DB_PORT='$DB_PORT'
EOF
pct push "$CTID" "$tmpf" /root/nexus-db.deploy.env --perms 600
rm -f "$tmpf"
}
# ── trailing summary ─────────────────────────────────────────────────────────
print_app_summary() {
local pg_state
pg_state=$(pct exec "$CTID" -- systemctl is-active postgresql 2>/dev/null | tr -d '\r\n')
cat <<EOF
PostgreSQL 16: $IP_CT:$DB_PORT — $pg_state
Database: $DB_NAME (owner: $DB_USER, extension: vector)
Allowed client: $NEXUS_APP_IP/32 — all other hosts are rejected
Credentials + DSN: /root/nexus-db.credentials (inside the LXC)
Smoke test FROM THE NEXUS LXC (uses the DSN from the credentials file):
psql "postgresql://$DB_USER:<password>@$IP_CT:$DB_PORT/$DB_NAME" -c "SELECT extname FROM pg_extension;"
Negative test from any OTHER host (must fail):
psql "postgresql://$DB_USER:<password>@$IP_CT:$DB_PORT/$DB_NAME" -c "SELECT 1;"
Logs: pct exec $CTID -- journalctl -u postgresql -f
EOF
}
# ── orchestrate ───────────────────────────────────────────────────────────────
trap _on_error ERR
preflight_pve
show_header "$APP" "$APP_DESCRIPTION"
prompt_lxc_config
prompt_app_config
resolve_debian_template
create_lxc
push_app_config
bootstrap_install_script "$INSTALL_SCRIPT_URL"
print_summary