Conntrack

Le suivi de connexion

Le suivi de connexion est un concept essentiel dans Netfilter. C'est une sorte d'intelligence artificielle qui permet d'établir des liens de cause à effet entre les paquets qui passent dans la pile. Il faut, à un moment donné, dire quelques mots à propos de ce suivi de connexion. Comme c'est une notion qui va intervenir aussi bien dans le filtrage que dans la traduction d'adresses, autant en parler tout de suite.

Expérience préliminaire

Le principe du suivi de connexion permet de réaliser un « firewall statefull », c'est à dire qu'il va réagir intelligemment sur une connexion donnée, suivant son état. Voyez éventuellement à ce propos le chapitre sur la sécurité. Comme ce n'est pas très simple d'expliquer ça, nous allons observer un exemple sur le terrain. Nous allons établir une connexion TCP à partir du protocole HTTP :

No. Time        Source       Destination  Protocol Info
10 10.181832   192.168.0.10  212.27.35.1  TCP      4252 > http [SYN]
11 10.204707   212.27.35.1   192.168.0.10 TCP      http > 4252 [SYN, ACK]
12 10.204848   192.168.0.10  212.27.35.1  TCP      4252 > http [ACK]
13 10.205333   192.168.0.10  212.27.35.1  HTTP     GET / HTTP/1.1

L'établissement d'une connexion TCP suit un protocole strict :

  • Une requête de synchronisation [SYN] de la part de l'initiateur du dialogue (le client),
  • une réponse d'accusé réception de la synchronisation [SYN,ACK] de la part du serveur,
  • un accusé réception du client [ACK]

Observons également les sockets.

  • Trame n°10:
    Le client [192.168.0.10] s'adresse au serveur [212.27.35.1] sur le port http (80). Il attend une réponse sur le port 4252. C'est un numéro pris plus ou moins au hasard. A priori, personne ne peut le deviner à l'avance.
  • Trame n°11:
    Le serveur, bien élevé, répond au client sur le port demandé (4252)

La même chose, mais plus détaillée, ce qui donne l'occasion de voir l'ensemble des « flags » exploitables au niveau TCP :

Frame 10 (62 bytes on wire, 62 bytes captured)
...
Transmission Control Protocol
    Source port: 4252 (4252)
    Destination port: http (80)
    Sequence number: 3290220049
    Header length: 28 bytes
    Flags: 0x0002 (SYN)
        0... .... = Congestion Window Reduced (CWR): Not set
        .0.. .... = ECN-Echo: Not set
        ..0. .... = Urgent: Not set
        ...0 .... = Acknowledgment: Not set
        .... 0... = Push: Not set
        .... .0.. = Reset: Not set
        .... ..1. = Syn: Set
        .... ...0 = Fin: Not set
...

Frame 11 (62 bytes on wire, 62 bytes captured)
...
Transmission Control Protocol
    Source port: http (80)
    Destination port: 4252 (4252)
    Sequence number: 1602605975
    Acknowledgement number: 3290220050
    Header length: 28 bytes
    Flags: 0x0012 (SYN, ACK)
        0... .... = Congestion Window Reduced (CWR): Not set
        .0.. .... = ECN-Echo: Not set
        ..0. .... = Urgent: Not set
        ...1 .... = Acknowledgment: Set
        .... 0... = Push: Not set
        .... .0.. = Reset: Not set
        .... ..1. = Syn: Set
        .... ...0 = Fin: Not set
 ...

Frame 12 (54 bytes on wire, 54 bytes captured)
...
Transmission Control Protocol
    Source port: 4252 (4252)
    Destination port: http (80)
    Sequence number: 3290220050
    Acknowledgement number: 1602605976
    Header length: 20 bytes
    Flags: 0x0010 (ACK)
        0... .... = Congestion Window Reduced (CWR): Not set
        .0.. .... = ECN-Echo: Not set
        ..0. .... = Urgent: Not set
        ...1 .... = Acknowledgment: Set
        .... 0... = Push: Not set
        .... .0.. = Reset: Not set
        .... ..0. = Syn: Not set
        .... ...0 = Fin: Not set
    Window size: 16944
    Checksum: 0xe79b (correct)

De ces premières observations, nous pouvons déduire quelques choses intéressantes. En considérant des échanges TCP entre deux sockets toujours les mêmes :

  • Lorsqu'un paquet TCP contient le flag SYN, c'est que c'est une nouvelle connexion qui commence.
  • lorsqu'un paquet TCP contient les flags SYN et ACK, c'est que la connexion est acceptée, elle est donc établie,
  • lorsqu'un paquet ne contient que le flag ACK, c'est que la connexion se continue.

