Original in fr Frédéric Raynal
xinetd offre des capacités de contrôle d'accès similaires à celles offertes par tcp_wrapper. Toutefois, ses possibilités s'étendent bien au-delà :
La première partie de cet article explique le fonctionnement
général de xinetd. Nous nous attarderons, par l'intermédiaire
d'exemples, sur la configuration générale d'un service, sur quelques
options particulières (attachement à une interface, redirection).
La seconde partie illustre le fonctionnement de xinetd, les logs qu'il
génère et finit avec une petite astuce très utile.
Certains signaux influencent le comportement de xinetd:
Deux utilitaires (itox et xconv.pl) sont fournis avec xinetd et permettent de convertir le fichier /etc/inetd.conf en fichier de configuration pour xinetd. Bien évidemment, ceci ne suffit pas dans la mesure où les règles stipulées dans les fichiers de configuration du wrapper sont ignorées. itox n'évolue plus, même s'il est encore maintenu. xconv.pl offre une meilleure solution, même si le résultat nécessite d'être modifié, ne serait-ce que parce que xinetd offre plus de possibilités qu'inetd.
>>/usr/local/sbin/xconv.pl < /etc/inetd.conf > /etc/xinetd.confLe fichier de configuration commence par une section par défaut dont les attributs seront utilisés pour tous les services pris en charge par xinetd. Suivent alors autant de sections que de services désirés, chacun pouvant redéfinir des options qui lui seront spécifiques par rapport à celles par défaut.
La section des valeurs par défauts s'écrit:
defaultsChacun des attributs défini dans cette section conserve la (les) valeur(s) fournie(s) pour les services décrits ensuite. Ainsi, l'attribut only_from permet de stipuler une liste d'adresses autorisées à se connecter aux serveurs:
{
attribut opérateur valeur(s)
...
}
only_from = 192.168.1.0/24 192.168.5.0/24 192.168.10.17Par la suite, tous les services déclarés autorisent l'accès aux machines dont les adresses correspondent à l'une de celles indiquées. Il est toutefois possible de modifier ces valeurs par défaut au sein de chaque service (voir les opérateurs expliqués ci-après). Cependant, cette démarche comporte certains risques. En effet, il vaut mieux, pour des raisons de simplicité et de sécurité, ne jamais définir de valeur par défaut qui soit ensuite modifiée dans un service. Dans le cas de droits d'accès par exemple, la politique la plus simple et la plus efficace consiste à interdire par défaut l'accès à tout le monde pour ensuite autoriser l'accès à chaque service seulement à ceux qui en ont besoin (avec tcp_wrapper, cette politique se traduit par un hosts.deny contenant ALL:ALL@ALL, et un hosts.allow ne contenant que les services autorisés avec les adresses adéquates).
Chaque section décrivant un service dans le fichier de configuration est de la forme:
service nom_du_serviceIl existe trois opérateurs: '=', '+=' et '-='. La plupart des attributs ne supporte que l'opérateur '=', qui fixe une valeur à un attribut. L'opérateur '+=' ajoute un élément à une liste de valeurs, alors que '-=' retire cet élément.
{
attribut opérateur valeur(s)
...
}
Le tableau 1 décrit succinctement quelques
uns des attributs. Nous verrons leur utilisation dans les exemples qui
suivront. La lecture de la page man
de xinetd.conf fournit de plus
amples informations.
Attribut | Valeurs et description |
flags | Seules les valeurs les plus courantes sont décrites ici, regarder
dans la documentation pour découvrir les autres:
|
log_type | xinetd utilise syslogd et le
sélecteur daemon.info par
défaut.
|
log_on_success | Différentes informations sont enregistrables quand un serveur
démarre:
|
log_on_failure | Là encore, xinetd dispose de multiples informations à
enregistrer quand un serveur ne peut démarrer, soit par manque de
ressources, soit à cause des règles d'accès:
|
nice | Modifie la priorité du serveur, exactement comme avec la commande nice. |
no_access | Liste de clients dont on refuse les connexions à ce service. |
only_from | Liste de clients autorisés. Si cet attribut n'a pas de valeur, l'accès à ce service est interdit. |
port | Le port associé au service. Si celui-ci est également défini dans /etc/services, les 2 numéros de port doivent correspondre. |
protocol | Le protocole stipulé doit exister dans /etc/protocols. S'il n'en est pas fournit, le protocole par défaut associé à ce service est employé. |
server | Le chemin vers le serveur à utiliser. |
server_args | Des arguments à passer au serveur. |
socket_type | stream (TCP), dgram (UDP), raw (accès direct à IP) ou seqpacket (). |
type | xinetd gère 3 types de services:
|
wait | Définit le comportement du service vis-à-vis des threads.
Deux valeurs sont possibles:
|
cps | Limite le nombre de connexions entrantes. Le premier argument est ce nombre lui-même. Lorsque ce seuil est dépassé, le service se désactive pour un délai, exprimé en secondes, fourni par le second argument. |
instances | Détermine le nombre maximal de serveurs d'un même type pouvant fonctionner en même temps. |
max_load | Ce réel indique la charge maximale que peut occuper un serveur (par exemple, 2 ou 2.5). Au-delà de cette limite, les requêtes sur ce serveur sont rejetées. |
per_source | Soit un entier, soit UNLIMITED, pour restreindre le nombre de connexions ayant une même origine à un serveur |
Les quatre derniers attributs présentés dans le tableau 1 permettent de contrôler les ressources attachées aux serveurs. Ceci permet de lutter efficacement contre certaines attaques par Deny of Service (DoS), dont le but est de paralyser une machine en occupant toutes ses ressources.
Cette partie présentait quelques unes des possibilités offertes par xinetd. Les parties suivantes montrent comment s'en servir et précisent certaines règles de bon fonctionnement.
Interdire, par défaut, tout accès à sa machine est la première étape d'une politique de sécurisation viable. Ensuite, il ne restera qu'à autoriser, en fonction des services, l'accès à qui de droit. Nous avons vu deux attributs permettant de contrôler l'accès à sa machine fondés sur les adresses IP: only_from et no_access. Choisir le second en mettant:
no_access = 0.0.0.0/0bloque complètement l'accès aux services. Cependant, si vous souhaitez autoriser tout le monde à accéder, par exemple, à echo, vous devrez alors mettre dans le service echo:
only_from = 0.0.0.0/0Voici le message enregistré dans les logs avec cette configuration:
Sep 17 15:11:12 charly xinetd[26686]: Service=echo-stream: only_from list and no_access list match equally the address 192.168.1.1En fait, le contrôle d'accès se fait en comparant les listes d'adresses contenues dans les deux attributs. Quand l'adresse du client correspond aux 2 listes, la liste la moins générale l'emporte. En cas d'égalité, comme c'est le cas dans notre exemple, xinetd se met dans une situation d'indécidabilité et refuse la connexion. Pour lever l'ambiguité, il aurait suffit de mettre:
only_from = 192.0.0.0/8Un autre solution, moins problématique, consiste à contrôler l'accès uniquement avec l'attribut:
only_from =Ne rien préciser comme valeur condamne toutes les connexions à l'échec :) Ensuite, chaque service délivre ses autorisations d'accès au moyen de ce même attribut.
Détail important, voire essentiel: en cas d'absence complète de règles d'accès (i.e. ni only_from, ni no_access) pour un service (allouées soit de manière directe, soit par le biais de la section default), l'accès au service est autorisé!
Voici un exemple de defaults :
defaultsParmi les services internes, servers, services, et xadmin permettent de gérer xinetd. Nous en reparlerons plus loin.
{
instances = 15
log_type = FILE /var/log/servicelog
log_on_success = HOST PID USERID DURATION EXIT
log_on_failure = HOST USERID RECORD
only_from =
per_source = 5disabled = shell login exec comsat
disabled = telnet ftp
disabled = name uucp tftp
disabled = finger systat netstat#INTERNAL
disabled = time daytime chargen servers services xadmin#RPC
disabled = rstatd rquotad rusersd sprayd walld
}
Certains attributs doivent impérativement apparaître en fonction du
type du service (INTERNAL, UNLISTED ou RPC) :
Attribut | Commentaire |
socket-type | Tous les services. |
user | Uniquement pour les services non INTERNAL |
server | Uniquement pour les services non INTERNAL |
wait | Tous les services. |
protocol | Tous les services RPC et absents de /etc/services. |
rpc_version | Tous les services RPC. |
rpc_number | Tous les services RPC, absents de /etc/rpc. |
port | Tous les services non RPC, absents de /etc/services. |
L'exemple ci-dessous illustre la définition de services :
service ntalkConstatons que ces services ne sont autorisés que sur le réseau interne (192.168.1.0/24). Pour le FTP, des restrictions supplémentaires sont prévues: le nombre d'instances est limité à 4 et il ne sera possible de l'utiliser que pendant certaines plages horaires.
{
socket_type = dgram
wait = yes
user = nobody
server = /usr/sbin/in.ntalkd
only_from = 192.168.1.0/24
}service ftp
{
socket_type = stream
wait = no
user = root
server = /usr/sbin/in.ftpd
server_args = -l
instances = 4
access_times = 7:00-12:30 13:30-21:00
nice = 10
only_from = 192.168.1.0/24
}
Par exemple, une société veut mettre en place
un serveur FTP pour ses employés (afin qu'ils puissent consulter
des documents internes). Elle tient également à
fournir à ses clients un accès FTP vers ses produits: bind
est fait pour cette société :) Nous allons définir
deux services FTP. Toutefois, xinetd doit pouvoir les distinguer: c'est
là qu'intervient l'attribut id,
qui définit de manière unique un service (lorsqu'il n'est
pas défini dans un service, il vaut, par défaut, le nom du
service).
service ftpL'utilisation de bind va permettre, selon la destination des paquets, d'invoquer le démon correspondant. Ainsi, un client sur le réseau local, dans cette configuration, doit préciser l'adresse locale (ou le nom qui y est associé) pour accéder aux données internes. Dans le fichier de logs, les traces suivantes apparaissent:
{
id = ftp-public
wait = no
user = root
server = /usr/sbin/in.ftpd
server_args = -l
instances = 4
nice = 10
only_from = 0.0.0.0/0 #autorisation pour tous les clients
bind = 212.198.253.142 #adresse IP publique de ce serveur
}service ftp
{
id = ftp-private
socket_type = stream
wait = no
user = root
server = /usr/sbin/in.ftpd
server_args = -l
only_from = 192.168.1.0/24 #usage interne seulement
bind = 192.168.1.1 #adresse IP locale de ce serveur (charly)
}
00/9/17@16:47:46: START: ftp-public pid=26861 from=212.198.253.142La première série résulte de la commande ftp 212.198.253.142, alors que la seconde, exécutée directement de charly vers lui-même: ftp 192.168.1.1.
00/9/17@16:47:46: EXIT: ftp-public status=0 pid=26861 duration=30(sec)
00/9/17@16:48:19: START: ftp-interne pid=26864 from=192.168.1.1
00/9/17@16:48:19: EXIT: ftp-interne status=0 pid=26864 duration=15(sec)
Un problème apparaît clairement: que se passe-t-il si la machine ne dispose pas de deux adresses IP fixes? Ce cas de figure se produit par exemple avec des connexions ppp ou si vous utilisez le protocole dhcp. Il semble donc qu'il vaudrait mieux lier les services aux interfaces qu'aux adresses. Toutefois, ceci n'est pas encore prévu avec xinetd et pose de nombreux problèmes (entre autres, écrire un module C pour accéder aux interfaces et aux adresses dépend fortement de l'OS de l'ordinateur, or xinetd est supporté par beaucoup d'OS différents...) L'utilisation d'un script permet de résoudre ce problème à moindre coût:
#!/bin/shCe script prend le fichier /etc/xinetd.base, qui contient la configuration souhaitée avec PUBLIC_ADDRESS à la place de l'adresse variable, et le transforme en /etc/xinetd.conf, en remplaçant la chaîne de caractères PUBLIC_ADDRESS par l'adresse associée à l'interface passée en argument du script. Ensuite, l'appel de ce script dépend du type de connexion utilisé: le plus simple consiste à en ajouter l'appel dans le fichier ifup-* adéquate et de (re)lancer xinetd.PUBLIC_ADDRESS=`/sbin/ifconfig $1 | grep "inet addr" | awk '{print $2}'| awk -F: '{print $2}'`
sed s/PUBLIC_ADDRESS/"$PUBLIC_ADDRESS"/g /etc/xinetd.base > /etc/xinetd.conf
service telnetRegardons ce qui se passe maintenant :
{
flags = REUSE
socket_type = stream
wait = no
user = root
server = /usr/sbin/in.telnetd
only_from = 192.168.1.0/24
redirect = 192.168.1.15 23
}
>>telnet charlyEffectivement, la connexion semble se faire sur charly, sauf que la suite montre bien qu'en fait, sabrina (une alpha, d'où le "Digital UNIX") a pris le relais. Ce mécanisme peut s'avérer à la fois utile et dangereux. Lors de sa mise en place, des logs doivent avoir lieu à chaque extrémité de la connexion. De plus, pour ce type de service, l'utilisation d'une DMZ et de firewalls n'est pas à négliger ;-)
Trying 192.168.1.1...
Connected to charly.
Escape character is '^]'.
Digital UNIX (sabrina) (ttyp1)
login:
defaults {Avant de les initaliser, mieux vaut prendre quelques précautions :
...
disabled = servers services xadmin
...
}
service xadminLe service xadmin dispose de 5 commandes :
{
type = INTERNAL UNLISTED
port = 9100
protocol = tcp
socket_type = stream
wait = no
instances = 1
only_from = 192.168.1.1 #charly
}
Nous n'avons besoin que du service finger :
service fingerxinetd n'a pas été compilé avec l'option --with-libwrap (voir l'attribut server). La section defaults est du même genre que celle fournie précédemment: tout accès est interdit vers charly quelle que soit la source de la connexion. Le service finger n'est pas désactivé, et pourtant:
{
flags = REUSE NAMEINARGS
server = /usr/sbin/tcpd
server_args = in.fingerd
socket_type = stream
wait = no
user = nobody
only_from = 192.168.1.1 #charly
}
pappy@charly >> finger pappy@charly
[charly]
pappy@charly >>pappy@bosley >> finger pappy@charly
[charly]pappy@bosley >>
Il semble que la requête n'ait pas fonctionné correctement,
qu'elle soit exécutée de charly
(192.168.1.1), machine autorisée, ou bien de bosley
(192.168.1.10). Examinons les fichiers de logs:
/var/log/servicelog :La requête en provenance de charly (les deux premières lignes) se déroule bien du point de vue de xinetd: l'accès est autorisé et la requête prend 5 secondes. En revanche, la requête de issue de bosley est rejeté (le FAIL).
00/9/18@17:15:42: START: finger pid=28857 from=192.168.1.1
00/9/18@17:15:47: EXIT: finger status=0 pid=28857 duration=5(sec)
00/9/18@17:15:55: FAIL: finger address from=192.168.1.10
/var/log/services:Remarquons que nous ne découvrons qu'une seule ligne correspondant à nos deux requêtes! En effet, celle en provenance de bosley (la seconde) a été interceptée par xinetd, donc, il est tout à fait normal de ne pas la retrouver dans ce fichier. La ligne sélectionnée correspond bien à la requête qu'a autorisée xinetd émise depuis charly vers charly (la première): l'heure et surtout le PID sont identiques.
Sep 18 17:15:42 charly in.fingerd[28857]: refused connect from 192.168.1.1
Résumons les éléments en notre possession:
En fait, telles que les lignes server et server_args du service ont été définies, les capacités du wrapper restent accessibles (banner -il existe aussi un attribut banner avec xinetd-, spawn, twist, ...). Rappelons que l'option de compilation --with-libwrap ajoute uniquement une vérification préalable, à l'aide des hosts.{allow,deny}, des droits d'accès avant que le processus propre à xinetd ne s'enclenche. Les écritures présentées ici permettent donc de continuer à employer les mécanismes mis en oeuvre avec le wrapper.
Ce chevauchement des capacités, s'il peut fonctionner, peut également conduire à d'étranges comportements. Pour faire cohabiter xinetd, inetd et portmap, il vaut mieux qu'un service ne soit géré que par un seul de ces "super-démons".
chroot [options] nouvelle_racineCeci s'utilise couramment pour protéger des services tels que bind/DNS ou ftp. Pour reproduire ce comportement tout en bénéficiant des capacités de xinetd, il suffit de déclarer chroot en tant que serveur. Il ne reste plus alors qu'à passer les autres arguments par l'intermédiaire de l'attribut server_args :)
service ftpAinsi, lorsqu'une requête est destinée à ce service, la première instruction utilisée est chroot. Ensuite, le premier argument qui lui est passé est le premier de la ligne server_args, c'est-à-dire la nouvelle racine. Enfin, le serveur désiré est démarré.
{
id = ftp
socket_type = stream
wait = no
user = root
server = /usr/sbin/chroot
server_args = /var/servers/ftp /usr/sbin/in.ftpd -l
}
Vous pouvez vous demander quel démon choisir entre xinetd et inetd.
En fait, xinetd demande un petit effort supplémentaire d'administration,
surtout tant qu'il ne sera pas inclus directement dans les
distributions (il l'est dans la Red Hat 7.0).
La solution la plus sûre consiste à utiliser xinetd sur votre
(vos) machine(s) ayant un accès public (comme Internet) car il offre
une meilleure ligne de défense. Pour les autres, à l'intérieur
de votre réseau, inetd suffit
amplement. Ne pas négliger toutefois l'emploi d'un firewall: son
travail ne se situe pas au même niveau.
pop3
Un service très populaire semble être pop3
: j'ai
reçu beaucoup de messages concernant sa mise en oeuvre au travers de
xinetd
. Voici la configuration nécessaire :
service pop3 { disable = no socket_type = stream wait = no user = root server = /usr/sbin/ipop3d # log_on_success += USERID # log_on_failure += USERID }Il faut que vous placiez, à la ligne contenant
server
, le
chemin vers le serveur que vous utilisez.
L'utilisation de pop3
via xinetd
peut
s'avérer laborieuse selon les directives de logs que vous donnez. En
effet, comme nous l'avons vu, l'emploi de la valeur
USERID
provoque une requête de xinetd
vers
un serveur identd
du client pop
. Or, si
aucun serveur de ce type ne fonctionne, le timeout est de 30
secondes.
Ainsi, lorsqu'une personne se connecte pour retirer son courrier, elle
devra attendre au moins 30 secondes si aucun serveur
identd
ne fonctionne chez elle. Vous devez donc choisir
entre :
identd
sur toutes les machines
clientes afin de conserver des logs complets (attention
toutefois: les informations délivrées par un serveur
identd
peuvent être modifiées) ;
bug
24279 rapporté sur bugzilla.
Certains services configurés dans le répertoire
/etc/xinetd.d
ne sont pas définis dans le fichier
/etc/services
.
[pappy@rootdurum xinetd.d]# grep service *udp chargen-udp:service chargen-udp daytime-udp:service daytime-udp echo-udp:service echo-udp time-udp:service time
Le responsable du bug chez RH signale qu'on ne peut pas utiliser la
correction que j'ai suggérée car ça poserait des problèmes de
manipulation avec chkconfig
et ntsysv
.
Entre ces outils et l'utilisation de xinetd
, mon choix est
vite fait ;-)