Sécurité réseau sous Unix

Ce document se veut un résumé d'une brève discussion qui a eu lieu en mars 1999 sur la liste de diffusion de l'association Guilde. La question de départ était « comment configurer une station Linux pour réduire les risques de pénétration par le réseau ». Comme le propos n'est pas très spécifique à Linux, j'ai mis « Unix » dans le titre. Après avoir rédigé quelques paragraphes, je me rends compte que ce que je fais ressemble plus à un exposé magistral qu'à un compte rendu de discussion. J'espère qu'on ne m'en voudra pas trop...

Plan


Le fonctionnement des serveurs réseau

Les serveurs réseau sous Unix peuvent en général être lancés de deux façons différentes : en tant que serveurs indépendants (mode standalone) ou via inetd. Dans le premier cas, le serveur est lancé au démarrage de la machine et tourne en permanence jusqu'à son extinction. Il ne cesse d'écouter le port correspondant à son service et répond aux requêtes arrivant sur ce port. Lorsqu'un service passe par inetd, le serveur associé est lancé à la demande, uniquement lorsqu'une requête arrive sur le port associé. Inetd est une sorte de « méta-serveur » qui est à l'écoute de tous les ports associés aux services qui lui sont confiés. Quand inetd reçoit une requête, il lance le serveur concerné pour répondre à cette requête particulière.

Tout cela paraît bien simple, n'est-ce pas ? Eh bien comme c'est trop simple, on va rajouter une couche ! Inetd n'étant pas très doué pour gérer la sécurité, on utilise tcpd comme intermédiaire entre inetd et les serveurs. Tcpd fait partie d'un paquetage appelé Tcp Wrappers. Le principe est le suivant : pour tous les services qui passent par inetd, on va expliquer à inetd que le serveur compétent est tcpd. Ce dernier va donc lui aussi fonctionner comme un méta-serveur et passer la main au vrai serveur, après avoir fait les contrôles de sécurité nécessaires.

Certaines distributions de Linux utilisent maintenant xinetd. Xinetd est un méta-serveur qui réunit les fonctionnalités de inetd et de tcpd en un seul programme, plus des fonctionnalités supplémentaires.

Principes pour une bonne sécurité

Éliminer les services inutiles

Chaque port qui est ouvert en écoute sur votre machine est un point d'entrée possible. Pour améliorer la sécurité, la première chose à faire est de supprimer tous les services dont on n'a pas l'utilité. La distinction entre un service utile et un gadget est évidemment très subjective et dépend du niveau de sécurité recherché.

Parfois on se rend compte qu'on a des services actifs dont on ignore l'utilité. Dans ce cas, le plus probable est qu'ils ne nous soient pas utiles. Dans le doute, on enlève donc le service en question, quitte à le remettre si on se rend compte ensuite qu'il servait vraiment à quelque chose. Si on est curieux, on peut aussi lire le manuel du serveur concerné, voire le RFC qui décrit le service. Si, si, ça se lit.

À ce propos, plusieurs personnes se sont demandées ce qu'était le service auth. Voici la réponse de Mahdi Nadir :

auth est susceptible d'être utilisé par tcpd, apache, sendmail (...) et irc. tcpd ajoute l'utilisateur dans les logs, sendmail dans l'enveloppe, apache dans une variable d'environnement. L'absence de identd n'est en principe jamais bloquante et de moins en moins de serveurs le proposent. À considérer comme un daemon de courtoisie pas trop sensible aux trous de sécurité.

Pour savoir quels sont les services actifs sur le système, il suffit d'utiliser la commande netstat -atu. Fabien Blanc-Pâques a posté sur la liste un scanner de ports qui peut aussi être utilisé à cette fin.

Il arrive qu'on voie un port ouvert avec netstat et qu'on se demande quel est est le processus qui est en écoute sur ce port. Pour le savoir, on utilise la commande fuser. Par exemple

$ fuser -v telnet/tcp

                     USER        PID ACCESS COMMAND
telnet/tcp           root        259 f....  inetd
J'ai pu constater que cette fonctionnalité de fuser est moins bien documentée dans la version en français du manuel que dans la version en anglais.

Ne pas écouter le réseau

Certains services sont à peu près indispensables à une utilisation courante d'une station de travail. C'est par exemple le cas du serveur X, de XDM (ou équivalent) et du serveur d'impression. Pourtant, on n'a pas forcément besoin de rendre ces services disponibles à d'autres machines que celle sur laquelle ils tournent. Il est souvent possible de configurer ces services pour qu'ils n'écoutent pas le réseau. Deux possibilités se présentes : soit on demande au serveur de ne pas ouvrir le port de communication, soit on lui demande de l'ouvrir uniquement sur l'interface loopback.

Ne pas ouvrir de port TCP ou UDP

