Ga naar inhoud

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

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.

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.

Werk als root op je verse server (zie SSH-sleutels maken). Vervang jij overal door je eigen gebruikersnaam.

  1. 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.

  2. Update alles.

    Terminal window
    apt update && apt full-upgrade -y
  3. Maak je eigen gebruiker met sudo-rechten. adduser vraagt een wachtwoord: dat heb je straks nodig voor sudo, niet om in te loggen.

    Terminal window
    adduser jij
    Terminal window
    usermod -aG sudo jij
  4. Geef die gebruiker je SSH-sleutel. Dit kopieert de map .ssh van root, met je authorized_keys.

    Terminal window
    cp -r /root/.ssh /home/jij/ && chown -R jij:jij /home/jij/.ssh
  5. Test in een tweede terminal. Laat de root-sessie openstaan.

    Terminal window
    ssh jij@<JOUW-SERVER-IP>
    Terminal window
    sudo whoami

    Na je sudo-wachtwoord:

    Dit zou je nu moeten zien

    root

    Op Ubuntu 26.04 is sudo vervangen door sudo-rs: de vraag heet dan [sudo: authenticate] Password: en je ziet sterretjes terwijl je typt.

  6. Zet SSH op alleen-sleutel, zonder root. Je eigen bestand begint met 00-, zodat het vóór 50-cloud-init.conf gelezen wordt.

    Terminal window
    sudo tee /etc/ssh/sshd_config.d/00-hardening.conf >/dev/null <<'EOF'
    PasswordAuthentication no
    KbdInteractiveAuthentication no
    PermitRootLogin no
    AllowUsers jij
    EOF
  7. Controleer wat er écht geldt. sshd -t checkt de syntax, sshd -T toont de uiteindelijke waarden.

    Terminal window
    sudo sshd -t
    Terminal window
    sudo sshd -T | grep -Ei 'passwordauthentication|permitrootlogin'

    Dit zou je nu moeten zien

    permitrootlogin no
    passwordauthentication no

    Zie je yes? Niet herstarten: kijk eerst welk bestand in /etc/ssh/sshd_config.d/ het overschrijft.

  8. Herstart SSH, met je tweede sessie nog open.

    Terminal window
    sudo systemctl restart ssh

    Open 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).
  9. Zet ufw aan. Eerst SSH toestaan, dan pas aanzetten.

    Terminal window
    sudo ufw default deny incoming
    Terminal window
    sudo ufw default allow outgoing
    Terminal window
    sudo ufw allow OpenSSH
    Terminal window
    sudo ufw enable

    Over 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)? y
    Firewall is active and enabled on system startup
    Terminal window
    sudo ufw status verbose

    Dit zou je nu moeten zien

    Status: active
    Logging: on (low)
    Default: deny (incoming), allow (outgoing), disabled (routed)
    New profiles: skip
    To Action From
    -- ------ ----
    22/tcp (OpenSSH) ALLOW IN Anywhere
    22/tcp (OpenSSH (v6)) ALLOW IN Anywhere (v6)

    De (v6)-regel komt doordat ufw standaard ook IPv6 filtert (IPV6=yes in /etc/default/ufw).

    Draai je een website? Dan ook sudo ufw allow 80,443/tcp.

  10. Bind Docker-poorten aan 127.0.0.1. Docker zet gepubliceerde poorten langs ufw heen open. Schrijf in je docker-compose.yml dus niet "5678:5678" maar:

    ports:
    - "127.0.0.1:5678:5678"

    Controleer wat er publiek luistert (alles wat niet op 127.0.0.1 of [::1] staat):

    Terminal window
    sudo ss -tlnp
  11. Check de automatische updates. unattended-upgrades staat standaard aan op Ubuntu.

    Terminal window
    cat /etc/apt/apt.conf.d/20auto-upgrades

    Dit zou je nu moeten zien

    APT::Periodic::Update-Package-Lists "1";
    APT::Periodic::Unattended-Upgrade "1";

    Bestand bestaat niet? sudo apt install unattended-upgrades maakt het aan.

  12. 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";
    EOF
    Terminal window
    apt-config dump | grep -i automatic-reboot

    Dit 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.

  13. 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 jij met je sleutel (die zit in de snapshot), kijk of je data er is en verwijder de testserver weer.

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:

Terminal window
sudo ufw allow in on tailscale0 to any port 22 proto tcp
Terminal window
sudo ufw delete allow OpenSSH

Doe 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.

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

  1. 1Je zet PasswordAuthentication no onderaan sshd_config, maar sshd -T zegt yes. Hoe kan dat?

  2. 2ufw blokkeert alles behalve SSH. Je start n8n met -p 5678:5678. Is n8n bereikbaar van buitenaf?

  3. 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.