Salta al contenuto principale

ss: vedere connessioni e porte in ascolto

Inviato da tuxsa il

Il server web è davvero in ascolto sulla porta 443? Quale programma sta occupando la porta 8080? Quante connessioni ha aperte il mio computer, e verso chi? Sono domande di tutti i giorni per chi gestisce un server, e ss risponde a tutte.

Cos'è

ss (socket statistics) è lo strumento che mostra i socket del sistema, cioè i punti di comunicazione di rete usati dai programmi. Fa parte del pacchetto iproute2, lo stesso di ip, e ha sostituito netstat, che è obsoleto e spesso non è più installato. È più veloce, mostra più informazioni e ha una sintassi quasi identica per le opzioni più usate. Prima di cominciare, due parole:

  • un programma in ascolto (listening) aspetta connessioni su una porta, come un server web sulla 80 e sulla 443;
  • una connessione stabilita (established) è invece un collegamento già aperto tra due indirizzi, ciascuno composto da indirizzo IP e porta, per esempio 192.168.1.25:51234 verso 203.0.113.10:443.

Cosa fa

Elenca le connessioni e le porte in ascolto, con i protocolli (TCP, UDP, socket Unix), gli indirizzi, lo stato di ogni connessione e, se richiesto, il programma che le possiede. Senza opzioni mostra solo le connessioni stabilite, e quasi sempre le opzioni si scrivono unite in un'unica parola, come in ss -tulpn.

Come si usa

$ ss [opzioni] [filtro]

Gli indirizzi negli esempi sono di documentazione o di reti private (192.168.1.x, 203.0.113.x), e i processi sono d'esempio.

Esempio 1: le porte in ascolto, il comando più usato

$ sudo ss -tulpn
Netid State  Recv-Q Send-Q  Local Address:Port   Peer Address:Port Process
udp   UNCONN 0      0       127.0.0.53%lo:53          0.0.0.0:*     users:(("systemd-resolve",pid=612,fd=13))
tcp   LISTEN 0      511           0.0.0.0:80          0.0.0.0:*     users:(("nginx",pid=925,fd=6))
tcp   LISTEN 0      128           0.0.0.0:22          0.0.0.0:*     users:(("sshd",pid=801,fd=3))
tcp   LISTEN 0      80          127.0.0.1:3306        0.0.0.0:*     users:(("mariadbd",pid=1034,fd=24))
tcp   LISTEN 0      511              [::]:80             [::]:*     users:(("nginx",pid=925,fd=7))
tcp   LISTEN 0      128              [::]:22             [::]:*     users:(("sshd",pid=801,fd=4))

Le cinque lettere significano: -t TCP, -u UDP, -l solo quelli in ascolto (listening), -p mostra il processo e -n numeri al posto dei nomi (porte come 80 e non http, e nessuna risoluzione DNS, quindi più veloce). Senza sudo i processi degli altri utenti non vengono mostrati. Come leggere le colonne:

  • Netid: il protocollo (tcp, udp);
  • State: LISTEN per TCP in ascolto, UNCONN per UDP (che non ha connessioni);
  • Local Address:Port: dove il programma è in ascolto;
  • Peer Address:Port: l'altra parte (per i socket in ascolto è 0.0.0.0:*, cioè "chiunque");
  • Process: il programma, con il suo PID.

Esempio 2: l'indirizzo di ascolto cambia tutto

0.0.0.0:80          nginx visibile da tutte le interfacce, IPv4
[::]:80             lo stesso, in IPv6
127.0.0.1:3306      MariaDB visibile solo dal computer stesso
192.168.1.25:8080   in ascolto solo su quell'indirizzo specifico

Il campo più importante per la sicurezza è l'indirizzo locale. 0.0.0.0 (o [::] per IPv6, o *) significa "in ascolto su tutte le interfacce": il servizio è raggiungibile dalla rete, se un firewall non lo impedisce. 127.0.0.1 (o [::1]) significa "solo da questo computer": ideale per un database o per un servizio dietro un reverse proxy come nginx, ma da fuori sono irraggiungibili. In pratica, quando un servizio "non risponde da fuori", la prima cosa da controllare è se è in ascolto su 0.0.0.0 o solo su 127.0.0.1.

