Le support du mail virtuel est une demande toujours grandissante. Sendmail affirme qu'il supporte le mail virtuel. En fait, il se contente d'être à l'écoute de mail pour différents domaines. Vous pouvez alors demander à faire suivre le mail quelque part. Cependant, si vous le faites suivre sur la machine locale et que vous avez du mail pour bob@domaine1.com et bob@domaine2.com, ils vont atteindre la même boîte. C'est un problème puisque les bob sont deux personnes différentes, avec du courrier électronique différent.
Vous pouvez vous assurer que chaque nom d'utilisateur est unique en utilisant une numérotation des noms d'utilisateurs : bob1, bob2, etc... Vous pourriez également hacker le mail et le pop pour que ces conversions soient invisibles, mais cela peut devenir désordonné. Le mail sortant à pour domaine domaineprincipal.com et vous désirez que chaque mail envoyé dans chaque sous-domaine ait une adresse From: différente.
J'ai deux solutions. L'une fonctionne avec sendmail et l'autre avec Qmail. La solution avec sendmail devrait fonctionner avec une installation standard de sendmail. Cependant elle partage toutes les limitations établies dans sendmail. Il est nécessaire aussi qu'un sendmail ait été lancé en mode de file d'attente (queue mode) pour chaque domaine. Avoir 50 ou plus processus sendmail en mode de file d'attente qui se réveillent toutes les heures peut ajouter des contraintes sur une machine.
La solution pour Qmail ne requiert pas plusieurs exemplaires de Qmail et peut n'utiliser qu'un seul répertoire de file d'attente. Il a besoin d'un autre programme puisque que Qmail ne se fonde pas sur virtuald. Je crois qu'une procédure similaire peut être faite avec Sendmail. Cependant, Qmail se prête plus aisément à cette solution.
Je ne cautionne aucun des deux programmes en particulier. L'installation de Sendmail est un peu moins complexe mais Qmail est pobablement le plus puissant des deux paquetages de serveur Mail.
Chaque système de fichiers virtuel fournit à chaque domaine un fichier /etc/passwd
. Cela signifie que bob@domaine1.com et bob@domaine2.com sont des utilisateurs différents dans des fichiers /etc/passwd
différents, donc le mail ne constituera aucun problème. Ils possèdent également chacun un spool de mail, donc les boîtes aux lettres seront des fichiers différents sur des système de fichiers virtuels différents.
Créez /etc/sendmail.cf
comme vous le feriez d'habitude avec m4 :
divert(0) VERSIONID(`tcpproto.mc') OSTYPE(linux) FEATURE(redirect) FEATURE(always_add_domain) FEATURE(use_cw_file) FEATURE(local_procmail) MAILER(local) MAILER(smtp)
Éditez /virtual/domain1.com/etc/sendmail.cf pour l'adapter à votre domaine virtuel :
vi /virtual/domain1.com/etc/sendmail.cfApproximativement à la ligne 86 il doit y avoir :
#Dj$w.Foo.COMRemplacez-le avec :
Djdomain1.com
Éditez /virtual/domain1.com/etc/sendmail.cw
vi /virtual/domain1.com/etc/sendmail.cw mail.domain1.com domain1.com domain1 localhost
Cependant, sendmail nécessite un changement mineur de son code source. Sendmail utilise un fichier nommé /etc/sendmail.cw
qui contient tous les noms de machine pour lequel il distribuera le mail localement au lieu de le faire suivre à une autre machine. Sendmail fait une vérification interne de toutes les interfaces réseau de la machine pour initialiser cette liste avec les adresses IP locales. Cela présente un problème si vous envoyez des mails entre deux domaines virtuels de la même machine. Sendmail pensera que l'autre domaine virtuel est une adresse locale et il distribuera le mail localement. Par exemple, bob@domaine1.com envoie un mail à fred@domaine2.com. Puisque le sendmail de domaine1.com pense que domaine2.com est une adresse locale, il va envoyer ce mail à domaine1.com et ne l'enverra jamais à domaine2.com. Vous avez à modifier sendmail (ce que j'ai fait sans problème sur la version 8.8.6) :
vi v8.8.5/src/main.cVers la ligne 494 vous devriez remplacer la ligne :
load_if_names();Par :
/* load_if_names(); Commenté puisque cela casse les domaines virtuels */
Notez que cette modification n'est nécessaire que si vous désirez envoyer du mail entre des domaines virtuels, ce qui est probable, je pense.
Cela corrigera le problème. Cependant, l'adaptateur réseau principal eth0 n'est pas supprimé. Ainsi, si vous envoyez un mail depuis une adresse IP virtuelle vers une adresse sur eth0 de la même machine, il sera délivré localement. Pour cela, j'utilise une adresse IP bidon virtuel1.domaine.com (10.10.10.157). Je n'envoie jamais de mail à cet hôte, les domaines virtuels non plus. C'est aussi l'adresse IP que j'utiliserai pour me connecter sur la machine via ssh, pour vérifier si le système fonctionne.
Depuis la version 8.8.6 de Sendmail, il existe une option qui désactive le
chargement des interfaces réseaux supplémentaires. Ce qui implique la NON
nécéssité de toucher au code source. Elle s'appelle DontProbeInterfaces
.
Editer /virtual/domain1.com/etc/sendmail.cf
vi /virtual/domain1.com/etc/sendmail.cfAjouter la ligne :
O DontProbeInterfaces=True
Sendmail ne peut pas être lancé tel quel, vous allez devoir le lancer à travers inetd. C'est un moyen inefficace qui implique un temps de réponse plus long, mais si vous avez un site tellement occupé pour que la différence soit importante, alors vous devriez utiliser une machine dédiée à ce site. Notez que vous ne de devez PAS utiliser l'option -bd
. Notez également que vous devez lancer sendmail -q
pour chaque domaine que vous gérez. Le nouveau fichier sendmail.init
est le suivant :
#!/bin/sh # Source function library. . /etc/rc.d/init.d/functions case "$1" in start) echo -n "Starting sendmail: " daemon sendmail -q1h echo echo -n "Starting virtual sendmail: " for i in /virtual/* do if [ ! -d "$i" ] then continue fi if [ "$i" = "/virtual/lost+found" ] then continue fi chroot $i sendmail -q1h echo -n "." done echo " done" touch /var/lock/subsys/sendmail ;; stop) echo -n "Stopping sendmail: " killproc sendmail echo rm -f /var/lock/subsys/sendmail ;; *) echo "Usage: sendmail {start|stop}" exit 1 esac exit 0
Pop devrait s'installer normalement, sans effort supplémentaire. Vous n'avez qu'à modifier l'entrée pour pop dans le fichier inetd.conf
pour utiliser le démon virtuald. Pour sendmail et pop, cela donne :
pop-3 stream tcp nowait root /usr/bin/virtuald \ virtuald /virtual/conf.pop in.qpop -s smtp stream tcp nowait root /usr/bin/virtuald \ virtuald /virtual/conf.mail sendmail -bs
Cette solution prend la responsabilité de distribution du qmail-local, donc
l'utilisation des fichiers .qmail dans les répertoire home des domaines virtuels
ne marcheront pas. Cependant, chaque domaine aura toujours un utilisateur maître
par domaine qui contrôlera les aliasing du domaine. Deux programmes externes
seront utilisés pour le fichier .qmail-default
des maîtres de domaine.
Le mail passera par ces deux programmes afin de distribuer le courrier à chaque domaine.
Deux programmes sont necéssaires, car l'un deux est lancé avec le setuid root. C'est un petit programme qui se change en un utilisateur non administrateur et lance le second programme. Consultez le site le plus proche relatif à la sécurité pour une discussion sur le pourquoi est-ce nécessaire.
Cette solution se passe de virtuald. Qmail est assez flexible pour ne pas avoir besoin d'une configuration virtuald génerale. La conception de Qmail utilise l'enchaînement de programmes pour distribuer les mails. Cette conception facilite l'insertion d'une section virtuelle dans le processus de distribution de Qmail sans altérer une installation standard de Qmail.
Depuis que vous utilisez Qmail, tout nom de domaine non qualifié sera développé en utilisant le serveur principal. C'est dû au fait que vous n'avez pas un Qmail pour chaque domaine. Donc, soyez sûr que vos clients (Eudora, elm, mutt, etc.) puissent developper tous vos noms de domaines non qualifiés.
Qmail doit être configuré de manière à accepter les mails pour chaque domaine que vous désservez. Tapez la commande suivante :
echo "domain1.com:domain1" >> /var/qmail/control/virtualdomains
Ajouter à votre fichier /etc/passwd
principal l'utilisateur domain1.
Je choisirais le shell /bin/false pour que le maître du domaine ne puisse se connecter.
Cet utilisateur sera capable d'ajouter des fichier .qmail et tous les mails
passeront par ce compte. Notez que les noms d'utilisateurs ne peuvent faire que
8 caractères et les noms de domaines peuvent être plus long. Les caractères
restants seront ignorés. Ce qui implique, que les utilisateurs domain12 et
domain123 seraient le même utilisateur et Qmail pourra être perturbé. Donc,
attention à votre convention pour nommer les utilisateurs maîtres des domaines.
Créer les fichiers .qmail du maître de domaine avec les commandes suivantes. Ajoutez les autres alias système au même endroit. Par exemple, webmaster ou hostmaster.
echo "user@domain1.com" > /home/d/domain1/.qmail-mail-daemon echo "user@domain1.com" > /home/d/domain1/.qmail-postmaster echo "user@domain1.com" > /home/d/domain1/.qmail-root
Créez le fichier .qmail-default du maître de domaine. Il filtre tous les mails du domaine virtuel.
echo "| /usr/local/bin/virtmailfilter" > /home/d/domain1/.qmail-default
Qmail a besoin d'un pop spécial qui supporte le format maildir. Le programme pop
doit être rendu virtuel. L'auteur de Qmail recommande d'utiliser tcpserver
(un remplacement à inetd) avec Qmail, donc mes exemples utilisent tcpserver et
NON inetd.
Tcpserver n'a pas besoin de fichier de configuration. Toutes les informations peuvent etre passées par une ligne de commande. Ci-dessous, se trouve le fichier tcpserver.ini que vous devez utiliser pour le démon mail et le serveur pop :
#!/bin/sh . /etc/rc.d/init.d/functions QMAILDUSER=`grep qmaild /etc/passwd | cut -d: -f3` QMAILDGROUP=`grep qmaild /etc/passwd | cut -d: -f4` # Regarder comment nous étions appellés. case "$1" in start) echo -n "Starting tcpserver: " tcpserver -u 0 -g 0 0 pop-3 /usr/local/bin/virtuald \ /virtual/conf.pop qmail-popup virt.domain1.com \ /bin/checkpassword /bin/qmail-pop3d Maildir & echo -n "pop " tcpserver -u $QMAILDUSER -g $QMAILDGROUP 0 smtp \ /var/qmail/bin/qmail-smtpd & echo -n "qmail " echo touch /var/lock/subsys/tcpserver ;; stop) echo -n "Stopping tcpserver: " killall -TERM tcpserver echo -n "killing " echo rm -f /var/lock/subsys/tcpserver ;; *) echo "Usage: tcpserver {start|stop}" exit 1 esac exit 0
Vous pouvez utiliser le script standard de Qmail.init fourni. Qmail est livré avec une très bonne documentation décrivant comment le mettre en place.
Vous avez besoin de deux autres programmes pour que votre serveur mail virtuel fonctionne avec Qmail. Ce sont virtmailfilter et virtmaildelivery. Ceci est le code source en C de virtmailfilter. Il doit être installé dans /usr/local/bin avec les permissions 4750, l'utilisateur root et le groupe nofiles.
#include <sys/wait.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <stdio.h> #include <ctype.h> #include <pwd.h> #define VIRTPRE "/virtual" #define VIRTPWFILE "etc/passwd" #define VIRTDELIVERY "/usr/local/bin/virtmaildelivery" #define VIRTDELIVERY0 "virtmaildelivery" #define PERM 100 #define TEMP 111 #define BUFSIZE 8192 int main(int argc,char **argv) { char *username,*usernameptr,*domain,*domainptr,*homedir; char virtpath[BUFSIZE]; struct passwd *p; FILE *fppw; int status; gid_t gid; pid_t pid; if (!(username=getenv("EXT"))) { fprintf(stdout,"environment variable EXT not set\n"); exit(TEMP); } for(usernameptr=username;*usernameptr;usernameptr++) { *usernameptr=tolower(*usernameptr); } if (!(domain=getenv("HOST"))) { fprintf(stdout,"environment variable HOST not set\n"); exit(TEMP); } for(domainptr=domain;*domainptr;domainptr++) { if (*domainptr=='.' && *(domainptr+1)=='.') { fprintf(stdout,"environment variable HOST has ..\n"); exit(TEMP); } if (*domainptr=='/') { fprintf(stdout,"environment variable HOST has /\n"); exit(TEMP); } *domainptr=tolower(*domainptr); } for(domainptr=domain;;) { snprintf(virtpath,BUFSIZE,"%s/%s",VIRTPRE,domainptr); if (chdir(virtpath)>=0) break; if (!(domainptr=strchr(domainptr,'.'))) { fprintf(stdout,"domain failed: %s\n",domain); exit(TEMP); } domainptr++; } if (!(fppw=fopen(VIRTPWFILE,"r+"))) { fprintf(stdout,"fopen failed: %s\n",VIRTPWFILE); exit(TEMP); } while((p=fgetpwent(fppw))!=NULL) { if (!strcmp(p->pw_name,username)) break; } if (!p) { fprintf(stdout,"user %s: not exist\n",username); exit(PERM); } if (fclose(fppw)==EOF) { fprintf(stdout,"fclose failed\n"); exit(TEMP); } gid=p->pw_gid; homedir=p->pw_dir; if (setgid(gid)<0 || setuid(p->pw_uid)<0) { fprintf(stdout,"setuid/setgid failed\n"); exit(TEMP); } switch(pid=fork()) { case -1: fprintf(stdout,"fork failed\n"); exit(TEMP); case 0: if (execl(VIRTDELIVERY,VIRTDELIVERY0,username,homedir,NU { fprintf(stdout,"execl failed\n"); exit(TEMP); } default: if (wait(&status)<0) { fprintf(stdout,"wait failed\n"); exit(TEMP); } if (!WIFEXITED(status)) { fprintf(stdout,"child did not exit normally\n"); } break; } exit(WEXITSTATUS(status)); }
Ceci est le code source de virtmaildelivery. Il doit etre installé dans /usr/local/bin avec les permissions 0755, l'utilisateur root et le groupe root.
#include <sys/stat.h> #include <sys/file.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <stdio.h> #include <errno.h> #include <time.h> #define TEMP 111 #define BUFSIZE 8192 #define ATTEMPTS 10 int main(int argc,char **argv) { char *user,*homedir,*dtline,*rpline,buffer[BUFSIZE],*p,mail[BUFSIZE]; char maildir[BUFSIZE],newmaildir[BUFSIZE],host[BUFSIZE]; int fd,n,nl,i,retval; struct stat statp; time_t thetime; pid_t pid; FILE *fp; retval=0; if (!argv[1]) { fprintf(stdout,"invalid arguments: need username\n"); exit(TEMP); } user=argv[1]; if (!argv[2]) { fprintf(stdout,"invalid arguments: need home directory\n"); exit(TEMP); } homedir=argv[2]; if (!(dtline=getenv("DTLINE"))) { fprintf(stdout,"environment variable DTLINE not set\n"); exit(TEMP); } if (!(rpline=getenv("RPLINE"))) { fprintf(stdout,"environment variable RPLINE not set\n"); exit(TEMP); } while (*homedir=='/') homedir++; snprintf(maildir,BUFSIZE,"%s/Maildir",homedir); if (chdir(maildir)<0) { fprintf(stdout,"chdir failed: %s\n",maildir); exit(TEMP); } time(&thetime); pid=getpid(); if (gethostname(host,BUFSIZE)<0) { fprintf(stdout,"gethostname failed\n"); exit(TEMP); } for(i=0;i<ATTEMPTS;i++) { snprintf(mail,BUFSIZE,"tmp/%u.%d.%s",thetime,pid,host); errno=0; stat(mail,&statp); if (errno==ENOENT) break; sleep(2); time(&thetime); } if (i>=ATTEMPTS) { fprintf(stdout,"could not create %s\n",mail); exit(TEMP); } if (!(fp=fopen(mail,"w+"))) { fprintf(stdout,"fopen failed: %s\n",mail); retval=TEMP; goto unlinkit; } fd=fileno(fp); if (fprintf(fp,"%s",rpline)<0) { fprintf(stdout,"fprintf failed\n"); retval=TEMP; goto unlinkit; } if (fprintf(fp,"%s",dtline)<0) { fprintf(stdout,"fprintf failed\n"); retval=TEMP; goto unlinkit; } while(fgets(buffer,BUFSIZE,stdin)) { for(p=buffer;*p=='>';p++) ; if (!strncmp(p,"From ",5)) { if (fputc('>',fp)<0) { fprintf(stdout,"fputc failed\n"); retval=TEMP; goto unlinkit; } } if (fprintf(fp,"%s",buffer)<0) { fprintf(stdout,"fprintf failed\n"); retval=TEMP; goto unlinkit; } } p=buffer+strlen(buffer); nl=2; if (*p=='\n') nl=1; for(n=0;n<nl;n++) { if (fputc('\n',fp)<0) { fprintf(stdout,"fputc failed\n"); retval=TEMP; goto unlinkit; } } if (fsync(fd)<0) { fprintf(stdout,"fsync failed\n"); retval=TEMP; goto unlinkit; } if (fclose(fp)==EOF) { fprintf(stdout,"fclose failed\n"); retval=TEMP; goto unlinkit; } snprintf(newmaildir,BUFSIZE,"new/%u.%d.%s",thetime,pid,host); if (link(mail,newmaildir)<0) { fprintf(stdout,"link failed: %s %s\n",mail,newmaildir); retval=TEMP; goto unlinkit; } unlinkit: if (unlink(mail)<0) { fprintf(stdout,"unlink failed: %s\n",mail); retval=TEMP; } exit(retval); }
Merci à Vicente Gonzalez (vince@nycrc.net) pour son aide qui a rendu possible la solution pour Qmail. Vous pouvez certainement écrire a Vince, pour le remercier, ainsi que lui poser vos questions et commentaires a propos de Qmail, le reste concernant ce HOWTO devant m'être adressé.