Un port ouvert et oublié est la porte d'entrée classique d'un serveur : une base de données sur une adresse publique, un tableau de bord de débogage, un conteneur qui a publié un port que personne ne voulait publier. La vérification prend une minute quand on connaît deux commandes et un piège. Nous avons tout fait ci-dessous sur un serveur Ubuntu 24.04 neuf issu de notre image, puis nous l'avons scanné depuis une seconde machine : les sorties sont celles que vous verrez sur votre propre serveur.
1. Ce qui écoute
La commande est ss. Les lettres signifient TCP, UDP, sockets en écoute, ports numériques et nom du processus :
sudo ss -tulpn
Sur un serveur neuf, elle affiche six lignes :
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 colonne qui compte est Local Address. Elle dit qui peut se connecter :
| Adresse | Qui peut la joindre |
|---|---|
127.0.0.1:… ou 127.0.0.53 | Seulement les programmes de ce serveur |
0.0.0.0:… | Tout le monde en IPv4, sauf blocage par le pare-feu |
[::]:… | Tout le monde en IPv6 (et en général IPv4 aussi) |
10.0.0.5:… (une adresse précise) | Quiconque peut joindre cette adresse |
Le serveur neuf n'expose donc qu'une chose : SSH sur le port 22, en IPv4 et en IPv6. Le résolveur DNS sur 127.0.0.53 est privé.
Pour ne lister que ce qu'Internet pourrait voir, filtrez les adresses de bouclage :
sudo ss -tlnp | awk 'NR>1 && $4 !~ /^(127\.|\[::1\])/ {print $4, $6}'
Lancez-la après chaque installation. Nous avons installé nginx, Docker et une petite application Python, et la liste est passée de 2 à 10 lignes, dont un docker-proxy sur 0.0.0.0:8081 et un socket containerd sur un port localhost aléatoire.
2. Quel processus occupe un port
Quand un port est pris et que vous voulez savoir par qui :
sudo lsof -i :80 -P -n
sudo fuser 80/tcp
sudo ss -tlnp 'sport = :80'
lsof est le plus lisible. Pour nginx, il a listé un processus maître en root et un worker en www-data, tous deux sur *:80, en IPv4 et IPv6. fuser n'affiche que les PID, pratique dans les scripts. S'il faut libérer le port, arrêtez le service au lieu de tuer le PID, sinon systemd le relance : sudo systemctl stop nginx.
3. Ce qu'Internet voit
ss donne le point de vue du serveur. Celui d'Internet peut différer, parce qu'un pare-feu s'intercale ou qu'un service est lié à 127.0.0.1. Pour voir la réalité, scannez le serveur depuis une autre machine, votre portable ou un second serveur. L'outil est nmap (apt install nmap, ou l'installateur de nmap.org sous Windows et macOS).
nmap your-server-ip
nmap -p- your-server-ip
nmap -p 22,80,443,3306 your-server-ip
La première forme teste les 1 000 ports les plus courants, la deuxième les 65 535, la troisième une liste. Sur notre serveur neuf, avant tout pare-feu :
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
Le scan complet des 65 535 ports a pris 8,9 secondes et a trouvé le même port unique. Les ports fermés répondent instantanément par un reset, d'où la rapidité.
Trois états comptent :
- open : un programme a accepté la connexion. C'est votre surface d'attaque.
- closed : le serveur a répondu que rien n'écoute là. Sans danger, mais cela confirme que le serveur existe et ce qu'il ne fait pas tourner.
- filtered : pas de réponse. Un pare-feu a jeté le paquet. Le scanner ne peut pas savoir si un service se cache derrière.
Pour un seul port sans rien installer, nc -zv ip-du-serveur 443 répond succeeded ou refused, et reste bloqué jusqu'au délai d'attente sur un port filtré. Pour l'UDP, ajoutez -sU à nmap et attendez-vous à de la lenteur ; sur notre serveur, les trois ports UDP courants, 53, 123 et 161, étaient closed.
Une remarque pour les offres NAT : depuis Internet, seul le port SSH que nous vous avons redirigé est joignable. Un scan de l'adresse partagée montre notre bordure, pas votre serveur, et les autres ports sont injoignables, quoi qu'il y écoute. Avec une offre dotée de sa propre adresse IPv4, chaque port ouvert est à vous de gérer.
4. Fermer ce qui ne sert pas
Trois méthodes, de la meilleure à la plus large.
Lier à localhost. Si seuls des programmes du serveur utilisent un service, faites-le écouter sur 127.0.0.1. Nous avons lancé une petite application web avec --bind 127.0.0.1 8080 : ss l'a montrée sur 127.0.0.1:8080, et nmap depuis l'extérieur a indiqué 8080/tcp closed. Pour les bases de données, c'est le bon réglage par défaut : Redis, MySQL et PostgreSQL écoutent sur localhost dès l'installation sous Ubuntu, et nos guides Redis et MySQL le laissent ainsi.
Arrêter le service. sudo systemctl disable --now nom supprime l'écoute pour de bon. Vérifiez avec ss qu'elle a disparu.
Activer le pare-feu. Autorisez d'abord SSH, puis ce que vous servez, puis activez :
sudo ufw allow OpenSSH
sudo ufw allow 80/tcp
sudo ufw enable
sudo ufw status verbose
Le scan depuis l'extérieur a complètement changé :
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
Chaque port non autorisé est passé de closed à filtered, et le scan complet des 65 535 ports a pris 110 secondes au lieu de 8,9. Plus de temps par port pour le scanner, moins d'informations sur votre serveur. Notre guide UFW couvre la limitation de débit, l'autorisation d'une seule IP et l'IPv6.
Mais regardez la troisième ligne.
5. La surprise Docker
Le port 8081 est resté ouvert, pare-feu actif et sans règle l'autorisant. Ce port appartient à un conteneur lancé avec docker run -p 8081:80 nginx:alpine. Docker ajoute ses propres règles au pare-feu du noyau, et elles sont évaluées avant celles d'ufw. La règle qu'il a écrite sur notre serveur :
-A DOCKER ! -i docker0 -p tcp --dport 8081 -j DNAT --to-destination 172.17.0.2:80
Tout paquet destiné au port 8081 est redirigé dans le conteneur avant qu'ufw n'ait son mot à dire. C'est le cas de chaque conteneur avec -p ou une entrée ports:, et ss l'affiche honnêtement comme docker-proxy sur 0.0.0.0:8081.
La correction consiste à publier sur localhost uniquement :
docker rm -f web
docker run -d --name web -p 127.0.0.1:8081:80 nginx:alpine
Dans Compose, écrivez "127.0.0.1:8081:80" sous ports:. Après la modification, ss a montré 127.0.0.1:8081, la règle Docker a gagné -d 127.0.0.1/32, et le scan depuis l'extérieur a indiqué 8081/tcp filtered. Placez nginx en reverse proxy devant pour le côté public, avec HTTPS, afin que les seuls ports ouverts du serveur soient 22, 80 et 443.
Check-list après chaque changement
sudo ss -tulpn: chaque ligne0.0.0.0ou[::]est-elle voulue ?nmap ip-du-serveurdepuis votre portable : les ports ouverts correspondent-ils ?- Une base de données, un cache ou un panneau d'administration sur une adresse publique : passez-le sur
127.0.0.1ou derrière le pare-feu aujourd'hui. - Chaque entrée Docker
ports:: commence-t-elle par127.0.0.1:si ce n'est pas le port web public ? - SSH ouvert à tous : ajoutez fail2ban ou la connexion par clé uniquement.
Sous Windows Server, la même vérification est Get-NetTCPConnection -State Listen, et les règles vivent dans le pare-feu Windows. Chaque offre Linux, dès le Nano à $3, existe en version avec sa propre adresse IPv4, et là, ce que vous voyez dans ss est ce que le monde voit, moins votre pare-feu.
Commentaires
Pas encore de commentaires. Soyez le premier.