Back to Blog

VPS Security Setup

Bevor newwaves.dev auf meinem VPS gelandet ist, wollte ich den Server nicht einfach offen ins Internet stellen. Also habe ich zuerst eine kleine Basis-Härtung gemacht: weniger Angriffsfläche, kein Root-Login per SSH, keine Passwort-Logins und nur die Ports offen, die wirklich gebraucht werden.

Das ist keine perfekte Security-Architektur und ersetzt kein professionelles Hardening. Für meinen kleinen Ubuntu-VPS war es aber ein sinnvoller Mindeststandard, bevor die Website live ging.

Erst aktualisieren, dann bauen

Direkt nach dem Einrichten des VPS habe ich das System aktualisiert. Das ist unspektakulär, aber wichtig: bevor Website, SSH-Konfiguration und Webserver dazukommen, möchte ich nicht auf einem alten Paketstand weiterarbeiten.

sudo apt update
sudo apt upgrade -y

Danach war der VPS technisch noch nicht fertig, aber bereit für die nächsten Schritte. Erst auf dieser Basis habe ich SSH und Firewall sauber eingerichtet.

Nicht dauerhaft als Root arbeiten

Der erste echte Schritt war ein eigener Admin-User. Root ist praktisch, aber als täglicher Login unnötig riskant. Deshalb bekommt der normale User sudo-Rechte und arbeitet nur dann administrativ, wenn es wirklich gebraucht wird.

adduser USERNAME
usermod -aG sudo USERNAME

Wichtig war danach, den SSH-Key sauber für den neuen User zu hinterlegen und die Dateirechte zu setzen. Gerade bei SSH sind falsche Rechte schnell der Grund, warum ein Login nicht funktioniert.

mkdir -p /home/USERNAME/.ssh
cp /root/.ssh/authorized_keys /home/USERNAME/.ssh/
chown -R USERNAME:USERNAME /home/USERNAME/.ssh
chmod 700 /home/USERNAME/.ssh
chmod 600 /home/USERNAME/.ssh/authorized_keys

SSH absichern

Danach habe ich den direkten Root-Login per SSH deaktiviert und Passwort-Logins abgeschaltet. Der Server soll per SSH-Key erreichbar sein, aber nicht per Passwort durchprobiert werden können.

PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
KbdInteractiveAuthentication no

Nach Änderungen an der SSH-Konfiguration prüfe ich die Config, bevor der Dienst neu geladen wird. Das ist wichtig, weil man sich sonst im schlechtesten Fall selbst aussperrt.

sudo sshd -t
sudo systemctl reload ssh
sudo sshd -T | grep -Ei 'permitrootlogin|passwordauthentication|pubkeyauthentication|kbdinteractiveauthentication'

Zusätzlich läuft SSH nicht mehr auf dem Standardport 22, sondern auf einem eigenen Port. Das ist keine echte Sicherheitsmaßnahme wie SSH-Key-only, aber es reduziert stumpfes Bot-Rauschen auf Port 22.

Port <SSH_PORT>

Firewall aktivieren

Danach kam UFW. Die Grundregel ist simpel: eingehend wird alles blockiert, ausgehend ist erlaubt. Danach werden nur die Ports freigegeben, die der Server wirklich braucht.

sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow <SSH_PORT>/tcp comment 'SSH'
sudo ufw allow 80/tcp comment 'HTTP'
sudo ufw allow 443/tcp comment 'HTTPS'
sudo ufw enable

Beim Kontrollblick zwei Wochen später sah der relevante Teil weiterhin so aus:

Status: active
Default: deny (incoming), allow (outgoing), disabled (routed)

<SSH_PORT>/tcp  ALLOW IN  Anywhere      # SSH
80/tcp          ALLOW IN  Anywhere      # HTTP
443/tcp         ALLOW IN  Anywhere      # HTTPS

Automatische Sicherheitsupdates

Ich möchte nicht daran denken müssen, jede kleine Security-Aktualisierung sofort manuell einzuspielen. Dafür läuft unattended-upgrades. Es ersetzt keine regelmäßige Kontrolle, aber es reduziert das Risiko vergessener Sicherheitsupdates.

sudo apt install unattended-upgrades -y
sudo dpkg-reconfigure -plow unattended-upgrades
sudo systemctl status unattended-upgrades --no-pager

Beim späteren Check lief der Dienst aktiv und es gab keine offenen Paketupdates:

unattended-upgrades.service: active (running)

apt list --upgradable
Listing... Done

Fail2ban als Zusatzschutz

Fail2ban ist für SSH ein sinnvoller zusätzlicher Schutz. Wenn eine IP wiederholt an der Anmeldung scheitert, kann sie automatisch gebannt werden. Bei SSH-Key-only sollte das im Normalfall kaum passieren, aber als zweite Schicht ist es trotzdem nützlich.

[DEFAULT]
bantime = 1h
findtime = 10m
maxretry = 5
backend = systemd

[sshd]
enabled = true
port = <SSH_PORT>

Der aktuelle Status war unauffällig: ein aktives Jail für SSH, keine aktuell fehlgeschlagenen Logins und keine gebannten IPs.

Number of jail: 1
Jail list: sshd

Currently failed: 0
Currently banned: 0

Offene Ports prüfen

Nach Firewall und SSH-Konfiguration reicht es nicht, nur die Regeln anzuschauen. Ich will auch sehen, welche Dienste wirklich lauschen. Dafür ist ss -tulpn praktisch.

ss -tulpn

Beim Check waren die erwarteten Dienste sichtbar: Webserver auf 80 und 443, SSH auf dem eigenen Port und lokale DNS-Resolver auf 127.0.0.53 bzw. 127.0.0.54. Keine unerwarteten offenen Dienste.

Kontrolle nach zwei Wochen

Einige Zeit nach dem Setup habe ich den Server nochmal geprüft. Das war kein Pentest, sondern ein einfacher Reality Check: läuft die Firewall, sind Dienste kaputt, gibt es Updates, und arbeitet Fail2ban noch?

sudo ufw status verbose
sudo systemctl --failed
ss -tulpn
sudo fail2ban-client status sshd
sudo systemctl status unattended-upgrades --no-pager
apt list --upgradable
  • UFW war aktiv.
  • Es gab keine fehlgeschlagenen systemd-Units.
  • Fail2ban lief mit dem SSH-Jail.
  • Automatische Sicherheitsupdates liefen.
  • Es gab keine offenen Paketupdates.
  • Die sichtbaren Ports passten zum geplanten Setup.

Meine Checkliste

Für zukünftige VPS-Setups bleibt für mich diese kleine Reihenfolge hängen:

  • System direkt aktualisieren.
  • Eigenen Admin-User statt Root nutzen.
  • SSH-Key für den Admin-User einrichten.
  • Root-Login per SSH deaktivieren.
  • Passwort-Login per SSH deaktivieren.
  • Firewall aktivieren und nur nötige Ports öffnen.
  • Automatische Sicherheitsupdates aktivieren.
  • Fail2ban für SSH einrichten.
  • Offene Ports und fehlerhafte Dienste regelmäßig prüfen.

Das Setup macht den VPS nicht unangreifbar, aber es nimmt die offensichtlichsten Risiken aus dem Weg. Für meine kleine statische Website ist das genau der richtige Startpunkt: erst eine saubere Basis, dann deployen.