Bei Contabo wird Webmin — sofern als Option mitgebucht — standardmäßig auf einer automatisch generierten Subdomain (vmd<id>.contaboserver.net:10000) bereitgestellt, üblicherweise mit einem selbstsignierten Zertifikat. Das ist für den produktiven Betrieb ungeeignet, da Browser warnen und Anmeldedaten auf einem nicht vertrauenswürdig signierten Kanal eingegeben werden müssten. In dieser Anleitung sicherst du Webmin mit einem kostenlosen Let's Encrypt SSL-Zertifikat ab, machst es über deine eigene Domain erreichbar und setzt Nginx als Reverse Proxy davor — der saubere Standard-Aufbau.
Vorbereitung
Diese Anleitung setzt folgenden Stand voraus:
VPS:Du hast Zugriff auf deinen Contabo-VPS per SSH und kannstsudonutzen.Webmin:Webmin ist installiert (entweder als Contabo-Option oder manuell aus dem offiziellen Repository) und unterhttps://<contabo-host>:10000erreichbar.Nginx:Nginx ist installiert und läuft (systemctl status nginx).Certbot:Certbot wird im weiteren Verlauf installiert — du brauchst es noch nicht vorab.Domain:Du besitzt eine Domain und hast Zugriff auf deren DNS-Verwaltung.
Betriebssystem: Beispiele basieren auf Ubuntu 22.04/24.04 bzw. Debian 12. Auf anderen Distributionen weichen Paketnamen und Pfade leicht ab.
Firewall am Anfang: Solange du Webmin noch direkt aufrufen können musst, muss Port
10000(TCP) erreichbar sein. Sobald der Reverse Proxy fertig steht (Abschnitt 7), schließen wir Port10000wieder — Webmin lauscht dann nur noch auf Loopback.
DNS-Eintrag
Ziel: Webmin ist unter webmin.mydomain.com erreichbar (Domain natürlich durch deine ersetzen).
Damit das Routing funktioniert, muss ein A-Record für die Subdomain auf die öffentliche IPv4-Adresse deines VPS zeigen. Wenn dein Server eine IPv6-Adresse hat, kommt zusätzlich ein AAAA-Record dazu.
Beispiel-DNS-Konfiguration
| Typ | Name | Value/Ziel | TTL |
|---|---|---|---|
| A | webmin | 203.0.113.42 | 3600 |
| AAAA | webmin | 2001:db8::42 | 3600 |
| A | @ | 203.0.113.42 | 3600 |
| CNAME | www | mydomain.com. | 3600 |
Hinweis:
203.0.113.42und2001:db8::42sind Dokumentations-IPs (RFC 5737 / RFC 3849). Ersetze sie durch die tatsächliche IP deines VPS — diese findest du im Contabo-Kundenportal oder perip -4 addr showdirekt auf dem Server. Für Webmin selbst ist nur der A-Record (und ggf. AAAA) fürwebminzwingend; die übrigen Zeilen sind nur als Beispiel für eine vollständige Domain-Konfiguration aufgeführt.
A-Records nehmen ausschließlich IP-Adressen entgegen, niemals Domainnamen — dafür gibt es CNAME. Eine häufige Stolperfalle.
Nach dem Speichern dauert die DNS-Propagation je nach TTL und Provider zwischen Sekunden und einigen Stunden. Prüfen kannst du sie z. B. so:
dig +short webmin.mydomain.com A
# erwartet: 203.0.113.42Hostname und /etc/hosts (optional)
Damit der Server selbst seinen eigenen FQDN korrekt auflöst — was z. B. bei Hostname-basierten Logs, Mail-Versand und einigen Webmin-Selbstchecks praktisch ist — kann ein lokaler Eintrag in /etc/hosts sinnvoll sein.
Wichtig zur Klarstellung: /etc/hosts hat keinen Einfluss darauf, ob dein Server von außen über webmin.mydomain.com erreichbar ist. Das regelt ausschließlich der DNS-Eintrag aus Abschnitt 2. Der Hosts-Eintrag ist eine reine lokale Namensauflösung auf dem Server.
127.0.0.1 localhost
203.0.113.42 mydomain.com webmin.mydomain.com
Certbot installieren und Zertifikat ausstellen
Wir nutzen den Webroot-Authenticator von Certbot. Vorteil gegenüber --standalone: Nginx muss für die Ausstellung und für jedes Renewal nicht gestoppt werden — Certbot legt die ACME-Challenge einfach in einem Verzeichnis ab, das der laufende Webserver ausliefert.
1 — System aktualisieren
sudo apt update && sudo apt upgrade -y2 — Certbot installieren
sudo apt install certbot -yAuf älteren Systemen (Ubuntu 20.04 und davor) wird die Snap-Variante von Certbot empfohlen — siehe certbot.eff.org. Die Konfiguration im Folgenden bleibt identisch.
3 — Minimal-Vhost für die ACME-Challenge anlegen
Damit der Webroot-Authenticator funktioniert, braucht es einen Server-Block, der auf Port 80 für webmin.mydomain.com antwortet und das Verzeichnis /var/www/html ausliefert. Lege dafür /etc/nginx/sites-available/webmin.mydomain.com an:
server {
listen 80;
listen [::]:80;
server_name webmin.mydomain.com;
root /var/www/html;
}Symlink in sites-enabled setzen, Konfiguration prüfen und Nginx neu laden:
sudo ln -s /etc/nginx/sites-available/webmin.mydomain.com /etc/nginx/sites-enabled/
sudo nginx -t
sudo systemctl reload nginx4 — Zertifikat ausstellen
sudo certbot certonly --webroot -w /var/www/html -d webmin.mydomain.comCertbot fragt nach E-Mail und AGB-Zustimmung, legt eine ACME-Challenge unter /var/www/html/.well-known/acme-challenge/ ab und stellt das Zertifikat aus. Der Vhost bleibt dabei in dem minimalen Zustand aus Schritt 3 — den finalen Reverse-Proxy-Block schreiben wir gleich selbst (Abschnitt 6). Das hält die Konfiguration deterministisch und nicht durch Certbot-Manipulation überschrieben.
Die Zertifikatsdateien liegen anschließend hier:
/etc/letsencrypt/live/webmin.mydomain.com/fullchain.pem
/etc/letsencrypt/live/webmin.mydomain.com/privkey.pemRenewal läuft danach automatisch via systemd-Timer (systemctl list-timers | grep certbot); ein manueller Probe-Lauf:
sudo certbot renew --dry-runWebmin auf Loopback umstellen
Webmin bringt einen eigenen Mini-Webserver (miniserv) mit. Damit der Reverse Proxy davor sauber funktioniert, soll Webmin
- nur noch auf
127.0.0.1lauschen (nicht mehr öffentlich), - den ursprünglichen Port
10000behalten, - intern kein TLS mehr machen — Nginx terminiert HTTPS außen, intern reicht Klartext über Loopback.
Öffne /etc/webmin/miniserv.conf und passe folgende Werte an (bzw. ergänze sie):
bind=127.0.0.1
port=10000
ssl=0
# Webmin akzeptiert sonst nur Requests, deren Host-Header zur eigenen
# Erkennung passt — die Subdomain explizit erlauben:
referers=webmin.mydomain.com
# Damit Webmin X-Forwarded-Proto/Host vom Reverse Proxy auswertet
# und Redirects korrekt mit https:// erzeugt:
trust_x_forwarded=1Webmin neu starten:
sudo systemctl restart webminWarum nicht
port=443? Auf Port 443 lauscht ab Abschnitt 6 Nginx. Webmin auf 443 zu legen würde nur deshalb funktionieren, weil es ausschließlich an127.0.0.1bindet — verwirrend, fehleranfällig und ohne Vorteil. Der Standard ist: Webmin bleibt auf10000(intern), der Proxy regelt 443 nach außen.
Nginx als Reverse Proxy
Jetzt erweitern wir die in Abschnitt 4 angelegte Server-Konfiguration zu einem vollwertigen Reverse Proxy. Öffne /etc/nginx/sites-available/webmin.mydomain.com erneut und ersetze den Inhalt durch:
server {
listen 80;
listen [::]:80;
server_name webmin.mydomain.com;
# ACME-Challenges für Renewal weiter erlauben, alles andere
# auf HTTPS umleiten.
location /.well-known/acme-challenge/ {
root /var/www/html;
}
location / {
return 301 https://$host$request_uri;
}
}
server {
listen 443 ssl http2;
listen [::]:443 ssl http2;
server_name webmin.mydomain.com;
ssl_certificate /etc/letsencrypt/live/webmin.mydomain.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/webmin.mydomain.com/privkey.pem;
# Sicherheitsheader
add_header Strict-Transport-Security "max-age=31536000" always;
add_header X-Frame-Options "SAMEORIGIN" always;
add_header X-Content-Type-Options "nosniff" always;
# Webmin-Uploads (Module-Installer, Backups) brauchen Spielraum.
client_max_body_size 256M;
location / {
proxy_pass http://127.0.0.1:10000;
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;
proxy_set_header X-Forwarded-Proto $scheme;
# Webmin-Sessions sind teils langlebig (Updates, Backups).
proxy_read_timeout 300s;
}
}Konfiguration prüfen und neu laden:
sudo nginx -t
sudo systemctl reload nginxKein
includeSubDomainsund keinpreloadim HSTS-Header — beides verbietet sich, solange du nicht für alle Subdomains vonmydomain.comHTTPS garantieren kannst. Setze diese Flags erst bewusst, wenn du das gesamte Setup verstanden hast.
Firewall schließen und Setup verifizieren
Da Webmin nun ausschließlich über den Reverse Proxy erreichbar sein soll, kann (und sollte) Port 10000 nach außen geschlossen werden:
sudo ufw delete allow 10000/tcp # falls vorher freigegeben
sudo ufw status verboseAlternativ mit nftables oder iptables — entscheidend ist, dass 10000 von außen nicht mehr erreichbar ist. Test von einem anderen Host:
curl -v --max-time 5 https://<server-ip>:10000 # sollte Timeout werfen
curl -I https://webmin.mydomain.com # 200 OK + gültiges CertErwartetes Ergebnis
https://webmin.mydomain.comöffnet das Webmin-Login mit gültigem Let's-Encrypt-Zertifikat.- HTTP-Aufrufe (
http://...) werden auf HTTPS umgeleitet. - Direkter Zugriff auf
:10000von außen ist blockiert. - Renewal läuft per systemd-Timer alle 12 h, ohne dass Nginx oder Webmin manuell gestoppt werden müssen.
Damit ist der Webmin-Admin-Zugang produktionsreif aufgesetzt: korrekt signiert, hinter einem Reverse Proxy, nicht öffentlich exponiert und mit automatischer Zertifikatserneuerung.