Mais voyons un peu plus loin…

No. Time       Source        Destination  Protocol Info
29 10.427697   192.168.0.10  212.27.35.1  TCP      4252 > http [ACK]
30 10.731147   192.168.0.10  212.27.35.1  TCP      span class="bhly">4253 > http span class="bhly">[SYN]
31 10.752981   212.27.35.1   192.168.0.10 TCP      http > 4253 [SYN, ACK]
32 10.753165   192.168.0.10  212.27.35.1  TCP      4253 > http [ACK]
33 10.753707   192.168.0.10  212.27.35.1  HTTP     GET /images/titre.gif HTTP/1.1
34 10.780941   192.168.0.10  212.27.35.1  TCP      4252 > http [FIN, ACK]

Ce que nous observons ici, c'est l'établissement d'une nouvelle connexion TCP entre les mêmes protagonistes. Bien entendu pour éviter les mélanges, le port du client n'est plus le même. C'est cette fois-ci 4253.

Autrement dit, le même client (192.168.0.10) ouvre une nouvelle connexion TCP sur le même serveur (212.27.35.1), toujours sur le port 80, mais attend les réponses sur un nouveau port, alors que la connexion précédente existe toujours.

Le client met fin à la précédente (trame 34) avec le flag [FIN] une fois seulement que la seconde connexion est établie.

Dans ces conditions, il est pertinent de penser que cette nouvelle connexion est en relation directe avec la première. Nous dirons que c'est une connexion en relation avec la première.

Premières déductions

Si l'on met en place un système capable de mémoriser ce qu'il se passe sur la couche TCP, alors il va devenir possible de savoir si une connexion est dans l'un de ces états :

  • NEW
    nouvelle connexion (elle contient le flag SYN),
  • ESTABLISHED
    connexion déjà établie, elle ne devrait pas contenir de SYN ni de FIN,
  • RELATED
    la connexion présente une relation directe avec une connexion déjà établie,
  • INVALID
  • la connexion n'est pas conforme, contient un jeu de flags anormal, n'est pas classable dans l'une des trois catégories précédentes.

Ceci est intéressant, parce que l'on pourra interdire à priori à toute connexion NEW d'entrer dans notre installation, il n'y a aucune raison qu'il en rentre si nous n'avons pas de serveur. Eventuellement, nous pourrons par exemple créer des exceptions pour les port ssh, si nous souhaitons accéder à notre machine depuis le Net.

En revanche, toute connexion ESTABLISHED ou RELATED devra pouvoir entrer depuis le Net.

Dans l'autre sens, du LAN vers le Net, les paquets NEW doivent pouvoir passer, de même, bien entendu que les ESTABLISHED et les RELATED.

Les paquets INVALID pourront éventuellement être tracés dans les logs.

Et pour l'UDP ?

Là, c'est plus délicat puisqu'il n'y a justement pas de connexion. Il sera donc impossible de définir de façon précise l'état d'un échange UDP. Ce que l'on pourra faire, c'est mettre en place un « timer » pour décider de l'état d'un paquet UDP. Nous pouvons prendre l'exemple simple d'une requête DNS depuis notre réseau privé.

  • Le premier paquet UDP sort de notre réseau, sur un port connu et identifié (53) vers un serveur DNS. Nous pouvons décider de le laisser passer et nous le qualifions de NEW. Il déclenche un timer,
  • si avant expiration du « timer », nous recevons un paquet UDP dudit serveur DNS, nous considèrerons que c'est un paquet ESTABLISHED.

Dans la pratique

Un module principal de suivi de connexion est chargé dynamiquement en cas de besoin, il s'agit du module ip_conntrack. Cependant tout n'est pas toujours si simple et ce module peut montrer ses limites sur des protocoles particulièrement complexes, comme par exemple FTP.

ip_conntrack ne pourra assurer qu'une connexion FTP de type passive. Si l'on souhaite assurer le suivi de connexion sur du FTP en mode actif, il faudra avoir recours au module spécialisé nf_conntrack_ftp. Mais celui-ci ne se chargera pas dynamiquement, Nous aurons à le charger nous-même. Nous aurons aussi besoin dans ce cas de nf_nat_ftp; si notre passerelle fait du NAT.

Nous verrons sur le terrain le travail de conntrack dans divers exemples.