Installation d’un Sendmail chrooté

Les étapes qui suivent détaillent l’installation d’un Sendmail chrooté.
Récupérez l’archive de Sendmail (ici la version 8.13.7), et placez-la, par exemple, dans /usr/src.
Décompactez l’archive par :
# gzip -dc sendmail.8.13.7.tar.gz | tar xvf –
# cd sendmail-8.13.7

Installez Sendmail de façon conventionnelle :
# sh Build
# sh Build install

Une fois Sendmail installé, lancez la création du sous-environnement :
# mkdir /sendmail

Sendmail va tourner dans le sous-environnement que vous allez créer dans le répertoire /sendmail, et aucun autre répertoire ne sera visible à  l’extérieur de ce dernier.
Il vous faut maintenant fournir les éléments vitaux au logiciel (bibliothèques, répertoires, fichiers, etc.). Vous devez pour cela créer des fichiers physiques, car les liens symboliques sont proscrits car ils ne peuvent sortir du répertoire prison /sendmail :
# cd /sendmail
# mkdir usr
# mkdir usr/sbin

Sendmail est enfermé dans sa prison, avec des droits restreints :
# cp /usr/sbin/sendmail /sendmail/usr/sbin
# chown 0:0 /sendmail/usr/sbin/sendmail
# chmod 500 /sendmail/usr/sbin/sendmail

Des applications, telles que les serveurs Web, utilisent divers raccourcis pour accéder à  Sendmail. Vous devez donc créer ces raccourcis :
# mkdir /sendmail/lib # cd /sendmail/lib
# ln -s ../sbin/sendmail sendmail
# mkdir /sendmail/bin
# cd /sendmail/bin
# ln -s ../sbin/sendmail sendmail

Fournissez maintenant à  Sendmail les programmes annexes dont il a besoin, en commençant par extraire les bibliothèques dynamiques qu’il utilise :
# ldd /sendmail/usr/sbin/sendmail
libdb.so.3 => /lib/libdb.so.3 (0x40016000)
libresolv.so.2 => /lib/libresolv.so.2 (0x4004f000)
libcrypt.so.1 => /lib/libcrypt.so.1 (0x4005d000)
libnsl.so.1 => /lib/libnsl.so.1 (0x40060000)
libdl.so.2 => /lib/libdl.so.2 (0x40075000)
libc.so.6 => /lib/libc.so.6 (0x40078000)
/lib/ld-linux.so.2 => /lib/ld-linux.so.2 (0x40000000)

Le résultat peut varier suivant les options de compilation et les versions des noyaux :
# cd /sendmail/lib
# cp /lib/libdb.so.3 .
# cp /lib/libresolv.so.2 .
# cp /lib/libcrypt.so.1 .
# cp /lib/libnsl.so.1 .
# cp /lib/libdl.so.2 .
# cp /lib/libc.so.6 .
# cp /lib/ld-linux.so.2 .

Créez la file d’attente :
# mkdir /sendmail/var
# mkdir /sendmail/var/spool
# mkdir /sendmail/var/spool/mqueue
# chmod 700 /sendmail/var/spool/mqueue

Créez le répertoire /var/run pour que Sendmail y place son numéro de processus (sendmail.pid) :
# mkdir /sendmail/var/run

# touch /sendmail/var/run/sendmail.pid

Ajoutez l’unité /dev/null, dont Sendmail a besoin en interne :
# mkdir /sendmail/dev
# mknod /sendmail/dev/null c 1 3
# chown 0:0 /sendmail/dev/null
# chmod 666 /sendmail/dev/null

La définition du noeud (c 1 3) n’est valable que sous Linux.
Pour écrire ses historiques, Sendmail communique avec syslog via un socket. Vous devez donc créer ce dernière dans l’environnement chroot :
# /usr/sbin/syslogd -m 0 -p /sendmail/dev/log

L’emplacement de syslogd peut différer d’un système à  un autre. Prenez garde à  n’avoir qu’une seule instance de syslogd dans le système.

A ce stade, vous devez régler le délicat problème de la résolution de noms. Sendmail ne peut fonctionner correctement sans cette résolution. Cette configuration s’effectue dans le répertoire /etc :
# mkdir /sendmail/etc

Créez le fichier d’aiguillage des services (etc/nsswitch.conf) de façon à  renseigner l’emplacement des fichiers, indispensables à  la résolution de noms. Le contenu du fichier /sendmail/etc/nsswitch.conf est le suivant :
services: files