Esempio 3: chi sta usando una porta?

$ sudo ss -ltnp 'sport = :8080'
State  Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0      4096         0.0.0.0:8080      0.0.0.0:*     users:(("python3",pid=4410,fd=5))
$ sudo ss -ltnp | grep ':5678'
LISTEN 0 4096 0.0.0.0:5678 0.0.0.0:* users:(("docker-proxy",pid=2211,fd=4))

Quando un programma non parte perché "Address already in use", la porta è già occupata da qualcun altro. Si può filtrare per porta con 'sport = :8080' (source port, cioè la porta locale), oppure usare grep sull'output. Il campo Process dice di quale programma si tratta (con il PID, con cui si può controllare il processo con ps e fermarlo con kill). I servizi in Docker compaiono spesso come docker-proxy.

Esempio 4: le connessioni stabilite

$ ss -tn
State Recv-Q Send-Q Local Address:Port    Peer Address:Port
ESTAB 0      0      192.168.1.25:51234   203.0.113.10:443
ESTAB 0      0      192.168.1.25:22      192.168.1.40:60112
ESTAB 0      0      192.168.1.25:38840   198.51.100.7:993
$ ss -tnp state established
$ ss -tn state established '( dport = :443 )'

Senza -l, ss mostra le connessioni già aperte. ESTAB significa stabilita: ogni riga è una conversazione in corso tra due indirizzi. Nel primo esempio il computer è collegato a un sito in HTTPS (porta 443); nel secondo qualcuno (192.168.1.40) è collegato in SSH al tuo computer; nel terzo il computer controlla la posta (porta 993). Il filtro state established limita l'elenco, e dport (destination port) filtra per porta di destinazione.

Esempio 5: contare le connessioni per stato

$ ss -tan | awk 'NR>1 {print $1}' | sort | uniq -c | sort -nr
    214 ESTAB
     37 TIME-WAIT
      4 LISTEN
      1 CLOSE-WAIT
$ ss -s
Total: 412
TCP:   255 (estab 214, closed 40, orphaned 0, timewait 37)
Transport Total     IP        IPv6
TCP       215       200       15
UDP       12        9         3

