Hochsicherheits-Komplettanleitung: Authentik & Nginx Proxy Manager unter Debian 13
- Zero-Trust-Prinzip: Niemand kommt an deine internen Dienste (wie Nextcloud, Vaultwarden oder Heimdall), ohne sich zuerst an deiner zentralen Authentik-Instanz auszuweisen.
- Echte Isolation: Durch ein geschlossenes Docker-Netzwerk und striktes Localhost-Binding sind deine Container von außen unsichtbar. Nur der Proxy filtert den echten Traffic.
- Zukunftssicher: Optimiert für das brandneue Debian 13 und vollgepackt mit automatischen Schutzmechanismen wie Fail2Ban und UFW.
⚠️ Wichtig vor dem Start: Diese Anleitung ist auf maximale Sicherheit ausgelegt. Befolge die Schritte exakt in der vorgegebenen Reihenfolge, um dich nicht selbst auszusperren. Schnapp dir einen Kaffee, öffne dein Terminal und lass uns dein System absichern!
Hochsicherheits-Komplettanleitung für dein Debian 13
Schritt 1: Vom nackten System zur Absicherung (SSH & User)
Logge dich als root per SSH auf deinen neuen VServer ein.
bash
root ein, erstelle einen Admin-User und sperre den Root-Zugriff per SSH.# 1. System auf den neuesten Stand bringen apt update && apt upgrade -y # 2. Neuen User anlegen (Ersetze 'deinuser' durch deinen Namen) adduser deinuser usermod -aG sudo deinuser # 3. SSH-Konfiguration anpassen sudo nano /etc/ssh/sshd_config # 4. Root-Login verbieten & SSH absichern # Wir erlauben den Login nur für deinen neuen User sed -i 's/#PermitRootLogin yes/PermitRootLogin no/' /etc/ssh/sshd_config sed -i 's/PermitRootLogin prohibit-password/PermitRootLogin no/' /etc/ssh/sshd_config # Diese Zeilen suchen und so anpassen (entferne das '#' falls vorhanden): PermitRootLogin no PasswordAuthentication yes # 5. SSH neu starten sudo systemctl restart ssh
Wichtig: Logge dich ab jetzt nur noch mit deinuser ein!
sudo apt update && sudo apt upgrade -y sudo apt install -y ufw fail2ban curl wget # Firewall-Regeln (SSH, Web & Admin-Ports) sudo ufw default deny incoming sudo ufw default allow outgoing sudo ufw allow ssh # WICHTIG: SSH erlauben, damit du dich nicht aussperrst! sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw allow 81/tcp # Für NPM Admin-Panel sudo ufw allow 9000/tcp # Für Authentik Erst-Setup per IP sudo ufw enable # Fail2Ban starten (schützt SSH sofort vor Brute-Force) sudo systemctl enable --now fail2ban
sudo nano /etc/fail2ban/jail.local [sshd] enabled = true maxretry = 3 findtime = 10m bantime = 1h sudo systemctl restart fail2ban
Kontrolle
sudo fail2ban-client status sshd
Du solltest sehen:
- aktive Jails
- gebannte IPs (falls vorhanden)
# Docker installieren curl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh # Das Netzwerk 'proxy-nw' manuell erstellen (löst deinen Start-Fehler) docker network create proxy-nw || true
mkdir -p ~/npm && cd ~/npm
cat <<EOF > docker-compose.yml
services:
app:
image: 'jc21/nginx-proxy-manager:latest'
container_name: npm-server
restart: unless-stopped
ports:
- '80:80'
- '443:443'
- '81:81'
volumes:
- ./data:/data
- ./letsencrypt:/etc/letsencrypt
networks:
- proxy-nw
networks:
proxy-nw:
external: true
EOF
docker compose up -d
:latest und legen den container_name fest, damit NPM ihn findet.mkdir -p ~/authentik/media && cd ~/authentik
sudo chown -R 1000:1000 ~/authentik/media
# Passwörter generieren (NUR wenn .env nicht existiert)
if [ ! -f .env ]; then
echo "PG_PASS=$(openssl rand -base64 36 | tr -d '\n')" > .env
echo "AUTHENTIK_SECRET_KEY=$(openssl rand -base64 60 | tr -d '\n')" >> .env
echo "AUTHENTIK_STORAGE__MEDIA__BACKEND=local" >> .env
echo "AUTHENTIK_STORAGE__MEDIA__PATH=/data/media" >> .env
fi
cat <<EOF > docker-compose.yml
services:
postgresql:
image: docker.io/library/postgres:16-alpine
restart: unless-stopped
volumes: [ './database:/var/lib/postgresql/data' ]
environment:
POSTGRES_PASSWORD: \${PG_PASS}
POSTGRES_USER: authentik
POSTGRES_DB: authentik
networks: [ 'proxy-nw' ]
server:
image: ghcr.io/goauthentik/server:2026.2.3
container_name: authentik-server
restart: unless-stopped
command: server
environment:
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: \${PG_PASS}
AUTHENTIK_SECRET_KEY: \${AUTHENTIK_SECRET_KEY}
volumes:
- ./media:/data/media
ports: [ "9000:9000" ]
depends_on: [ postgresql ]
networks: [ 'proxy-nw' ]
worker:
image: ghcr.io/goauthentik/server:2026.2.3
restart: unless-stopped
command: worker
environment:
AUTHENTIK_POSTGRESQL__HOST: postgresql
AUTHENTIK_POSTGRESQL__USER: authentik
AUTHENTIK_POSTGRESQL__NAME: authentik
AUTHENTIK_POSTGRESQL__PASSWORD: \${PG_PASS}
AUTHENTIK_SECRET_KEY: \${AUTHENTIK_SECRET_KEY}
user: root
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./media:/data/media
depends_on: [ postgresql ]
networks: [ 'proxy-nw' ]
networks:
proxy-nw:
external: true
EOF
docker compose up -d
- Fehler "Password Authentication failed":
Nur beim allerersten Setup erlaubt! Führt im Betrieb zu totalem Datenverlust!".
docker compose down && sudo rm -rf ~/authentik/database && docker compose up -d
Upload-Button fehlt:
Stelle sicher, dass in NPM für auth.domain.at der Websockets Support auf AN steht. Nutze im Browser den Inkognito-Modus (Cache-Problem).
Das Update-Verfahren (Zukunft)
- In den Ordner gehen:
cd ~/authentik - Datenbank sichern:
docker compose exec postgresql pg_dump -U authentik authentik > backup_$(date +%F).sql - Version anpassen:
nano docker-compose.yml(Versionsnummer oben ändern). - Update starten:
bash
docker compose pull docker compose up -d
Logs prüfen: docker logs -f authentik-server (Warten bis "Listening at..." erscheint).
Notfall-Fix bei Fehlern (z.B. "Password authentication failed")
- Stoppen:
cd ~/authentik && docker compose down - Löschen:
sudo rm -rf ~/authentik/database(Setzt die Datenbank zurück). - Starten:
docker compose up -d(Warte 2-3 Minuten).
- Warten: Authentik braucht 1-3 Minuten zum Starten.
- Initial Setup: Öffne im Browser
http://DEINE-SERVER-IP:9000/if/flow/initial-setup/und erstelle den Adminakadmin. - NPM Konfiguration: Öffne
http://DEINE-IP:81. Erstelle Proxy Host fürauth.domain.at:- Details: Forward Host:
authentik-server| Port:9000| Websockets Support: AN ✅ - SSL: Request new | Force SSL: AN ✅ | HTTP/2: AN ✅ | HSTS Enabled: AN ✅
- Details: Forward Host:
# Ports in der Firewall schließen sudo ufw delete allow 81/tcp sudo ufw delete allow 9000/tcp # Logo ändern # Im Authentik Admin Interface -> System -> Settings -> Branding Settings.
cd ~/authentik docker compose run --rm server set_password akadmin "DEIN_NEUES_PASSWORT"
Zwei-Faktor-Authentisierung (2FA) abzusichern. Damit ist dein Server selbst dann geschützt, wenn jemand dein Passwort stiehlt.
- Logge dich unter
https://auth.domain.atein. - Klicke oben rechts auf Admin Interface.
- Gehe links im Menü auf Authentifizierung -> Stages.
- Klicke auf Erstellen und wähle TOTP Authenticator Stage.
- Name:
default-totp-setup - Klicke auf Speichern.
- Gehe links auf Authentifizierung -> Flows.
- Klicke auf den Flow
default-authentication-flow. - Klicke oben auf den Reiter Stage Bindings.
- Klicke auf Stage binden.
- Wähle bei Stage deine neue
default-totp-setup. - Setze die Order (Reihenfolge) auf 30 (damit sie nach der Passwortabfrage kommt).
- Klicke auf Speichern.
- Klicke ganz oben rechts auf dein Profilbild (oder den Buchstaben) und wähle User Settings.
- Suche den Bereich Multi-Faktor-Authentisierung.
- Klicke bei Authenticator App (TOTP) auf Register.
- Scanne den angezeigten QR-Code mit deiner Authenticator-App auf dem Handy.
- Gib den 6-stelligen Code zur Bestätigung ein.
Schritt 9: Backup (Sicherung)
cd ~/authentik
# 1. Datenbank dumpen
docker compose exec postgresql pg_dump -U authentik authentik > db_backup.sql
# 2. Alles in ein Archiv packen
tar -czvf authentik_backup.tar.gz .env docker-compose.yml db_backup.sql media/
Schritt 10: Umzug (Migration auf neuen Server)
- Auf neuem Server: Docker installieren &
docker network create proxy-nw. mkdir ~/authentik && cd ~/authentik.authentik_backup.tar.gzhochladen und entpacken:tar -xzvf authentik_backup.tar.gz.- Datenbank wiederherstellen:
docker compose up -d postgresql sleep 10 cat db_backup.sql | docker compose exec -T postgresql psql -U authentik authentik
Alles starten: docker compose up -d.
Schritt 11: Notfall-Befehle
- Passwort-Reset:
docker compose run --rm server set_password akadmin "NEUES_PW" - Update:
cd ~/authentik && docker compose pull && docker compose up -d
mkdir -p ~/backups/authentik
nano ~/backup_authentik.sh
Kopiere diesen Inhalt hinein:
#!/bin/bash # Variablen BACKUP_DIR="$HOME/backups/authentik" TIMESTAMP=$(date +"%Y-%m-%d_%H-%M") BACKUP_NAME="authentik_backup_$TIMESTAMP.tar.gz" echo "--- Backup gestartet: $(date) ---" # 1. In das authentik Verzeichnis wechseln cd "$HOME/authentik" || exit # 2. Datenbank-Dump erstellen docker compose exec -T postgresql pg_dump -U authentik authentik > db_backup.sql # 3. Alles in ein Archiv packen (.env, docker-compose, DB-Dump und Media-Ordner) tar -czvf "$BACKUP_DIR/$BACKUP_NAME" .env docker-compose.yml db_backup.sql media/ # 4. Aufräumen: Datenbank-Dump nach dem Packen löschen rm db_backup.sql # 5. Alte Backups löschen (behält nur die letzten 7 Tage) find "$BACKUP_DIR" -type f -mtime +7 -name "*.tar.gz" -delete echo "--- Backup abgeschlossen: $BACKUP_NAME ---"
chmod +x ~/backup_authentik.sh
- Öffne die Cron-Tabelle:
crontab -e - Scrolle ganz nach unten und füge diese Zeile ein:
0 3 * * * /bin/bash /home/deinuser/backup_authentik.sh >> /home/deinuser/backups/authentik/backup_log.txt 2>&1
(Ersetze deinuser durch deinen echten Benutzernamen!)
- Vollautomatisch: Du musst dich um nichts mehr kümmern.
- Datenbank-Sicherung: Es zieht die aktuellen Daten direkt aus dem laufenden Container.
- Platzsparend: Es löscht automatisch Sicherungen, die älter als 7 Tage sind, damit deine Festplatte nicht vollläuft.
- Protokoll: Fehler oder Erfolgsmeldungen werden in
backup_log.txtgespeichert.
~/backups gelegentlich auf deinen heimischen PC zu kopieren, falls der gesamte VServer einmal ausfällt.Wenn der Nginx Proxy Manager (NPM) vor Authentik geschaltet ist und weiterhin 172.18.0.1 angezeigt wird, liegt das daran, dass NPM die IP-Adresse nicht korrekt per HTTP-Header weitergibt oder Authentik die IP des NPM-Containers selbst nicht vertraut.
- Öffne die Weboberfläche des Nginx Proxy Managers.
- Bearbeite den Proxy Host für deine Authentik-Domain.
- Wechsel auf den Reiter Advanced.
- Füge dort den folgenden Code-Block ein, um die Weitergabe der echten Client-IP zu erzwingen:
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; # Deaktiviert das Überschreiben der Header durch NPM-Standardeinstellungen proxy_hide_header X-Forwarded-For; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- Klicke auf Save.
- Wenn NPM die IP an Authentik weitergibt, sieht Authentik die interne Container-IP von NPM.
- Daher musst du in der
.env-Datei von Authentik sicherstellen, dass Authentik dem gesamten Docker-Bereich vertraut.
.env-Datei von Authentik und stelle sicher, dass diese Zeile exakt so existiert:AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRS=127.0.0.1/32,172.16.0.0/12,192.168.0.0/16,10.0.0.0/8
docker compose up -d --force-recreate
.env-Einstellung immer noch die 172.18.0.1 angezeigt wird, liegt das an einer bekannten Docker-Besonderheit: 172.18.0.1 und kann keine echte IP an Authentik weiterreichen. - Öffne die Docker-Konfigurationsdatei auf deinem Host-Server (z. B. via SSH):
sudo nano /etc/docker/daemon.json
Falls die Datei leer ist, füge diesen Inhalt ein:
{
"userland-proxy": false
}
- (Falls dort schon andere Einstellungen stehen, füge
"userland-proxy": falseeinfach mit einem Komma getrennt hinzu). - Speichere die Datei (
Strg+O,Enter) und schließe sie (Strg+X). - Starte den Docker-Dienst neu, um die Änderung zu aktivieren:
sudo systemctl restart docker
tarte danach deine Authentik- und NPM-Container einmal neu.
userland-proxy deaktiviert hast, musst du Docker explizit anweisen, wie es mit IPv6-Verkehr innerhalb des Container-Netzwerks umgehen soll, damit die echte IPv6-Quell-IP am NPM ankommt. daemon.json durch diese Variante:{
"userland-proxy": false,
"ipv6": true,
"fixed-cidr-v6": "fd00::/80",
"ip6tables": true
}
fixed-cidr-v6: Weist Docker an, internen Containern private IPv6-Adressen (ULA) zuzuweisen.ip6tables: Sorgt dafür, dass Docker die Firewall-Regeln für IPv6 analog zu IPv4 automatisch verwaltet.
.env erweiternAUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRS in deiner .env-Generierung um den IPv6-Loopback (::1/128) sowie das interne Docker-IPv6-Netzwerk (fd00::/8): AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRS=127.0.0.1/32,172.16.0.0/12,192.168.0.0/16,10.0.0.0/8,::1/128,fd00::/8
Wichtig für NPM Schritt 4 (Das Docker-Netzwerk)
proxy-nw) kein IPv6 zu, selbst wenn es in der daemon.json global aktiviert ist. Damit NPM auch IPv6-Pakete intern verarbeiten und routen kann, sollte das Netzwerk in deiner Doku explizit als Dual-Stack definiert sein: proxy-nw jedoch als external: true definiert ist, musst du sicherstellen, dass du es auf dem Host einmalig mit dem IPv6-Flag erstellt hast.# Das Netzwerk 'proxy-nw' explizit mit IPv4 und IPv6 (Dual-Stack) erstellen docker network create --ipv6 proxy-nw || true
- Ein IPv6-Client ruft deine Domain auf.
- Dank
"userland-proxy": falseleitet der Linux-Kernel das Paket inklusive der echten IPv6-Quelladresse direkt an den NPM-Container weiter. - NPM verarbeitet die Anfrage. Deine eingetragenen Header-Zeilen schreiben die echte IPv6-Adresse in den
X-Forwarded-For-Header. - NPM schickt die Anfrage über das dual-stack-fähige
proxy-nwan Authentik weiter. - Authentik empfängt das Paket. Da am Ende deiner
AUTHENTIK_LISTEN__TRUSTED_PROXY_CIDRSnun::1/128,fd00::/8steht, akzeptiert Authentik den Header und schreibt die echte IPv6-Adresse des Besuchers in die Logs.

