Securing BSD Daemons
By Dru Lavigne
02/07/2001
Source : http://www.oreillynet.com/pub/a/bsd/2001/02/07/FreeBSD_Basics.html
Translation by eberkut - http://www.chez.com/keep

Dans un article de la semaine dernière, nous avons utilisé l'utilitaire sockstat pour déterminer quels daemons écoutaient en attente d'un connexion réseau sur un système FreeBSD. Continuons où nous avions arrêté en regardant plus étroitement /etc/inetd.conf.

Rappelez-vous qu'inetd est le super-serveur internet qui écoute les requêtes au nom d'autres démons ; il lit /etc/inetd.conf pour déterminer sur quels ports vous souhaitez écouter. Vous devriez toujours mettre en commentaire les lignes qui représentent les démons pour lesquels vous ne souhaitez pas que des personnes établissent des connexions réseau. Une bonne règle générale est que si vous ne savez pas ce qu'un démon fait, mettez le en commentaire.

Sur mon système, inetd écoute les requêtes de connexion IPv4 des démons suivants : ftp, telnet, comsat, ntalk, et tftp. Je n'ai pas l'intention de mettre à jour un serveur tftp ou ftp, et je ne suis pas totalement convaincu que je dois fournir des services comsat ou ntalk. Cependant, j'ai besoin de telnet dans mon système FreeBSD occasionnellement. Donc, je deviens super-utilisateur, j'ouvre /etc/inetd.conf en utilisant l'éditeur vi, et j'ajoute des caractères de commentaire aux 4 lignes qui représentent les démons au nom desquels je ne veux pas qu'inetd écoute. Quand j'ai terminé, cette partie de fichier de mon /etc/inetd.conf ressemblera à

#ftp stream tcp nowait root /usr/libexec/ftpd ftpd -l
telnet stream tcp nowait root /usr/libexec/telnetd telnetd
#shell stream tcp nowait root /usr/libexec/rshd rshd
#login stream tcp nowait root /usr/libexec/rlogind rlogind
#finger stream tcp nowait/3/10 nobody /usr/libexec/fingerd fingerd -s
#exec stream tcp nowait root /usr/libexec/rexecd rexecd
#uucpd stream tcp nowait root /usr/libexec/uucpd uucpd
#nntp stream tcp nowait usenet /usr/libexec/nntpd nntpd
# run comsat as root to be able to print partial mailbox contents w/ biff,
# or use the safer tty:tty to just print that new mail has been received.
#comsat dgram udp wait tty:tty /usr/libexec/comsat comsat
#ntalk dgram udp wait tty:tty /usr/libexec/ntalkd ntalkd
#tftp dgram udp wait nobody /usr/libexec/tftpd tftpd /tftpboot
#bootps dgram udp wait root /usr/libexec/bootpd bootpd

Maintenant voyons ce qui se passe si j'essaie de ftp sur mon ordinateur FreeBSD.

ftp 24.141.117.39
Connected to 24.141.117.39.
220 foo.bar.com FTP server (Version 6.00LS) ready.
Name (24.141.117.39:genisis): anonymous
530 User anonymous unknown.
ftp: Login failed.
Remote system type is UNIX.
Using binary mode to transfer files.
ftp>

Que s'est passé ici ? Il semble que mon système FreeBSD reçoit toujours des connexions ftp. Nous avons juste découvert qu'il n'est pas suffisant d'éditer /etc/inetd.conf ; nous devons également dire à inetd de cesser d'écouter au nom des démons que nous avons mis en commentaires. Nous devrons envoyer un signal hang up ou HUP à inetd pour le mettre au courant de nos changements. Afin de faire cela, nous devons découvrir le PID d'inetd :

ps -acux | grep inetd
root 172 0.0 0.6 1040 784 ?? Is Thu03PM 0:00.52 inetd

Puisque le processus est exécuté par le root, nous devons devenir super-utilisateur afin de lui envoyer ce signal HUP :

su
Password:

kill -1 172
exit

Pour voir si ça a marché, relançons l'utilitaire sockstat :

