- auth.py: .env auch in schreibgeschütztem Ordner aktualisieren (Server-Login) - Controller: ohne Spotify-Login warten statt abstürzen; Playlist darf fehlen; nicht abspielbare Wünsche überspringen statt hängen zu bleiben - install-server.sh: alle SSH-Ports freigeben (kein Aussperren), curl und python3-systemd (fail2ban) installieren - install-player.sh: Bluetooth per rfkill entsperren und Dienst starten Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
171 lines
8.1 KiB
Bash
Executable File
171 lines
8.1 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
# Audiofy – Installation der Rolle "Server": Website für Gäste, Admin-Bereich, Player-API.
|
||
#
|
||
# install/install-server.sh Neuinstallation (fragt alles Nötige ab)
|
||
# install/install-server.sh --update nur Code, Python-Umgebung und Dienst aktualisieren
|
||
#
|
||
# Voraussetzungen: Debian 12+ (oder abgeleitet), eine (Sub-)Domain, deren A/AAAA-Record
|
||
# auf diesen Rechner zeigt, und offene Ports 80/443 (auch in einer Cloud-Firewall).
|
||
set -euo pipefail
|
||
source "$(dirname "$0")/lib.sh"
|
||
|
||
UPDATE=0
|
||
for arg in "$@"; do
|
||
case "$arg" in
|
||
--update) UPDATE=1 ;;
|
||
-h|--help) sed -n '2,9p' "$0"; exit 0 ;;
|
||
*) abbruch "Unbekannte Option: $arg" ;;
|
||
esac
|
||
done
|
||
|
||
ENV_DATEI="$AUDIOFY_DIR/.env"
|
||
export AUDIOFY_DIR AUDIOFY_USER
|
||
export APP_PORT="${APP_PORT:-8000}"
|
||
|
||
schritt "Audiofy-Server: $([ $UPDATE = 1 ] && echo Update || echo Neuinstallation)"
|
||
pruefe_system
|
||
if [ $UPDATE = 1 ] && ! $SUDO test -f "$ENV_DATEI"; then
|
||
abbruch "Keine bestehende Installation in $AUDIOFY_DIR gefunden – ohne --update ausführen."
|
||
fi
|
||
|
||
# ------------------------------------------------------------------ Abfragen
|
||
if [ $UPDATE = 0 ]; then
|
||
schritt "Angaben"
|
||
frage DOMAIN "Domain der Website (z. B. musik.example.org)"
|
||
[ -n "$DOMAIN" ] || abbruch "Ohne Domain geht es nicht (HTTPS über Let's Encrypt)."
|
||
frage ACME_EMAIL "E-Mail für Let's Encrypt (Ablaufwarnungen)"
|
||
info "Spotify-App (developer.spotify.com): Redirect-URI http://127.0.0.1:8888/callback eintragen."
|
||
frage SPOTIFY_CLIENT_ID "Spotify Client ID"
|
||
frage_geheim SPOTIFY_CLIENT_SECRET "Spotify Client Secret (verdeckt)"
|
||
frage PLAYLIST "Grund-Playlist (Link oder ID, später im Admin änderbar)"
|
||
PLAYLIST_ID="$(sed -E 's#.*playlist[/:]([A-Za-z0-9]{22}).*#\1#' <<<"$PLAYLIST")"
|
||
if [ -n "$PLAYLIST_ID" ] && ! [[ "$PLAYLIST_ID" =~ ^[A-Za-z0-9]{22}$ ]]; then
|
||
warnung "Das sieht nicht nach einer Playlist aus – bitte später im Admin-Bereich eintragen."
|
||
PLAYLIST_ID=""
|
||
fi
|
||
# alle Ports, auf denen SSH lauscht, plus den der aktuellen Sitzung – damit sich niemand aussperrt
|
||
SSH_PORTS="$( { ${SUDO} sshd -T 2>/dev/null | awk '/^port /{print $2}'; [ -n "${SSH_CONNECTION:-}" ] && awk '{print $4}' <<<"$SSH_CONNECTION"; } | sort -un | tr '\n' ' ')"
|
||
SSH_PORTS="${SSH_PORTS:-22}"
|
||
SSH_PORT="${SSH_PORTS%% *}" # für fail2ban: erster Port
|
||
ja_nein "Firewall (ufw) einrichten? Erlaubt SSH (Port ${SSH_PORTS% }), 80 und 443" j && FIREWALL=1 || FIREWALL=0
|
||
ja_nein "fail2ban für SSH einrichten?" j && FAIL2BAN=1 || FAIL2BAN=0
|
||
ja_nein "Automatische Sicherheitsupdates aktivieren?" j && AUTOUPDATE=1 || AUTOUPDATE=0
|
||
export DOMAIN ACME_EMAIL SSH_PORT
|
||
fi
|
||
|
||
# ------------------------------------------------------------------ Pakete
|
||
schritt "Pakete"
|
||
$SUDO apt-get update -qq
|
||
PAKETE=(python3-venv sqlite3 caddy curl)
|
||
if [ $UPDATE = 0 ]; then
|
||
[ $FIREWALL = 1 ] && PAKETE+=(ufw)
|
||
[ $FAIL2BAN = 1 ] && PAKETE+=(fail2ban python3-systemd)
|
||
[ $AUTOUPDATE = 1 ] && PAKETE+=(unattended-upgrades)
|
||
fi
|
||
pakete "${PAKETE[@]}"
|
||
|
||
# ------------------------------------------------------------------ Benutzer, Ordner, Code
|
||
schritt "Dienst-Benutzer, Ordner und Code"
|
||
dienst_benutzer
|
||
$SUDO install -d -o root -g "$AUDIOFY_USER" -m 750 "$AUDIOFY_DIR"
|
||
$SUDO install -d -o "$AUDIOFY_USER" -g "$AUDIOFY_USER" -m 700 "$AUDIOFY_DIR/data"
|
||
code_kopieren "$REPO/server" "$AUDIOFY_DIR/app"
|
||
$SUDO install -o root -g "$AUDIOFY_USER" -m 640 "$REPO/player/auth.py" "$AUDIOFY_DIR/app/auth.py"
|
||
venv_einrichten "$AUDIOFY_DIR/app/requirements.txt"
|
||
info "Code in $AUDIOFY_DIR/app, Python-Umgebung in $AUDIOFY_DIR/.venv"
|
||
|
||
# ------------------------------------------------------------------ Konfiguration
|
||
schritt "Konfiguration ($ENV_DATEI)"
|
||
$SUDO test -f "$ENV_DATEI" || $SUDO install -o "$AUDIOFY_USER" -g "$AUDIOFY_USER" -m 600 /dev/null "$ENV_DATEI"
|
||
VERWALTUNG=("${ALS_DIENST[@]}" "$AUDIOFY_DIR/.venv/bin/python" "$AUDIOFY_DIR/app/verwaltung.py")
|
||
"${VERWALTUNG[@]}" geheimnisse --env "$ENV_DATEI" | sed 's/^/ /'
|
||
if [ $UPDATE = 0 ]; then
|
||
env_setzen "$ENV_DATEI" DB_PATH "$AUDIOFY_DIR/data/audiofy.db"
|
||
env_setzen "$ENV_DATEI" PUBLIC_URL "https://$DOMAIN"
|
||
env_setzen "$ENV_DATEI" SPOTIFY_CLIENT_ID "$SPOTIFY_CLIENT_ID"
|
||
env_setzen "$ENV_DATEI" SPOTIFY_CLIENT_SECRET "$SPOTIFY_CLIENT_SECRET"
|
||
[ -n "$PLAYLIST_ID" ] && env_setzen "$ENV_DATEI" SPOTIFY_PLAYLIST_ID "$PLAYLIST_ID"
|
||
unset SPOTIFY_CLIENT_SECRET
|
||
|
||
schritt "Spotify-Login für die Suche"
|
||
info "Öffne gleich die angezeigte Adresse im Browser, melde dich an und füge danach die"
|
||
info "Adresse aus der Adresszeile (http://127.0.0.1:8888/callback?...) hier ein."
|
||
if ja_nein "Jetzt anmelden?" j; then
|
||
"${ALS_DIENST[@]}" "$AUDIOFY_DIR/.venv/bin/python" "$AUDIOFY_DIR/app/auth.py" --profil server --env "$ENV_DATEI" \
|
||
|| warnung "Login fehlgeschlagen – später nachholen (siehe README)."
|
||
fi
|
||
|
||
schritt "Admin-Passwort"
|
||
"${VERWALTUNG[@]}" passwort --env "$ENV_DATEI" || warnung "Kein Passwort gesetzt – später nachholen (siehe README)."
|
||
if ja_nein "Cloudflare Turnstile (Bot-Schutz für Login und Wünsche) einrichten?" n; then
|
||
"${VERWALTUNG[@]}" turnstile --env "$ENV_DATEI" || warnung "Turnstile nicht eingerichtet."
|
||
fi
|
||
fi
|
||
|
||
# ------------------------------------------------------------------ Dienst
|
||
schritt "Dienst audiofy-web"
|
||
vorlage audiofy-web.service /etc/systemd/system/audiofy-web.service
|
||
$SUDO systemctl daemon-reload
|
||
$SUDO systemctl enable audiofy-web >/dev/null 2>&1
|
||
$SUDO systemctl restart audiofy-web
|
||
sleep 3
|
||
systemctl is-active --quiet audiofy-web || abbruch "audiofy-web läuft nicht – siehe: journalctl -u audiofy-web"
|
||
curl -fsS "http://127.0.0.1:$APP_PORT/healthz" >/dev/null && info "audiofy-web läuft"
|
||
|
||
# ------------------------------------------------------------------ Caddy & Schutz (nur Neuinstallation)
|
||
if [ $UPDATE = 0 ]; then
|
||
schritt "Caddy (HTTPS)"
|
||
if $SUDO grep -q "Audiofy" /etc/caddy/Caddyfile 2>/dev/null || $SUDO grep -q "/usr/share/caddy" /etc/caddy/Caddyfile 2>/dev/null; then
|
||
vorlage Caddyfile /etc/caddy/Caddyfile
|
||
$SUDO caddy validate --config /etc/caddy/Caddyfile --adapter caddyfile >/dev/null 2>&1 || abbruch "Caddyfile ungültig"
|
||
$SUDO systemctl enable caddy >/dev/null 2>&1
|
||
$SUDO systemctl reload-or-restart caddy
|
||
info "Caddy holt das Zertifikat für $DOMAIN (dauert ein paar Sekunden)."
|
||
else
|
||
warnung "/etc/caddy/Caddyfile enthält schon eine eigene Konfiguration – nicht verändert."
|
||
warnung "Bitte einen Block für $DOMAIN mit 'reverse_proxy 127.0.0.1:$APP_PORT' ergänzen (Vorlage: install/vorlagen/Caddyfile)."
|
||
fi
|
||
|
||
if [ $FIREWALL = 1 ]; then
|
||
schritt "Firewall (ufw)"
|
||
$SUDO ufw default deny incoming >/dev/null
|
||
$SUDO ufw default allow outgoing >/dev/null
|
||
for p in $SSH_PORTS; do $SUDO ufw allow "$p/tcp" comment "SSH" >/dev/null; done
|
||
$SUDO ufw allow 80/tcp comment "HTTP (Caddy/ACME)" >/dev/null
|
||
$SUDO ufw allow 443/tcp comment "HTTPS (Caddy)" >/dev/null
|
||
$SUDO ufw --force enable >/dev/null
|
||
info "ufw aktiv: SSH ${SSH_PORTS% }, 80, 443"
|
||
fi
|
||
if [ $FAIL2BAN = 1 ]; then
|
||
schritt "fail2ban"
|
||
vorlage fail2ban-audiofy.conf /etc/fail2ban/jail.d/audiofy.conf
|
||
$SUDO systemctl enable fail2ban >/dev/null 2>&1
|
||
$SUDO systemctl restart fail2ban
|
||
info "SSH-Schutz auf Port $SSH_PORT aktiv"
|
||
fi
|
||
if [ $AUTOUPDATE = 1 ]; then
|
||
schritt "Automatische Sicherheitsupdates"
|
||
vorlage 20auto-upgrades /etc/apt/apt.conf.d/20auto-upgrades
|
||
info "aktiviert"
|
||
fi
|
||
schritt "Logs begrenzen"
|
||
$SUDO install -d /etc/systemd/journald.conf.d
|
||
vorlage journald-10-audiofy.conf /etc/systemd/journald.conf.d/10-audiofy.conf
|
||
$SUDO systemctl restart systemd-journald
|
||
info "journald max. 200 MB"
|
||
fi
|
||
|
||
# ------------------------------------------------------------------ Zusammenfassung
|
||
schritt "Fertig"
|
||
URL="$(env_wert "$ENV_DATEI" PUBLIC_URL)"
|
||
info "Website: $URL (Gäste nur mit Einladungslink/QR-Code)"
|
||
info "Admin: $URL/admin"
|
||
info "Logs: journalctl -u audiofy-web -f"
|
||
if [ $UPDATE = 0 ]; then
|
||
info ""
|
||
info "Für die Player-Installation brauchst du:"
|
||
info " Server-Adresse: $URL"
|
||
info " Player-Token: sudo -u $AUDIOFY_USER grep PLAYER_API_TOKEN $ENV_DATEI"
|
||
info "Gastzugang ist anfangs aus – im Admin unter „Gäste“ einschalten."
|
||
fi
|