Con -a (all) si vedono tutti i socket, in ogni stato. Combinando awk, sort e uniq -c si ottiene un conteggio per stato, utile per capire se un server ha troppe connessioni aperte. ss -s offre direttamente un riepilogo statistico. Gli stati più comuni: ESTAB (aperta), TIME-WAIT (chiusa da poco, in attesa di sicurezza), CLOSE-WAIT (l'altro lato ha chiuso, ma il programma locale no: se se ne accumulano tanti, è di solito un difetto del programma) e SYN-RECV (connessione in avvio: un numero molto alto può indicare un attacco o un sovraccarico).

Esempio 6: chi si collega al mio server?

$ ss -tn state established '( sport = :443 )' | awk 'NR>1 {print $4}' | cut -d: -f1 | sort | uniq -c | sort -nr | head -n 5
     38 198.51.100.23
     12 203.0.113.77
      9 192.0.2.45
      4 198.51.100.9
      2 203.0.113.5

Un uso tipico su un server web: le connessioni stabilite sulla porta 443 (sport = :443, la porta locale), con l'indirizzo del cliente estratto dalla quarta colonna (Peer Address:Port), contate per indirizzo e ordinate. Si individuano subito i client più "pesanti" o un abuso. Funziona con IPv4; con IPv6 gli indirizzi contengono altri due punti, e il cut va adattato. Gli stessi strumenti sono quelli degli articoli su cut, sort e uniq.

Esempio 7: filtri per indirizzo e per porta

$ ss -tn dst 203.0.113.10
$ ss -tn dst 203.0.113.0/24
$ ss -tn dport = :22
$ ss -tn 'dport = :22 or dport = :443'
$ ss -tn src 192.168.1.25 sport = :22

Il filtro finale accetta src / dst (indirizzo sorgente e destinazione, anche con rete e maschera), sport / dport (porta sorgente e destinazione), con operatori come =, !=, >, < e le parole and / or. Se il filtro ha parentesi, spazi o simboli di confronto, va scritto tra apici perché non lo interpreti la shell. Per ss "sorgente" significa sempre il lato locale e "destinazione" il lato remoto: sport = :443 sono le connessioni arrivate alla porta 443 di questo computer (un server), mentre dport = :443 sono quelle che questo computer ha aperto verso la porta 443 di altri (un client).

Esempio 8: i dettagli di una connessione e i tempi

$ ss -tnpo state established '( dport = :443 )'
Recv-Q Send-Q Local Address:Port    Peer Address:Port Process
0      0      192.168.1.25:51234   203.0.113.10:443  users:(("firefox",pid=3210,fd=92)) timer:(keepalive,119min,0)
$ ss -tni dst 203.0.113.10
ESTAB 0 0 192.168.1.25:51234 203.0.113.10:443
	 cubic wscale:7,7 rto:204 rtt:14.5/6.2 mss:1448 cwnd:10 bytes_sent:5120 bytes_received:48213 ...

Con -o si vedono i temporizzatori (per esempio quando scade il keepalive), con -i le informazioni interne di TCP: tempo di andata e ritorno (rtt, in millisecondi), finestra di congestione (cwnd), byte inviati e ricevuti. Sono utili per diagnosticare connessioni lente o instabili, e vanno oltre l'uso quotidiano.

Esempio 9: i socket Unix e gli altri protocolli

$ ss -xl
Netid State  Recv-Q Send-Q Local Address:Port
u_str LISTEN 0      4096   /run/mysqld/mysqld.sock 24571   * 0
u_str LISTEN 0      4096   /var/run/docker.sock 25881      * 0
$ ss -4 -tn
$ ss -6 -tn
$ ss -ltn6

Oltre alla rete, ss mostra i socket Unix (-x): canali di comunicazione tra programmi sulla stessa macchina, che al posto di una porta usano un file (come /run/mysqld/mysqld.sock per il database o /var/run/docker.sock per Docker). Con -4 e -6 si limita l'elenco a IPv4 o a IPv6. Altre opzioni per i protocolli: -w per i socket grezzi (raw) e --packet per quelli a livello di pacchetto.

Esempio 10: seguire i cambiamenti nel tempo

$ watch -n 2 'ss -s'
$ watch -n 1 "ss -Htn state established '( sport = :443 )' | wc -l"

ss mostra una fotografia dell'istante in cui lo si lancia. Per osservare l'evoluzione si usa watch, che ripete il comando a intervalli (qui ogni 2 secondi o ogni secondo) e mostra l'ultimo risultato a schermo; si esce con Ctrl+C. Nel secondo esempio -H toglie la riga d'intestazione e wc -l conta le connessioni: si vede in diretta quante ce ne sono.

Esempio 11: uso negli script

#!/bin/bash
PORTA="${1:?Indica la porta da controllare}"

if ss -ltn "sport = :$PORTA" | grep -q LISTEN; then
    echo "Qualcuno è in ascolto sulla porta $PORTA"
else
    echo "Nessun servizio in ascolto sulla porta $PORTA" >&2
    exit 1
fi

Uno script così verifica che un servizio sia in ascolto prima di fare altro (per esempio dopo un riavvio con systemctl). Il filtro "sport = :$PORTA" è un unico argomento tra virgolette doppie, in modo che la shell sostituisca la variabile. grep -q non stampa nulla e restituisce solo un codice di uscita (0 se trova LISTEN), che if usa. Per controllare da fuori se una porta risponde, invece, ss non basta: si usa uno strumento come curl o nc da un altro computer, perché un firewall potrebbe bloccarla anche se il servizio è in ascolto.

Opzioni utili

  • -t / -u / -x: socket TCP / UDP / Unix; -4 / -6: solo IPv4 / IPv6.
  • -l: solo quelli in ascolto; -a: tutti, in ascolto e non; senza opzioni: solo le connessioni stabilite.
  • -n: numeri invece dei nomi (più veloce, senza risoluzione DNS); -r: risolve i nomi.
  • -p: mostra il processo (serve sudo per quelli degli altri utenti).
  • -s: riepilogo statistico; -o: temporizzatori; -i: informazioni interne TCP; -e: dettagli estesi (utente, inode).
  • -H: nasconde la riga d'intestazione (comodo negli script); -O: una riga per socket.
  • state STATO: filtra per stato (established, listening, time-wait, close-wait…); src, dst, sport, dport: filtri per indirizzo e porta.
  • -K: chiude le connessioni corrispondenti al filtro (se il kernel lo permette; richiede sudo).

Attenzione a

  • Servono sudo e -p per conoscere il programma: senza privilegi, la colonna Process è vuota per i processi degli altri utenti, e questo è l'errore più comune quando "non si vede" il servizio.
  • Un servizio in ascolto non è raggiungibile da fuori: ss dice cosa succede nel computer. Un firewall (ufw, nftables), il router o il fornitore della VPS possono comunque bloccare la porta. E se l'indirizzo di ascolto è 127.0.0.1, da fuori il servizio non c'è.
  • 0.0.0.0 è diverso da 127.0.0.1: un database in ascolto su 0.0.0.0:3306 è offerto a chiunque possa raggiungerlo. Salvo necessità reale, i servizi interni dovrebbero restare su 127.0.0.1, o essere protetti da un firewall.
  • Senza -n è più lento e meno chiaro: ss prova a trasformare gli indirizzi in nomi (e le porte in nomi di servizio, come http o ssh), e per alcuni indirizzi l'operazione richiede tempo. Salvo esigenze particolari, usa sempre -n.
  • Le opzioni si fondono, e l'ordine non conta: -tulpn, -lntp e -pntlu sono equivalenti. Senza -t e -u (o -a) può darsi che non si veda ciò che si cerca.
  • I filtri vogliono gli apici: parentesi, >, < e ! hanno un significato per la shell. Scrivi '( sport = :443 )' tra apici, e lascia gli spazi intorno a = e alle parentesi.
  • Sono dati istantanei: le connessioni nascono e muoiono in millisecondi, quindi una sola esecuzione può non mostrare un problema intermittente. Per seguirlo nel tempo, watch o un registro.
  • Docker cambia il quadro: le porte pubblicate da un container compaiono come docker-proxy (o, in alcune configurazioni, non compaiono affatto, perché il traffico viene deviato dal firewall senza un processo in ascolto). Se un servizio in Docker "non c'è" in ss, controlla con docker ps.
  • TIME-WAIT in gran numero è normale su un server molto frequentato; CLOSE-WAIT che cresce nel tempo no, perché indica un programma che non chiude le connessioni.
  • Non fidarti solo di grep: ss -tln | grep 80 trova anche 8080, 1800 e :80 sul lato remoto. Usa il filtro 'sport = :80', oppure un modello preciso come grep ':80 '.
  • -K chiude connessioni reali: con sudo ss -K dst 203.0.113.10 si interrompono tutte le connessioni verso quell'indirizzo, comprese quelle SSH con cui stai lavorando. Va usato con molta cautela (e funziona solo se il kernel è compilato con il supporto).
  • I vecchi comandi: netstat -tulpn corrisponde a ss -tulpn; chi conosce il primo trova le stesse opzioni. lsof -i :8080 è un'altra alternativa, per sapere chi usa una porta.

Riepilogo

ComandoCosa fa
sudo ss -tulpnPorte TCP e UDP in ascolto, con i programmi
sudo ss -ltnp 'sport = :8080'Chi usa la porta 8080
ss -tnConnessioni TCP stabilite
ss -tnp state establishedConnessioni stabilite, con il programma
ss -tanTutti i socket TCP, in ogni stato
ss -sRiepilogo statistico
ss -tn dst 203.0.113.10Connessioni verso un certo indirizzo
ss -tn 'dport = :443'Connessioni verso la porta 443
ss -tniInformazioni interne di TCP (tempi, byte)
ss -xlSocket Unix in ascolto
ss -4 / ss -6Solo IPv4 / solo IPv6
watch -n 2 'ss -s'Segue le statistiche nel tempo