sockstat -4
USER     COMMAND    PID   FD PROTO  LOCAL ADDRESS  FOREIGN ADDRESS      
root     XF86_SVG  3752    0 tcp4   *:6000         *:*                  
root     sshd       185    4 tcp4   *:22           *:*                  
root     sendmail   181    4 tcp4   *:25           *:*                  
root     sendmail   181    5 tcp4   *:587          *:*                  
root     lpd        177    6 tcp4   *:515          *:*                  
root     inetd      172    5 tcp4   *:23           *:*                  
daemon   portmap    152    3 udp4   *:111          *:*                  
daemon   portmap    152    4 tcp4   *:111          *:*                  
root     syslogd    149    4 udp4   *:514          *:*                  
root     dhclient   125    3 udp4   *:*            *:*                  
root     dhclient   125    6 udp4   *:68           *:*

Notez qu'il y a maintenant seulement une entrée pour inetd et qu'il surveille seulement le port 23, le port telnet. Voyons ce qui se produit maintenant si j'essaie un ftp sur mon système FreeBSD :

ftp 24.141.117.39
ftp: connect: Connection refused

Pour être complets, nous devrions également voir si inetd est disposé à recevoir des connexions de clients IPv6 :

sockstat -6
USER   COMMAND  PID  FD PROTO  LOCAL ADDRESS  FOREIGN ADDRESS
root   sshd     185   3 tcp46  *:22           *:*                  
root   inetd    172   9 tcp6   *:21           *:*                  
root   inetd    172  10 tcp6   *:23           *:*

Dans mon exemple, je n'ai aucun besoin de recevoir des connexions ftp (port 21) ou telnet (port 23) de clients exécutant IPv6, ainsi je mettrai en commentaires ces 2 lignes dans mon fichier /etc/inetd.conf. La section IPv6 est située vers le tiers inférieur du fichier. Encore une fois, si vous éditez ce fichier, n'oubliez pas d'envoyer le signal HUP et de vérifier que vos changements sont entrés en vigueur en exécutant sockstat encore une fois.

L'édition de /etc/inetd.conf vous permet de contrôler quels ports inetd surveillera pour des requêtes de connexion. Cependant, il n'a pas de mécanisme pour permettre le contrôle des clients dont vous êtes disposé à recevoir des requêtes de connexion. Sans modification maintenant, toute personne avec une connexion réseau, ce qui inclut malheureusement une connexion Internet, avec mon système FreeBSD peut faire un telnet sur mon ordinateur. Heureusement, ils seront incités à se logger, ce qui est une raison pour laquelle il est important de mettre en oeuvre une bonne politique de passwords. Mais il semble raisonnable d'ajouter une autre couche de sécurité en disant à inetd de refuser la requête de connexion s'il ne vient pas d'un client auquel je fais confiance.

C'est là où TCP Wrappers entre en jeu. Si vous exécutez FreeBSD 3.2 ou plus, il est déjà installé dans votre système et il vous attend juste pour configurer les clients dont vous êtes disposés à recevoir des requêtes de connexion. Le nom de de son fichier de configuration est /etc/hosts.allow, mais vous trouverez la syntaxe du fichier dans man 5 hosts_access et des options supplémentaires dans man 5 hosts_options.

Il y a 2 choses à garder à l'esprit lors de l'édition de ce fichier :

La syntaxe elle-même est assez épurée ; chaque règle a 2 zones et peut utiliser une 3ème zone facultative :

daemon: client  :command

Si votre règle ne rentre pas sur une ligne, utilisez le caractère \ à l'extrémité de la ligne pour indiquer que la règle continue à la prochaine ligne.

Il y a quelques wildcards et opérateurs utiles auxquels vous devriez faire attention.

Comme vous créez vos règles, vous voudrez employer l'utilitaire tcpdmatch pour vous assurer que vos règles créent réellement le contrôle d'accès que vous attendez. Avant que je commence la création de règles d'accès pour le démon telnet sur mon système FreeBSD, voyons ce qui se produit quand j'essaie un telnet sur ma box FreeBSD.

telnet 24.141.117.39
Trying 24.141.117.39...
Connected to genisis.
Escape character is '^]'.

FreeBSD/i386 (genisis) (ttyp1)

login: genisis
Password:
Last login: Thu Jan 18 17:44:29 on ttyv4
Copyright (c) 1980, 1983, 1986, 1988, 1990, 1991, 1993, 1994
    The Regents of the University of California.  All rights reserved.

FreeBSD 4.2-RELEASE (SOUND) #0: Tue Dec 12 20:01:29 EST 2000

  _   _   _   _   _     _   _   _   _   _   _   _   _   _   _   _  
 / \ / \ / \ / \ / \   / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ / \ 
