Skip to content

Teil 2 – ISPConfig hinter Nginx auf Port 443 (Reverse-Proxy) mit Rettungszugang & Basic Auth & saubere Zertifikate

Last updated on 17. August 2025

Voraussetzung: Teil 1 ist erledigt (FQDN + PTR stehen stabil, Hostname fix, Basis-Installation mit Nginx). Siehe: Teil 1 – Saubere FQDN/PTR-Konfiguration & Grundinstallation von ISPConfig mit Nginx.

Teil 1 Link: Teil 1 — Saubere FQDN/PTR-Konfiguration & Grundinstallation von ISPConfig (mit NGINX)

Zielbild

  • Öffentlich nur Port 443 am Hostnamen server.example.com
  • ISPConfig (Backend) weiter auf https://127.0.0.1:8080
  • phpMyAdmin (optional) auf https://127.0.0.1:8081/phpmyadmin
  • Rettungszugang auf Port 4443 (nur per IP & Self-Signed, damit HSTS/Cache dich nicht aussperrt)

Ersetze im Folgenden:

  • server.example.com → dein Panel-Hostname
  • <DEINE_IPV4> / <DEINE_IPV6> → deine Server-IP(s)

1) Rettungszugang auf 4443 (Self-Signed, SAN=IP)

1.1 Self-Signed Zertifikat für die IP erzeugen

sudo mkdir -p /etc/nginx/selfsigned

sudo tee /etc/nginx/selfsigned/rescue-openssl.cnf >/dev/null <<'EOF'
[req]
distinguished_name = dn
x509_extensions = v3_req
prompt = no
[dn]
CN = <DEINE_IPV4>
[v3_req]
subjectAltName = @alt_names
[alt_names]
IP.1 = <DEINE_IPV4>
# Falls IPv6:
# IP.2 = <DEINE_IPV6>
EOF

sudo openssl req -x509 -newkey rsa:2048 -nodes -days 7 \
  -keyout /etc/nginx/selfsigned/rescue.key \
  -out /etc/nginx/selfsigned/rescue.crt \
  -config /etc/nginx/selfsigned/rescue-openssl.cnf

# Prüfen:
openssl x509 -in /etc/nginx/selfsigned/rescue.crt -noout -subject -ext subjectAltName

1.3 Test

sudo ss -ltnp | grep ':4443'
openssl s_client -connect <DEINE_IPV4>:4443 </dev/null | openssl x509 -noout -subject -ext subjectAltName
curl -vkI https://<DEINE_IPV4>:4443/

Im Browser: https://<DEINE_IPV4>:4443/ → Self-Signed Warnung bestätigen → Anmeldeseite von ISPConfig sehen.


2) Produktiv-VHost für 80/443 (Host: server.example.com)

2.1 ACME-Pfad (HTTP-01) & Redirect

sudo tee /etc/nginx/sites-available/server-panel.conf >/dev/null <<'EOF'
# :80 → Redirect auf :443 + ACME http-01
server {
  listen <DEINE_IPV4>:80;
  listen [::]:80;
  server_name server.example.com;

  # ACME http-01 ohne Auth
  location ^~ /.well-known/acme-challenge/ {
    auth_basic off;
    root /var/www/letsencrypt;
    default_type text/plain;
    try_files $uri =404;
  }

  return 301 https://$host$request_uri;
}

# :443 → Reverse Proxy auf ISPConfig / phpMyAdmin
server {
  listen <DEINE_IPV4>:443 ssl http2;
  # listen [<DEINE_IPV6>]:443 ssl http2;
  server_name server.example.com;

  ssl_protocols TLSv1.2 TLSv1.3;
  ssl_certificate     /etc/letsencrypt/live/server.example.com/fullchain.pem;
  ssl_certificate_key /etc/letsencrypt/live/server.example.com/privkey.pem;

  # Security-Header (HSTS erst später aktivieren)
  # add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;

  add_header X-Frame-Options DENY always;
  add_header X-Content-Type-Options nosniff always;
  add_header Referrer-Policy strict-origin-when-cross-origin always;
  add_header Permissions-Policy "geolocation=(), microphone=(), camera=()" always;

  # globales Basic Auth (ACME ausgenommen)
  auth_basic "Restricted";
  auth_basic_user_file /etc/nginx/.htpasswd;

  location ^~ /.well-known/acme-challenge/ {
    auth_basic off;
    root /var/www/letsencrypt;
    default_type text/plain;
    try_files $uri =404;
  }

  # ISPConfig (Backend)
  location / {
    proxy_pass https://127.0.0.1:8080/;
    proxy_ssl_verify off;
    proxy_ssl_server_name on;
    proxy_set_header Host              $host;
    proxy_set_header X-Forwarded-For   $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
    proxy_read_timeout 120s;
  }

  # phpMyAdmin (optional)
  location ^~ /phpmyadmin/ {
    proxy_pass https://127.0.0.1:8081/phpmyadmin/;
    proxy_ssl_verify off;
    proxy_ssl_server_name on;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto https;
  }
}
EOF

sudo ln -sf /etc/nginx/sites-available/server-panel.conf /etc/nginx/sites-enabled/server-panel.conf
sudo nginx -t && sudo systemctl reload nginx

Tipp: Falls du Cloudflare nutzt, setze den DNS-Eintrag server.example.com für die Ausstellung zunächst auf “DNS only” (Graue Wolke). Nach erfolgreichem Zertifikat kannst du ggf. wieder “Proxy” (Orange) aktivieren.

2.2 Zertifikat ausstellen / prüfen

Wenn noch kein Zertifikat existiert:

# Webroot muss mit dem ACME-Block übereinstimmen
sudo mkdir -p /var/www/letsencrypt
sudo certbot certonly --webroot -w /var/www/letsencrypt -d server.example.com

