Je ne me souvenais plus : comment fonctionne une capture réseau avec la libpcap (tcpdump
, wireshark
, etc.) dans le noyau Linux ? À quel endroit du noyau le trafic est-il capturé par la libpcap ?
Explication concise dans les pages 10-12 de ce document que l'on peut recouper avec le code Linux de la libpcap.
En gros :
Création d'une socket de type RAW (entête Ethernet conservé), famille d'adresses AF/PF_PACKET (contournement de la partie TCP/IP de la pile réseau), protocole ETH_P_ALL (tous les protocoles). Dans le noyau (Linux), le protocole PF_PACKET, en tant que protocole générique, est traité très tôt par la pile réseau, après que le pilote de la carte réseau ait copié le paquet en RAM, et bien avant la couche IP (schéma) et Netfilter (le pare-feu de Linux). On voit la socket dans /proc/<PID_tcpdump>/fd
;
tcpdump -p
permet de ne pas l'activer ;tcpdump -d
affiche le bytecode BPF ;Depuis quelques mois, nous avons un serveur coturn installé depuis les dépôts Debian GNU/Linux. Implémentation de TURN, l'un des nombreux palliatifs pour tenter de faire fonctionner des logiciels multimédias pair-à-pair avec du NAT au milieu.
Ce service se vautre plusieurs fois par semaine. Évidemment, rien dans son journal. Sauf aujourd'hui : segfault. Mes recherches sur le web retournent rien (ou des paths vieux de 5-7 ans, qui ont donc été intégré depuis, en ce qui concerne l'erreur de segmentation). On pourrait lancer coturn en mode ultra-verbeux (« -V »), sortir gdb
(pour la segfault), etc. Mais en attendant ?
Pour ces cas-là, systemd propose Restart=on-failure
, c'est-à-dire redémarrer un service à chaque fois qu'il crashe.
J'utilise ça dans des units systemd depuis plus de 5 ans. Mais, dans Debian, coturn est livré avec un script sysv qui utilise start-stop-daemon
. Est-ce que ça fonctionne ? Oui.
Utiliser la commande systemctl edit coturn.service
.
Saisir :
[Service]
RemainAfterExit=no
Restart=on-failure
RestartSec=10s
Enregistrer.
Désormais, systemctl status coturn
affiche deux lignes supplémentaires : « Drop-In: /etc/systemd/system/coturn.service.d / override.conf ».
« RemainAfterExit » est nécessaire, sinon systemd ne fait pas le job.
« RestartSec » n'est pas nécessaire mais laisse un peu de répit à coturn pour démarrer et éviter une boucle de redémarrage (systemd essaye de faire redémarrer un service en boucle, jusqu'à une limite pré-définie et surchargeable, après quoi il laisse le service HS, ce que je ne veux pas).
Si l'on veut tester, on remplace « on-failure » par « always » puis killall coturn
.
Après avoir voulu me remémorer comment fonctionne une capture réseau du point de vue du noyau Linux, je me suis interrogé sur l'activation, en détail, du mode promiscuous.
Une carte réseau Ethernet fait remonter au système d'exploitation uniquement les trames Ethernet qui lui sont destinées. Le tri est opéré sur l'adresse MAC de destination. Sont conservés les paquets dont l'adresse MAC est celle de la carte réseau, l'adresse broadcast (FF:FF:FF:FF:FF:FF), ou l'une des adresses multicast (01:00:5E:XX:XX:XX pour IPv4, 33:33:XX:XX:XX:XX pour IPv6).
Ce filtrage est effectué matériellement par la carte réseau.
Le mode promiscuous permet de retirer ce filtrage, donc d'écouter tout ce qui passe sur le réseau.
Bon, il faut relativiser :
Le mode promiscuous est global à une interface réseau : exécuter un tcpdump -p
(on demande à ne pas activer le mode promiscuous) à côté d'un tcpdump
(mode promiscuous activé) est vain : quel que soit l'ordre de lancement des commandes, le tcpdump -p
verra les paquets qui ne sont pas destinés à la machine tant que le tcpdump
(promiscuous) est en cours d'exécution.
Voir si l'une des interfaces réseau du système est en mode promiscuous : ip -d l sh | grep -B 1 promiscuity
. Si le nombre affiché est supérieur à 1, alors plusieurs applications (+ le noyau voir ligne suivante) ont activé le mode promiscuous.
Activer le mode promiscuous sans passer par libpcap : sudo ip l set promisc on dev <INTERFACE>
. Pour le désactiver : sudo ip l set promisc off dev <INTERFACE>
.
Le mode promiscuous ne permet pas de voir TOUT le trafic réseau adressé à la machine. Une carte réseau peut filtrer matériellement le trafic qu'elle juge invalide : toute trame 802.1Q quand aucun VLAN est configuré sur le système, trames réseaux trop courtes ou dont la somme de contrôle est incorrecte, etc.
Tous les paquets qui arrivent sur une interface configurée en mode promiscuous ne parviendront pas jusqu'aux logiciels "serveurs" en écoute, car le noyau fait le tri.
Le tri le plus simple à mettre en évidence est que, si le transfert de paquets IP n'est pas activé (/proc/sys/net/ipv4/ip_forward = 0), alors tous les paquets IP dont l'adresse IP de destination n'est pas l'une des IPs de la machine (multicast inclus) seront détruits. Ils n'arriveront même pas jusqu'à la chaîne FORWARD de Netfilter/iptables. Il doit exister d'autres filtrages.
Si l'on regarde le code, la libpcap (utilisée par tcpdump
, wireshark
, etc.) utilise la fonction système setsockopt()
pour positionner l'option. Il ne reste plus qu'à remonter le code de Linux.
setsockopt()
est une fonction implémentée pour chaque famille/protocole. Une socket libpcap est de type AF_PACKET / PF_PACKET, donc c'est la fonction packet_setsockopt()
située dans le fichier net/packet/af_packet.c qui est utilisée. Elle appelle packet_mc_add()
qui appelle packet_dev_mc()
, toujours dans af_packet.c.
dev_set_promiscuity()
dans net/core/dev.c est appelée et elle appelle __dev_set_promiscuity()
toujours dans net/core/dev.c. On notera que c'est cette fonction qui journalise « device XXXX entered promiscuous mode » dans kern.log.
__dev_set_promiscuity()
appelle dev_change_rx_flags()
(toujours dans net/core/dev.c) qui appelle ndo_change_rx_flags
. Le pilote peut implémenter une fonction (ou non) pour remplacer celle par défaut et l'enregistrer dans la structure netdev_ops de la structure net_device. Exemple : le pilote e1000e n'implémente pas cette fonction.__dev_set_promiscuity()
appelle dev_set_rx_mode()
(toujours dans net/core/dev.c) qui appelle ndo_set_rx_mode()
. Le pilote e1000e la remplace avec e1000e_set_rx_mode()
(« .ndo_set_rx_mode = e1000e_set_rx_mode, ») dont le commentaire indique « e1000e_set_rx_mode - secondary unicast, Multicast and Promiscuous mode ». Cette fonction met à jour les registres de la carte réseau et, surtout, restaure+écrit les adresses multicast et unicast quand on sort du mode promiscuous.Quand on crée une tâche de déploiement d'une image disque (sur une seule machine, en unicast, ou sur une partie du parc, en multicast, même combat), l'interface web de notre serveur FOG (Free and Opensource Ghost), met énormément de temps (> 30 secs) à répondre pour annoncer que la tâche est créée.
Si l'on n'attend pas, que l'on va dans l'onglet « Tasks », on voit que la tâche est créée.
Origine du problème : un « storage node » apparaît toujours dans la liste alors qu'il n'existe plus. L'image disque que l'on veut déployer ne dépend pas de ce serveur, mais sa suppression depuis l'interface web de FOG résout la lenteur de celle-ci.
Nous avons un serveur avec plusieurs instances de MariaDB. Une instance = un processus, un port TCP, un fichier de configuration dédié, etc.
Sur ce serveur, si l'on tape la commande mysql -P <numéro_port_instance> -u <user> -p <nom_bdd>
, MariaDB nous retourne l'erreur « ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2 "No such file or directory" ».
Si l'instance par défaut (celle qui écoute sur tcp/3306 / /var/run/mysqld/mysqld.sock) est toujours active, l'erreur est, en toute logique, « ERROR 1045 (28000): Access denied for user `
Le paramètre « -P », qui permet de préciser le port TCP à utiliser, ne se suffit pas à lui-même : il faut ajouter --protocol=tcp
.
Ou utiliser la socket UNIX de l'instance à laquelle on veut accéder avec -S /var/run/mysqld/<socket>
.
Sur notre parc Ubuntu GNU/Linux, l'authentification des utilisateurs pour l'ouverture de session se fait auprès de notre domaine Samba 4 AD DC (et sur un OpenLDAP auparavant).
On voudrait ajouter tous les utilisateurs du domaine à des groupes locaux (qui n'existent pas dans notre domaine). On ne peut donc pas utiliser /etc/group
puisque l'utilisateur est enregistré dans une autre base de données.
Quel intérêt de faire ça ? Permettre à nos utilisateurs d'utiliser des logiciels qui réclament l'appartenance à un groupe local. Exemple : il faut être membre du groupe « vboxusers » pour avoir le droit de connecter des périphériques USB (clé USB, webcam, etc.) à une machine virtuelle VirtualBox. On n'a pas envie de pourrir notre domaine avec des goupes, sans compter que VirtualBox réclame une appartenance au groupe « vboxusers », pas au groupe « vboxusers@domaine.monorganisation.example »).
Comme d'hab, PAM vient à notre rescousse avec son module pam_group.
D'abord, on ajoute une ligne *;*;*;Al0000-2400;vboxusers
dans le fichier /etc/security/group.conf
. On peut restreindre l'application de la règle à certains services PAM (sshd, par exemple), c'est le premier champ. Ou à certains terminaux (deuxième champ). Ou à certains utilisateurs / groupes d'utilisateurs (troisième champ). Que à certains horaires (quatrième champ), ici tous les jours (« Al »), de 0 h à 24 h (0000-2400).
Ensuite, il faut ajouter pam_group à la liste des modules PAM exécutés sur le système. Par défaut, il est appelé depuis le fichier /etc/pam.d/login
donc l'ajout de groupes fonctionne depuis les tty (ctrl+alt+Fx), mais pas sur une connexion SSH ou depuis un émulateur de terminaux lancé depuis l'interface graphique (gnome-terminal, etc.). Pour corriger cela, j'ajoute une ligne auth optional pam_group.so
à la fin de /etc/pam.d/common-auth
. Attention : si tu utilises pam_script, qui permet de lancer un script lors de l'authentification, de l'ouverture / fermeture de session, il faut que pam_group soit appelé avant pam_script sinon l'utilisateur ne sera pas ajouté aux groupes.
Il faut redémarrer le système pour que la modification devienne effective. Fermer la session ne suffit pas. Redémarrer le gestionnaire d'affichage (gdm3, dans mon cas) ne suffit pas.
Note : un bug dans systemd empêchait l'ajout de groupes par pam_group lors d'une ouverture de session graphique. Il est corrigé depuis longtemps, y compris dans Ubuntu 20.04.
Merci Alex d'avoir mis ça en prod'.