( M | e | r | r | y ) ( C | h | r | i | s | t | m | a | s | ! | ! )
 \_/ \_/ \_/ \_/ \_/   \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ \_/ 
You have mail.

genisis@~ logout
Connection closed by foreign host.

Notez que mon système reçoit des connexions telnet et répond en donnant un prompt de connexion sur le terminal ttyp1. Une fois que je suis loggé, je reçois mon MOTD, mon prompt personnalisé, et il est juste comme quand je m'assois devant ma box FreeBSD. Il s'avère qu'inetd a fait son travail correctement. Maintenant, voyons à quoi ressemble le fichier par défaut /etc/hosts.allow.

more /etc/hosts.allow
#
# hosts.allow access control file for "tcp wrapped" applications.
# $FreeBSD: src/etc/hosts.allow,v 1.8.2.3 2000/07/20 15:17:44 ume Exp $
#
# NOTE: Le fichier hosts.deny est désapprouvé.
#       Placez les règles 'allow' et 'deny'dans le fichier hosts.allow.
#	Voyez hosts_options(5) pour le format de ce fichier.
#	hosts_access(5) ne s'applique plus vraiment.

#	 _____                                      _          _ 
#	| ____| __  __   __ _   _ __ ___    _ __   | |   ___  | |
#	|  _|   \ \/ /  / _` | | '_ ` _ \  | '_ \  | |  / _ \ | |
#	| |___   >  <  | (_| | | | | | | | | |_) | | | |  __/ |_|
#	|_____| /_/\_\  \__,_| |_| |_| |_| | .__/  |_|  \___| (_)
#					   |_|                   
# !!! Ceci est un exemple ! Vous aurez besoin de le modifier pour vos conditions
# !!! spécifiques !


# Commencez par autoriser tout (ceci empêche le reste du fichier
# de travailler, donc supprimez le si vous avez besoin de protection).
# Les règles ici travaillent sur la base de la "Première correspondance gagne".
ALL : ALL : allow

Arretons nous ici pour voir si nous pouvons déterminer pourquoi cette première règle empêche le reste du fichier de travailler. Elle indique fondamentalement : pour tous les démons, depuis tous les clients, permettez l'accès. Puisque cette règle fait correspondre tous les clients, et que c'est la première correspondance, elle sera toujours utilisé. Voyons ce qui se produit si nous mettons en commentaire cette première règle. Je deviens super-utilisateur, j'ouvre /etc/hosts.allow en utilisant l'éditeur vi, et je mets un # devant cette première règle, et je sauvegarde mes modifications.

Maintenant j'essaie la commande telnet à nouveau :

telnet 24.141.117.39
Trying 24.141.117.39...
Connected to genisis.
Escape character is '^]'.
You are not welcome to use telnetd from biko.
Connection closed by foreign host.

Il semble que j'ai effectivement bloqué toutes les connexions telnet à mon système. Jetons un coup d'oeil au reste de /etc/hosts.allow pour voir où aller à partir de là pour permettre l'accès limité par l'intermédiaire de telnet.

Continuons avec plus de /etc/hosts.allow :

# Filtrer sshd(8) n'est pas normalement une bonne idée, mais si vous avez
# de le faire, voici comment
#sshd : .evil.cracker.example.com : deny 

# Empêche ceux sans reverse DNS de se connecter.
ALL : PARANOID : RFC931 20 : deny

# Permet n'importe quoi depuis localhost. Notez qu'une adresse IP (pas un nom d'hôte)
# *DOIT* être indiqué pour portmap(8).
ALL : localhost 127.0.0.1 : allow
ALL : my.machine.example.com 192.0.2.35 : allow

# Pour utiliser des adresses IPv6 vous devez les encapsuler dans des []
ALL : [fe80::%fxp0]/10 : allow
ALL : [fe80::]/10 : deny
ALL : [3ffe:fffe:2:1:2:3:4:3fe1] : deny
ALL : [3ffe:fffe:2:1::]/64 : allow

# Sendmail peut vous aider à vous protéger contre les spammers et relay-rapers
sendmail : localhost : allow
sendmail : .nice.guy.example.com : allow
sendmail : .evil.cracker.example.com : deny
sendmail : ALL : allow

# Exim est une alternative à sendmail, disponible dans l'arborescence des ports
exim : localhost : allow
exim : .nice.guy.example.com : allow
exim : .evil.cracker.example.com : deny
exim : ALL : allow