# Dry-Run der Erneuerung
sudo certbot renew --dry-run

Prüfen, ob der richtige Zertifikatsname ausgeliefert wird:

openssl s_client -connect <DEINE_IPV4>:443 -servername server.example.com </dev/null \
  | openssl x509 -noout -subject -ext subjectAltName

3) Direktzugang abdichten (Ports 8080/8081 nur intern)

Laufen dürfen 8080/8081 weiterhin – aber nur lokal erreichbar.

# Wenn UFW aktiv ist (Beispiel):
sudo ufw deny in proto tcp to any port 8080
sudo ufw deny in proto tcp to any port 8081

# Lauschen prüfen:
sudo ss -ltnp | egrep ':443|:8080|:8081'

4) HSTS & Monitoring

4.1 HSTS erst aktivieren, wenn alles stabil

# im :443-Serverblock ergänzen (und Nginx neu laden)
add_header Strict-Transport-Security "max-age=15552000; includeSubDomains" always;

Achtung: Mit includeSubDomains zwingst du alle Subdomains auf HTTPS. Nur aktivieren, wenn du das willst.

Header prüfen:

curl -sI https://server.example.com | grep -i strict-transport-security

4.2 Kleines Fingerprint-Monitoring (Frühwarnung)

FPR_EXPECTED=$(openssl x509 -in /etc/letsencrypt/live/server.example.com/cert.pem -noout -fingerprint -sha256|cut -d= -f2)
FPR_LIVE=$(openssl s_client -connect <DEINE_IPV4>:443 -servername server.example.com </dev/null 2>/dev/null \
  | openssl x509 -noout -fingerprint -sha256 | cut -d= -f2)

[ "$FPR_EXPECTED" = "$FPR_LIVE" ] || echo "ALERT: TLS mismatch" | mail -s "server.example.com TLS" admin@example.com

Als Cronjob (z. B. stündlich) eintragen.


5) (Optional) Cloudflare richtig setzen

  • Für die Zertifikatsausstellung server.example.com DNS only.
  • Danach ggf. Proxy aktivieren und im CF-Dashboard SSL/TLS = Full (Strict) wählen.
  • Falls du Cloudflare häufig nutzt: Real-IP im Nginx sauber konfigurieren (siehe Teil 1/ggf. eigener Artikel).

6) Aufräumen: Rescue wieder schließen

Wenn alles sauber läuft:

sudo ufw delete allow 4443/tcp
sudo rm -f /etc/nginx/sites-enabled/000-rescue-4443.conf
sudo rm -f /etc/nginx/sites-available/000-rescue-4443.conf
sudo rm -f /etc/nginx/selfsigned/rescue.crt /etc/nginx/selfsigned/rescue.key /etc/nginx/selfsigned/rescue-openssl.cnf
sudo nginx -t && sudo systemctl reload nginx

7) Troubleshooting (häufige Stolpersteine)

7.1 Falsches Zertifikat wird ausgeliefert

  • Mehrere server_name server.example.com in unterschiedlichen VHosts.
    → Mit sudo nginx -T | grep -n "server_name server.example.com" prüfen und Duplikate entfernen.
  • Ein anderer VHost lauscht auf listen 443 als default_server oder auf 0.0.0.0:443 und matched zuerst.
    → Produktiv-VHost an die konkrete IP binden (listen <DEINE_IPV4>:443 ssl http2;).

7.2 ACME schlägt fehl (404 im Challenge-Pfad)

  • /.well-known/acme-challenge/ muss ohne Basic Auth erreichbar sein.
  • root /var/www/letsencrypt; stimmt? Datei per curl testweise abrufen.

7.3 HSTS sperrt dich aus

  • Nutze den Rescue-Port 4443 per IP (Self-Signed) – kein HSTS, keine Host-Caches.
  • Danach Ursache beseitigen (siehe 7.1) und normal über Hostname testen.

7.4 Werkzeuge für die Diagnose

# Vollständige geladene Nginx-Konfiguration
sudo nginx -T

# Zertifikatsdetails via SNI
openssl s_client -connect <DEINE_IPV4>:443 -servername server.example.com </dev/null \
  | openssl x509 -noout -subject -issuer -dates -ext subjectAltName

# DNS umgehen und Hostname → IP erzwingen
curl -vk --resolve server.example.com:443:<DEINE_IPV4> https://server.example.com/ -I

8) Zertifikate automatisch erneuern (Certbot/Timer)

  • Prüfen:
systemctl status certbot.timer
sudo certbot certificates
sudo certbot renew --dry-run
  • Alte/obsolet gewordene Zertifikate entfernen (z. B. ehemaliges panel.example.com):
sudo certbot delete --cert-name panel.example.com

Fazit

Mit diesem Setup hängt ISPConfig sicher hinter Nginx auf Port 443, direkte Backend-Ports bleiben intern, HSTS schützt (auf Wunsch) Browser-Clients und über den IP-Rescue-Port 4443 kommst du im Notfall jederzeit wieder ins Panel.

Wenn du willst, erstelle ich dir noch Meta-Title/Description & Tags für Teil 2 – oder passe den Text an deinen Stil an.

Weiterführende Quellen

Hinweis:: Wenn dir dieser Beitrag gefallen oder geholfen hat, kannst du mich gerne mit einer kleinen Unterstützung motivieren 😊

☕ Buy me a coffee

💙 Support via PayPal

₿/Ξ: Donate with Bitcoin
Address: bc1qt7wc6jfth4t2szc2hp6340sqp3y0pa9r3ywgrr

Show QR codeCrypto QR code
Published inAllgemeinLinux & SerververwaltungNginx & Hosting Know-HowPHP & MySQL Notizen

Schreib den ersten Kommentar

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert