EmailVersandHack
Diese Konfiguration habe ich für unser SE2P Projekt auf unserem Ubuntu Virtual Server (SE2P Version) vorgenommen. Diese Anleitung dient dazu, dass man dem virtuellen Server, der von der SMTP-Welt ausgeschlossen ist, die Mailfunktionalität beibringt, indem man die Mails von Jenkins und Redmine irgendwo anders in Auftrag gibt. Diese Variante führt bei Erhalt eines Mails ein spezielles Skript aus, welches dann ein Skript auf einem öffentlichen Server über HTTP ankickt und die Daten mitschickt.
Inhaltsverzeichnis
Virtueller Server
Annahmen
- Dir steht ein (PHP-fähiger) Server zur Verfügung, der via HTTP erreichbar ist und Mails verschicken kann
- sendmail ist (vor)installiert
- Eingeloggt via ssh als Benutzer root
Vorbereitung
Installiere php (ja sorry, ich kann halt immernoch php am besten ;)
$ sudo apt-get install php5 php5-cli
Konfiguration
/etc/aliases
Der virtuelle lokale Benutzer "special" wird die E-Mails annehmen und sendmail "pipe"d so den Inhalt an das Skript weiter:
Suche nach:
# Other aliases
und ergänze mit:
special: |/usr/local/scripts/specialmailer/send.php
/etc/mail/sendmail.mc
Sendmail erweitern wir um das Feature, eigendlich externe Adressen intern to "relayen", dazu sind dann auch ein paar Einträge in anderen Files notwendig:
Suche nach:
dnl DAEMON_OPTIONS(`Family=inet6, Name=MSP-v6, Port=submission, M=Ea, Addr=::1')dnl DAEMON_OPTIONS(`Family=inet, Name=MSP-v4, Port=submission, M=Ea, Addr=127.0.0.1')dnl
und ergänze unterhalb mit:
dnl # Define virtualusertable FEATURE(`virtusertable', `hash /etc/mail/virtusertable')dnl VIRTUSER_DOMAIN_FILE(`/etc/mail/virtual-domains')dnl
/etc/mail/virtusertable
Wir definieren eine "catch-all" Regel und leiten diese an unseren speziellen "special" user weiter.
Erstelle Datei mit Inhalt:
@hsr.ch special
/etc/mail/virtual-domains
Damit die Regel überhaupt zieht, müssen wir definieren, dass sendmail die gewünschten Domains überhaupt beachtet.
Erstelle Datei mit Inhalt:
hsr.ch
/usr/local/scripts/specialmailer/send.php
Nun erstellen wir das Skript, welches angekickt wird im Falle, dass "special" eine Mail bekommt. Das kann natürlich auch ein eigenes Skript sein. Diese Variante leitet den ganzen Mail-Content per POST an das gewünschte Skript des öffentlichen Servers weiter.
!WICHTIG! Ändere $url zu der Adresse, wo sich das andere Skript dann befinden wird. !WICHTIG! Denke daran, dieser Datei Ausführberechtigung zu geben, z.B. mit:
$ chmod +x send.php
Erstelle Datei mit Inhalt:
#!/usr/bin/php -q <?php stream_set_blocking(STDIN, 0); $mail = ""; while(($line = fgets(STDIN)) !== FALSE) { $mail .= $line; } //file_put_contents("/tmp/send.log", $mail); $url = 'http://stuff.deinedomain.ch/hsrmail.php'; $data = array('msg' => $mail); $options = array( 'http' => array( 'header' => "Content-type: application/x-www-form-urlencoded\r\n", 'method' => 'POST', 'content' => http_build_query($data), ), ); $context = stream_context_create($options); $result = file_get_contents($url, false, $context);
Abschluss
Übernehme die neue Konfiguration mit (die Fragen grundsätzlich mit y=Yes beantworten)
$ newaliases $ sendmailconfig
Öffentlicher Server
Skript
Mein Mailversandskript nutzt das "alte" Zend Framework, um einfacher den Mail-Header und -body auseinander zu nehmen und das neue Mail dann zu verschicken. Ihr benötigt also noch das Zend Framework v1.x.x, auspacken, und das "library"-Verzeichnis in die gleiche ebene wie das Skript ablegen.
!WICHTIG! Schütze dein öffentliches Skript vor unbefugten Zugriffen, z.B. Alles blocken ausser der Anfragende hat die zugelassene IP
<php if($_SERVER['REMOTE_ADDR'] != "11.22.33.44") { die("You're not permitted to send mails over this script"); } @error_reporting(E_ALL^E_NOTICE); @ini_set('display_errors','on'); set_include_path(get_include_path().PATH_SEPARATOR.'./library'); include_once("Zend/Mail.php"); include_once("Zend/Mail/Message.php"); //file_put_contents("send.log", $_POST['msg']); $parsed_mail = new Zend_Mail_Message(array('raw' => $_POST['msg'])); //file_put_contents("debug.log", print_r($parsed_mail, true)); //file_put_contents("debug2.log", print_r($parsed_mail->to, true)); $mail = new Zend_Mail('UTF-8'); $mail->setFrom('special@dein-virtueller-server.edu.hsr.ch', 'DragonSlayer'); if(preg_match("/hsr\.ch/", $parsed_mail->to)) { foreach(explode(",", $parsed_mail->to) as $recipient) { $mail->addTo(trim($recipient)); } } else { // der Empfänger war special, welches dann an alle gehen sollte $mail->addTo('mitglied1@hsr.ch', 'mitglied1 name'); $mail->addTo('mitglied2@hsr.ch', 'mitglied2 name'); $mail->addTo('mitglied3@hsr.ch', 'mitglied3 name'); $mail->addTo('mitglied4@hsr.ch', 'mitglied4 name'); } $mail->setSubject($parsed_mail->subject); $mail->setBodyText(trim($parsed_mail->getContent())); $mail->send();
Konfiguration der Tools
Jenkins
Benutze als Mailserver jeweils "localhost". Soll das Mail an alle gehen, nutze "special" oder "special@localhost" als Empfänger, ich empfehle auch "special@localhost" als Absender zu definieren. Es wird keine Authentifizierung (da lokal) benötigt. Diese Variante ist für Jenkins interessant.
Konfiguration GUI
Redmine
Sobald ein Mail an eine @hsr.ch -Adresse geschickt wird, wird diese auch automatisch an "special" übermittelt. Das externe Skript erkennt jedoch, dass dieses Mail an einen (oder mehrere) direkt gesendet wird.
/etc/redmine/default/configuration.yml
Erstelle Datei mit Inhalt:
production: email_delivery: delivery_method: :sendmail
Konfiguration GUI
Debugging
Generell ist es schwierig zu debuggen, wenn man sich nicht in das Thema gearbeitet hat oder sich nicht auskennt. Anlaufstellen sind:
- Manuell ein Mail auslösen:
$ echo "Testmail" | mail -s "Testmail" mitglied1@hsr.ch $ echo "Mastermail" | mail -s "Mastermail" special
- die file_put_contents der Skripts aktivieren, und schauen was drin steht (in Zusammenhang mit den oberen beiden Befehlen natürlich den Inhalt wechseln)
- Die sendmail-logs durchsuchen, befinden sich unter /var/log/mail*