C'est le moyen le plus radical de rendre X et XDM indisponibles de l'extérieur. Les clients X locaux se connectent normalement au serveur en utilisant des sockets Unix. Le port TCP 6000 ouvert par le serveur ne sert donc qu'aux clients distants. On peut désactiver l'écoute de ce port en lançant le serveur X avec l'option -nolisten tcp :

# This file should contain an entry to start the server on the
# local display; [...]
#
:0 local /usr/X11R6/bin/X -nolisten tcp

Attention quand même à avoir votre variable DISPLAY bien configurée. Elle doit valoir :0. Si vous la réglez à localhost:0 cela ne marchera pas.

Dans le cas de XDM, les ports qu'il ouvre n'ont aucune utilité en local. Ils servent uniquement à recevoir des requêtes XDMCP en provenance de terminaux X. Si vous n'avez pas besoin de cette fonctionnalité, vous pouvez la désactiver en donnant à XDM l'option -udpPort 0. Ceci se fait dans le fichier /etc/inittab. Dans ma Mandrake, j'ai ça vers la fin de inittab :

# Run xdm in runlevel 5
# xdm is now a separate service
x:5:respawn:/etc/X11/prefdm -nodaemon -udpPort 0
Ici prefdm est un wrapper qui lance kdm (ou gdm, au choix). Ces programmes comprennent les mêmes options que XDM.

Ouvrir un port uniquement sur l'interface loopback

Si votre serveur a absolument besoin d'ouvrir un port TCP ou UDP, vous pouvez peut-être lui demander de ne le faire que sur l'interface loopback. C'est par exemple le cas du serveur Web Apache, du serveur de Guinness guinnessd (option -a localhost) et du serveur d'impression CUPS. Pour ce dernier, j'ai remplacé dans /etc/cups/cupsd.conf la commande Port 631 par Listen 127.0.0.1:631.

Utiliser la cryptographie

Mahdi nous met aussi en garde contre les dangers de faire circuler des mots de passe en clair sur le réseau. Si quelqu'un peut mettre un sniffeur sur notre réseau, il pourra récupérer les mots de passe. En milieu très corrosif, on remplace telnet, rshd et rlogind par ssh, on limite le ftp a l'anonymous.

Il faudrait quand même signaler que l'utilisation de ssh pose des problèmes de légalité. En France, on n'a le droit d'utiliser librement un système cryptographiques que si la longueurs des clés de chiffrement utilisées est inférieure à une certaine limite. Cette limite est passée de 40 bits à 128 bits en mars 1999. Ssh utilisant des clés de 1024 bits, son usage est donc encore illégal en France. En attendant, l'IN2P3 à mis au point un système nommé ssf compatible avec ssh mais avec des clefs limitées à 128 bits.

Les fichiers de configuration

Serveurs standalone

C'est le plus compliqué. Étant donné que chaque serveur standalone gère tout seul son port de communication, c'est à lui de s'occuper de la sécurité de ce port. Il faut donc regarder au cas par cas en se rapportant à la documentation du serveur.

Évidemment, la première chose à faire consiste à désactiver tous les serveurs inutiles ! Pour ce faire, il faut modifier la liste des services qu'on active dans les niveaux d'exécution (runlevels) qu'on utilise. On peut faire ça à l'aide d'outils ad-hoc (linuxconf, ntsysv, chkconfig...) ou bien en déplaçant à la main les liens symboliques dans /etc/rc.d/rc<level>.d si on sait ce qu'on fait.

Ces modifications n'étant prises en compte qu'au prochain redémarrage, il faut aussi tuer les serveurs qui tournent déjà. Oui, évidemment, on peut aussi réamorcer la machine pour obtenir ce résultat, mais c'est pas très sport ;-). La façon propre de tuer ces serveurs consiste à utiliser les scripts qui s'occupent normalement de leur lancement et de leur arrêt. Par exemple, si on veut arrêter sendmail, on tape /etc/rc.d/init.d/sendmail stop.

Configurer inetd

La configuration ici est assez simple : il suffit de dire à inetd de quels services on veut qu'il s'occupe. Ceci se fait via le fichier de configuration /etc/inetd.conf qui est décrit dans la page de manuel de inetd. Comme par défaut on a dans un système vierge un inetd.conf assez complet, tout ce qu'on a à faire consiste à mettre en commentaire les services dont on ne veut pas et à s'assurer que ceux qu'on veut ne sont pas en commentaire. Vous remarquerez que dans la configuration par défaut inetd renvoie toutes les requêtes vers tcpd.

Quand on a modifié /etc/inetd.conf, il faut demander à inetd de relire son fichier de configuration. Ceci se fait en lui envoyant le signal SIGHUP avec la commande killall -HUP inetd. Attention : si vous n'êtes pas sous Linux, lisez man killall avant d'utiliser cette commande.

