Een open en vergeten poort is de klassieke weg naar binnen: een database op een publiek adres, een debug-dashboard, een container die een poort publiceerde die niemand wilde publiceren. Controleren kost een minuut als je twee commando's en één valkuil kent. We deden alles hieronder op een verse Ubuntu 24.04-server van ons image en scanden hem vanaf een tweede machine, dus de uitvoer is wat je op je eigen server ziet.
1. Wat luistert er
Het commando is ss. De letters staan voor TCP, UDP, luisterende sockets, numerieke poorten en de procesnaam:
sudo ss -tulpn
Op een verse server geeft het zes regels:
Netid State Local Address:Port Process
udp UNCONN 127.0.0.54:53 systemd-resolve
udp UNCONN 127.0.0.53%lo:53 systemd-resolve
tcp LISTEN 127.0.0.54:53 systemd-resolve
tcp LISTEN 127.0.0.53%lo:53 systemd-resolve
tcp LISTEN 0.0.0.0:22 sshd
tcp LISTEN [::]:22 sshd
De kolom die telt is Local Address. Die vertelt wie kan verbinden:
| Adres | Wie het bereikt |
|---|---|
127.0.0.1:… of 127.0.0.53 | Alleen programma's op deze server |
0.0.0.0:… | Iedereen via IPv4, tenzij de firewall blokkeert |
[::]:… | Iedereen via IPv6 (en meestal ook IPv4) |
10.0.0.5:… (een specifiek adres) | Iedereen die dat adres kan bereiken |
De verse server stelt dus precies één ding open: SSH op poort 22, via IPv4 en IPv6. De DNS-resolver op 127.0.0.53 is privé.
Om alleen te tonen wat het internet zou kunnen zien, filter je de loopback-adressen eruit:
sudo ss -tlnp | awk 'NR>1 && $4 !~ /^(127\.|\[::1\])/ {print $4, $6}'
Draai dit na elke installatie. We installeerden nginx, Docker en één kleine Python-app, en de lijst groeide van 2 naar 10 regels, waaronder een docker-proxy op 0.0.0.0:8081 en een containerd-socket op een willekeurige localhost-poort.
2. Welk proces een poort bezet
Als een poort bezet is en je wilt weten door wie:
sudo lsof -i :80 -P -n
sudo fuser 80/tcp
sudo ss -tlnp 'sport = :80'
lsof leest het prettigst. Voor nginx toonde het een masterproces als root en een worker als www-data, beide op *:80, voor IPv4 en IPv6. fuser geeft alleen de PID's, handig in scripts. Moet je de poort vrijmaken, stop dan de dienst in plaats van de PID te killen, anders start systemd hem opnieuw: sudo systemctl stop nginx.
3. Wat het internet ziet
ss toont het zicht van de server. Het zicht van het internet kan afwijken, omdat er een firewall tussen zit of omdat een dienst aan 127.0.0.1 is gebonden. Voor het echte beeld scan je de server vanaf een andere machine, je laptop of een tweede server. Het gereedschap is nmap (apt install nmap, of de installer van nmap.org op Windows en macOS).
nmap your-server-ip
nmap -p- your-server-ip
nmap -p 22,80,443,3306 your-server-ip
De eerste vorm controleert de 1.000 meest gebruikte poorten, de tweede alle 65.535, de derde een lijst. Op onze verse server, nog zonder firewall:
Not shown: 999 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
Nmap done: 1 IP address (1 host up) scanned in 6.83 seconds
De volledige scan van 65.535 poorten duurde 8,9 seconden en vond dezelfde enige poort. Gesloten poorten antwoorden meteen met een reset, daarom gaat scannen zo snel.
Drie toestanden tellen:
- open: een programma accepteerde de verbinding. Dit is je aanvalsoppervlak.
- closed: de server antwoordde dat daar niets luistert. Veilig, maar het bevestigt dat de server bestaat en wat hij niet draait.
- filtered: geen antwoord. Een firewall gooide het pakket weg. De scanner weet niet of er een dienst achter zit.
Voor één poort zonder iets te installeren meldt nc -zv server-ip 443 succeeded of refused, en blijft hij op een gefilterde poort hangen tot de time-out. Voor UDP voeg je -sU aan nmap toe en reken je op wachten; op onze server waren de drie gebruikelijke UDP-poorten, 53, 123 en 161, closed.
Een opmerking voor NAT-abonnementen: vanaf het internet is alleen de SSH-poort bereikbaar die we naar je doorsturen. Een scan van het gedeelde adres toont onze rand, niet je server, en de andere poorten zijn onbereikbaar, wat er ook op luistert. Met een abonnement met een eigen IPv4-adres beheer je elke open poort zelf.
4. Sluit wat je niet nodig hebt
Drie manieren, van beste naar breedste.
Aan localhost binden. Gebruiken alleen programma's op de server een dienst, laat hem dan op 127.0.0.1 luisteren. We startten een kleine webapp met --bind 127.0.0.1 8080: ss toonde hem op 127.0.0.1:8080 en nmap van buitenaf meldde 8080/tcp closed. Voor databases is dit de juiste standaard: Redis, MySQL en PostgreSQL luisteren op Ubuntu standaard op localhost, en onze handleidingen voor Redis en MySQL laten dat zo.
De dienst stoppen. sudo systemctl disable --now naam haalt de listener definitief weg. Controleer met ss dat hij weg is.
De firewall aanzetten. Sta eerst SSH toe, dan wat je aanbiedt, en zet hem dan aan:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw enable
sudo ufw status verbose
De scan van buitenaf veranderde volledig:
Not shown: 997 filtered tcp ports (no-response)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
8081/tcp open blackice-icecap
Nmap done: 1 IP address (1 host up) scanned in 10.18 seconds
Elke niet-toegestane poort ging van closed naar filtered, en de volledige scan van 65.535 poorten duurde nu 110 seconden in plaats van 8,9. Meer tijd per poort voor de scanner, minder informatie over je server. Onze UFW-handleiding behandelt rate limits, één IP toestaan en IPv6.
Maar kijk naar de derde regel.
5. De Docker-verrassing
Poort 8081 bleef open met de firewall aan en zonder regel die hem toestond. Die poort hoort bij een container die we startten met docker run -p 8081:80 nginx:alpine. Docker voegt eigen regels toe aan de kernelfirewall, en die worden vóór die van ufw beoordeeld. De regel die het op onze server schreef:
-A DOCKER ! -i docker0 -p tcp --dport 8081 -j DNAT --to-destination 172.17.0.2:80
Elk pakket voor poort 8081 wordt naar de container doorgestuurd voordat ufw iets te zeggen heeft. Elke container met -p of een ports:-regel doet dit, en ss toont het eerlijk als docker-proxy op 0.0.0.0:8081.
De oplossing is alleen op localhost publiceren:
docker rm -f web
docker run -d --name web -p 127.0.0.1:8081:80 nginx:alpine
Schrijf in Compose "127.0.0.1:8081:80" onder ports:. Na de wijziging toonde ss 127.0.0.1:8081, kreeg de Docker-regel -d 127.0.0.1/32 erbij en meldde de scan van buitenaf 8081/tcp filtered. Zet nginx als reverse proxy ervoor voor de publieke kant, met HTTPS, zodat alleen de poorten 22, 80 en 443 op de server open staan.
Checklist na elke wijziging
sudo ss -tulpn: is elke regel met0.0.0.0of[::]iets wat je wilde openstellen?nmap server-ipvanaf je laptop: kloppen de open poorten?- Een database, cache of beheerpaneel op een publiek adres: verplaats het vandaag naar
127.0.0.1of achter de firewall. - Elke Docker-
ports:-regel: begint die met127.0.0.1:als het niet de publieke webpoort is? - SSH open voor iedereen: voeg fail2ban of inloggen alleen met een sleutel toe.
Op Windows Server is dezelfde controle Get-NetTCPConnection -State Listen, en de regels staan in de Windows Firewall. Elk Linux-abonnement, vanaf de Nano voor $3, heeft een versie met een eigen IPv4-adres, en daar ziet de wereld wat jij in ss ziet, min je firewall.
Reacties
Nog geen reacties. Wees de eerste.