Observer les ports et les connexions
ss -lntp, conflits de ports et adresses d’écoute.
Objectifs
À la fin de cette leçon, vous saurez :
- cartographier les ports en écoute avec
ss -lntp, ligne par ligne ; - distinguer une écoute sur
127.0.0.1d'une écoute sur0.0.0.0— et prédire la joignabilité ; - diagnostiquer un conflit de port (EADDRINUSE) et identifier le processus fautif ;
- observer des connexions TCP vivantes avec
ss -tnp.
🔗 Pour vous rafraîchir la mémoire : observer les processus · TCP, UDP et sockets
Lister ce qui écoute : ss -lntp
ss -lntp
Décodage des options : listen (uniquement en écoute), numérique (ne résous pas les noms, plus rapide et sans dépendance DNS), tcp, processus.
Sortie type, annotée :
State Recv-Q Local Address:Port Peer Address:Port Process
LISTEN 0 0.0.0.0:22 0.0.0.0:* users:(("sshd",pid=512))
LISTEN 0 127.0.0.1:3000 0.0.0.0:* users:(("node",pid=2041))
LISTEN 0 [::]:22 [::]:* users:(("sshd",pid=512))
Colonne par colonne :
- State
LISTEN: le processus attend des connexions entrantes (vs connexions établies, voir plus bas) ; - Local Address:Port : OÙ il attend — c'est la colonne décisive ;
- Peer : en écoute, toujours
*(« n'importe qui ») ; - Process : le propriétaire, nom + PID — le lien direct avec le chapitre processus.
La distinction qui explique 80 % des mystères de connexion
Regardez les deux lignes Node/SSH ci-dessus : même machine, ports différents, mais surtout adresses d'écoute différentes :
| Écoute sur | Signification | Joignable depuis |
|---|---|---|
0.0.0.0:3000 | toutes les interfaces réseau de la machine | le monde entier (si firewall/NAT le permettent) |
127.0.0.1:3000 | uniquement la boucle locale | la machine elle-même, point final |
192.168.65.3:3000 | uniquement cette interface précise | ce segment réseau seulement |
([::] est l'équivalent IPv6 de 0.0.0.0.)
C'est LA cause n°1 du classique « mon serveur tourne mais je n'y accède pas depuis l'autre machine » : le serveur écoute sur 127.0.0.1, donc il ne répond qu'à son propre hôte — comportement souvent voulu (défaut prudent de beaucoup de frameworks), jamais accidentel à ignorer. Le réflexe professionnel avant toute autre investigation : ss -lntp | grep <port> et lire l'adresse d'écoute.
Créons un serveur pour expérimenter
Lançons un petit serveur HTTP jetable :
$ python3 -m http.server 8000
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
Dans un second terminal, vérifions :
ss -lntp | grep 8000
LISTEN 0 2048 0.0.0.0:8000 0.0.0.0:* users:(("python3",pid=4120,fd=3))
Le serveur écoute sur toutes interfaces : joignable depuis le réseau local (sous réserve firewall/NAT, comme toujours). Notez au passage le PID 4120 : nous savons désormais exactement quel processus possède ce port.
Provoquer le conflit : EADDRINUSE
Tentons un second serveur sur le même port :
$ python3 -m http.server 8000
Traceback (most recent call last):
...
OSError: [Errno 98] Address already in use
Le noyau refuse : rappel de la théorie, une seule socket en écoute par couple adresse/port — sinon, qui recevrait les paquets ? Ce message (EADDRINUSE) est l'un des plus fréquents de votre carrière ; il a maintenant un sens précis.
Trois issues possibles, dans l'ordre logique :
- Changer de port pour le nouveau processus (le plus simple) ;
- Arrêter l'occupant si c'est un vestige oublié — vous avez son PID :
kill 4120 # signal poli SIGTERM (chapitre 2) ss -lntp | grep 8000 # réponse vide → port libéré - Comprendre que l'occupant est légitime (un vrai service tourne déjà là !) et ne rien toucher.
⚠️ Cas subtil à connaître : le port semble libre mais l'erreur persiste après avoir tué le processus. Cause fréquente : une connexion TCP encore en cours de fermeture retient le port quelques dizaines de secondes (TIME_WAIT) ; ou bien un autre utilisateur/service détient le port mais ss ne vous montre pas son PID sans droits suffisants. Diagnostic : sudo ss -lntp | grep 8000 révèle tout.
Observer les connexions vivantes : ss -tnp
Sans -l, on voit les connexions établies plutôt que les écoutes :
ss -tnp
ESTAB 0 0 192.168.65.3:22 192.168.65.1:51234 users:(("sshd",pid=512))
Lecture : votre session SSH actuelle ! Chaque ligne EST un quadruplet complet de la théorie : IP locale + port local ↔ IP distante + port distant. Remarquez le port distant 51234 : c'est le port source éphémère choisi par le client SSH côté hôte — la théorie du quadruplet sous vos yeux.
Faites l'expérience complète : lancez le serveur Python ci-dessus, puis dans un autre terminal connectez-vous dessus :
curl http://localhost:8000 > /dev/null &
ss -tnp | grep 8000
ESTAB 0 0 127.0.0.1:8000 127.0.0.1:45678 users:(("python3",pid=4120))
Deux extrémités, deux rôles : python3 sur SON port d'écoute (8000), curl sur un port éphémère (45678). La connexion meurt aussitôt après l'échange — relancez le grep, elle aura disparu.
Exercice
- Listez les ports en écoute de votre VM. Quels services tournent déjà ? Lesquels sont accessibles depuis l'extérieur ?
- Lancez
python3 -m http.server 8000et retrouvez-le dansss -lntp. Sur quelle adresse écoute-t-il ? Que peut-on en déduire sur sa joignabilité ? - Tentez d'en lancer un second sur le même port : notez l'erreur exacte, puis libérez le port proprement.
- Faites une requête
curlvers lui pendant qu'il tourne et attrapez la connexion ESTAB avecss -tnp. Identifiez le quadruplet. - Question de réflexion : PostgreSQL (port 5432) devrait-il écouter sur
0.0.0.0ou127.0.0.1sur un serveur web ? Pourquoi ?
Résumé
ss -lntp= la cartographie des portes ouvertes de la machine, avec propriétaires.- Adresse d'écoute = décision de sécurité :
127.0.0.1(invisible) vs0.0.0.0(exposé). - EADDRINUSE = un couple adresse/port, un seul propriétaire ; diagnostic avant démarrage.
ss -tnpmontre les quadruplets vivants : la théorie rendue visible.
Correction disponibleCherchez d’abord par vous-même.Voir la correction
Correction
Réponses détaillées
Question 1. Sortie type d'une VM de cours : sshd sur 0.0.0.0:22 (exposé), éventuellement un agent local sur 127.0.0.1:... (privé). Lecture systématique à adopter : pour chaque ligne, demandez-vous « qui doit pouvoir y accéder ? » puis « est-ce cohérent avec l'adresse d'écoute ? ». Un service interne sur 0.0.0.0 est une alerte potentielle ; un service public sur 127.0.0.1 est un mystère de connexion garantie.
Question 2. 0.0.0.0:8000 — toutes interfaces. Déduction en chaîne : joignable depuis le réseau local SI (a) aucun firewall ne bloque ET (b) le NAT/routage laisse passer. L'écoute seule ne suffit pas à garantir l'accès, mais son absence garantit le refus.
Question 3.
OSError: [Errno 98] Address already in use
Libération propre :
ss -lntp | grep 8000 # retrouver le PID
kill <pid> # SIGTERM poli
ss -lntp | grep 8000 # vide → libéré, vérifié
Si l'erreur persiste malgré un port « libre » : pensez TIME_WAIT ou droits insuffisants (sudo ss -lntp).
Question 4.
ESTAB 0 0 127.0.0.1:8000 127.0.0.1:<port_éphémère> users:(("python3",...))
Quadruplet complet vu en direct : destination fixe (le port d'écoute) ↔ source aléatoire (le client). Si vous avez fait le curl depuis la même machine, les deux adresses sont 127.0.0.1 : la boucle locale de la leçon 08-01 en action.
Question 5. Sur 127.0.0.1 sans hésitation. Une base de données n'a pas à parler au monde extérieur : ses clients légitimes (l'application backend) tournent sur la MÊME machine et passent par la boucle locale. Une écoute sur 0.0.0.0 exposerait PostgreSQL aux tentatives de connexion du réseau entier — mot de passe fort ou non, chaque surface exposée est une surface attaquée. Principe général à retenir pour tout le cursus déploiement : on n'expose publiquement que ce qui doit l'être (web : 80/443), tout le reste écoute en local.