Lorsqu'on parle de binder en réseau, cela concerne l'action d'écouter sur un port TCP ou UDP afin de pouvoir recevoir des données et en transmettre en retour. C'est donc un service qui va être attaché à une interface réseau précise ou à toutes les interfaces.
Interface physique, adresse, bind
Un ordinateur dispose généralement de deux interfaces à minima: une ethernet et une loopback (Dans le cas d'un ordinateur portable, une interface wifi sera sans doute présente)
Pour visualiser ces interfaces, la commande ip link show
donne la liste:
blindaue@debian:~$ ip link show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN mode DEFAULT group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP mode DEFAULT group default qlen 1000
link/ether de:ad:ee:34:55:ad brd ff:ff:ff:ff:ff:ff
altname enp0s18
Sur ces interfaces, on va généralement avoir une ou plusieurs adresses IP:
blindaue@debian:~$ ip addr show
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN group default qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host noprefixroute
valid_lft forever preferred_lft forever
2: ens18: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc fq_codel state UP group default qlen 1000
link/ether de:ad:ee:34:55:ad brd ff:ff:ff:ff:ff:ff
altname enp0s18
inet 130.79.214.186/27 brd 130.79.214.191 scope global ens18
valid_lft forever preferred_lft forever
inet6 fe80::dcad:eeff:fe34:55ad/64 scope link
valid_lft forever preferred_lft forever
Lorsqu'un service réseau est configuré (par exemple, un serveur web ou une API), vous devez décider sur quelle interface il va écouter. Cela dépend du choix de l'adresse IP liée au service :
- 0.0.0.0 : signifie que le service écoute sur toutes les interfaces disponibles (public et local).
- 127.0.0.1 : limite l'accessibilité du service à la machine locale.
- Une IP spécifique (ex: 130.79.214.186) : restreint l'accès à une interface réseau particulière.
Le choix de cette configuration détermine si votre service sera accessible depuis l'extérieur ou non.
Exemple basique
Nous allons voir comment créer un serveur simple en Python puis observer les effets du binding sur l'accessibilité du service.
Serveur
import socket
server_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server_socket.bind(("0.0.0.0", 12345))
server_socket.listen(5)
print("Serveur écoutant sur 0.0.0.0:12345")
while True:
client_socket, addr = server_socket.accept()
client_socket.sendall(b"Hello world!\n")
client_socket.close()
Tests
Si on tente de se connecter sur le port 12345:
blindaue@debian:~$ telnet 130.79.214.186 12345
Trying 130.79.214.186...
Connected to vm-26.iutrs.unistra.fr.
Escape character is '^]'.
Hello world!
Connection closed by foreign host.
Maintenant, on change l'adresse de bind pour ne plus utiliser 0.0.0.0
(qui veut dire: écouter sur toutes les adresses), mais on précise 127.0.0.1
server_socket.bind(("127.0.0.1", 12345))
Et on reteste la connexion:
blindaue@debian:~$ telnet 130.79.214.186 12345
Trying 130.79.214.186...
telnet: connect to address 130.79.214.186: Connection refused
telnet: Unable to connect to remote host
blindaue@debian:~$ telnet 127.0.0.1 12345
Trying 127.0.0.1...
Connected to vm-26.iutrs.unistra.fr.
Escape character is '^]'.
Hello world!
Connection closed by foreign host.
La connexion est refusée via l'adresse publique, mais reste accessible via l'adresse de loopback.
Vérifications
Le système d'exploitation dispose d'outils pour visualiser l'état des connexions, avec ss
(ou netstat
pour des bsd et anciens linux). Listons uniquement les ports TCP en écoute, pour le premier serveur avec le listen sur 0.0.0.0
et le second pour 127.0.0.1
: On voir bien l'adresse de bind dans le résultat de la commande, en mode LISTEN
:
blindaue@debian:~$ ss -tl
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:ssh 0.0.0.0:*
LISTEN 0 5 0.0.0.0:12345 0.0.0.0:*
LISTEN 0 128 [::]:ssh [::]:*```
blindaue@debian:~$ ss -tl
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 128 0.0.0.0:ssh 0.0.0.0:*
LISTEN 0 5 127.0.0.1:12345 0.0.0.0:*
LISTEN 0 128 [::]:ssh [::]:*
Dans les applications tierces
Dans le cas des applications tierces, les auteurs ont forcément prévu de la configuration pour préciser sur quelle adresse binder l'application. C'est le premier élément de sécurité au niveau du réseau. Il faut donc se référer à la documentation. Eh oui, la doc n'a pas été écrite pour Mme Michu!
Dans le désordre, je donne quelques informations:
ollama
: Ollama binds 127.0.0.1 port 11434 by default. Change the bind address with the OLLAMA_HOST environment variablenginx
use the listen keywork:listen 172.20.1.1:80 default_server;
Les images dockers
Dans certains cas, vous utilisez des images dockers pour déployer un service. Et ce service, pour l'exploiter, il faut bien le sortir de son container. La doc vous dit d'utiliser l'option -p
pour publier le port. Exemple de redis qui est accessible sur le port 6379
docker run -d -p 6379:6379 redis
Et la, ..., c'est le drame!
Vous exposez votre service sur le port 6379 sur toutes les interfaces. Pour vous, cela fonctionne! Mais également pour les autres, sans authentification et ils peuvent y supprimer et y écrire à volonté!
Vous trouverez également un autre syntaxe pour exposer un port, dans le Dockerfile, par exemple pour un nginx:
ports:
- "8080:80"
La aussi, même erreur, le bind est sur toutes les interfaces
Pour corriger cela, il faut spécifier l'adresse où sera fait le bind. Dans ce cas, le port du container est exposé, mais juste sur 127.0.0.1. Et la, votre VM y aura accès et pas le reste du monde:
ports:
- "127.0.0.1:8080:80"
ou en cli:
docker run -d -p 127.0.0.1:8080:80 nginx
No: A vous de jouer ici pour bien vérifier que le service est bien bindé sur cette interface :)
Bien configurer le bind
- Tests et développement : En local, on préfère souvent 127.0.0.1 pour éviter toute exposition externe accidentelle.
- Sécurité : Un service écoute par défaut sur 0.0.0.0 ce qui expose les ports inutilement: C'est une surface d'attaque supplémentaire.
- On n'est pas à l'abri d'un exploit sur le service exposé
- On n'est pas à l'abri d'une erreur de configuration. LEs exemples de donénes exposés par erreur sont légions
- On n'est pas à l'abri d'une modification non connu des firewall pour protéger les services
- Loi de Murphy: si cela peut mal tourner, cela tournera forcement mal