La ligne services renseigne le fichier etc/services, lequel contient la corrélation entre un numéro de port TCP ou UDP et le nom usuel du service.
Pour utiliser ce dispositif, vous devez pouvoir accéder à  la bibliothèque libnss_files-2.2.2.so :
# cd /sendmail/lib
# cp /lib/libnss_files-2.2.2.so .

Cette bibliothèque est généralement appelée via un autre nom, libnss_files.so.2 :
# ln -s libnss_files-2.2.2.so libnss_files.so.2

Voici la bibliothèque nécessaire à  la résolution de noms (DNS) :
# cp /lib/libnss_dns.so.2 .
/
et le fichier /sendmail/etc/services :
smtp 25/tcp mail

Puisque le chroot concerne uniquement SMTP, seule cette ligne est nécessaire.
Voici enfin le fichier du client DNS etc/resolv.conf :
nameserver 127.0.0.1

Nous supposons ici que vous disposez d’un serveur DNS local (127.0.0.1). Il est recommandé de mettre celui de votre FAI, car cela simplifie grandement la configuration de votre serveur.
Pour le répertoire /etc/mail contenant les données de configuration, vous disposez d.une alternative :
· Placer les bases de données et le fichier de configuration dans /sendmail/etc/mail par le biais d’un petit script exécuté à  chaque modification du paramétrage. De cette façon, vous isolez et sécurisez les fichiers source (alias, access, etc.) et le fichier mc dans l’environnement primaire.
· Créer un lien de /etc/mail vers /sendmail/etc/mail, exposant ainsi les fichiers source dans l’environnement chroot.
Choisissez ici la seconde possibilité, la plus commode :
# mkdir /sendmail/etc/mail
# ln -s /sendmail/etc/mail /etc/mail

Voici le fichier d’état de Sendmail, sendmail.st :
# touch /etc/mail/sendmail.st