Configurer tcpd

Jusqu'ici, la gestion de la sécurité paraît assez brutale. Un service est disponible pour les clients potentiels ou il ne l'est pas. Avec tcpd, on peut avoir une gestion plus fine en précisant quels clients ont accès à quels services. Ceci se fait via les fichiers de configuration /etc/hosts.allow et /etc/hosts.deny documentés dans la page de manuel de hosts_access. Attention, si vous tapez man hosts_access vous risquez de tomber sur la documentation de la bibliothèque de tcpd. Il faut faire man 5 hosts_access pour trouver la documentation de ces fichiers.

Quid de /etc/services ?

Comme le fait remarquer Mahdi Nadir, /etc/services n'est pas un fichier de configuration mais un fichier de définition. Il permet d'établir la correspondance entre les noms des services et les numéros de ports associés. On peut éditer ce fichier mais uniquement pour ajouter des définitions de services. La référence pour les services standards se trouve dans le RFC assigned numbers. Le dernier en date porte le numéro 1700.

Exemples de fichiers de configuration

/etc/services

Non, je ne donnerai pas de fichier /etc/services. Celui que vous avez par défaut dans votre système est très bien. Sinon, vous pouvez recopier la section ad-hoc du RFC 1700. À titre purement indicatif, voici les lignes que j'ai ajoutées au fichier que j'ai eu d'origine :

ipp		631/tcp			# Internet Printing Protocol 1.1
ipp		631/udp			# Internet Printing Protocol 1.1
xdmcp		177/tcp			# XDM Control Protocol
xdmcp		177/udp			# XDM Control Protocol
x11		6000/tcp		# X Window System
x11		6000/udp		# X Window System
afpovertcp	548/tcp			# AFP over TCP
afpovertcp	548/udp			# AFP over TCP
netbus		12345/tcp		# Net Bus
bo		31337/udp		# Back Orifice
Il y a un point un peu discutable ci-dessus à propos de X11. En fait le port 6000 correspond au display 0. Dans le cas général, c'est le port 6000 + numéro_de_display qui est utilisé.

/etc/inetd.conf

C'est juste un exemple. En supposant que je veux avoir ftp, telnet, talk, ntalk et auth, ce fichier devrait ressembler à ça :

ftp	stream	tcp	nowait	root	/usr/sbin/tcpd	in.ftpd -l -a
telnet	stream  tcp 	nowait  root    /usr/sbin/tcpd	in.telnetd
talk	dgram	udp	wait	root	/usr/sbin/tcpd	in.talkd
ntalk	dgram	udp	wait	root	/usr/sbin/tcpd	in.ntalkd
auth    stream  tcp     nowait  nobody  /usr/sbin/in.identd in.identd -l -e -o

Plus, évidemment plein de lignes en commentaire qu'on garde dans le fichier pour pouvoir facilement ajouter des services.

/etc/hosts.allow et /etc/hosts.deny

Ces deux fichiers fonctionnent ensemble. Le premier décrit quelles sont les connexions qu'on autorise. Le deuxième décrit quelles sont celles qu'on interdit. Si une connexion n'est ni autorisée ni interdite, ou bien si elle est à la fois autorisée et interdite, alors elle est acceptée. Le plus souvent on veut interdire tout ce qui n'est pas explicitement autorisé. Ceci s'obtient très facilement avec le fichier /etc/hosts.deny suivant :

ALL: ALL

Ensuite il ne reste plus qu'à donner dans /etc/hosts.allow la liste de ce qu'on autorise. C'est ici que ça devient très dépendant de la situation de chacun. Juste pour l'exemple, supposons qu'on veuille autoriser tous les services disponibles à toutes les machines du réseau local, et aussi autoriser quelques services (rsh, finger) à tout l'Internet. /etc/hosts.allow ressemblera alors à ça :

ALL: LOCAL
in.rshd, in.fingerd: ALL

Je vous conseille vivement de lire man 5 hosts_access avant de remplir ces fichiers. Si le niveau de fonctionnalités de tcpd ne vous suffit pas, vous pouvez envisager d'installer xinetd.

Références

Une seule pour l'instant : Lance Spitzner a écrit le Beginner's guide to armoring Linux. C'est un très bon guide, plus complet que celui-ci, mais en anglais. Attention cependant, il vous conseille de désactiver xfs, le serveur de fontes de X11. Certains utilisateurs se sont aperçus que ceci rendait leur serveur X inutilisable. Vous pouvez aussi jeter un coup d'oeil à ses autres publications.


Edgar Bonet <webmaster@edg...>.
[pour une Europe sans brevets logiciels] [HTML by Vim] [HTML 4.01 valide !]