Lizenzserver-Deployment: install-license.sh/update-license.sh + systemd/nginx-Vorlagen
Fork von install.sh/update.sh/tesm.service/nginx-Site (TESM selbst), beschraenkt auf /srv/tesm-license -- deployt ausschliesslich den in Phase 2 gebauten Master-Lizenzserver, laesst eine evtl. auf demselben Host vorhandene TESM-Installation komplett unberuehrt (eigener Pfad-/Env-Var- Namespace, siehe app.py). Gleiches Backup+Health-Check+Rueckroll- Sicherheitsnetz wie bei TESMs eigenem Installer. Teil von Phase 3 (Infrastruktur), siehe Plan toasty-twirling-hickey.md. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,18 @@
|
|||||||
|
server {
|
||||||
|
listen 80;
|
||||||
|
server_name _;
|
||||||
|
|
||||||
|
location / {
|
||||||
|
client_max_body_size 4m;
|
||||||
|
proxy_pass http://127.0.0.1:5000;
|
||||||
|
proxy_set_header Host $host;
|
||||||
|
proxy_set_header X-Real-IP $remote_addr;
|
||||||
|
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||||
|
proxy_set_header X-Forwarded-Proto $scheme;
|
||||||
|
}
|
||||||
|
|
||||||
|
location /static/ {
|
||||||
|
alias /srv/tesm-license/static/;
|
||||||
|
expires 7d;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=TESM Lizenzserver (Master)
|
||||||
|
After=network.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User=root
|
||||||
|
WorkingDirectory=/srv/tesm-license
|
||||||
|
# Analog zu TESM_WEB_PROCESS bei TESM selbst -- markiert diesen Prozess als
|
||||||
|
# den echten Web-Dienst (startet den Lizenz-Heartbeat- und den Auditlog-
|
||||||
|
# Archivierungs-Thread genau einmal). Anders als bei TESM importiert hier
|
||||||
|
# kein Hilfsskript app.py periodisch als Modul (kein Geräte-Polling auf
|
||||||
|
# dem Lizenzserver) -- das Flag bleibt trotzdem gesetzt, für den Fall
|
||||||
|
# künftiger Hilfsskripte und zur Konsistenz mit dem TESM-Muster.
|
||||||
|
Environment=TESM_LICENSE_WEB_PROCESS=1
|
||||||
|
# Kein WebSocket-Terminal wie bei TESM -- gthread/--timeout 0 daher nicht
|
||||||
|
# nötig, ein paar Threads reichen für die parallele Aktivierungs-/
|
||||||
|
# Heartbeat-API mehrerer Kunden.
|
||||||
|
# "--bind 127.0.0.1:5000" wie bei TESM: nur über den nginx-Reverse-Proxy
|
||||||
|
# von außen erreichbar.
|
||||||
|
ExecStart=/srv/tesm-license/venv/bin/gunicorn --workers 1 --threads 8 \
|
||||||
|
--bind 127.0.0.1:5000 app:app
|
||||||
|
Restart=always
|
||||||
|
RestartSec=5
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
@@ -0,0 +1,155 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ============================================================================
|
||||||
|
# TESM-Lizenzserver — Installer/Updater
|
||||||
|
# Fork von install.sh (TESM selbst), auf /srv/tesm-license statt /srv/tesm
|
||||||
|
# beschränkt -- deployt NUR den Master-Lizenzserver-Teil dieses Repos
|
||||||
|
# (srv/tesm-license/), lässt srv/tesm/ komplett unangetastet. Für dasselbe
|
||||||
|
# Sicherheitsnetz (Backup + Health-Check + automatischer Rückroll) wie bei
|
||||||
|
# TESM, siehe dortigen Kommentarkopf für die ausführliche Begründung --
|
||||||
|
# hier nur die Kurzfassung je Schritt.
|
||||||
|
#
|
||||||
|
# Auszuführen als root, nachdem dieses Repo/Release-Paket z.B. nach
|
||||||
|
# /root/tesm entpackt wurde:
|
||||||
|
# sudo ./install-license.sh
|
||||||
|
# ============================================================================
|
||||||
|
set -e
|
||||||
|
|
||||||
|
REPO_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
print_status() { echo -e "${GREEN}✔${NC} ${1} completed."; }
|
||||||
|
step() { echo -e "${RED}→${NC} ${1}..." | tee -a /var/log/tesm-license-install.log; }
|
||||||
|
|
||||||
|
install_if_changed() {
|
||||||
|
local src="$1" dst="$2" mode="${3:-644}"
|
||||||
|
if [ -f "$dst" ] && cmp -s "$src" "$dst"; then
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
|
sudo install -m "$mode" "$src" "$dst"
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
clear 2>/dev/null || true
|
||||||
|
|
||||||
|
# ---- Bestehende Installation erkennen ----
|
||||||
|
EXISTING_INSTALL=0
|
||||||
|
[ -f /srv/tesm-license/sqlite.db ] && EXISTING_INSTALL=1
|
||||||
|
|
||||||
|
NEEDS_MIGRATION_GUARD=0
|
||||||
|
BACKUP_DIR=""
|
||||||
|
|
||||||
|
if [ "$EXISTING_INSTALL" -eq 1 ]; then
|
||||||
|
OLD_SCHEMA_VERSION=""
|
||||||
|
[ -f /srv/tesm-license/SCHEMA_VERSION ] && OLD_SCHEMA_VERSION="$(tr -d '[:space:]' < /srv/tesm-license/SCHEMA_VERSION)"
|
||||||
|
NEW_SCHEMA_VERSION=""
|
||||||
|
[ -f "$REPO_DIR/srv/tesm-license/SCHEMA_VERSION" ] && NEW_SCHEMA_VERSION="$(tr -d '[:space:]' < "$REPO_DIR/srv/tesm-license/SCHEMA_VERSION")"
|
||||||
|
|
||||||
|
step "Stopping tesm-license.service for update"
|
||||||
|
systemctl stop tesm-license.service 2>/dev/null || true
|
||||||
|
print_status "Service stopped"
|
||||||
|
|
||||||
|
if [ -n "$OLD_SCHEMA_VERSION" ] && [ -n "$NEW_SCHEMA_VERSION" ] && [ "$OLD_SCHEMA_VERSION" == "$NEW_SCHEMA_VERSION" ]; then
|
||||||
|
echo -e "${GREEN}Bestehende Installation erkannt, Datenbank-Schema unverändert (Version ${OLD_SCHEMA_VERSION}):${NC} In-Place-Update."
|
||||||
|
else
|
||||||
|
echo -e "${YELLOW}Bestehende Installation erkannt, Schema-Version geändert oder unbekannt:${NC} Update mit automatischer Migration."
|
||||||
|
fi
|
||||||
|
NEEDS_MIGRATION_GUARD=1
|
||||||
|
BACKUP_DIR="/srv/tesm-license-backup-pre-update-$(date +%Y%m%d-%H%M%S)"
|
||||||
|
step "Backing up current installation to $BACKUP_DIR before update"
|
||||||
|
cp -a /srv/tesm-license "$BACKUP_DIR"
|
||||||
|
print_status "Backup created"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---- Pakete ----
|
||||||
|
step "Installing system packages"
|
||||||
|
sudo apt-get update >>/var/log/tesm-license-install.log 2>&1 && print_status "apt update"
|
||||||
|
sudo apt-get install -y python3 python3-venv python3-pip nginx sqlite3 rsync logrotate certbot >>/var/log/tesm-license-install.log 2>&1 && print_status "Packages installed"
|
||||||
|
|
||||||
|
# ---- Log-Verzeichnis ----
|
||||||
|
sudo mkdir -p /var/log/tesm-license
|
||||||
|
sudo chmod 755 /var/log/tesm-license
|
||||||
|
|
||||||
|
# ---- App-Verzeichnis ----
|
||||||
|
step "Deploying application to /srv/tesm-license"
|
||||||
|
sudo mkdir -p /srv/tesm-license
|
||||||
|
sudo rsync -a --delete --exclude 'venv' --exclude 'sqlite.db' --exclude 'fernet.key' --exclude 'secret.key' \
|
||||||
|
--exclude 'license.json' --exclude 'master_signing_key.json' \
|
||||||
|
"$REPO_DIR/srv/tesm-license/" /srv/tesm-license/ >>/var/log/tesm-license-install.log 2>&1
|
||||||
|
print_status "Application files copied"
|
||||||
|
|
||||||
|
# ---- Python venv ----
|
||||||
|
step "Setting up Python virtual environment"
|
||||||
|
cd /srv/tesm-license
|
||||||
|
sudo python3 -m venv venv
|
||||||
|
sudo ./venv/bin/python3 -m pip install --upgrade pip >>/var/log/tesm-license-install.log 2>&1
|
||||||
|
sudo ./venv/bin/python3 -m pip install -r requirements.txt >>/var/log/tesm-license-install.log 2>&1
|
||||||
|
print_status "Virtual environment ready"
|
||||||
|
|
||||||
|
# ---- Datenbank ----
|
||||||
|
if [ ! -f /srv/tesm-license/sqlite.db ]; then
|
||||||
|
step "Initializing database"
|
||||||
|
sudo ./venv/bin/python3 create_db.py >>/var/log/tesm-license-install.log 2>&1
|
||||||
|
print_status "Database initialized"
|
||||||
|
echo -e "${RED}→${NC} Kein Admin-Benutzer vorhanden. Bitte danach ausführen:"
|
||||||
|
echo " sudo /srv/tesm-license/venv/bin/python3 /srv/tesm-license/create_admin.py"
|
||||||
|
echo -e "${RED}→${NC} Und die eigene Bootstrap-Lizenz erzeugen:"
|
||||||
|
echo " sudo /srv/tesm-license/venv/bin/python3 /srv/tesm-license/create_master_license.py"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ---- systemd ----
|
||||||
|
step "Copying systemd unit"
|
||||||
|
UNITS_CHANGED=0
|
||||||
|
install_if_changed "$REPO_DIR/etc/systemd/system/tesm-license.service" /etc/systemd/system/tesm-license.service 644 && UNITS_CHANGED=1
|
||||||
|
print_status "Unit copied"
|
||||||
|
|
||||||
|
# ---- nginx ----
|
||||||
|
step "Configuring nginx reverse proxy"
|
||||||
|
if [ ! -f /etc/nginx/sites-available/tesm-license ]; then
|
||||||
|
sudo cp "$REPO_DIR/etc/nginx/sites-available/tesm-license" /etc/nginx/sites-available/tesm-license
|
||||||
|
fi
|
||||||
|
sudo ln -sf /etc/nginx/sites-available/tesm-license /etc/nginx/sites-enabled/tesm-license
|
||||||
|
sudo rm -f /etc/nginx/sites-enabled/default
|
||||||
|
sudo nginx -t >>/var/log/tesm-license-install.log 2>&1 && sudo systemctl reload nginx
|
||||||
|
print_status "nginx configured"
|
||||||
|
|
||||||
|
# ---- Dienst aktivieren ----
|
||||||
|
step "Enabling service"
|
||||||
|
if [ "$UNITS_CHANGED" -eq 1 ]; then
|
||||||
|
sudo systemctl daemon-reload
|
||||||
|
fi
|
||||||
|
sudo systemctl enable --now tesm-license.service
|
||||||
|
print_status "Service enabled and started"
|
||||||
|
|
||||||
|
# ---- Sicherheitsnetz prüfen ----
|
||||||
|
if [ "$NEEDS_MIGRATION_GUARD" -eq 1 ]; then
|
||||||
|
step "Verifying migrated installation"
|
||||||
|
HEALTHY=0
|
||||||
|
for i in $(seq 1 15); do
|
||||||
|
sleep 2
|
||||||
|
if curl -fsS -o /dev/null http://127.0.0.1:5000/login 2>/dev/null; then
|
||||||
|
HEALTHY=1
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
if [ "$HEALTHY" -eq 1 ]; then
|
||||||
|
print_status "Migration verified healthy"
|
||||||
|
rm -rf "$BACKUP_DIR"
|
||||||
|
echo -e "${GREEN}✔${NC} Update erfolgreich -- Backup wieder entfernt."
|
||||||
|
else
|
||||||
|
echo -e "${RED}✖ Update fehlgeschlagen -- Dienst antwortet nicht gesund. Rolle zurück...${NC}"
|
||||||
|
sudo systemctl stop tesm-license.service 2>/dev/null || true
|
||||||
|
FAILED_DIR="/srv/tesm-license-failed-update-$(date +%Y%m%d-%H%M%S)"
|
||||||
|
sudo mv /srv/tesm-license "$FAILED_DIR"
|
||||||
|
sudo cp -a "$BACKUP_DIR" /srv/tesm-license
|
||||||
|
sudo systemctl start tesm-license.service
|
||||||
|
echo -e "${YELLOW}Zurückgerollt auf den Stand vor dem Update.${NC}"
|
||||||
|
echo "Backup bleibt erhalten unter: ${BACKUP_DIR}"
|
||||||
|
echo "Die fehlgeschlagene Installation liegt zur Fehlersuche unter: ${FAILED_DIR}"
|
||||||
|
echo "Bitte /var/log/tesm-license/app.log dort prüfen, bevor erneut aktualisiert wird."
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${GREEN}✔${NC} Installation abgeschlossen. Web-App erreichbar auf Port 80 (nginx) bzw. 5000 (Flask direkt)."
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# ============================================================================
|
||||||
|
# TESM-Lizenzserver — Update/Reinstall (Bootstrapper)
|
||||||
|
# Fork von update.sh (TESM selbst) -- lädt dasselbe gepackte Gitea-Release
|
||||||
|
# herunter (das Repo enthält beide Apps: srv/tesm/ UND srv/tesm-license/)
|
||||||
|
# und übergibt an install-license.sh aus dem Paket, statt install.sh.
|
||||||
|
#
|
||||||
|
# Auszuführen als root auf dem Zielsystem:
|
||||||
|
# sudo ./update-license.sh
|
||||||
|
# Optional übersteuerbar (z.B. für eine bestimmte Version statt "latest"):
|
||||||
|
# TESM_RELEASE_TAG=v1.0.0 sudo -E ./update-license.sh
|
||||||
|
# ============================================================================
|
||||||
|
set -e
|
||||||
|
|
||||||
|
GITEA_BASE="${TESM_GITEA_BASE:-https://gitea.int.eertmoed.net}"
|
||||||
|
REPO_OWNER="${TESM_REPO_OWNER:-alientim}"
|
||||||
|
REPO_NAME="${TESM_REPO_NAME:-tesm}"
|
||||||
|
RELEASE_TAG="${TESM_RELEASE_TAG:-latest}"
|
||||||
|
GITEA_USER="${TESM_GITEA_USER:-}"
|
||||||
|
GITEA_TOKEN="${TESM_GITEA_TOKEN:-}"
|
||||||
|
PACKAGE_NAME="tesm-${RELEASE_TAG}.tar.gz"
|
||||||
|
DOWNLOAD_URL="${GITEA_BASE}/${REPO_OWNER}/${REPO_NAME}/releases/download/${RELEASE_TAG}/${PACKAGE_NAME}"
|
||||||
|
|
||||||
|
WORK_DIR="/tmp/tesm-license-update-$(date +%s)"
|
||||||
|
PACKAGE_FILE="$WORK_DIR/${PACKAGE_NAME}"
|
||||||
|
|
||||||
|
RED='\033[0;31m'
|
||||||
|
GREEN='\033[0;32m'
|
||||||
|
YELLOW='\033[1;33m'
|
||||||
|
NC='\033[0m'
|
||||||
|
|
||||||
|
if [ "$(id -u)" -ne 0 ]; then
|
||||||
|
echo "Bitte als root ausführen (sudo ./update-license.sh)." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${YELLOW}============================================================================${NC}"
|
||||||
|
echo -e "${YELLOW} TESM-Lizenzserver Update/Reinstall — Release \"${RELEASE_TAG}\"${NC}"
|
||||||
|
echo -e "${YELLOW}============================================================================${NC}"
|
||||||
|
echo
|
||||||
|
echo "Lädt das Release-Paket herunter (enthält TESM und den Lizenzserver"
|
||||||
|
echo "zusammen) und übergibt Installation/Update an install-license.sh"
|
||||||
|
echo "daraus -- deployt ausschließlich srv/tesm-license/, lässt eine"
|
||||||
|
echo "eventuell auf demselben Host vorhandene TESM-Installation unberührt."
|
||||||
|
echo
|
||||||
|
|
||||||
|
read -r -p "Fortfahren? [y/N] " confirm
|
||||||
|
if [[ "$confirm" != "y" && "$confirm" != "Y" ]]; then
|
||||||
|
echo "Abgebrochen — nichts wurde verändert."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo -e "${RED}→${NC} Lade Paket von ${DOWNLOAD_URL}..."
|
||||||
|
mkdir -p "$WORK_DIR"
|
||||||
|
CURL_AUTH=()
|
||||||
|
if [ -n "$GITEA_USER" ] && [ -n "$GITEA_TOKEN" ]; then
|
||||||
|
CURL_AUTH=(-u "${GITEA_USER}:${GITEA_TOKEN}")
|
||||||
|
fi
|
||||||
|
curl -fsSL "${CURL_AUTH[@]}" -o "$PACKAGE_FILE" "$DOWNLOAD_URL"
|
||||||
|
echo -e "${GREEN}✔${NC} Paket heruntergeladen ($(du -h "$PACKAGE_FILE" | cut -f1))."
|
||||||
|
|
||||||
|
echo -e "${RED}→${NC} Entpacke Paket..."
|
||||||
|
mkdir -p "$WORK_DIR/pkg"
|
||||||
|
tar xzf "$PACKAGE_FILE" -C "$WORK_DIR/pkg" --strip-components=1
|
||||||
|
echo -e "${GREEN}✔${NC} Paket entpackt."
|
||||||
|
|
||||||
|
echo -e "${RED}→${NC} Starte install-license.sh aus dem Paket..."
|
||||||
|
bash "$WORK_DIR/pkg/install-license.sh"
|
||||||
|
|
||||||
|
echo
|
||||||
|
echo -e "${GREEN}============================================================================${NC}"
|
||||||
|
echo -e "${GREEN} TESM-Lizenzserver Update/Reinstall (Release \"${RELEASE_TAG}\") abgeschlossen.${NC}"
|
||||||
|
echo -e "${GREEN}============================================================================${NC}"
|
||||||
Reference in New Issue
Block a user