et un très simple fichier mc :
include(`/usr/lib/sendmail-cf/m4/cf.m4′)
VERSIONID(`Sendmail chrooté ‘)dnl
OSTYPE(`linux’)dnl
define(`confMAX_MESSAGE_SIZE’, `5000000′)dnl
define(`STATUS_FILE’, `/etc/mail/sendmail.st’)dnl
define(`confDOMAIN_NAME’, `monserveur.com’)dnl
define(`ALIAS_FILE’, `/etc/mail/aliases’)dnl
define(`SMART_HOST’,`smtp.monfai.com’)dnl
define(`confLOG_LEVEL’,`14′)dnl
define(`confSEPARATE_PROC’,`true’)dnl
FEATURE(`access_db’,`hash -o /etc/mail/access’)dnl
FEATURE(always_add_domain)dnl
MAILER(smtp)dnl

qui vous permet de construire votre sendmail.cf :
# m4 sendmail.mc >sendmail.cf

Le niveau d’enregistrement des événements a été fixé à  14 (define(`confLOG_LEVEL’,`14′)). Cette valeur est particulièrement élevée afin de repérer les éventuelles erreurs. Dans cette configuration, tout le courrier est expédié vers le serveur de votre FAI, smtp.fai.com.
Voici le fichier /etc/mail/aliases :
MAILER-DAEMON: postmaster
postmaster: root
bin: root
daemon: root
nobody: root
system: root
root: monemail@monadresse.com

que vous créez de la façon suivante :
# chroot /sendmail /usr/sbin/sendmail -bi -v

Le root est en fait un alias vers une autre adresse e-mail. Sur le serveur chrooté, vous n’acceptez, par commodité, aucune livraison de courrier (voir plus haut).
Voici le fichier /etc/mail/access correspondant, qui contrôle les adresses autorisées à  utiliser le relais :
127.0.0.1 RELAY
10 RELAY
localhost RELAY
localhost.localdomain RELAY

que vous créez de la manière suivante :
# makemap -v hash access.db <access

Seule la machine locale et le réseau IP 10.0.0.0 sont autorisés à  utiliser le relais SMTP.
Démarrez Sendmail :
# chroot /sendmail /usr/sbin/sendmail -bd -q15m

Le serveur doit maintenant fonctionner correctement.
L’utilitaire strace est recommandé pour la mise au point d’un Sendmail chrooté. Il permet notamment de déterminer les fichiers et bibliothèques manquants.
Désormais, vous disposez d’un Sendmail parfaitement sécurisé, enfermé dans une prison logique.

Surveiller un serveur Sendmail

Du fait de sa criticité, un serveur Sendmail doit être surveillé afin de pouvoir agir rapidement. Les programmes présentés ci-dessous s’appuient sur des scripts Perl, couplés à  MRTG et à  SNMP. Le but est de pouvoir prévenir toute panne et agir proactivement.

Surveiller la partition /var d’un serveur Sendmail
Le petit script PERL ci-dessous récupère la taille maximale de la partition /var (index 3) ainsi que l’espace utilisé. Il calcule le pourcentage utilisé et prépare les résultats pour les passer à  MRTG :
#!/usr/bin/perl
# getdisk.pl
$max=`snmpget 192.168.10.2 mdp host.hrStorage.hrStorageTable.hrStorageEntry.hrStorageSize.3`;
$used=`snmpget 192.168.10.2 mdp host.hrStorage.hrStorageTable.hrStorageEntry.hrStorageUsed.3`;
$max =~ s/.*\:(.*)/$1/g;
$used =~ s/.*\:(.*)/$1/g;
if($max==0)
{
$pct=0;
}
else
{
$pct = ($used * 100) / $max;
}
$pct =~ s/(.*)\..*/$1/g;
print « $pct\n$pct\n\n\n »;

Le chapitre MRTG ci-dessous crée un graphique du pourcentage de l’espace utilisé sur la partition /var :
Target[sendmail.diskvar]: `/home/mrtg/bin/getdisk.pl`
Directory[sendmail.diskvar]: sendmail
MaxBytes[sendmail.diskvar]: 100
Unscaled[sendmail.diskvar]: dwmy
Options[sendmail.diskvar]: gauge
Title[sendmail.diskvar]: Charge du disque /var
XSize[sendmail.diskvar]: 580
YSize[sendmail.diskvar]: 100
YLegend[sendmail.diskvar]: %
ShortLegend[sendmail.diskvar]: %
PageTop[sendmail.diskvar]:<H1> Charge du disque /var </H1>
Legend1[sendmail.diskvar]: Espace disque utilisé en pourcentage
LegendI[sendmail.diskvar]: Volumetrie
LegendO[sendmail.diskvar]: Volumetrie

Surveiller le nombre de connexions sur un serveur Sendmail
Le petit script PERL ci-dessous extrait le nombre de connexions sur le serveur Sendmail et le prépare dans un format compréhensible par MRTG :
# !/usr/bin/perl
# gestestab.pl
$cnx=` snmpget 192.168.10.2 mdp tcp.tcpCurrEstab.0`;
chomp($cnx);
($cnx) = ($cnx =~ /= Gauge32: (.+)/);
if($cnx eq «  »)
{
print « 0\n0\n »;
}
else
{
print « $cnx \n$cnx \n »;
}

Voici le chapitre MRTG servant à  afficher le nombre de connexions :
Target[sendmail.estab]: `/home/mrtg/bin/getestab.pl`
Directory[sendmail.estab]: sendmail
Options[sendmail.estab]: gauge,nopercent
MaxBytes[sendmail.estab]: 50
Unscaled[sendmail.estab]: d
XSize[sendmail.estab]: 580
YSize[sendmail.estab]: 100
YLegend[sendmail.estab]: Nombre de connexions
ShortLegend[sendmail.estab]: Connexions
Title[sendmail.estab]: Connexions sur Sendmail
PageTop[sendmail.estab]: <H1>Nombre de connexions sur le serveur Sendmail</H1>

Surveiller la charge moyenne d’un serveur Sendmail
Le petit script PERL ci-dessous récupère la charge sur la dernière minute, l’arrondit et affiche le résultat sous une forme compréhensible par MRTG :
#!/usr/bin/perl
# getlat.pl
$charge=`snmpget 192.168.10.2 mdp enterprises.ucdavis.laTable.laEntry.laLoad.1`;
chomp($charge);
($charge) = ($charge =~/= (.+)/);
$charge=int($charge);
if($charge eq «  »)
{
print « 0\n0\n »;
}
else
{
print « $charge\n$charge\n »;
}

Voici le chapitre MRTG utilisé pour tracer la charge moyenne du serveur Sendmail : Target[sendmail.charge]: `/home/mrtg/bin/getlat.pl`
Directory[sendmail.charge]:sendmail
Options[sendmail.charge]: gauge,nopercent
MaxBytes[sendmail.charge]: 5
Unscaled[sendmail.charge]: dwmy
XSize[sendmail.charge]: 580
YSize[sendmail.charge]: 100
YLegend[sendmail.charge]: Charge
ShortLegend[sendmail.charge]: Charge
Title[sendmail.charge]: Charge du serveur Sendmail
PageTop[sendmail.charge]: <H1> Charge du serveur Sendmail </H1>

Surveiller la quantité de messages passant sur un serveur Sendmail
Tout d’abord voici un petit script Perl permettant la récupération des informations du fichier sendmail.st via la commande mailstats:
open (FD, »mailstats | »);
while (my $li = )
{
if($li =~ /===/)
{
$li=<FD>;
$li=~ s/ +/ /g;
@l0= split (/ /,$li);
($l0[3])=($l0[3] =~ /(.+)K/);
($l0[5])=($l0[5] =~ /(.+)K/);
if($l0[2] eq «  »)
{
print « 0 « ;
}
else
{
print « $l0[2] « ;
}
if($l0[4] eq «  »)
{
print « 0 « ;
}
else
{
print « $l0[4] « ;
}
}
}
close (FD);

Il ne faut pas oublier de supprimer le fichier /etc/mail/sendmail.st pour réinitialiser les informations de statistiques.
Le résultat du script est appelé au moyen d’une requête SNMP via la définition suivante dans le fichier snmpd.conf (exemple ucd-snmp) :
exec .1.3.6.1.4.1.2021.61 msg_geres /usr/bin/perl /usr/bin/pl/mlstat.pl

Le script ci-dessous reprend l’ensemble de la requête ainsi que le formatage pour MRTG :
#!/usr/bin/perl
# getmsg.pl
$etat=`snmpget 192.168.10.2 mdp enterprises.ucdavis.61.101.1`;
chomp($etat);
($etat, $etat2) = ($etat =~ /= « (.+) (.+) »/);
if($etat eq «  »)
{
print « 0\n »;
}
else
{
print « $etat\n »;
}
if($etat2 eq «  »)
{
print « 0\n »;
}
else
{
print « $etat2\n »;
}

Voici le chapitre MRTG permettant de tracer la volumétrie des messages :
Target[sendmail.msg]: `/home/mrtg/bin/getmsg.pl`
Directory[sendmail.msg]: sendmail
Options[sendmail.msg]: gauge,nopercent
MaxBytes[sendmail.msg]: 250
Unscaled[sendmail.msg]: d
XSize[sendmail.msg]: 580
YSize[sendmail.msg]: 100
YLegend[sendmail.msg]: Nbre de messages
ShortLegend[sendmail.msg]: msg
Legend1[sendmail.msg]: Messages entants
Legend2[sendmail.msg]: Messages sortants
LegendI[sendmail.msg]: Messages entants
LegendO[sendmail.msg]: Messages sortants
Title[sendmail.msg]: Volumétrie de la messagerie
PageTop[sendmail.msg]: <H1>Messages entrants / sortants Sendmail</H1>

Surveiller la file d’attente de Sendmail

Lorsque Sendmail reçoit un message, il tente de le livrer à  destination immédiatement. Si cela n’est pas possible alors il le dépose dans sa file d’attente et tente une nouvelle livraison par la suite. Le délai entre deux livraisons est fixé grâce au paramètre « -q » de Sendmail. Par exemple :
# /usr/sbin/sendmail -q15m

fixera à  15 minutes le délai entre deux tentatives de livraison. Idéalement, une file d’attente devrait être toujours vide ; mais bien entendu, cela n’est pas possible : le serveur de destination n’est pas disponible, la charge moyenne du serveur est trop importante, il n’y a pas assez de place sur le disque, etc.

Il est intéressant de surveiller le nombre de messages contenus dans la file d’attente ; ceci permet d’anticiper les pannes et aussi d’isoler certains problèmes récurrents tels que les serveurs en difficulté. Lorsqu’une file d’attente croà®t régulièrement sans se stabiliser alors il arrive un moment où il faut se poser des questions : est-ce que mon serveur est suffisamment bien calibré, ai-je assez de bande passante, etc.

Pour surveiller cette file, nous allons utiliser une combinaison de scripts que nous passerons à  MRTG via SNMP.

Tout d’abord, nous devons extraire le nombre de requêtes contenues dans la file. Ceci se fait via la commande « mailq ». Les informations résultantes de cette dernière ressemblent à  ceci :
h9GFItZ2020003 95399 Thu Oct 16 17:18
8BITMIME (host map: lookup (toulouse.xyz.fr): deferred)
<123@toulouse.xyz.fr>
h9GEvdZ2017358 95399 Thu Oct 16 16:57
8BITMIME (host map: lookup (xyz.fr): deferred)
<5432@xyz.fr>
h9GEEgZ2012871 4567 Thu Oct 16 16:14 <>
8BITMIME (Deferred: Connection refused by m5.labas.com.)

h9GCYYZ2007297 13646 Thu Oct 16 14:34 <>
8BITMIME (Deferred: Connection refused by newton.vanpiperzen.fr.)

Total requests: 708

La dernière ligne nous intéresse directement; ici, il y a donc 708 messages bloqués dans la file d’attente. Afin d’extraire le nombre, une simple combinaison de commandes et un script Awk suffit :
# /usr/bin/mailq | /usr/bin/tail -1 | /usr/bin/awk ‘BEGIN {} { j= $3} END {printf « %s « ,j }’

* La première instruction (mailq) déclenche l’affichage de la file d’attente
* La deuxième passe le résultat dans la commande « tail » qui n’affiche que la dernière ligne ( Total requests: 708)
* Le script awk n’affiche que le dernier mot (708)

Ceci fait, nous allons désormais passer ces informations à  MRTG; la façon la plus commode est d’utiliser SNMP.
Pour ce faire, il faut rajouter un OID propriétaire dans le snmpd.conf ; ici, nous travaillons avec ucd-snmp. Voici cet OID :
exec .1.3.6.1.4.1.2021.61 queue /home/s/queue.sh

Avec le script queue.sh contenant:
# !/bin/sh
# /usr/bin/mailq | /usr/bin/tail -1 | /usr/bin/awk ‘BEGIN {} { j= $3} END {printf « %s « ,j }’

Ceci terminé, il faut relancer le SNMP : # service snmpd restart (Sous RedHat)
# /etc/init.d/snmpd restart (Autre)

Essayons d’extraire les différents OID depuis la NMS (Network Management Server) :
# snmpwalk serveur-sendmail communaute enterprises.ucdavis.61
enterprises.ucdavis.61.1.1 = 1
enterprises.ucdavis.61.2.1 = « queue »
enterprises.ucdavis.61.3.1 = « /home/s/queue.sh »
enterprises.ucdavis.61.100.1 = 0
enterprises.ucdavis.61.101.1 = « 522 »
enterprises.ucdavis.61.102.1 = 0

Ca fonctionne, et nous allons donc travailler avec l’OID enterprises.ucdavis.61.101.1
La prochaine étape est de créer une entrée MRTG afin de grapher régulièrement les entrées obtenues par notre script. La seule difficulté est que notre script Awk renvoie l’information sous forme de caractères; MRTG, quand à  lui, attend un nombre. Nous allons donc devoir encore développer un script pour convertir la chaà®ne de caractères en nombre. Voici ce script, écrit en PERL, nous l’appellerons queue.pl :
#!/usr/bin/perl
$ip = shift @ARGV;
$com = shift @ARGV;
$mib1 = shift @ARGV;
$etat=`snmpget $ip $com $mib1`;
chomp($etat);
($etat) = ($etat =~ /= « (.+) »/);
if($etat eq «  »)
{
print « 0\n0\n »;
}
else
{
print « $etat\n$etat\n »;
}

Le script commence par récupérer l’OID au moyen de l’instruction « snmpget », puis il nettoie la chaà®ne des guillemets et finalement affiche le nombre en deux exemplaires, car MRTG attend toujours un minimum de deux nombres.
Pour utiliser notre script, il faudra donc taper :
# ./queue.pl
522
522

Il ne nous reste plus qu’à  créer le chapitre MRTG :
Target[sendmail.queue]: `/home/s/queue.pl 192.168.0.2 com enterprises.ucd avis.61.101.1`
Directory[sendmail.queue]: sendmail
Options[sendmail.queue]: gauge,nopercent
MaxBytes[sendmail.queue]: 3500
XSize[sendmail.queue]: 580
YSize[sendmail.queue]: 100
YLegend[sendmail.queue]: Nbre de messages en attente
ShortLegend[sendmail.queue]: msg
Legend1[sendmail.queue]: Messages en attente
Legend2[sendmail.queue]: Messages en attente
LegendI[sendmail.queue]: Messages en attente
LegendO[sendmail.queue]: Messages en attente
Title[sendmail.queue]:Messages dans la file d’attente
PageTop[sendmail.queue]: <H1>Messages dans la file d’attente sur Sendmail</H1>

Note: des problèmes peuvent survenir avec SNMP et la commande mailq en cas de surcharge du serveur ; il est tout à  fait envisageable que MRTG soit dans l’incapacité de récupérer les informations. Pour pallier à  ce problème, le mieux est de faire tourner le script qui va chercher le nombre de messages dans la file d’attente directement sur le serveur SMTP ; ce script devra être lancé régulièrement via une entrée dans la crontab et le résultat placé dans un fichier. Après, ce nombre pourra être récupéré par SNMP par un simple « cat ».