Vorwort
Für die Ablösung von Microsoft Teams nutzen wir zukünftig Nextcloud Talk.
Damit auch Meetings mit 5+ Teilnehmenden gut funktionieren benötigt man ein sogenanntes „High Performance Backend“, welches die Orchestrierung der Teilnehmer untereinander übernimmt (meist ist eine Nextcloud-Instanz auf keinem potenten Server installiert).
Über claude.ai habe ich mir ein Script erstellen lassen, welches unter Ubuntu 24.04 in einem Rutsch das HPB installiert und auch Betriebssystem- und HPB-Updates einrichtet.
Quasi ein All-in-One-Script.
Voraussetzungen
- frisch installierter Ubuntu Server 24.04 LTS
- dem Server ist eine öffentliche IP zugewiesen
- es gibt einen Host-Namen, der auf die öffentliche IP auflöst
- Abarbeiten der Hinweise von meinem Artikel Ubuntu, Nano, Docker, Docker Compose & Firewall
DAS Script
Öffne eine Datei install-hpb.sh …
nano install-hpb.sh
… und füge den folgenden Inhalt hinzu …
#!/usr/bin/env bash
#
# install-hpb.sh
# ---------------------------------------------------------------------------
# Installiert ein vollständiges High Performance Backend (HPB) für
# Nextcloud Talk auf Ubuntu 24.04 LTS:
# - coturn (STUN/TURN)
# - Janus (WebRTC-Gateway)
# - NATS (Messaging, per Docker)
# - nextcloud-spreed-signaling (Signaling-Server, aus Quellcode gebaut)
# - nginx + Let's Encrypt (Reverse Proxy für den Signaling-Server)
# - systemd-Timer, der täglich um 02:00 Uhr Betriebssystem- UND
# HPB-Updates einspielt
#
# Aufruf: sudo ./install-hpb.sh
# Erneuter Aufruf ist unkritisch (idempotent) - vorhandene Secrets und
# Konfigurationen werden nicht überschrieben, nur ergänzt/aktualisiert.
# ---------------------------------------------------------------------------
set -euo pipefail
########################################################################
# KONFIGURATION – BITTE VOR DEM ERSTEN AUFRUF ANPASSEN
########################################################################
SIGNALING_DOMAIN="talk.example.com" # FQDN dieses HPB-Servers (DNS muss bereits hierher zeigen!)
NEXTCLOUD_URL="https://cloud.example.com" # URL der bestehenden Nextcloud-Instanz
LETSENCRYPT_EMAIL="[email protected]" # Kontakt-Adresse für Let's Encrypt
SIGNALING_VERSION="latest" # "latest" = neuestes Git-Tag, oder z.B. "v1.3.0"
########################################################################
SECRETS_FILE="/root/hpb-secrets.env"
SIGNALING_SRC="/opt/nextcloud-spreed-signaling"
SIGNALING_CONF="/etc/nextcloud-signaling.conf"
LOGFILE="/var/log/hpb-install.log"
exec > >(tee -a "$LOGFILE") 2>&1
echo "=== HPB-Installation gestartet: $(date -Is) ==="
if [[ $EUID -ne 0 ]]; then
echo "Bitte als root ausführen (sudo ./install-hpb.sh)." >&2
exit 1
fi
if [[ "$SIGNALING_DOMAIN" == "talk.example.com" ]]; then
echo "FEHLER: Bitte zuerst die KONFIGURATION am Anfang des Skripts anpassen" >&2
echo " (SIGNALING_DOMAIN, NEXTCLOUD_URL, LETSENCRYPT_EMAIL)." >&2
exit 1
fi
. /etc/os-release || true
if [[ "${VERSION_ID:-}" != "24.04" ]]; then
echo "WARNUNG: Dieses Skript ist für Ubuntu 24.04 LTS getestet (gefunden: ${VERSION_ID:-unbekannt}). Fortfahren in 5s..." >&2
sleep 5
fi
########################################################################
# 1. Secrets erzeugen (nur beim ersten Lauf) oder laden
########################################################################
if [[ -f "$SECRETS_FILE" ]]; then
echo "--> Bestehende Secrets aus $SECRETS_FILE werden wiederverwendet."
# shellcheck disable=SC1090
source "$SECRETS_FILE"
else
echo "--> Erzeuge neue Secrets."
TURN_SECRET=$(openssl rand -hex 16)
JANUS_API_KEY=$(openssl rand -hex 16)
SESSION_HASHKEY=$(openssl rand -hex 16)
SESSION_BLOCKKEY=$(openssl rand -hex 16)
INTERNAL_SECRET=$(openssl rand -hex 16)
BACKEND_SECRET=$(openssl rand -hex 16)
cat > "$SECRETS_FILE" <<EOF
TURN_SECRET=$TURN_SECRET
JANUS_API_KEY=$JANUS_API_KEY
SESSION_HASHKEY=$SESSION_HASHKEY
SESSION_BLOCKKEY=$SESSION_BLOCKKEY
INTERNAL_SECRET=$INTERNAL_SECRET
BACKEND_SECRET=$BACKEND_SECRET
EOF
chmod 600 "$SECRETS_FILE"
fi
########################################################################
# 2. Basis-Update + Pakete
########################################################################
echo "--> apt update/upgrade"
export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get -y -o Dpkg::Options::="--force-confold" upgrade
echo "--> Installiere benötigte Pakete"
apt-get install -y \
coturn janus nginx certbot python3-certbot-nginx \
golang-go git make build-essential automake protobuf-compiler \
docker.io openssl jq curl unattended-upgrades
########################################################################
# 3. coturn (STUN/TURN)
########################################################################
echo "--> Konfiguriere coturn"
if ! getent group turnserver >/dev/null; then
groupadd --system turnserver
fi
if ! getent passwd turnserver >/dev/null; then
useradd --system --gid turnserver --shell /usr/sbin/nologin \
--comment "Coturn STUN/TURN server" turnserver
fi
cat > /etc/turnserver.conf <<EOF
listening-port=3478
fingerprint
use-auth-secret
static-auth-secret=${TURN_SECRET}
realm=${SIGNALING_DOMAIN}
total-quota=0
bps-capacity=0
no-tls
no-dtls
stale-nonce
no-loopback-peers
no-multicast-peers
proc-user=turnserver
proc-group=turnserver
EOF
echo "TURNSERVER_ENABLED=1" > /etc/default/coturn
# Eigene, robuste systemd-Unit (überschreibt ggf. Paket-Unit, /etc hat Vorrang)
cat > /etc/systemd/system/coturn.service <<'EOF'
[Unit]
Description=coturn STUN/TURN server
Documentation=man:coturn(1) man:turnadmin(1) man:turnserver(1)
After=syslog.target network.target
[Service]
Type=forking
User=turnserver
Group=turnserver
RuntimeDirectory=turnserver
RuntimeDirectoryMode=0750
EnvironmentFile=/etc/default/coturn
PIDFile=/run/turnserver/turnserver.pid
ExecStart=/usr/bin/turnserver --daemon --pidfile /run/turnserver/turnserver.pid --syslog -c /etc/turnserver.conf $EXTRA_OPTIONS
Restart=on-failure
LimitNOFILE=1000000
[Install]
WantedBy=multi-user.target
EOF
update-rc.d coturn disable 2>/dev/null || true
systemctl daemon-reload
systemctl enable --now coturn.service
systemctl restart coturn.service
########################################################################
# 4. Janus (WebRTC-Gateway)
########################################################################
echo "--> Konfiguriere Janus"
python3 - "$JANUS_API_KEY" "$SIGNALING_DOMAIN" <<'PYEOF'
import re, sys
janus_api_key, signaling_domain = sys.argv[1], sys.argv[2]
def set_kv(content, section, key, value):
pattern = re.compile(r'(' + re.escape(section) + r':\s*\{)(.*?)(\n\})', re.S)
m = pattern.search(content)
if not m:
return content
header, body, footer = m.group(1), m.group(2), m.group(3)
kv_pattern = re.compile(r'^[ \t]*#?[ \t]*' + re.escape(key) + r'[ \t]*=.*$', re.M)
line = f'\t\t{key} = {value}'
if kv_pattern.search(body):
body = kv_pattern.sub(line, body, count=1)
else:
body = body + '\n' + line
return content[:m.start()] + header + body + footer + content[m.end():]
# janus.jcfg: nat-Sektion
path = "/etc/janus/janus.jcfg"
with open(path) as f:
content = f.read()
content = set_kv(content, "nat", "stun_server", f'"{signaling_domain}"')
content = set_kv(content, "nat", "stun_port", "3478")
content = set_kv(content, "nat", "full_trickle", "true")
content = set_kv(content, "nat", "turn_rest_api_key", f'"{janus_api_key}"')
with open(path, "w") as f:
f.write(content)
# janus.transport.http.jcfg: general-Sektion (nur lokal erreichbar)
path = "/etc/janus/janus.transport.http.jcfg"
with open(path) as f:
content = f.read()
content = set_kv(content, "general", "base_path", '"/janus"')
content = set_kv(content, "general", "http", "true")
content = set_kv(content, "general", "port", "8088")
content = set_kv(content, "general", "ip", '"127.0.0.1"')
content = set_kv(content, "general", "https", "false")
with open(path, "w") as f:
f.write(content)
# janus.transport.websockets.jcfg: general-Sektion (nur lokal erreichbar)
path = "/etc/janus/janus.transport.websockets.jcfg"
with open(path) as f:
content = f.read()
content = set_kv(content, "general", "ws", "true")
content = set_kv(content, "general", "ws_port", "8188")
content = set_kv(content, "general", "ws_ip", '"127.0.0.1"')
content = set_kv(content, "general", "wss", "false")
with open(path, "w") as f:
f.write(content)
PYEOF
systemctl enable --now janus.service
systemctl restart janus.service
########################################################################
# 5. NATS (per Docker)
########################################################################
echo "--> Richte NATS via Docker ein"
systemctl enable --now docker.service
if ! docker ps -a --format '{{.Names}}' | grep -qx nats; then
docker run --name=nats -d -p 127.0.0.1:4222:4222 --restart=always nats:latest
else
docker start nats >/dev/null || true
fi
########################################################################
# 6. nextcloud-spreed-signaling (Signaling-Server)
########################################################################
echo "--> Baue nextcloud-spreed-signaling"
if ! getent group nextcloud-signaling >/dev/null; then
groupadd --system nextcloud-signaling
fi
if ! getent passwd nextcloud-signaling >/dev/null; then
useradd --system --gid nextcloud-signaling --shell /usr/sbin/nologin \
--comment "Nextcloud Talk signaling server" nextcloud-signaling
fi
if [[ ! -d "$SIGNALING_SRC" ]]; then
git clone https://github.com/strukturag/nextcloud-spreed-signaling.git "$SIGNALING_SRC"
fi
cd "$SIGNALING_SRC"
git fetch --tags --force
if [[ "$SIGNALING_VERSION" == "latest" ]]; then
TARGET_REF=$(git tag -l 'v*' | sort -V | tail -n1)
if [[ -z "$TARGET_REF" ]]; then
TARGET_REF="main"
fi
else
TARGET_REF="$SIGNALING_VERSION"
fi
echo " Verwende Version: $TARGET_REF"
git checkout -q "$TARGET_REF"
make build
cat > "$SIGNALING_CONF" <<EOF
[http]
listen = 127.0.0.1:8090
[sessions]
hashkey = ${SESSION_HASHKEY}
blockkey = ${SESSION_BLOCKKEY}
[clients]
internalsecret = ${INTERNAL_SECRET}
[backend]
backends = backend1
allowall = false
[backend1]
url = ${NEXTCLOUD_URL}
secret = ${BACKEND_SECRET}
[nats]
url = nats://127.0.0.1:4222
[mcu]
type = janus
url = ws://127.0.0.1:8188
[turn]
apikey = ${JANUS_API_KEY}
secret = ${TURN_SECRET}
servers = turn:${SIGNALING_DOMAIN}:3478?transport=udp,turn:${SIGNALING_DOMAIN}:3478?transport=tcp
EOF
chmod 600 "$SIGNALING_CONF"
chown nextcloud-signaling: "$SIGNALING_CONF"
cat > /etc/systemd/system/nextcloud-signaling.service <<EOF
[Unit]
Description=Nextcloud Talk signaling server
After=network.target janus.service coturn.service docker.service
[Service]
ExecStart=${SIGNALING_SRC}/bin/signaling --config ${SIGNALING_CONF}
User=nextcloud-signaling
Group=nextcloud-signaling
Restart=on-failure
NoNewPrivileges=yes
PrivateTmp=yes
ProtectHome=yes
ProtectSystem=strict
ReadWritePaths=${SIGNALING_CONF}
[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now nextcloud-signaling.service
systemctl restart nextcloud-signaling.service
########################################################################
# 7. nginx + Let's Encrypt
########################################################################
echo "--> Konfiguriere nginx"
cat > /etc/nginx/sites-available/${SIGNALING_DOMAIN}.conf <<EOF
upstream signaling {
server 127.0.0.1:8090;
}
server {
listen 80;
listen [::]:80;
server_name ${SIGNALING_DOMAIN};
location /standalone-signaling/ {
proxy_pass http://signaling/;
proxy_http_version 1.1;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
}
location /standalone-signaling/spreed {
proxy_pass http://signaling/spreed;
proxy_http_version 1.1;
proxy_set_header Upgrade \$http_upgrade;
proxy_set_header Connection "Upgrade";
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
}
}
EOF
ln -sf /etc/nginx/sites-available/${SIGNALING_DOMAIN}.conf /etc/nginx/sites-enabled/${SIGNALING_DOMAIN}.conf
nginx -t
systemctl reload nginx
echo "--> Fordere Let's-Encrypt-Zertifikat an (setzt voraus, dass DNS für ${SIGNALING_DOMAIN} bereits auf diesen Server zeigt und Port 80 von außen erreichbar ist)"
if certbot certificates 2>/dev/null | grep -q "Domains: ${SIGNALING_DOMAIN}"; then
echo " Zertifikat existiert bereits, überspringe Anforderung."
else
certbot --nginx -d "${SIGNALING_DOMAIN}" --non-interactive --agree-tos \
-m "${LETSENCRYPT_EMAIL}" --redirect || \
echo " WARNUNG: certbot ist fehlgeschlagen. Bitte DNS/Port 80 prüfen und certbot manuell erneut ausführen."
fi
########################################################################
# 8. Firewall (nur falls ufw aktiv ist)
########################################################################
if command -v ufw >/dev/null && ufw status | grep -q "Status: active"; then
echo "--> ufw ist aktiv, öffne benötigte Ports"
ufw allow 80/tcp
ufw allow 443/tcp
ufw allow 3478/tcp
ufw allow 3478/udp
fi
########################################################################
# 9. Update-Skript + systemd-Timer für 02:00 Uhr
########################################################################
echo "--> Richte automatische tägliche Updates (02:00 Uhr) ein"
cat > /usr/local/sbin/hpb-update.sh <<EOF
#!/usr/bin/env bash
# Automatisch von install-hpb.sh erzeugt.
# Aktualisiert Betriebssystem-Pakete UND alle HPB-Komponenten.
set -euo pipefail
export DEBIAN_FRONTEND=noninteractive
LOG="/var/log/hpb-update.log"
exec >> "\$LOG" 2>&1
echo "=== HPB-Update gestartet: \$(date -Is) ==="
# 1) Betriebssystem- und Paket-Updates (deckt u.a. coturn, janus, nginx ab)
apt-get update -y
apt-get -y -o Dpkg::Options::="--force-confold" dist-upgrade
apt-get -y autoremove --purge
apt-get -y clean
# 2) NATS-Docker-Image aktualisieren
docker pull nats:latest
RUNNING_ID=\$(docker inspect --format '{{.Image}}' nats 2>/dev/null || echo "")
LATEST_ID=\$(docker inspect --format '{{.Id}}' nats:latest 2>/dev/null || echo "")
if [[ "\$RUNNING_ID" != "\$LATEST_ID" ]]; then
echo "Neues NATS-Image gefunden, Container wird neu erstellt."
docker rm -f nats || true
docker run --name=nats -d -p 127.0.0.1:4222:4222 --restart=always nats:latest
fi
# 3) nextcloud-spreed-signaling auf neueste Version aktualisieren
cd "${SIGNALING_SRC}"
CURRENT_REF=\$(git rev-parse HEAD)
git fetch --tags --force
LATEST_TAG=\$(git tag -l 'v*' | sort -V | tail -n1)
if [[ -n "\$LATEST_TAG" ]]; then
LATEST_REF=\$(git rev-parse "\$LATEST_TAG")
if [[ "\$CURRENT_REF" != "\$LATEST_REF" ]]; then
echo "Aktualisiere Signaling-Server auf \$LATEST_TAG"
git checkout -q "\$LATEST_TAG"
make build
systemctl restart nextcloud-signaling.service
fi
fi
# 4) Dienste durchstarten, falls Pakete aktualisiert wurden
systemctl restart coturn.service janus.service nginx.service || true
# 5) Hinweis, falls ein Reboot nötig ist (kein automatischer Reboot,
# um laufende Gespräche nicht zu unterbrechen)
if [[ -f /var/run/reboot-required ]]; then
echo "HINWEIS: Ein Neustart des Servers ist erforderlich (siehe /var/run/reboot-required)."
fi
echo "=== HPB-Update abgeschlossen: \$(date -Is) ==="
EOF
chmod 750 /usr/local/sbin/hpb-update.sh
cat > /etc/systemd/system/hpb-update.service <<'EOF'
[Unit]
Description=Tägliches Update von Betriebssystem und Nextcloud Talk HPB
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/hpb-update.sh
EOF
cat > /etc/systemd/system/hpb-update.timer <<'EOF'
[Unit]
Description=Startet hpb-update.service täglich um 02:00 Uhr
[Timer]
OnCalendar=*-*-* 02:00:00
RandomizedDelaySec=300
Persistent=true
[Install]
WantedBy=timers.target
EOF
systemctl daemon-reload
systemctl enable --now hpb-update.timer
########################################################################
# 10. Zusammenfassung
########################################################################
cat <<SUMMARY
============================================================================
Installation abgeschlossen: $(date -Is)
============================================================================
Trage in Nextcloud unter Einstellungen -> Talk folgende Werte ein:
STUN-Server:
${SIGNALING_DOMAIN}:3478
TURN-Server:
Server: ${SIGNALING_DOMAIN}:3478
Secret: ${TURN_SECRET}
(Protokolle UDP und TCP wie vorgegeben belassen)
Signaling-Server (High-Performance-Backend):
URL: https://${SIGNALING_DOMAIN}/standalone-signaling/
Secret: ${BACKEND_SECRET}
Alle erzeugten Secrets liegen zusätzlich in: ${SECRETS_FILE} (chmod 600)
Automatische Updates:
- systemd-Timer 'hpb-update.timer' läuft täglich um 02:00 Uhr
(+ bis zu 5 Min. zufällige Verzögerung)
- Aktualisiert: Betriebssystem-Pakete, coturn, Janus, nginx,
NATS-Docker-Image sowie nextcloud-spreed-signaling (neuestes Git-Tag)
- Log: /var/log/hpb-update.log
- Status prüfen: systemctl list-timers hpb-update.timer
Wichtig:
- Stelle sicher, dass DNS für ${SIGNALING_DOMAIN} auf diesen Server zeigt
und die Ports 80/443 (TCP) sowie 3478 (TCP+UDP) von außen erreichbar sind.
- Falls certbot oben fehlgeschlagen ist: erneut ausführen mit
certbot --nginx -d ${SIGNALING_DOMAIN}
============================================================================
SUMMARY
- Passe nun die folgenden Variablen im Script an:
SIGNALING_DOMAIN="talk.example.com" # muss per DNS bereits auf den Server zeigen
NEXTCLOUD_URL="https://cloud.example.com" # deine bestehende Nextcloud
LETSENCRYPT_EMAIL="[email protected]"
Beende den Editor, gib dem Script die Ausführen-Berechtigung und starte es …
sudo chmod +x install-hpb.sh
sudo ./install-hpb.sh
Konfiguration in Nextcloud
Trage nun die angezeigten Werte so in deine Nextcloud-Installation im Bereich „Talk“ ein, wie es dir das Setup-Script sagt.
Wenn du alles korrekt eingetragen hast solltest du lauter „grüne Haken“ sehen.


HPB-Server härten (gegen Angriffe absichern)
Öffne eine Datei harden-hpb.sh …
sudo nano harden-hpb.sh
… und füge folgenden Inhalt ein …
#!/usr/bin/env bash
#
# harden-hpb.sh
# ---------------------------------------------------------------------------
# Härtet den HPB-Server:
# - ufw: standardmäßig alles blockieren, nur die nötigen Ports öffnen
# * SSH nur von den angegebenen Admin-IPs
# * 80/443 (nginx / Let's Encrypt)
# * 3478 tcp+udp (coturn STUN/TURN)
# * TURN-Relay-Portbereich (udp) für Media-Relay
# Janus/NATS/Signaling-Server bleiben unangetastet, da sie ohnehin nur
# auf 127.0.0.1 lauschen (siehe install-hpb.sh) und keine öffentlichen
# Ports brauchen.
# - fail2ban: Bannt IPs mit fehlgeschlagenen SSH-Logins bzw. auffälligem
# nginx-Verhalten automatisch.
#
# Aufruf: sudo ./harden-hpb.sh
# ---------------------------------------------------------------------------
set -euo pipefail
########################################################################
# KONFIGURATION – BEI BEDARF ANPASSEN
########################################################################
SSH_PORT=22
SSH_ALLOWED_IPS=("1.2.3.4" "5.6.7.8")
TURN_RELAY_MIN=49152 # muss zu min-port in /etc/turnserver.conf passen (Standard, falls dort nicht gesetzt)
TURN_RELAY_MAX=65535 # muss zu max-port in /etc/turnserver.conf passen (Standard, falls dort nicht gesetzt)
########################################################################
LOGFILE="/var/log/hpb-harden.log"
exec > >(tee -a "$LOGFILE") 2>&1
echo "=== Härtung gestartet: $(date -Is) ==="
if [[ $EUID -ne 0 ]]; then
echo "Bitte als root ausführen (sudo ./harden-hpb.sh)." >&2
exit 1
fi
echo "--> Installiere ufw und fail2ban"
export DEBIAN_FRONTEND=noninteractive
apt-get update -y
apt-get install -y ufw fail2ban
########################################################################
# 1. ufw: Grundregeln
########################################################################
echo "--> Setze ufw-Grundregeln"
ufw default deny incoming
ufw default allow outgoing
# Vorhandene, zu offene SSH-Regeln entfernen (falls z.B. "OpenSSH" oder
# "22/tcp" bisher für alle IPs offen war)
ufw delete allow OpenSSH 2>/dev/null || true
ufw delete allow "${SSH_PORT}/tcp" 2>/dev/null || true
ufw delete allow ssh 2>/dev/null || true
########################################################################
# 2. SSH nur von den Admin-IPs
########################################################################
echo "--> Erlaube SSH (Port ${SSH_PORT}) nur von den Admin-IPs"
for ip in "${SSH_ALLOWED_IPS[@]}"; do
ufw allow from "${ip}" to any port "${SSH_PORT}" proto tcp comment "SSH Admin-Zugriff"
done
########################################################################
# 3. Öffentlich benötigte HPB-Ports
########################################################################
echo "--> Öffne öffentlich benötigte Ports (HTTP/HTTPS, TURN)"
ufw allow 80/tcp comment "nginx und Lets Encrypt"
ufw allow 443/tcp comment "nginx HTTPS und Signaling Proxy"
ufw allow 3478/tcp comment "coturn STUN und TURN"
ufw allow 3478/udp comment "coturn STUN und TURN"
ufw allow "${TURN_RELAY_MIN}:${TURN_RELAY_MAX}/udp" comment "coturn Media Relay"
########################################################################
# 4. ufw aktivieren
########################################################################
echo "--> Aktiviere ufw"
ufw --force enable
ufw status verbose
########################################################################
# 5. fail2ban konfigurieren
########################################################################
echo "--> Konfiguriere fail2ban"
IGNORE_IPS="127.0.0.1/8 ::1"
for ip in "${SSH_ALLOWED_IPS[@]}"; do
IGNORE_IPS="${IGNORE_IPS} ${ip}"
done
cat > /etc/fail2ban/jail.local <<EOF
[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
ignoreip = ${IGNORE_IPS}
backend = systemd
[sshd]
enabled = true
port = ${SSH_PORT}
[nginx-http-auth]
enabled = true
logpath = /var/log/nginx/error.log
[nginx-botsearch]
enabled = true
logpath = /var/log/nginx/access.log
# Wiederholungstäter (z.B. mehrfach in anderen Jails gebannt) langfristig sperren
[recidive]
enabled = true
bantime = 1w
findtime = 1d
maxretry = 5
logpath = /var/log/fail2ban.log
EOF
systemctl enable --now fail2ban
systemctl restart fail2ban
sleep 2
fail2ban-client status
########################################################################
# 6. Zusammenfassung
########################################################################
cat <<SUMMARY
============================================================================
Härtung abgeschlossen: $(date -Is)
============================================================================
ufw:
- Standard: eingehend blockiert, ausgehend erlaubt
- SSH (${SSH_PORT}/tcp): nur von ${SSH_ALLOWED_IPS[*]}
- 80/tcp, 443/tcp: öffentlich (nginx / Let's Encrypt)
- 3478/tcp+udp: öffentlich (STUN/TURN)
- ${TURN_RELAY_MIN}-${TURN_RELAY_MAX}/udp: öffentlich (TURN-Media-Relay)
- Alles andere: blockiert
Status jederzeit prüfen mit: ufw status verbose
fail2ban:
- sshd-Jail aktiv (zusätzliche Absicherung, falls die ufw-Regel je
entfernt/geändert wird)
- nginx-http-auth / nginx-botsearch-Jails aktiv (Schutz vor Scans/Bots
gegen den öffentlichen nginx-Reverse-Proxy)
- recidive-Jail sperrt Wiederholungstäter für 1 Woche
- Admin-IPs (${SSH_ALLOWED_IPS[*]}) sind von Bans ausgenommen
Status jederzeit prüfen mit: fail2ban-client status
Bestimmten Jail prüfen mit: fail2ban-client status sshd
WICHTIG:
- Bevor du dich abmeldest: In einer ZWEITEN Terminal-Sitzung testen, ob
SSH von einer der beiden erlaubten IPs noch funktioniert, bevor du die
aktuelle Sitzung schließt – sonst sperrst du dich ggf. aus, falls
SSH_ALLOWED_IPS falsch eingetragen wurde.
- Der TURN-Relay-Portbereich muss mit min-port/max-port in
/etc/turnserver.conf übereinstimmen. Ist dort nichts gesetzt, gilt der
coturn-Standardbereich (49152-65535), passend zu diesem Skript.
============================================================================
SUMMARY
- passe die Variable
SSH_ALLOWED_IPS=("1.2.3.4" "5.6.7.8")an deine IP-Adresse(n) an (wenn du eine fixe IP-Adresse besitzt), über die du ggf. den SSH-Zugriff auf deinen Server beschränken möchtest - setze die Berechtigung des Script auf „ausführbar“ und starte es
sudo chmod +x harden-hpb.sh
sudo ./harden-hpb.sh
Antworten