# Portmapper est utilisé pour tous les services RPC ; protégez votre NFS !
# (des adresse IP plutôt que des noms d'hôte *DOIVENT* être utilisées ici)
portmap : 192.0.2.32/255.255.255.224 : allow
portmap : 192.0.2.96/255.255.255.224 : allow
portmap : ALL : deny

# Fournit un peu de protection pour ftpd
ftpd : localhost : allow
ftpd : .nice.guy.example.com : allow
ftpd : .evil.cracker.example.com : deny
ftpd : ALL : allow

# Vous devez être intelligent avec finger ; _pas_ de backfinger ! Vous pouvez facilement
# déclencher une "finger war".
fingerd : ALL \
	: spawn (echo Finger. | \
	 /usr/bin/mail -s "tcpd\: %u@%h[%a] fingered me!" root) & \
	: deny

# Le reste des démons sont protégés.
ALL : ALL \
	: severity auth.info \
	: twist /bin/echo "You are not welcome to use %d from %h."

Notez qu'il n'y a aucune règle qui mentionnent spécifiquement telnetd. La dernière règle dans le fichier est destinée à couvrir tous les démons restant qui ne correspondent pas aux règles ci-dessus. Notez que la dernière règle a permis la connexion, mais l'avait clôturé après avoir écho un message, que nous avons vu quand j'ai tenté une connexion telnet. Le %d a été remplacé par le nom du démon (telnetd), et le %h a été remplacé par l'hostname du client essayant de se connecter (biko).

Nous pourrions avoir prévu ces résultats si nous avions utilisé l'utilitaire tcpdmatch. La syntaxe pour utiliser cet utilitaire est très simple :

tcpdmatch daemon_name host_name_of_client

Vous ne devez pas être super-utilisateur pour exécuter cet utilitaire. Voyons que ce qu'il indique qui se produira si l'hôte "biko" essaie de se relier au telnetd sur mon système FreeBSD :

tcpdmatch telnetd biko

client:   hostname biko
client:   address  24.141.117.40
server:   process  telnetd
matched:  /etc/hosts.allow line 77
option:   severity auth.info
option:   twist /bin/echo "You are not welcome to use telnetd from biko."
access:   delegated

C'est une sortie vraiment très utile car il nous indique quel numéro de ligne de /etc/hosts.allow contient la règle correspondante et ce que sera le résultat de cette règle pour ce client.

Modifions /etc/hosts.allow pour permettre au telnetd de recevoir des connexions depuis les hôtes "genisis" et "biko", mais rejeter les connexions de tous les autres clients. Je deviens super-utilisateur et ajoute les lignes suivantes ; peu importe où j'ajoute les lignes dans le fichier aussi longues qu'elles apparaissent avant cette dernière règle.

telnetd: biko,genisis :ALLOW
telnetd: ALL :DENY 

Je vérifierai alors que mes règles fonctionnent en exécutant tcpdmatch sur biko, genisis, et un 3ème hôte appelé creed :

tcpdmatch telnetd biko
client:   hostname biko
client:   address  24.141.117.40
server:   process  telnetd
matched:  /etc/hosts.allow line 74
option:   ALLOW 
access:   granted

tcpdmatch telnetd genisis
client:   hostname genisis
client:   address  24.141.117.39
server:   process  telnetd
matched:  /etc/hosts.allow line 74
option:   ALLOW 
access:   granted

tcpdmatch telnetd creed
client:   hostname creed
client:   address  24.141.117.41
server:   process  telnetd
matched:  /etc/hosts.allow line 75
option:   DENY 
access:   denied

Voyons ce qui se produit quand l'hôte creed essaie un telnet sur mon système FreeBSD :

telnet 24.141.117.39
Trying 24.141.117.39...
Connected to genisis.
Escape character is '^]'.
Connection closed by foreign host.

Notez que je n'ai reçu aucun message, car la règle sur la ligne 75 était la première correspondance, pas la règle sur la ligne 77.

Nous avons juste rayé la surface de la fonctionnalité fournie par TCP Wrappers, mais ça devrait être assez pour vous permettre de commencer. Selon vos besoins, vos règles peuvent être très simples et tout à fait élégantes. Vous voudrez certainement vérifier man 5 hosts.access et man 5 hosts.options pour voir toutes les options configurables disponibles.