Envoyer des mails via ssh/sendmail
Le 26 décembre, 2011 - 22:20 | Ulhume

Un problème classique d'unixien itinérant qui possède un serveur de mail type postfix : comment envoyer d'un client tel mutt (ou tout autre client sérieux) du courriel d'où que l'on soit, de manière sécurisée et sans trop se prendre la tête. La solution est encore et toujours notre meilleur ami, SSH.

Problématique

L'approche classique pour résoudre ce problème consiste à mettre en place un protocole de reconnaissance entre le client courriel et le serveur. Ce protocole peut aller de l'échange de mot de passes, à celui de certificat, ou plus généralement par l'autorisation de certaines IP. Dans le cadre itinérant, la solution par mot de passes semble la plus logique. Mais lorsque l'on a déjà un tunnel SSH proprement configuré entre la machine itinérante et le serveur, on se demande s'il n'y a pas un moyen de réutiliser cette mécanique et ainsi éviter de fastidieuses configurations. En d'autre terme, comment faire tout cela sans toucher à la configuration du serveur :-)

Mais étrangement, dés que j'ai cherché sur la voie "SMTP over SSH", je suis tombé sur des solutions finalement aussi casse-bonbons que de mettre en place l'authentification que je cherchais à éviter. J'ai donc essayer de reprendre le problème à la base.

Faux sendmail over SSH

Traditionnellement sur un système UNIX, l'envoi de courrier se fait par la commande sendmail. Cette commande prend un certains nombre de paramètres dont l'adresse courriel du destinataire principale et reçoit, via pipe, le corps brut (en-têtes compris) du message. Cette commande est disponible dés lors qu'un MTA (Mail Transport Agent) comme Postfix, Exim ou autre est présent. C'est donc aussi le cas sur notre serveur.

Sachant cela, il nous vient alors logiquement l'idée suivante : "pourquoi ne pas accéder au sendmail distant via SSH". En effet, SSH peut lancer n'importe quelle commande distante et la nourrir avec des données. Le principe développé est donc de faire sur la machine itinérante une fausse commande sendmail qui récupère les paramètres et le flux en entrée pour le rediriger vers le vrai sendmail distant. Un script qui semble somme toute simple à réaliser :

#! /bin/bash

/usr/bin/ssh mon.serveur.de.mail "/usr/sbin/sendmail $*"
exit $?

