Una porta aperta e dimenticata è il modo classico per entrare in un server: un database su un indirizzo pubblico, una dashboard di debug, un container che ha pubblicato una porta che nessuno voleva pubblicare. Il controllo richiede un minuto se conosci due comandi e una trappola. Abbiamo eseguito tutto quanto segue su un server Ubuntu 24.04 nuovo dalla nostra immagine e lo abbiamo scansionato da una seconda macchina, quindi gli output sono quelli che vedrai sul tuo server.
1. Cosa è in ascolto
Il comando è ss. Le lettere significano TCP, UDP, socket in ascolto, porte numeriche e nome del processo:
sudo ss -tulpn
Su un server nuovo stampa sei righe:
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
La colonna che conta è Local Address. Ti dice chi può connettersi:
| Indirizzo | Chi lo raggiunge |
|---|---|
127.0.0.1:… o 127.0.0.53 | Solo i programmi su questo server |
0.0.0.0:… | Chiunque via IPv4, salvo blocco del firewall |
[::]:… | Chiunque via IPv6 (di solito anche IPv4) |
10.0.0.5:… (un indirizzo specifico) | Chiunque raggiunga quell'indirizzo |
Il server nuovo espone quindi esattamente una cosa: SSH sulla porta 22, in IPv4 e IPv6. Il resolver DNS su 127.0.0.53 è privato.
Per elencare solo ciò che Internet potrebbe vedere, filtra gli indirizzi di loopback:
sudo ss -tlnp | awk 'NR>1 && $4 !~ /^(127\.|\[::1\])/ {print $4, $6}'
Eseguilo dopo ogni installazione. Abbiamo installato nginx, Docker e una piccola app Python, e la lista è passata da 2 a 10 righe, tra cui un docker-proxy su 0.0.0.0:8081 e un socket di containerd su una porta localhost casuale.
2. Quale processo occupa una porta
Quando una porta è occupata e vuoi sapere da chi:
sudo lsof -i :80 -P -n
sudo fuser 80/tcp
sudo ss -tlnp 'sport = :80'
lsof è il più leggibile. Per nginx ha mostrato un processo master come root e un worker come www-data, entrambi su *:80, per IPv4 e IPv6. fuser stampa solo i PID, comodo negli script. Se devi liberare la porta, ferma il servizio invece di uccidere il PID, altrimenti systemd lo riavvia: sudo systemctl stop nginx.
3. Cosa vede Internet
ss mostra la prospettiva del server. Quella di Internet può essere diversa, perché in mezzo c'è un firewall o perché un servizio è legato a 127.0.0.1. Per vedere la situazione reale, scansiona il server da un'altra macchina, il tuo portatile o un secondo server. Lo strumento è nmap (apt install nmap, oppure l'installer di nmap.org su Windows e macOS).
nmap your-server-ip
nmap -p- your-server-ip
nmap -p 22,80,443,3306 your-server-ip
La prima forma controlla le 1.000 porte più comuni, la seconda tutte le 65.535, la terza un elenco. Sul nostro server nuovo, prima di qualsiasi 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
La scansione completa delle 65.535 porte ha richiesto 8,9 secondi e ha trovato la stessa unica porta. Le porte chiuse rispondono subito con un reset, per questo scansionarle è veloce.
Contano tre stati:
- open: un programma ha accettato la connessione. È la tua superficie di attacco.
- closed: il server ha risposto che lì non ascolta nulla. Sicuro, ma conferma che il server esiste e cosa non esegue.
- filtered: nessuna risposta. Un firewall ha scartato il pacchetto. Lo scanner non può sapere se dietro c'è un servizio.
Per una singola porta senza installare nulla, nc -zv ip-del-server 443 risponde succeeded o refused, e su una porta filtrata resta in attesa fino al timeout. Per l'UDP aggiungi -sU a nmap e aspettati lentezza; sul nostro server le tre porte UDP comuni, 53, 123 e 161, erano closed.
Una nota per i piani NAT: da Internet arriva solo la porta SSH che ti abbiamo inoltrato. Una scansione dell'indirizzo condiviso mostra il nostro bordo, non il tuo server, e le altre porte sono irraggiungibili, qualunque cosa vi ascolti. Su un piano con indirizzo IPv4 proprio, ogni porta aperta la gestisci tu.
4. Chiudi ciò che non serve
Tre modi, dal migliore al più ampio.
Legare a localhost. Se un servizio è usato solo da programmi sul server, fallo ascoltare su 127.0.0.1. Abbiamo avviato una piccola app web con --bind 127.0.0.1 8080: ss la mostrava su 127.0.0.1:8080 e nmap dall'esterno riportava 8080/tcp closed. Per i database è l'impostazione predefinita giusta: Redis, MySQL e PostgreSQL ascoltano su localhost già di serie su Ubuntu, e le nostre guide a Redis e MySQL lo lasciano così.
Fermare il servizio. sudo systemctl disable --now nome elimina l'ascolto per sempre. Verifica con ss che sia sparito.
Attivare il firewall. Consenti prima SSH, poi ciò che servi, poi attiva:
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw enable
sudo ufw status verbose
La scansione dall'esterno è cambiata del tutto:
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
Ogni porta non consentita è passata da closed a filtered, e la scansione completa delle 65.535 porte ora richiedeva 110 secondi invece di 8,9. Più tempo per porta per lo scanner, meno informazioni sul tuo server. La nostra guida a UFW tratta i limiti di frequenza, l'accesso da un solo IP e l'IPv6.
Ma guarda la terza riga.
5. La sorpresa di Docker
La porta 8081 è rimasta aperta con il firewall attivo e senza una regola che la consentisse. Appartiene a un container avviato con docker run -p 8081:80 nginx:alpine. Docker aggiunge regole proprie al firewall del kernel, e vengono valutate prima di quelle di ufw. La regola che ha scritto sul nostro server:
-A DOCKER ! -i docker0 -p tcp --dport 8081 -j DNAT --to-destination 172.17.0.2:80
Qualsiasi pacchetto per la porta 8081 viene inoltrato nel container prima che ufw possa dire la sua. Lo fa ogni container con -p o una voce ports:, e ss lo mostra onestamente come docker-proxy su 0.0.0.0:8081.
La correzione è pubblicare solo su localhost:
docker rm -f web
docker run -d --name web -p 127.0.0.1:8081:80 nginx:alpine
In Compose scrivi "127.0.0.1:8081:80" sotto ports:. Dopo la modifica ss mostrava 127.0.0.1:8081, la regola di Docker ha guadagnato -d 127.0.0.1/32 e la scansione dall'esterno riportava 8081/tcp filtered. Metti nginx come reverse proxy davanti per la parte pubblica, con HTTPS, così le uniche porte aperte sul server sono 22, 80 e 443.
Checklist dopo ogni modifica
sudo ss -tulpn: ogni riga con0.0.0.0o[::]è qualcosa che volevi esporre?nmap ip-del-serverdal portatile: le porte aperte corrispondono?- Un database, una cache o un pannello di amministrazione su un indirizzo pubblico: spostalo oggi su
127.0.0.1o dietro il firewall. - Ogni voce
ports:di Docker: inizia con127.0.0.1:se non è la porta web pubblica? - SSH esposto a tutti: aggiungi fail2ban o l'accesso solo con chiave.
Su Windows Server lo stesso controllo è Get-NetTCPConnection -State Listen, e le regole stanno nel Windows Firewall. Ogni piano Linux, dal Nano a $3 in su, ha una versione con indirizzo IPv4 proprio, e lì ciò che vedi in ss è ciò che vede il mondo, meno il tuo firewall.
Commenti
Ancora nessun commento. Sii il primo.