Sådan sikrer du en ny VPS
Skridtene er sorteret efter, hvor meget risiko hvert af dem fjerner. Nøjes du med de første fire, har du stadig fjernet det meste af det, automatiserede angreb bygger på.
Slå root-login over SSH fra, brug nøgler i stedet for adgangskoder, flyt administrativ adgang ind på et privat netværk, og afvis som standard i firewallen. Automatisér derefter patching, tilføj indbrudsdetektion med overvågning af filintegritet, send logs væk fra maskinen, og læg backup et sted, der fejler uafhængigt, med gendannelse testet efter en fast plan. Resten er finjustering.
Skridtene, sorteret efter risiko
- Opret en almindelig bruger, og slå root-login over SSH fraDe fleste automatiserede angreb prøver at logge ind som root. Slår du root-login fra, stopper hovedparten af de tilfældige forsøg allerede der, før de andre skridt kommer i spil.
- Skift SSH til kun at acceptere nøglerPå en offentlig IP bliver login med adgangskode udsat for brute force døgnet rundt. Nøgler stopper den type angreb helt i stedet for at gøre det langsommere.
- Flyt SSH ind bag et privat netværkEt mesh-VPN fjerner SSH helt fra internettet. Det er bedre end at hærde en offentlig SSH-port, og intet andet enkelt skridt mindsker eksponeringen så meget på en lille server.
- Sæt firewallen til at afvise som standardÅbn kun de porte, tjenesten har brug for. Når firewallen afviser som standard, står en tjeneste, du senere glemmer, ikke automatisk åben.
- Slå automatiske sikkerhedsopdateringer tilUpatchede pakker er den mest almindelige vej ind. Automatisér opdateringerne. På en server, som ingen har ansvaret for, bliver manuel patching ikke gjort.
- Tilføj kernepatching uden genstartRettelser til sårbarheder i kernen kræver normalt planlagt nedetid, og derfor bliver de udskudt. Live patching lægger de fleste alvorlige kernerettelser ind uden genstart, så du kan holde kernen opdateret uden at planlægge nedetid hver gang. Du skal stadig genstarte, men efter en plan og ikke akut.
- Installer værtsbaseret indbrudsdetektion med overvågning af filintegritetDu skal have besked, når en fil ændrer sig, som ikke burde ændre sig. Håndterer du kortdata, er det også en obligatorisk kontrol i PCI DSS.
- Saml logs, og gem dem et sted uden for maskinenEn angriber sletter først de logs, der kun ligger på den kompromitterede server. PCI DSS krav 10 forpligter dig også til at opbevare dem.
- Sæt overvågning op, der alarmerer et menneskeMålinger, som ingen kigger på, hjælper ikke. Alarmen skal nå frem til en person, der kan handle på den.
- Læg backup et andet sted, i et andet fejldomæneEn ekstra disk i samme maskine hjælper kun ved diskfejl. Et andet sted betyder en udbyder eller region, der fejler uafhængigt af serveren.
- Gendan én backup, efter en fast planDu ved ikke, om en backup virker, før du har gendannet den. Test gendannelsen med faste mellemrum, og skriv ned, hvor lang tid det tog.
Tjekliste til hærdning af en VPS
Skridtene siger, hvad du skal gøre, og tjeklisten beviser, at hvert af dem er gjort. Det er to forskellige ting, for indstillingen i en konfigurationsfil og den indstilling, den kørende tjeneste bruger, er ofte ikke den samme. Kommandoerne forudsætter Debian eller Ubuntu. Hvor KernelCare, Wazuh, Netdata eller restic optræder, er det dem, vi kører, og tjekket kan overføres til det, du selv bruger. Ved skridt 3 skal du bekræfte, at SSH over den private adresse virker, før du lukker den offentlige port, og vide, hvor din udbyders webkonsol er, hvis du har en.
VPS HARDENING CHECKLIST: Debian and Ubuntu, run as root or with sudo.
Tick an item only when the command shows the expected result.
[ ] 1 Root cannot log in over SSH, and you work as a sudo user
sshd -T | grep -i '^permitrootlogin'
expect: permitrootlogin no
(the stock default prints without-password before
OpenSSH 10.1, prohibit-password after; both let root in
with a key)
sudo -l -U youruser
expect: the sudo rights you meant to give, and no more
[ ] 2 SSH accepts keys only
sshd -T | grep -Ei '^(passwordauth|kbdinteractiveauth)'
expect: both lines end in no
grep -ri passwordauthentication /etc/ssh/sshd_config.d/
expect: no file that says yes
sshd -T | grep -i '^authorizedkeysfile'
expect: you have read every file it names, for every user
[ ] 3 SSH is not reachable from the internet
nmap -Pn -p 22 YOUR.PUBLIC.IP (from a machine off your tailnet)
expect: filtered or closed, never open
tailscale ip -4 (on the server)
expect: ssh to this address works from your own devices
[ ] 4 Inbound traffic is denied by default
ufw status verbose
expect: Status: active, and Default: deny (incoming)
nft list ruleset (if you manage nftables directly)
expect: policy drop on the chain with hook input
ss -tlnp
expect: every listening port is one you can name
docker ps (only if Docker is installed)
expect: published ports show 127.0.0.1 unless meant to be public
[ ] 5 Security updates install themselves
systemctl list-timers 'apt-daily*'
expect: apt-daily.timer and apt-daily-upgrade.timer, each with
a NEXT time, and a LAST time once the timer has fired
(a new server shows - there; starting the service by
hand does not count)
apt-config dump | grep Periodic
expect: APT::Periodic::Unattended-Upgrade "1";
unattended-upgrade -v --dry-run
expect: "Allowed origins are:" lists a security origin
ls -lt /var/log/unattended-upgrades/
expect: logs written in the last day or two
[ ] 6 Kernel fixes load without waiting for a reboot
kcarectl --status; echo $? (KernelCare)
expect: 0, the latest patch level is loaded
canonical-livepatch status (Ubuntu Livepatch)
expect: all applicable live kernel patch modules applied
cat /sys/kernel/livepatch/*/enabled (kernel livepatch interface)
expect: 1 for every loaded patch; "No such file or directory"
means no patch is loaded at all
ls /var/run/reboot-required (written on kernel installs by
unattended-upgrades' hook on
Debian and Ubuntu, but on Ubuntu
not while Livepatch is enabled)
expect: no such file, or a reboot already booked
[ ] 7 Unexpected file changes are detected and reported
grep ^status /var/ossec/var/run/wazuh-agentd.state
expect: status='connected'
(the stock agent does not watch your web root; add it first,
see the file integrity monitoring guide)
touch /var/www/shop/fim-canary.txt (wait for the alert, then)
rm /var/www/shop/fim-canary.txt
expect: "File added to the system." and "File deleted."
alerts reach a person, not only a dashboard
[ ] 8 Logs leave the machine as they are written
logger -p auth.notice -t hardening-check "off-box test"
expect: the line appears on your log collector
[ ] 9 Monitoring alerts reach a person
sudo su -s /bin/bash netdata
/usr/libexec/netdata/plugins.d/alarm-notify.sh test
(path for Netdata's own packages; Ubuntu's netdata package uses
/usr/lib/netdata/plugins.d/, and Debian 13 has no netdata package)
expect: the test notifications arrive on a phone someone carries
[ ] 10 Backups run, and are stored in a different failure domain
(with your repository and password in the environment)
restic snapshots --latest 1
expect: a snapshot as recent as your schedule promises
restic check --read-data-subset=5%
expect: no errors were found
and: the repository is with a different provider or region
[ ] 11 A restore has been tested, and timed
time restic restore latest --target /tmp/restore-test --include /var/www/shop
diff -r /var/www/shop /tmp/restore-test/var/www/shop
expect: only files changed since the snapshot differ;
write down how long the restore took De her resultater snyder folk oftere end resten.
Login med adgangskode er stadig slået til, selv om du slog det fra. På Debian
og Ubuntu inkluderer sshd_config filerne /etc/ssh/sshd_config.d/*.conf
i toppen af filen, og for de fleste indstillinger bruger sshd den første værdi, den læser. Når
cloud-init sætter en indstilling for adgangskoder, skriver den den i
50-cloud-init.conf i den mappe, så et yes dér slår dit
no længere nede i hovedfilen. Derfor stoler tjeklisten på sshd -T,
som udskriver den gældende konfiguration, frem for på filen. Bruger du Match-blokke,
så tilføj -C med en bruger og en kildeadresse for at se, hvad der gælder for netop
den forbindelse.
Tjekket finder ingenting, og ingenting ligner en bestået test. OpenSSH 10.4,
udgivet 6. juli 2026, ændrede udskriften af konfigurationen fra navne med små bogstaver til
blandede store og små bogstaver, for eksempel PubkeyAuthentication i stedet for
pubkeyauthentication. Debian 13 (OpenSSH 10.0p2) og Ubuntu 24.04 (9.6p1) skriver
stadig med små bogstaver, så det rammer dig, når du skifter til en udgave, der leverer 10.4
eller nyere, eller bygger din egen. Et script, der kører grep efter den gamle stavemåde uden
-i, finder slet ingen linje, og det er let at forveksle med et rent resultat.
Derfor bruger alle grep-kommandoer på sshd -T i tjeklisten ovenfor -i.
Docker går uden om ufw. Ifølge Dockers dokumentation omdirigerer Docker trafik
til publicerede containerporte i nat-tabellen, før den når de INPUT- og OUTPUT-kæder, som ufw
bruger. En port, du publicerer uden en værtsadresse, eksponerer Docker på alle
netværksinterfaces. En container publiceret som 8080:80 kan nås fra internettet,
mens ufw status siger deny. Publicér til 127.0.0.1, og sæt din
reverse proxy foran.
Når et tjek fejler, har rettelsen som regel sin egen guide. Skal SSH være offentligt tilgængelig et stykke tid, gennemgår guiden om fail2ban og CrowdSec, hvordan du blokerer de adresser, der bliver ved med at prøve. Findes timerne, men bliver der ikke installeret noget, så læs unattended-upgrades virker ikke. Skridt 6 afhænger af din distribution, og guiden om kernel live patching sammenligner mulighederne. Til skridt 7 forklarer guiden om overvågning af filintegritet, hvorfor standardkonfigurationen ikke overvåger din webrod.
Det folk springer over, og fortryder
I praksis er det opbevaring af logs og test af gendannelse, folk springer over. Ingen af dem giver en synlig gevinst før den dag, du har akut brug for dem. Gør dem derfor på dag ét. Venter du til efter en hændelse, er de logs, du skulle bruge, allerede roteret væk, og backuppen viser sig at have udeladt en mappe, som ingen havde gennemgået. Begge har deres egen guide: backup der faktisk kan gendannes og overvågning af filintegritet.
Faldgruber
- Din hærdningsfil taber på navnet. sshd læser drop-in-filer i navneorden, og den første værdi vinder. Med
PasswordAuthentication yesi50-cloud-init.confvandt etPasswordAuthentication noi10-hardening.conf, men den samme linje i60-hardening.conftabte, ogsshd -Tsagde stadig yes. Giv din fil et nummer under 50, eller ret selve cloud-init-filen. - Standardindstillingen for root har et gammelt navn. På en uændret Debian 13 og Ubuntu 24.04 udskrev
sshd -Tlinjenpermitrootlogin without-password, det forældede alias forprohibit-password, så et tjek, der leder efterprohibit-password, finder ingenting. Ifølge udgivelsesnoterne til OpenSSH 10.1 udskriversshd -Tdet nuværende navn fra og med den version. Med begge navne kan root stadig logge ind med en nøgle. Det tjek, du vil have, erpermitrootlogin no. - OpenSSH 10.4 er kun halvvejs gået over til store og små bogstaver. Vi byggede OpenSSH 10.4p1 fra kildekoden og kørte
sshd -T.PasswordAuthenticationogPermitRootLoginkom ud med blandede store og små bogstaver, menport,listenaddressog et par andre stod stadig med små bogstaver. Udgivelsesnoterne til 10.5 nævner som en yderligere ændring, at de alle får blandede store og små bogstaver. En grep, der skelner mellem store og små bogstaver, kan ramme ved siden af i begge retninger, så brug altid-i. - Docker gik uden om ufw i vores test. Med ufw aktiv og sat til at afvise indgående trafik, og kun port 22 tilladt, svarede en container publiceret som
8080:80på HTTP-forespørgsler fra en anden maskine, og nmap meldte porten åben. Den samme container publiceret som127.0.0.1:8081:80meldte nmap som filtreret.ufw statusviste kun port 22 hele vejen igennem. - En ny server har ingen LAST-tid.
systemctl list-timersviser-under LAST, indtil timeren selv er udløst én gang. Da vi startedeapt-daily-upgrade.servicemanuelt, kørte unattended-upgrades, men LAST forblev tom. På en server, der er sat op i dag, beviser den kolonne derfor ikke noget endnu.
Sådan blev det testet. Den 23. september 2026 kørte vi tjeklistens kommandoer for skridt 1 til 6 i Docker-containere med systemd: Debian 13 (debian:trixie; OpenSSH 10.0p2, ufw 0.36.2, nftables 1.1.3, sudo 1.9.16p2) og Ubuntu 24.04.5 LTS (OpenSSH 9.6p1, ufw 0.36.2, nftables 1.0.9, sudo 1.9.15p5), hvor nmap 7.94 scannede den ene container fra den anden. Påstanden om blandede store og små bogstaver kontrollerede vi ved at bygge OpenSSH 10.4p1 fra udgivelsens tarball. Filnavnet fra cloud-init holdt vi op mod cloud-inits kildekode, og Docker-omgåelsen afprøvede vi med Docker 26.1.5 kørende inde i Debian-containeren. Kommandoerne til restic kørte vi med restic 0.19.1 mod et lokalt repository, og logger kontrollerede vi kun i den lokale journal. Ikke testet: Tailscale, en rigtig logopsamler uden for maskinen, KernelCare, Canonical Livepatch, /sys/kernel/livepatch, en kørende Wazuh-agent og notifikationer fra Netdata. Livepatch-tjekkene kunne ikke køre, fordi containerens kerne ikke understøtter livepatch. For Wazuh-agenten kontrollerede vi tilstandsfilen og regelteksterne i kildekoden til Wazuh 4.14.0.
Kilder
- sshd(8) og sshd_config(5), OpenBSD-manualsider (tilvalgene -T og -C, reglen om den første værdi, standardværdien for AuthorizedKeysFile)
- OpenSSH release notes, OpenSSH-projektet, version 10.4 af 6. juli 2026 (konfigurationsudskrift med blandede store og små bogstaver), med 10.1 (prohibit-password i sshd -T) og 10.5 (de resterende direktiver med blandede store og små bogstaver)
- debian-config.patch, Debians OpenSSH-pakke, som den leveres i Ubuntu 24.04 (Include-linjen øverst i sshd_config)
- ssh_util.py og cc_set_passwords.py, cloud-inits kildekode på GitHub (skriver PasswordAuthentication til 50-cloud-init.conf)
- sudo manual, sudo-projektet (tilvalgene -l og -U)
- Nmap options summary og port scanning basics, Nmap reference guide
- Use ufw to lock down an Ubuntu server, Tailscale-dokumentation, senest valideret 5. januar 2026
- ufw(8), Debians manualsider, og nft(8), Debians manualsider
- ss(8), Linux man-pages-projektet
- Packet filtering and firewalls og Port publishing and mapping, Docker-dokumentation
- Automatic updates, Ubuntu Server-dokumentation
- systemctl(1), Linux man-pages-projektet
- KernelCare Enterprise documentation, TuxCare (exitkoder for kcarectl --status)
- How to check the Livepatch Client status, Canonical Livepatch-dokumentation
- ABI: /sys/kernel/livepatch, The Linux Kernel documentation
- Checking connection with the Wazuh server, Wazuh-dokumentation, og regel 553 og 554, kildekoden til Wazuh v4.14.0 på GitHub
- logger(1), Linux man-pages-projektet
- Agent notifications reference, Netdata-dokumentation (test af notifikationer)
- Working with repositories og Restoring from backup, restic-dokumentation
Spørgsmål
Hvor lang tid tager det?
Et par timer for en, der har gjort det før, og det meste af en dag for en, der ikke har. Størstedelen af tiden går med backup og test af gendannelse. De første fire skridt tager under en halv time og lukker for hovedparten af de automatiserede angreb.
Min udbyder siger, at serveren allerede er sikret. Er den det?
Din udbyder sikrer sin platform, men maskinen er din. Så snart du har root på en VPS, er patching, firewallkonfiguration, adgangsstyring og backup dit ansvar, og det står ligeud i udbydernes modeller for delt ansvar. Tjek din udbyders vilkår. Fordelingen står som regel tydeligere, end folk regner med.
Hvad skal en tjekliste til hærdning af en VPS indeholde?
Root-login over SSH slået fra, SSH kun med nøgler, SSH kun tilgængelig over et privat netværk, en firewall der afviser som standard, automatiske sikkerhedsopdateringer, live patching af kernen, overvågning af filintegritet, logs sendt væk fra maskinen, alarmer der når frem til et menneske, backup et andet sted og en gendannelsestest, hvor tiden bliver taget. En brugbar tjekliste nævner også den kommando, der beviser hvert punkt, fordi indstillingen i en fil og den indstilling, en kørende tjeneste bruger, ofte er forskellige.
Hvorfor er login med adgangskode over SSH stadig slået til, når jeg har sat PasswordAuthentication no?
På Debian og Ubuntu inkluderer sshd_config filerne /etc/ssh/sshd_config.d/*.conf i toppen, og for de fleste indstillinger bruger sshd den første værdi, den læser. Når cloud-init sætter en indstilling for adgangskoder, skriver den den i 50-cloud-init.conf i den mappe, så et yes dér slår dit no længere nede. Kør sudo sshd -T for at se den gældende værdi, ret drop-in-filen, og genindlæs sshd.
Wardio kører alle elleve som standard og holder dem kørende. Se hvad det koster.