Rien de bien sorcier dans ce script (et un merci à Sputnik pour m'éviter le passage par des fichiers intermédiaires). Le message injecté via StdIn est routé dans le tunnel ssh et récupéré directement par la commande sendmail nourrie des paramètres qui ont été donnés au faux sendmail. On termine par un echo $? qui va retransmettre à l'appelant une éventuelle erreur.

Sur le principe, le faux sendmail fonctionne à merveille pour peux que vous ayez configuré votre tunnel SSH sans mot de passe (via un jeu de clefs publique/privée).

Pour tester, par exemple avec mutt, il suffit de spécifier le chemin vers le faux sendmail par une commande set sendmail=/chemin/vers/faux/sendmail. Une autre option encore plus simple consiste à mettre ce script en lieu et place d'un vrai sendmail, soit en /sbin/sendmail.

Conclusion

Cette solution très simple à mettre en œuvre m'a ainsi permis d'envoyer mes courriels de manière totalement sécurisée, rapide, et avec 0 configuration du serveur car pour lui, tout part en local, ce qui est toujours autorisé.

Maintenant cette solution n'est pas spécialement conventionnelle même si elle a le mérite de faire le job sans broncher. Une approche plus robuste serait sûrement de passer par UUCP comme l'indique Julm dans ses commentaires.

Mise à jour : création d'un tunnel synchronisé avec mutt

En quinze jours de baroude à travers le vietnam, j'ai eu le temps de tester le concept. Et finalement j'ai abouti sur une version plus bête encore que celle donnée plus haut, tout simplement en ne lançant pas mutt directement, mais en encapsulant ce lancement dans un script qui ouvre un tunnel (smtp et imap), attends que le tunnel soit opérationnel, lance mutt, et ferme le tunnel à la mort de mutt.

#! /bin/bash

# paramètres du tunnel
IMAP_LOCAL=10143
SMTP_LOCAL=10025
REMOTE_HOST=mon_serveur_distant

# Lancement du tunnel
ssh -nNT -L $IMAP_LOCAL:localhost:143 -L $SMTP_LOCAL:localhost:25 $REMOTE_HOST &
PID=$!

# On attends que le tunnel soit actif
until nc -zw30 localhost $IMAP_LOCAL;  do
  sleep 1
done
echo "Tunnel actif, lancement de mutt..."

mutt

# Nettoyage
kill $PID

Comme vous le voyez, c'est simple, efficace et relativement tout terrains (merci à Spoutnik pour ses corrections/simplifications !)

Vos remarques et commentaires...

julm, le 27 décembre, 2011 - 06:14

àmha c'est plus simple et _beaucoup_ plus propre d'utiliser ssh -L :

--- 8< --- localhost:/etc/postfix/main.cf
relayhost = [127.0.0.1]:1587
--- >8 ---

% ssh -TnNL 1587:localhost:587 remotehost.tld

(qui se couple tout aussi bien avec -o ControlMaster=auto d'ailleurs)

Ulhume, le 27 décembre, 2011 - 10:14

Ta solution est plus propre si et seulement si tu as l'usgae d'un mta sur ton portable, ce qui n'est pas mon cas. Je ne vois en effet réellement pas l'intérêt d'installer un soft supplémentaire juste pour envoyer des mails... Après, tout est question de point de vue évidement, mais ma solution fonctionne sur une machine sans mta, avec juste un accès ssh proprement configuré ce qui dans mon cas est une option de base.

Pour ce qui est du plus simple, tu la lances comment ta commande SSH ? à chaque ouverture de session ? A chaque envoie de mail ? Une solution plus efficace serait peut-être de créer une configuration (x)inetd pour ne pas maintenir ton port 1587 ouvert pour rien car cela peut poser problème lorsque tu passes ta vie à couper ta com (usage typique sur un portable).

Après c'est bien ce qui est magique avec *nix, chacun trouve sa solution dans un panel de possibles en fonction de ses goûts et de ses besoins :)

julm, le 27 décembre, 2011 - 19:14

l'intérêt d'avoir un agent de transfert de courriels sur son portable ? ben pour transférer des courriels c'est un peu fait pour. notamment ça garde les courriels au chaud quand on est hors-ligne.
pour ce qui est de (x)inetd, je doute qu'on puisse : ssh -L ouvre un port lui même et ne me semble pas avoir ce qu'il faut pour passer par (x)inetd.
ma connexion est lancée dans un script d'initialisation /etc/network/if-up/042-ssh-submission.
mais on peut aussi utiliser un transport postfix opportuniste :
--- 8< --- localhost:/etc/postfix/master.cf
ssh unix - n n - - pipe
flags=Fqhu user=julm argv=ssh $nexthop /usr/sbin/sendmail -bm -i $recipient
--- >8 ---
--- 8< --- localhost:/etc/postfix/main.cf
default_transport = ssh:remotehost.tld
--- >8 ---

si on veut se passer de postfix/exim pour une raison ou une autre, on peut aussi essayer taylor-uucp avec un port pipe ssh similaire, ça marche bien, mais c'est _plus_ difficile à configurer.

Ulhume, le 27 décembre, 2011 - 20:02

D'une manière générale, je n'envoie pas de courriels si je ne suis pas en ligne :) Mais le cas échéant, mutt stoque le courrier en erreur pour un renvoie la prochaine fois. Donc pour l'instant je vais continue à me passer d'un mta.

Pour le xinetd il ne s'agit pas d'automatiser le forwarding mais lancer un netcat distant permettant de rediriger le flux local:25 vers le distant.

pour l'heure je préfère toujours mon système simple et efficace et répondant à 100% du besoin ;-) Mais en tout cas merci pour toutes ces informations qui me serviront sûrement lorsque ma glutte ne me conviendra plus.

julm, le 28 décembre, 2011 - 03:42

bon, après en avoir discuté un peu autour de moi, uucp sur ssh c'est encore mieux ; c'est plus résistant aux connexions ssh qui flappent (pas de risque d'envoyer un courriel tronqué, et reprise du transfert si interrompu), et ça a une file de transfert aussi bien que postfix/exim ; en fait on n'a même pas besoin de postfix/exim, (on peut juste donner son mail à uux directement).

v'là pour la config du portable :

==> /home/julm/cfg/uucp/sys.cfg <==
system   autogeree.net
time     any
protocol e
commands rmail
call-login    *
call-password *
port          ssh-autogeree.net

==> /home/julm/cfg/uucp/call.cfg <==
autogeree.net  julm  password

==> /home/julm/cfg/uucp/port.cfg <==
port ssh-autogeree.net
type pipe
command /usr/bin/ssh -x -o BatchMode=yes autogeree.net /usr/sbin/uucico --slave --prompt -I cfg/uucp/uucp.cfg; uuxqt --system julm -I cfg/uucp/uucp.cfg& disown

==> /home/julm/cfg/uucp/uucp.cfg <==
hostname   julm
run-uuxqt  once
sysfile    /home/julm/cfg/uucp/sys.cfg
portfile   /home/julm/cfg/uucp/port.cfg
dialfile   /home/julm/cfg/uucp/dial.cfg
callfile   /home/julm/cfg/uucp/call.cfg
passwdfile /home/julm/cfg/uucp/passwd.cfg
logfile    /dev/null
statfile   /dev/null
debugfile  /home/julm/var/log/uucp/debug.log
spool      /home/julm/var/spool/uucp/
pubdir     /home/julm/pub/uucp/
lockdir    /home/julm/var/lock/uucp/

==> /etc/postfix/master.cf <==
uucp      unix  -       n       n       -       -       pipe
  flags=Fqhu user=julm:uucp argv=/usr/bin/uux -I /home/julm/cfg/uucp/uucp.cfg -n -z -a$sender - $nexthop!rmail ($recipient)

==> /etc/postfix/main.cf <==
default_transport = uucp:autogeree.net

et v'là pour la config de la passerelle :

==> /home/julm/cfg/uucp/sys.cfg <==
system   julm
time     any
protocol e
commands rmail

==> /home/julm/cfg/uucp/passwd.cfg <==
julm  password

==> /home/julm/cfg/uucp/uucp.cfg <==
hostname   autogeree.net
run-uuxqt  once
sysfile    /home/julm/cfg/uucp/sys.cfg
portfile   /home/julm/cfg/uucp/port.cfg
dialfile   /home/julm/cfg/uucp/dial.cfg
callfile   /home/julm/cfg/uucp/call.cfg
passwdfile /home/julm/cfg/uucp/passwd.cfg
logfile    /dev/null
statfile   /dev/null
debugfile  /home/julm/var/log/uucp/debug.log
spool      /home/julm/var/spool/uucp/
pubdir     /home/julm/pub/uucp/
lockdir    /home/julm/var/lock/uucp/

==> /home/julm/cfg/procmail/delivery.cfg <==
USER=`whoami`

:0
| /usr/bin/uux \
 -I "$HOME/cfg/uucp/uucp.cfg" \
 -n -z -a$USER -r \
 - "$USER!rmail" "($USER)"

Ulhume, le 29 décembre, 2011 - 01:18

Encore merci pour le temps passé là dessus. uucp me ramène assez loin dans le temps mais le principe semble très intéressant.

(note que j'ai bien compris que tu n'aimais pas du tout ma solution ;-))

sputnick, le 28 décembre, 2011 - 00:59

Salut,

la même chose en simplifiant à fonds, le -t c'est pour forcer une allocation de TTY sur le serveur distant.

#!/bin/bash

server=arcadia.karma-lab.net

/usr/bin/ssh -t $server "/usr/sbin/sendmail $*"
exit $?

Ulhume, le 29 décembre, 2011 - 01:29

Alors ton approche marche en effet très bien mise à part l'allocation du TTY qui justement coince chez moi (Pseudo-terminal will not be allocated because stdin is not a terminal.).

En revanche, si tu supprimes le -t, ça fonctionne parfaitement et sans erreurs. Mon erreur était de croire que le flux stdin n'était pas récupéré dans intermédiaire par la commande ssh (et idem à la réception). Grand merci pour cette simplification que je vais répercuter dans le tuto.

malic, le 25 janvier, 2012 - 22:36

Si tu commence à multiplier les connexions différentes au travers de SSH, peut-être qu'à un moment il va falloir envisager un VPN, hein...
C'est lourd à mettre en place mais ça simplifie grandement l'utilisation ensuite.

M

Ulhume, le 25 janvier, 2012 - 23:22

Pour l'instant je n'ai jamais eu besoin que de l'IMAP et du SMTP. Le reste est un peu trop lourd pour être proprement transporté en baroude.

Ceci dit, avec les temps qui courent, je risque de te demander de l'aide pour un VPN, mais ce sera pour de toutes autres raisons ;-)

sputnick, le 25 janvier, 2012 - 22:57

Tu a une erreur dans ton script bash de "Mise à jour : création d'un tunnel synchronisé avec mutt" ligne 10 : il faut mettre $! et pas $? pour avoir le pid de la dernière commande lancée en arrière plan.

J'ai aussi simplifié ta boucle:
http://pastie.org/3252204

Ulhume, le 25 janvier, 2012 - 23:20

Ah oui c'est beaucoup plus joli ainsi, merci !!

Publier un nouveau commentaire

Le contenu de ce champ sera maintenu privé et ne sera pas affiché publiquement. If you have a Gravatar account associated with the e-mail address you provide, it will be used to display your avatar.
  • To highlight piece of code, just surround them with <code type="language"> Your code &tl;/code>>. Language can be java,c++,bash,etc... Everything Geshi support.
  • Tags HTML autorisés : <a> <em> <strong> <cite> <code> <ul> <ol> <li> <dl> <dt> <dd> <blockquote> <div> <p> <br>
  • Les lignes et les paragraphes vont à la ligne automatiquement.
  • Les adresses de pages web et de courriels sont transformées en liens automatiquement.

Plus d'informations sur les options de formatage

CAPTCHA
Cette question est là pour déterminer si vous êtes humain ou pas...