166 lines
7.2 KiB
Bash
Executable File
166 lines
7.2 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# EasyMCP / SapB1Mcp — Setup auf dem Docker-Host (Pull-Deployment).
|
|
#
|
|
# Die einzige Datei, die ein Kunde braucht. Bezug:
|
|
# curl -fsSLO https://git.kraemerweb.de/kraemerweb/easymcp-release/raw/branch/main/setup.sh
|
|
#
|
|
# Aufruf:
|
|
# bash setup.sh # neueste Version (latest)
|
|
# bash setup.sh <version> # bestimmte Version (Tag im Release-Repo)
|
|
#
|
|
# Was das Skript tut:
|
|
# 1. Holt die Deployment-Dateien (docker-compose.yml, hydra.yml, .env.example)
|
|
# anonym aus dem öffentlichen Release-Repo — passend zur gewählten Version.
|
|
# 2. Erzeugt secrets/creds_key (32 Byte base64, Verschlüsselung der SAP-Credentials).
|
|
# 3. Legt .env aus .env.example an und generiert PG_PASSWORD + SECRETS_SYSTEM;
|
|
# setzt APP_VERSION (= gewählte Version bzw. latest) und PROJECT_DIR.
|
|
# 4. Pullt das Updater-Image (für die Update-Funktion des Admin-UI).
|
|
#
|
|
# Idempotent: eine vorhandene .env und ein vorhandener creds_key werden NIE
|
|
# überschrieben — nur APP_VERSION und PROJECT_DIR werden nachgezogen. Die
|
|
# Deployment-Dateien werden immer auf den Stand der gewählten Version gebracht.
|
|
set -euo pipefail
|
|
cd "$(dirname "$0")"
|
|
|
|
RELEASE_BASE="${RELEASE_BASE:-https://git.kraemerweb.de/kraemerweb/easymcp-release}"
|
|
REGISTRY_IMAGE_BASE="${REGISTRY_IMAGE_BASE:-git.kraemerweb.de/kraemerweb}"
|
|
|
|
VERSION="${1:-}"
|
|
if [ -n "$VERSION" ]; then
|
|
REF="tag/${VERSION}"
|
|
APP_VERSION="$VERSION"
|
|
else
|
|
REF="branch/main"
|
|
APP_VERSION="latest"
|
|
fi
|
|
|
|
info() { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
|
|
ok() { printf '\033[1;32m ✓\033[0m %s\n' "$*"; }
|
|
warn() { printf '\033[1;33m !\033[0m %s\n' "$*"; }
|
|
fail() { printf '\033[1;31mFEHLER:\033[0m %s\n' "$*" >&2; exit 1; }
|
|
|
|
# --- Zufallswerte (openssl bevorzugt, /dev/urandom als Fallback) -------------
|
|
rand_hex() { # $1 = Byte-Anzahl; Ausgabe hex (URL-sicher, auch für DSN-URLs)
|
|
if command -v openssl >/dev/null 2>&1; then
|
|
openssl rand -hex "$1" | tr -d ' \r\n'
|
|
else
|
|
od -An -N"$1" -tx1 /dev/urandom | tr -d ' \r\n'
|
|
fi
|
|
}
|
|
rand_b64() { # $1 = Byte-Anzahl; Ausgabe base64 (eine Zeile)
|
|
if command -v openssl >/dev/null 2>&1; then
|
|
openssl rand -base64 "$1" | tr -d '\r\n'
|
|
else
|
|
head -c "$1" /dev/urandom | base64 | tr -d '\r\n'
|
|
fi
|
|
}
|
|
set_env() { # $1 = Name, $2 = Wert — setzt bzw. ergänzt NAME=WERT in .env
|
|
if grep -q "^$1=" .env; then
|
|
sed -i "s|^$1=.*|$1=$2|" .env
|
|
else
|
|
printf '%s=%s\n' "$1" "$2" >> .env
|
|
fi
|
|
}
|
|
fetch() { # $1 = Datei im Release-Repo, $2 = Zielname — atomar ersetzen
|
|
curl -fsSL "$RELEASE_BASE/raw/$REF/$1" -o "$2.new" \
|
|
|| fail "Download fehlgeschlagen: $RELEASE_BASE/raw/$REF/$1 (Version/Netzwerk prüfen)"
|
|
mv "$2.new" "$2"
|
|
}
|
|
|
|
# --- Vorbedingungen -----------------------------------------------------------
|
|
command -v curl >/dev/null 2>&1 || fail "curl nicht gefunden — bitte installieren."
|
|
command -v docker >/dev/null 2>&1 || fail "docker nicht gefunden — bitte Docker Engine installieren."
|
|
docker info >/dev/null 2>&1 || fail "Docker-Daemon nicht erreichbar (läuft der Dienst? Rechte? Ggf. sudo verwenden)."
|
|
|
|
if docker compose version >/dev/null 2>&1; then
|
|
COMPOSE="docker compose"
|
|
elif command -v docker-compose >/dev/null 2>&1; then
|
|
COMPOSE="docker-compose"
|
|
else
|
|
fail "Weder 'docker compose' (Plugin v2) noch 'docker-compose' gefunden."
|
|
fi
|
|
|
|
# --- 1. Deployment-Dateien holen -----------------------------------------------
|
|
info "Hole Deployment-Dateien (${VERSION:-latest}) aus $RELEASE_BASE ..."
|
|
fetch docker-compose.yml docker-compose.yml
|
|
fetch hydra.yml hydra.yml
|
|
fetch .env.example .env.example
|
|
ok "docker-compose.yml, hydra.yml, .env.example aktualisiert."
|
|
|
|
# --- 2. creds_key --------------------------------------------------------------
|
|
info "Prüfe secrets/creds_key ..."
|
|
mkdir -p secrets
|
|
# Schutzmodell: das VERZEICHNIS sperrt fremde Host-Nutzer aus (700). Die Datei
|
|
# selbst muss 644 sein, weil die Container sie als non-root-User "app" (uid 1654)
|
|
# über den Compose-Secret-Mount lesen — mit 600 crashen login/mcp beim Start.
|
|
chmod 700 secrets
|
|
if [ -f secrets/creds_key ]; then
|
|
chmod 644 secrets/creds_key
|
|
ok "secrets/creds_key existiert bereits — bleibt unverändert (Rechte geprüft)."
|
|
else
|
|
rand_b64 32 > secrets/creds_key
|
|
chmod 644 secrets/creds_key
|
|
ok "secrets/creds_key erzeugt (Verzeichnis 700, Datei 644 für Container-User)."
|
|
echo " WICHTIG: Diese Datei SICHERN. Ohne sie sind die gespeicherten SAP-Zugangsdaten unlesbar."
|
|
fi
|
|
|
|
# --- 3. .env -------------------------------------------------------------------
|
|
info "Prüfe .env ..."
|
|
if [ -f .env ]; then
|
|
ok ".env existiert bereits — Secrets/Werte bleiben unverändert."
|
|
current="$(sed -n 's/^APP_VERSION=//p' .env | head -n1)"
|
|
set_env APP_VERSION "$APP_VERSION"
|
|
if [ -n "$current" ] && [ "$current" != "$APP_VERSION" ]; then
|
|
ok "Update: APP_VERSION ${current} -> ${APP_VERSION} (wirkt nach '${COMPOSE} pull' + '${COMPOSE} up -d')."
|
|
fi
|
|
else
|
|
cp .env.example .env
|
|
chmod 600 .env
|
|
PG_PASSWORD="$(rand_hex 24)" # nur [0-9a-f]: sicher für Hydras DSN-URL
|
|
SECRETS_SYSTEM="$(rand_hex 32)" # Hydra-Systemschlüssel — nach Inbetriebnahme NIE mehr ändern
|
|
set_env PG_PASSWORD "$PG_PASSWORD"
|
|
set_env SECRETS_SYSTEM "$SECRETS_SYSTEM"
|
|
set_env APP_VERSION "$APP_VERSION"
|
|
ok ".env angelegt, PG_PASSWORD + SECRETS_SYSTEM generiert (chmod 600)."
|
|
fi
|
|
# Host-Pfad des Deployments — braucht die Update-Funktion des Admin-UI (der
|
|
# Einweg-Updater mountet das Projektverzeichnis pfad-identisch). Bei jedem Lauf
|
|
# aktualisieren, falls das Verzeichnis umgezogen ist.
|
|
set_env PROJECT_DIR "$(pwd)"
|
|
|
|
# --- 4. Updater-Image für die Admin-UI-Update-Funktion --------------------------
|
|
info "Pulle Updater-Image ..."
|
|
if docker pull "$REGISTRY_IMAGE_BASE/sapb1mcp-updater:latest" >/dev/null; then
|
|
ok "Updater-Image bereit."
|
|
else
|
|
warn "Updater-Image konnte nicht gepullt werden — Registry-Zugriff prüfen; das Admin-UI-Update funktioniert sonst nicht."
|
|
fi
|
|
|
|
# --- 5. Nächste Schritte ---------------------------------------------------------
|
|
cat <<EOF
|
|
|
|
============================================================================
|
|
Setup abgeschlossen. Bitte jetzt in der .env die Umgebungswerte anpassen:
|
|
|
|
PUBLIC_ISSUER öffentliche HTTPS-URL des OAuth-Issuers (Reverse Proxy -> Port 4444)
|
|
LOGIN_URL öffentliche HTTPS-URL des Login-Dienstes (Reverse Proxy -> Port 8081)
|
|
MCP_RESOURCE_URL öffentliche HTTPS-URL des MCP-Servers (Reverse Proxy -> Port 8080)
|
|
SL_BASE_URL SAP Service Layer, z. B. https://sap-host:50000/b1s/ (trailing /b1s/ ist Pflicht)
|
|
SL_COMPANYDB SAP-Firmendatenbank
|
|
ADMIN_SAP_USER SAP-Benutzer, der das Admin-UI öffnen darf
|
|
MCP_ALLOWED_USERS kommagetrennte SAP-Benutzer mit MCP-Dauerzugriff
|
|
|
|
Optional: SL_INSECURE (self-signed SAP-Zertifikat), BIND_ADDR/ADMIN_BIND und
|
|
*_HOST_PORT, falls der Reverse Proxy auf einem anderen Host läuft.
|
|
|
|
Danach den Stack starten:
|
|
|
|
${COMPOSE} pull
|
|
${COMPOSE} up -d
|
|
|
|
Status prüfen: ${COMPOSE} ps (alle Dauerläufer müssen "healthy" werden)
|
|
Updates später: im Admin-UI unter „Update" — oder erneut 'bash setup.sh [version]'
|
|
gefolgt von '${COMPOSE} pull' + '${COMPOSE} up -d'. Details: INSTALL.md
|
|
============================================================================
|
|
EOF
|