Je server hardenen
Beveilig een verse Ubuntu-VPS in een kwartier - eigen gebruiker, SSH alleen met sleutel, firewall, automatische updates en een backup die je echt hebt teruggezet.
In het kort
Hardenen betekent: inloggen alleen met een SSH-sleutel en niet als root, alleen de poorten open die je nodig hebt (ufw plus de gratis Hetzner Cloud Firewall), automatische beveiligingsupdates en een geteste backup. Controleer SSH altijd met `sudo sshd -T`, want op Ubuntu kan `50-cloud-init.conf` wachtwoord-login stilletjes aan laten staan. Docker zet poorten langs ufw heen open, dus bind containers aan 127.0.0.1.
- Gemiddeld
- 8 min
- gratis (op je bestaande VPS)
- Feiten gecheckt op 2 november 2026
- Voltooid
Een nieuwe server wordt binnen minuten gescand. Bij Unit 42 was 80% van 320 honeypots binnen 24 uur gecompromitteerd (2021), en één SANS-honeypot telde ruim 20 miljoen SSH-pogingen in zo’n 100 dagen (juni 2026).
Na deze les heb je: een Ubuntu-VPS (24.04 of 26.04 LTS) waar je alleen met je sleutel inkomt, met twee firewalls, automatische updates en een backup die je hebt teruggezet.
Waarom dit voor AI-servers extra telt
Section titled “Waarom dit voor AI-servers extra telt”Aanvallers scannen niet zelf: ze zoeken in indexen als Shodan op standaardpoorten. Zo liggen AI-tools massaal open:
| Tool | Wat er openlag | Bron |
|---|---|---|
| Ollama | 12.269 servers zonder login, waarvan 1.004 bij Hetzner | LeakIX, feb 2026 |
| n8n | 103.476 mogelijk kwetsbaar voor CVE-2025-68613 | Censys, dec 2025 |
| OpenClaw | ruim 17.500 kwetsbaar voor CVE-2026-25253 | Hunt.io, feb 2026 |
En het hoeft niet via SSH. In december 2025 werd een Hetzner-server een Monero-miner via een kwetsbare Umami-container. De eigenaar merkte het pas na ±10 dagen, door een abuse-mail van Hetzner.
Stap voor stap
Section titled “Stap voor stap”Werk als root op je verse server (zie SSH-sleutels maken). Vervang jij overal door je eigen gebruikersnaam.
-
Zet de Hetzner Cloud Firewall aan. In de Hetzner Console: Firewalls → Create Firewall. Inbound: alleen TCP 22 vanaf je eigen IP-adres, en TCP 80/443 als je een website draait. Koppel hem bij Apply to aan je server. Hij is gratis en zit buiten je server. Verandert je thuis-IP later, dan kom je via de Hetzner Console weer binnen.
-
Update alles.
Terminal window apt update && apt full-upgrade -y -
Maak je eigen gebruiker met sudo-rechten.
adduservraagt een wachtwoord: dat heb je straks nodig voorsudo, niet om in te loggen.Terminal window adduser jijTerminal window usermod -aG sudo jij -
Geef die gebruiker je SSH-sleutel. Dit kopieert de map
.sshvan root, met jeauthorized_keys.Terminal window cp -r /root/.ssh /home/jij/ && chown -R jij:jij /home/jij/.ssh -
Test in een tweede terminal. Laat de root-sessie openstaan.
Terminal window ssh jij@<JOUW-SERVER-IP>Terminal window sudo whoamiNa je sudo-wachtwoord:
Dit zou je nu moeten zien
rootOp Ubuntu 26.04 is sudo vervangen door sudo-rs: de vraag heet dan
[sudo: authenticate] Password:en je ziet sterretjes terwijl je typt. -
Zet SSH op alleen-sleutel, zonder root. Je eigen bestand begint met
00-, zodat het vóór50-cloud-init.confgelezen wordt.Terminal window sudo tee /etc/ssh/sshd_config.d/00-hardening.conf >/dev/null <<'EOF'PasswordAuthentication noKbdInteractiveAuthentication noPermitRootLogin noAllowUsers jijEOF -
Controleer wat er écht geldt.
sshd -tcheckt de syntax,sshd -Ttoont de uiteindelijke waarden.Terminal window sudo sshd -tTerminal window sudo sshd -T | grep -Ei 'passwordauthentication|permitrootlogin'Dit zou je nu moeten zien
permitrootlogin nopasswordauthentication noZie je
yes? Niet herstarten: kijk eerst welk bestand in/etc/ssh/sshd_config.d/het overschrijft. -
Herstart SSH, met je tweede sessie nog open.
Terminal window sudo systemctl restart sshOpen dan een derde terminal en log opnieuw in als
jij. Lukt dat, probeer dan root:Terminal window ssh root@<JOUW-SERVER-IP>Dit zou je nu moeten zien
root@<JOUW-SERVER-IP>: Permission denied (publickey). -
Zet ufw aan. Eerst SSH toestaan, dan pas aanzetten.
Terminal window sudo ufw default deny incomingTerminal window sudo ufw default allow outgoingTerminal window sudo ufw allow OpenSSHTerminal window sudo ufw enableOver SSH vraagt ufw altijd om bevestiging. Typ
y: je hebt OpenSSH net toegestaan.Dit zou je nu moeten zien
Command may disrupt existing ssh connections. Proceed with operation (y|n)? yFirewall is active and enabled on system startupTerminal window sudo ufw status verboseDit zou je nu moeten zien
Status: activeLogging: on (low)Default: deny (incoming), allow (outgoing), disabled (routed)New profiles: skipTo Action From-- ------ ----22/tcp (OpenSSH) ALLOW IN Anywhere22/tcp (OpenSSH (v6)) ALLOW IN Anywhere (v6)De
(v6)-regel komt doordat ufw standaard ook IPv6 filtert (IPV6=yesin/etc/default/ufw).Draai je een website? Dan ook
sudo ufw allow 80,443/tcp. -
Bind Docker-poorten aan 127.0.0.1. Docker zet gepubliceerde poorten langs ufw heen open. Schrijf in je
docker-compose.ymldus niet"5678:5678"maar:ports:- "127.0.0.1:5678:5678"Controleer wat er publiek luistert (alles wat niet op
127.0.0.1of[::1]staat):Terminal window sudo ss -tlnp -
Check de automatische updates.
unattended-upgradesstaat standaard aan op Ubuntu.Terminal window cat /etc/apt/apt.conf.d/20auto-upgradesDit zou je nu moeten zien
APT::Periodic::Update-Package-Lists "1";APT::Periodic::Unattended-Upgrade "1";Bestand bestaat niet?
sudo apt install unattended-upgradesmaakt het aan. -
Laat de server zelf herstarten na een kernelupdate, ’s nachts om 04:30 (in de tijdzone van je server; check die met
timedatectl).Terminal window sudo tee /etc/apt/apt.conf.d/52reboot >/dev/null <<'EOF'Unattended-Upgrade::Automatic-Reboot "true";Unattended-Upgrade::Automatic-Reboot-Time "04:30";EOFTerminal window apt-config dump | grep -i automatic-rebootDit zou je nu moeten zien
Unattended-Upgrade::Automatic-Reboot "true";Unattended-Upgrade::Automatic-Reboot-Time "04:30";Zet je containers op
restart: unless-stopped, dan komen ze na de reboot vanzelf terug. -
Zet backups aan en test een herstel. Hetzner Console → Backups → Enable Backups → kies je server → Enable & Buy now (dagelijks, 7 roterende kopieën). Maak daarnaast een snapshot via Snapshots → Take Snapshot. Daarna Add server en kies bij Image je snapshot. Log in als
jijmet je sleutel (die zit in de snapshot), kijk of je data er is en verwijder de testserver weer.
Volgende laag: SSH helemaal niet publiek
Section titled “Volgende laag: SSH helemaal niet publiek”Met Tailscale (volgende les) log je in via je eigen privénetwerk. Werkt ssh jij@<tailscale-naam>? Haal dan poort 22 weg uit de Hetzner-firewall en beperk ufw tot de Tailscale-interface:
sudo ufw allow in on tailscale0 to any port 22 proto tcpsudo ufw delete allow OpenSSHDoe dit pas als de Tailscale-login werkt, anders sluit je jezelf buiten. Houd Tailscale up-to-date: in 2026 verschenen al 17 security-bulletins, waarvan zes over Tailscale SSH.
fail2ban, CrowdSec of alleen sleutels?
Section titled “fail2ban, CrowdSec of alleen sleutels?”| Optie | Wat het doet | Ons oordeel |
|---|---|---|
| Alleen sleutels + SSH niet publiek | Raden is kansloos, scanners zien niets | De basis. Dit doet het echte werk. |
| fail2ban | Blokkeert IP’s na mislukte pogingen, met lokale kennis | Nice to have: minder ruis in je logs, geen extra bescherming bij alleen-sleutel. |
| CrowdSec | Idem, plus een gedeelde blocklist van de community | Pas zinvol als je publieke webdiensten draait. Wel een extra daemon. |
Gebruik fail2ban nooit als reden om wachtwoord-login aan te laten.
Checklist
0/7
Je voortgang wordt alleen in deze browser bewaard.
Test jezelf
1Je zet PasswordAuthentication no onderaan sshd_config, maar sshd -T zegt yes. Hoe kan dat?
OpenSSH neemt de eerste waarde. Zet je regels in 00-hardening.conf.
2ufw blokkeert alles behalve SSH. Je start n8n met -p 5678:5678. Is n8n bereikbaar van buitenaf?
Bind aan 127.0.0.1 of gebruik de Hetzner Cloud Firewall.
3Wat doe je vóórdat je SSH herstart na een configwijziging?
Volgende les: security-check voor je app. Volg je het leerpad Je eigen 24/7-agent? Dan komt Tailscale hierna, zodat poort 22 helemaal dicht kan.