Postfix & Co.
Postfix: Mail-Relay mit SMTP-Auth via Submission/TLS für ausgehende Mails
0Ein Server soll als Relayhost (oder in der Windowswelt auch Smarthost gennant) dienen. Wer keine feste IP Adresse mit passenden DNS Reverse Eintrag hat wird schlechte Chancen haben das ein richtig konfigurierter Mailserver die Mails annehmen wird. Die Lösung ist die Mails an einen anderen Relayhost oder Mailserver zu schicken und dieser stellt dann die Mails zu. Manchmal bieten die Provider selbst Relayhosts an in vielen Fällen bleibt nichts anderes übrig als das ganze über ein Postfach zu versenden. Einige Provider bieten das Anliefern von Mails nur noch über Submission (TCP/587) und TLS verschlüsselt an. Ist mir persönlich auch lieber …
Beispielszenario
Provider: example.net
Mailaccount: user1@example.net Password: example%1
Mailserver: mail.example.net
Submission (TCP/587) und TLS sind notwendig.
Lösung
“/etc/postfix/main.cf” ergänzen mit:
relayhost = mail.example.net:submission smtp_use_tls=yes smtp_sasl_auth_enable = yes smtp_sasl_password_maps = hash:/etc/postfix/smtp_auth smtp_sasl_security_options = noanonymous, noplaintext smtp_sasl_tls_security_options = noanonymous
/etc/postfix/smtp_auth (Map-Datei, muss initialisiert werden!!!)
mail.example.net:submission user1@example.net:example%1
Folgende Kommandos sind noch auszuführen:
postmap /etc/postfix/smtp_auth
durchführen um die Datei zu mappen.
/etc/init.d/postfix restart
durchführen um die Änderungen zu aktivieren.
Python Version von getadsmtp.pl
0Übersetzung des AD Mailadressen Sammelskripts von Perl nach Python.
#!/usr/bin/python
# getadsmtp.py
# Version 1.0
# The script is an translation from the orginal perl script getadsmtp.pl
# This script will pull all users' SMTP addresses from your Active Directory
# (including primary and secondary email addresses) and list them in the
# format "user@example.com OK" which Postfix uses with relay_recipient_maps.
# Be sure to double-check the path to python above.
# This requires python-ldap to be installed. To install python-ldap on debian based systems,
# at a shell type "apt-get install python-ldap" or "sudo apt-get install python-ldap"
import os, sys, ldap
# Enter the path/file for the output
valid_addresses = "/etc/postfix/example_recipients"
# Enter the FQDN of your Active Directory domain controllers below
dc1="dc01.example.com"
dc2="dc02.example.com"
# Enter the LDAP container for your userbase.
# The syntax is CN=Users,dc=example,dc=com
# This can be found by installing the Windows 2000 Support Tools
# then running ADSI Edit.
# In ADSI Edit, expand the "Domain NC [domaincontroller1.example.com]" &
# you will see, for example, DC=example,DC=com (this is your base).
# The Users Container will be specified in the right pane as
# CN=Users depending on your schema (this is your container).
# You can double-check this by clicking "Properties" of your user
# folder in ADSI Edit and examining the "Path" value, such as:
# LDAP://domaincontroller1.example.com/CN=Users,DC=example,DC=com
# which would be hqbase="cn=Users,dc=example,dc=com"
# Note: You can also use just hqbase="dc=example,dc=com"
hqbase="cn=Users,dc=example,dc=com"
# Enter the username & password for a valid user in your Active Directory
# with username in the form cn=username,cn=Users,dc=example,dc=com
# Make sure the user's password does not expire. Note that this user
# does not require any special privileges.
# You can double-check this by clicking "Properties" of your user in
# ADSI Edit and examining the "Path" value, such as:
# LDAP://domaincontroller1.example.com/CN=user,CN=Users,DC=example,DC=com
# which would be $user="cn=user,cn=Users,dc=example,dc=com"
# Note: You can also use the UPN login: "user@example.com"
user="cn=user,cn=Users,dc=example,dc=com"
passwd="password"
try:
l = ldap.initialize("ldap://%s" %(dc1))
l.set_option(ldap.OPT_REFERRALS, 0)
l.protocol_version = 3
l.simple_bind_s(user, passwd)
except ldap.LDAPError, e:
try:
l = ldap.initialize("ldap://%s" %(dc2))
l.set_option(ldap.OPT_REFERRALS, 0)
l.protocol_version = 3
l.simple_bind_s(user, passwd)
except ldap.LDAPError, e:
print "Error connecting to specified domain controllers\n"
sys.exit()
# Play around with this to grab objects such as Contacts, Public Folders, etc.
# A minimal filter for just users with email would be:
# filter = "(&(sAMAccountName=*)(mail=*))"
filter = "(& (mailnickname=*) (| (&(objectCategory=person)(objectClass=user)(!(homeMDB=*))(!(msExchHomeServerName=*)))(&(objectCategory=person)(objectClass=user)(|(homeMDB=*)(msExchHomeServerName=*)))(&(objectCategory=person)(objectClass=contact))(objectCategory=group)(objectCategory=publicFolder)(objectClass=msExchDynamicDistributionList) ))"
attrs = ["proxyAddresses"]
scope = ldap.SCOPE_SUBTREE
r = l.search(hqbase, scope, filter, attrs)
type,a = l.result(r)
result_set = []
for x in a:
name,attrs = x
if hasattr(attrs, 'has_key') and attrs.has_key('proxyAddresses'):
proxyAddresses = attrs['proxyAddresses']
for y in proxyAddresses:
result_set.append("%s OK" %(y.replace("smtp:","").replace("SMTP:","")))
# Add additional restrictions, users, etc. to the output file below.
#result_set.append("user@example.com OK")
#result_set.append("user1@example.com 550 User unknown.")
#result_set.append("bad.example.com 550 User does not exist.")
#######################################################################
# Build file ...
output = file(valid_addresses,'w')
for line in result_set:
output.write("%s\n" %(line))
output.close()
Postfix als Frontend für einen Microsoft Exchange Server
1Für einen Kunden habe ich einen Postfix eingerichtet als Frontend Server für seine Exchange Infrastruktur. Die Mailadressen werden automatisch aus dem Active Directory ausgelesen und in einem Hash-File gespeichert. Vorteil ist das der Postfix so nur gültige Mailaliases zulässt. Der Frontend macht auch noch weitere Überprüfungen. (postgrey, policy-weight, etc.)
Folgendes Script wird für das Auslesen der Aliase aus dem AD verwendet:
#!/usr/bin/perl -T -w
# Version 1.02
# This script will pull all users' SMTP addresses from your Active Directory
# (including primary and secondary email addresses) and list them in the
# format "user@example.com OK" which Postfix uses with relay_recipient_maps.
# Be sure to double-check the path to perl above.
# This requires Net::LDAP to be installed. To install Net::LDAP, at a shell
# type "perl -MCPAN -e shell" and then "install Net::LDAP"
use Net::LDAP;
use Net::LDAP::Control::Paged;
use Net::LDAP::Constant ( "LDAP_CONTROL_PAGED" );
# Enter the path/file for the output
$VALID = "/etc/postfix/example_recipients";
# Enter the FQDN of your Active Directory domain controllers below
$dc1="domaincontroller1.example.com";
$dc2="domaincontroller2.example.com";
# Enter the LDAP container for your userbase.
# The syntax is CN=Users,dc=example,dc=com
# This can be found by installing the Windows 2000 Support Tools
# then running ADSI Edit.
# In ADSI Edit, expand the "Domain NC [domaincontroller1.example.com]" &
# you will see, for example, DC=example,DC=com (this is your base).
# The Users Container will be specified in the right pane as
# CN=Users depending on your schema (this is your container).
# You can double-check this by clicking "Properties" of your user
# folder in ADSI Edit and examining the "Path" value, such as:
# LDAP://domaincontroller1.example.com/CN=Users,DC=example,DC=com
# which would be $hqbase="cn=Users,dc=example,dc=com"
# Note: You can also use just $hqbase="dc=example,dc=com"
$hqbase="cn=Users,dc=example,dc=com";
# Enter the username & password for a valid user in your Active Directory
# with username in the form cn=username,cn=Users,dc=example,dc=com
# Make sure the user's password does not expire. Note that this user
# does not require any special privileges.
# You can double-check this by clicking "Properties" of your user in
# ADSI Edit and examining the "Path" value, such as:
# LDAP://domaincontroller1.example.com/CN=user,CN=Users,DC=example,DC=com
# which would be $user="cn=user,cn=Users,dc=example,dc=com"
# Note: You can also use the UPN login: "user\@example.com"
$user="cn=user,cn=Users,dc=example,dc=com";
$passwd="password";
# Connecting to Active Directory domain controllers
$noldapserver=0;
$ldap = Net::LDAP->new($dc1) or
$noldapserver=1;
if ($noldapserver == 1) {
$ldap = Net::LDAP->new($dc2) or
die "Error connecting to specified domain controllers $@ \n";
}
$mesg = $ldap->bind ( dn => $user,
password =>$passwd);
if ( $mesg->code()) {
die ("error:", $mesg->code(),"\n","error name: ",$mesg->error_name(),
"\n", "error text: ",$mesg->error_text(),"\n");
}
# How many LDAP query results to grab for each paged round
# Set to under 1000 for Active Directory
$page = Net::LDAP::Control::Paged->new( size => 990 );
@args = ( base => $hqbase,
# Play around with this to grab objects such as Contacts, Public Folders, etc.
# A minimal filter for just users with email would be:
# filter => "(&(sAMAccountName=*)(mail=*))"
filter => "(& (mailnickname=*) (| (&(objectCategory=person)
(objectClass=user)(!(homeMDB=*))(!(msExchHomeServerName=*)))
(&(objectCategory=person)(objectClass=user)(|(homeMDB=*)
(msExchHomeServerName=*)))(&(objectCategory=person)(objectClass=contact))
(objectCategory=group)(objectCategory=publicFolder)(objectClass=msExchDynamicDistributionList) ))",
control => [ $page ],
attrs => "proxyAddresses",
);
my $cookie;
while(1) {
# Perform search
my $mesg = $ldap->search( @args );
# Filtering results for proxyAddresses attributes
foreach my $entry ( $mesg->entries ) {
my $name = $entry->get_value( "cn" );
# LDAP Attributes are multi-valued, so we have to print each one.
foreach my $mail ( $entry->get_value( "proxyAddresses" ) ) {
# Test if the Line starts with one of the following lines:
# proxyAddresses: [smtp|SMTP]:
# and also discard this starting string, so that $mail is only the
# address without any other characters...
if ( $mail =~ s/^(smtp|SMTP)://gs ) {
push(@valid, $mail." OK\n");
}
}
}
# Only continue on LDAP_SUCCESS
$mesg->code and last;
# Get cookie from paged control
my($resp) = $mesg->control( LDAP_CONTROL_PAGED ) or last;
$cookie = $resp->cookie or last;
# Set cookie in paged control
$page->cookie($cookie);
}
if ($cookie) {
# We had an abnormal exit, so let the server know we do not want any more
$page->cookie($cookie);
$page->size(0);
$ldap->search( @args );
# Also would be a good idea to die unhappily and inform OP at this point
die("LDAP query unsuccessful");
}
# Only write the file once the query is successful
open VALID, ">$VALID" or die "CANNOT OPEN $VALID $!";
print VALID @valid;
# Add additional restrictions, users, etc. to the output file below.
#print VALID "user\@example.com OK\n";
#print VALID "user1\@example.com 550 User unknown.\n";
#print VALID "bad.example.com 550 User does not exist.\n";
close VALID;
Quelle: http://www-personal.umich.edu/~malth/gaptuning/postfix/
Ich habe das Perlscript in /etc/postfix abgelegt. Rechte sollten auch 700 sein. Diese 6 Parameter müssen geändert werden.
$VALID = "/etc/postfix/exchange_recipients"; $dc1="dc01.lanbugs.intra"; $dc2="dc02.lanbugs.intra"; $hqbase="cn=Users,dc=lanbugs,dc=intra"; $user="cn=sys-read-aliases-mailfront,cn=Users,dc=lanbugs,dc=intra"; $passwd="geheim123";
Das Script wird als Cronjob jede Stunde ausgeführt. Hierzu muss nur ein File mit dem Namen run_read_aliases_from_exchange.sh mit den Rechten 700 im Verzeichnis /etc/cron.hourly angelegt werden. (Debian/Ubuntu)
run_read_aliases_from_exchange.sh:
#!/bin/sh cd /etc/postfix ; ./getadsmtp.pl && postmap exchange_recipients
In der /etc/postfix/main.cf kann dann das neue Hash-File als relay_recipient_map verwendet werden.
main.cf:
... relay_recipient_maps = hash:/etc/postfix/exchange_recipients relay_domains = lanbugs.de transport_maps = hash:/etc/postfix/transport mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 10.1.1.20/32 ...
relay_domains erlaubt allgemein die Domain lanbugs.de. mynetworks wurde noch um die IP-Adresse des Exchange Servers erweitert damit dieser E-Mails zur Außenwelt senden kann. Die transport_maps sorgen dafür dass alle Mails an lanbugs.de an den Exchange Server weiter transportiert werden.
/etc/postfix/transport: (nach dem Anlegen postmap /etc/postfix/transport nicht vergessen!!!!)
langbugs.de smtp:[10.1.1.20]:25
10.1.1.20 ist der interne Exchange Server.
Viel Spaß mit dem Postfix Frontend
MySQL Multi Policy Server for Postfix
0MySQL Multi Policy Server befindet sich noch im BETA Status bzw. Entwicklungsphase.
Download komplett unter → Downloads
Status
-
Postgrey funktioniert soweit schon. (Valid ist noch nicht fertig.)
-
Autoresponder funktioniert.
Folgende Funktionen soll MMPS dann können wenn es fertig ist
-
Postgrey per User / Domain aktivieren / deaktivieren.
-
Delay per User / Domain
-
Valid per User / Domain
Datenbank Layout
CREATE TABLE `autoresponder_mail` ( `id` int(11) NOT NULL auto_increment, `sender` varchar(255) NOT NULL, `sender_name` varchar(255) NOT NULL, `subject` varchar(255) NOT NULL, `message` text NOT NULL, `von_timestamp` int(11) NOT NULL, `bis_timestamp` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `sender` (`sender`) ) TYPE=MyISAM COMMENT='Autoresponder Tabelle' AUTO_INCREMENT=2 ; -- -- Daten für Tabelle `autoresponder_mail` -- INSERT INTO `autoresponder_mail` (`id`, `sender`, `sender_name`, `subject`, `message`, `von_timestamp`, `bis_timestamp`) VALUES (1, ' test@example.com', 'TEST HELIX', 'Bin nicht im Büro', 'Hallo,\r\n\r\nich bin zurzeit nicht im Büro.\r\n\r\nAb 30. Mai 2008 bin ich wieder da.\r\n\r\nGruß\r\n\r\nMaximilian Thoma\r\n\r\näüö?ß', 0, 999999999); CREATE TABLE `black_mail` ( `id` int(11) NOT NULL auto_increment, `recipient` varchar(255) NOT NULL, `sender` varchar(255) NOT NULL, `aktiv` int(2) NOT NULL default '1', PRIMARY KEY (`id`) ) TYPE=MyISAM COMMENT='Blacklist' AUTO_INCREMENT=1 ; CREATE TABLE `grey_active` ( `id` int(11) NOT NULL auto_increment, `client_address` varchar(255) NOT NULL, `recipient` varchar(255) NOT NULL, `sender` varchar(255) NOT NULL, `timestamp` int(11) NOT NULL, PRIMARY KEY (`id`), KEY `recipient` (`recipient`,`sender`) ) TYPE=MyISAM COMMENT='Aktiven Greylistenings' AUTO_INCREMENT=38 ; CREATE TABLE `grey_mail` ( `id` int(11) NOT NULL auto_increment, `name` varchar(255) NOT NULL COMMENT 'Domain oder Mailadresse', `delay` int(11) NOT NULL COMMENT 'Verzögerung bis es durch Greylistening freigegeben wird', `valid` int(11) NOT NULL COMMENT 'Wie lange gilt die Freigabe in Sekunden', PRIMARY KEY (`id`), KEY `name` (`name`) ) TYPE=MyISAM COMMENT='User/Domains für die Greylistening aktiviert ist' AUTO_INCREMENT=3 ; -- -- Daten für Tabelle `grey_mail` -- INSERT INTO `grey_mail` (`id`, `name`, `delay`, `valid`) VALUES (1, ' postmaster@example.com', 60, 3600), (2, ' test@example.com', 15, 3600); CREATE TABLE `pps_log` ( `id` int(11) NOT NULL auto_increment, `sender` varchar(255) NOT NULL, `recipient` varchar(255) NOT NULL, `client_address` varchar(255) NOT NULL, `timestamp` int(11) NOT NULL, `comment` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) TYPE=MyISAM COMMENT='MTHPPS LOG' AUTO_INCREMENT=1 ; CREATE TABLE `white_mail` ( `id` int(11) NOT NULL auto_increment, `recipient` varchar(255) NOT NULL, `sender` varchar(255) NOT NULL, `aktiv` int(2) NOT NULL default '1', PRIMARY KEY (`id`) ) TYPE=MyISAM COMMENT='Whitelist' AUTO_INCREMENT=1 ;
PHP Script
<?php
// MySQL Multi Policy Server V.1.0
// Maximilian Thoma
// http://www.thoma.cc
// info@thoma.cc
//////////////////////////////////////////////////////////////////////////////////////
// OPTIONS
//////////////////////////////////////////////////////////////////////////////////////
$mysqlhost="localhost";
$mysqluser="mps";
$mysqlpass="PASSWORD";
$mysqldb="multipolicyserver";
//////////////////////////////////////////////////////////////////////////////////////
// Ab hier nichts mehr ändern !
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////
// DB Class
//////////////////////////////////////////////////////////////////////////////////////
class db {
var $link_id = 0;
var $query_id = 0;
var $record = array();
var $errdesc = "";
var $errno = 0;
var $show_error = 0;
var $server = "";
var $user = "";
var $password = "";
var $database = "";
var $appname = "MySQL Postgrey Policy Server";
function db($server,$user,$password,$database) {
$this->server=$server;
$this->user=$user;
$this->password=$password;
$this->database=$database;
$this->connect();
}
function connect() {
$this->link_id=mysql_connect($this->server,$this->user,$this->password);
if (!$this->link_id) $this->print_error("Link-ID == false, connect failed");
if ($this->database!="") $this->select_db($this->database);
}
function geterrdesc() {
$this->error=mysql_error();
return $this->error;
}
function geterrno() {
$this->errno=mysql_errno();
return $this->errno;
}
function select_db($database="") {
if ($database!="") $this->database=$database;
if(!@mysql_select_db($this->database, $this->link_id)) $this->print_error("cannot use database ".$this->database);
}
function query($query_string) {
global $query_count;
//echo $query_string."<br>";
$query_count++;
$this->query_id = mysql_query($query_string,$this->link_id);
if (!$this->query_id) $this->print_error("Invalid SQL: ".$query_string);
return $this->query_id;
}
function fetch_array($query_id=-1) {
if ($query_id!=-1) $this->query_id=$query_id;
$this->record = mysql_fetch_array($this->query_id);
return $this->record;
}
function fetch_row($query_id=-1) {
if ($query_id!=-1) $this->query_id=$query_id;
$this->record = mysql_fetch_row($this->query_id);
return $this->record;
}
function fetch_object($query_id=-1) {
if ($query_id!=-1) $this->query_id=$query_id;
$this->record = mysql_fetch_object($this->query_id);
return $this->record;
}
function free_result($query_id=-1) {
if ($query_id!=-1) $this->query_id=$query_id;
return @mysql_free_result($this->query_id);
}
function query_first($query_string) {
$this->query($query_string);
$returnarray=$this->fetch_array($this->query_id);
$this->free_result($this->$query_id);
return $returnarray;
}
function num_rows($query_id=-1) {
if ($query_id!=-1) $this->query_id=$query_id;
return mysql_num_rows($this->query_id);
}
function num_fields($query_id=-1) {
if ($query_id!=-1) $this->query_id=$query_id;
return mysql_num_fields($this->query_id);
}
function field_name($query_id=-1,$num) {
if ($query_id!=-1) $this->query_id=$query_id;
return mysql_field_name($this->query_id,$num);
}
function insert_id() {
return mysql_insert_id($this->link_id);
}
function print_error($errormsg) {
$this->errdesc=mysql_error();
$this->errno=mysql_errno();
$errormsg="Database error in $this->appname: $errormsg\n<br>";
$errormsg.="mysql error: $this->errdesc\n<br>";
$errormsg.="mysql error number: $this->errno\n<br>";
$errormsg.="Date: ".date("d.m.Y @ H:i")."\n<br>";
$errormsg.="Script: ".getenv("REQUEST_URI")."\n<br>";
$errormsg.="Referer: ".getenv("HTTP_REFERER")."\n<br><br>";
if($this->show_error) $errormsg = "$errormsg";
else $errormsg = "\n $errormsg \n";
die("</table>Database Error!\n".$errormsg);
}
}
//////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////
// DB INIT
//////////////////////////////////////////////////////////////////////////////////////
$db = new db($mysqlhost,$mysqluser,$mysqlpass,$mysqldb);
//////////////////////////////////////////////////////////////////////////////////////
// STDIN
//////////////////////////////////////////////////////////////////////////////////////
if ($fp=fopen("php://stdin","r")) {
while($stop!=1){
$line = fgets($fp,512);
$teile = explode("=", $line);
$stdin[$teile[0]]=$teile[1];
// Entfernen von Leerzeilen und Zeilenumbrüchen
$stdin[$teile[0]] = preg_replace("/\r|\n/s", "", $stdin[$teile[0]]);
if($line=="\n"){$stop=1;}
}
fclose($fp);
}
//////////////////////////////////////////////////////////////////////////////////////
// LOG
//$zeit=time();
//$db->query("INSERT INTO pps_log (zeit, recipient, sender, client_address) VALUES ('$zeit', '$stdin[recipient]', '$stdin[sender]', '$stdin[client_address]')");
// Mailadressen zerlegen
$sender_expl=explode('@',$stdin[sender]);
$sender_domain=$sender_expl[1];
$recipient_expl=explode('@',$stdin[recipient]);
////////////////////////////////////////////////////////////////////////////////
/// WHITELIST
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// BLACKLIST
////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
/// POSTGREYLISTENING
////////////////////////////////////////////////////////////////////////////////
// Überprüfen ob Domain oder User überhaupt mthpostgrey verwendet ?
$grey_req1=$db->query("SELECT * FROM grey_mail WHERE name='$stdin[recipient]'");
$grey_fet=$db->fetch_array($grey_req1);
$grey_sec=$grey_fet[delay];
$user_on=$db->num_rows($grey_req1);
if($user_on==0){
$action="DUNNO";
$stdout = fopen('php://stdout', 'w');
fwrite($stdout,"action=$action\n\n");
fclose($stdout);
exit;
} else {
$grey_test1_q=$db->query("SELECT * FROM grey_active WHERE sender='$stdin[sender]' AND recipient='$stdin[recipient]' AND client_address='$stdin[client_address]'");
$grey_test1_r=$db->num_rows($grey_test1_q);
if($grey_test1_r==0){
$action="defer_if_permit 1 MySQL Multi Policy Server is active. Refer to http://www.thoma.cc/ - Greylisted for: ".$wartezeit1." Seconds.";
$stdout = fopen('php://stdout', 'w');
fwrite($stdout,"action=$action\n\n");
fclose($stdout);
$a_time=time();
$db->query("INSERT INTO grey_active (sender, recipient, client_address,timestamp) VALUES ('$stdin[sender]','$stdin[recipient]','$stdin[clie$
exit;
} else {
$grey_test2_q=$db->query("SELECT * FROM grey_active WHERE sender='$stdin[sender]' AND recipient='$stdin[recipient]' AND client_address='$stdin[client_address]'");
$grey_test3_r=$db->fetch_array($db->query("SELECT * FROM grey_mail WHERE name='$stdin[recipient]'"));
$delay_time=$grey_test3_r[delay];
//$valid_time=$grey_test3_r[valid];
$grey_test2_r=$db->fetch_array($grey_test2_q);
$first_time=$grey_test2_r[timestamp];
$b_time=time();
//$time_valid_max=$b_time+$valid_time;
$time_delay=$b_time+$delay_time;
// Überprüfen ob Delay Time schon vorbei ist
if($delay_time<(time()-$first_time)){
$action="DUNNO";
$action2="PREPEND X-MTHPPS: Greylistening Filter active.";
$stdout = fopen('php://stdout', 'w');
fwrite($stdout,"action=$action2\n\n");
fwrite($stdout,"action=$action\n\n");
fclose($stdout);
} else {
$wartezeit1=$delay_time-(time()-$first_time);
$action="defer_if_permit 2 MySQL Multi Policy Server is active. Refer to http://www.thoma.cc/ - Greylisted for: ".$wartezeit1." Seconds.";
$stdout = fopen('php://stdout', 'w');
fwrite($stdout,"action=$action\n\n");
fclose($stdout);
exit;
}
}
}
////////////////////////////////////////////////////////////////////////////////
/// AUTORESPONDER
////////////////////////////////////////////////////////////////////////////////
$auto_test1_q=$db->query("SELECT * FROM autoresponder_mail where sender='$stdin[recipient]'");
$auto_test1_r=$db->num_rows($auto_test1_q);
if($auto_test1_r!=0){
$auto_test1_r2=$db->fetch_array($auto_test1_q);
$empfaenger = $stdin[sender];
$betreff = $auto_test1_r2[subject];
$text = $auto_test1_r2[message];
mail($empfaenger, $betreff, $text, "From: $auto_test1_r2[sender_name] <$auto_test1_r2[sender]>");
}
//////////////////////////////////////////////////////////////////////////////////////
//$action="DUNNO";
//$stdout = fopen('php://stdout', 'w');
//fwrite($stdout,"action=$action\n\n");
//fclose($stdout);
//////////////////////////////////////////////////////////////////////////////////////
?>
Anpassungen main.cf
smtpd_recipient_restrictions = permit_mynetworks, permit_sasl_authenticated, reject_non_fqdn_recipient, reject_unauth_destination, reject_unauth_pipelining, reject_invalid_hostname, check_policy_service inet:127.0.0.1:9990
Anpassungen master.cf
127.0.0.1:9990 inet n n n – 0 spawn user=phppostgrey argv=/opt/dev_postgrey/bin/php -f /postgrey/postgrey_v4.php
Weniger Spam & Phishing durch Sanesecurity & MSRBL-SPAM für ClamAV
0Trotz Amavis & Spamassassin und sehr restriktiver Regeln im Postfix kommen immernoch viele Phishing und Bilder/PDF Spams durch. Ich habe nach einer bequemen Lösung gesucht und bin auf Sanesecurity und MSRBL-SPAM gestoßen. Beide bieten Signaturenfiles für ClamAV an die Spams & Phishings erkennen können. Auf der Webseite von Sanesecurity werden mehrere Scripts angeboten die die Signaturen runterladen und ClamAV reloaden. Diese haben mir aber nicht gefallen deshalb habe ich ein kleineres und einfacheres geschieben das täglich als Cronjob ausgeführt wird.
Lösung
Benötigt wird rsync, gunzip und clamav.
Die Signaturen von ClamAV liegen bei Debian,Ubuntu,Gentoo,etc. in
/var/lib/clamav
Folgendes Script liegt in /etc/cron.daily und heisst get_sane:
cd /tmp wget http://www.sanesecurity.co.uk/clamav/scamsigs/scam.ndb.gz wget http://www.sanesecurity.co.uk/clamav/phishsigs/phish.ndb.gz rsync rsync://rsync.mirror.msrbl.com/msrbl/MSRBL-Images.hdb /tmp/MSRBL-Images.hdb rsync rsync://rsync.mirror.msrbl.com/msrbl/MSRBL-SPAM.ndb /tmp/MSRBL-SPAM.ndb gunzip -f scam.ndb.gz gunzip -f phish.ndb.gz cp -f scam.ndb /var/lib/clamav cp -f phish.ndb /var/lib/clamav cp -f MSRBL-Images.hdb /var/lib/clamav cp -f MSRBL-SPAM.ndb /var/lib/clamav chown clamav:clamav /var/lib/clamav/scam.ndb chown clamav:clamav /var/lib/clamav/phish.ndb chown clamav:clamav /var/lib/clamav/MSRBL-Images.hdb chown clamav:clamav /var/lib/clamav/MSRBL-SPAM.ndb /etc/init.d/clamav-daemon reload-database
Script noch ausführbar für root machen.
chmod 700 /etc/cron.daily/get_sane
Test
Da der Cronjob nur Täglich ausgeführt wird muss man es einmal manuell machen damit man es gleich testen kann.
Folgende Datei ausführen:
/etc/cron.daily/get_sane
Danach sollte ein ls -la /var/lib/clamav folgendes ausgeben:
sys@monster /tmp # ls -la /var/lib/clamav insgesamt 14276 drwxr-xr-x 3 clamav clamav 4096 2008-03-26 11:35 . drwxr-xr-x 33 root root 4096 2008-03-02 11:23 .. drwxr-xr-x 2 clamav clamav 4096 2008-03-26 11:35 daily.inc -rw-r--r-- 1 clamav clamav 11347852 2008-03-02 11:47 main.cvd -rw------- 1 clamav clamav 572 2008-03-26 11:28 mirrors.dat -rw-r--r-- 1 clamav clamav 24670 2008-03-26 06:27 MSRBL-Images.hdb -rw-r--r-- 1 clamav clamav 235227 2008-03-26 06:27 MSRBL-SPAM.ndb -rw-r--r-- 1 clamav clamav 1450465 2008-03-26 06:27 phish.ndb -rw-r--r-- 1 clamav clamav 1500415 2008-03-26 06:27 scam.ndb sys@monster /tmp #
Sanesecurity bietet Testfiles an um die Signaturen zu Testen.
Testdateien herunterladen und testen:
sys@monster ~ # cd /tmp sys@monster /tmp # wget http://www.sanesecurity.com/clamav/phish_sigtest.txt ... sys@monster /tmp # wget http://www.sanesecurity.com/clamav/scam_sigtest.txt ... sys@monster /tmp # clamscan phish_sigtest.txt phish_sigtest.txt: Html.Phishing.Sanesecurity.TestSig FOUND ----------- SCAN SUMMARY ----------- Known viruses: 253652 Engine version: 0.92.1 Scanned directories: 0 Scanned files: 1 Infected files: 1 Data scanned: 0.00 MB Time: 4.394 sec (0 m 4 s) sys@monster /tmp # clamscan scam_sigtest.txt scam_sigtest.txt: Html.Scam.Sanesecurity.TestSig FOUND ----------- SCAN SUMMARY ----------- Known viruses: 253652 Engine version: 0.92.1 Scanned directories: 0 Scanned files: 1 Infected files: 1 Data scanned: 0.00 MB Time: 4.462 sec (0 m 4 s) sys@monster /tmp #
Fertig! Viel Spaß ….
Standard SMTP Dialog
0Genauere Definition und Erklärung von SMTP bei Wikipedia
RFCs
Default Ports
-
25 TCP
-
587 TCP (Submission Port)
Status-Codes
Das SMTP-Protokoll hält zum Status der Kommunikation zwischen Mailserver und Mailclient folgende Error-Codes bereit:
-
1XX: Mailserver hat die Anforderung akzeptiert, ist aber selbst noch nicht tätig geworden. Eine Bestätigungsmeldung ist erforderlich.
-
2XX: Mailserver hat die Anforderung erfolgreich ohne Fehler ausgeführt.
-
3XX: Mailserver hat die Anforderung verstanden, benötigt aber zur Verarbeitung weitere Informationen.
-
4XX: Mailserver hat einen temporären Fehler festgestellt. Wenn die Anforderung ohne jegliche Änderung wiederholt wird, kann die Verarbeitung möglicherweise abgeschlossen werden.
-
5XX: Mailserver hat einen fatalen Fehler festgestellt. Ihre Anforderung kann nicht verarbeitet werden.
Quelle: Wikipedia
Standard Kommandos
| Kommando | Beschreibung |
|---|---|
| HELO host | Eigenen Servernamen senden und Dialog beginnen |
| EHLO host | Extended HELO; Zeigt Serverfunktionen/möglichkeiten an |
| MAIL FROM:xxx | Absender E-Mail Adresse |
| RCPT TO:xxx | Empfänger E-Mail Adresse |
| DATA | Beginn der Übermittlung von Daten Das Ende der Übermittlung wird mit <CR><LF>.<CR><LF> signalisiert. |
| QUIT | Dialog beenden |
Der SMTP Dialog
Um einen SMTP Server zu Testen kann man das Tool telnet verwenden:
# telnet mailserver.example.com 25
Wichtig ist die Portangabe 25. Protokoll ist TCP. Neuere Server verwenden unteranderem auch den Submission Port 587 TCP für Clienteinlieferungen.
>> sysadmin@vmserver01:~$ telnet mailserver.example.com 25 << Trying 10.23.45.10... << Connected to mailserver.example.com. << Escape character is '^]'. << 220 mailserver.example.com ESMTP Postfix (Debian/GNU) >> helo pc0815.example.com << 250 mailserver.example.com >> mail from:absender@example.org << 250 2.1.0 Ok >> rcpt to:empfaenger@example.org << 250 2.1.5 Ok >> data << 354 End data with <CR><LF>.<CR><LF> >> Dies ist eine Testmail. >> . << 250 2.0.0 Ok: queued as 708D72C80BE >> quit << 221 2.0.0 Bye << Connection closed by foreign host. << sysadmin@vmserver01:~$
SMTP Serverfunktionen abfragen mit EHLO (Extended HELO)
Der Dialog funktioniert genau so wie der andere es wird nur statt helo ehlo verwendet.
>> sysadmin@vmserver01:~$ telnet mailserver.example.com 25 << Trying 10.23.45.10... << Connected to mailserver.example.com. << Escape character is '^]'. << 220 mailserver.example.com ESMTP Postfix (Debian/GNU) >> ehlo pc0815.example.org << 250-mailserver.example.com << 250-PIPELINING << 250-SIZE 10240000 << 250-VRFY << 250-ETRN << 250-STARTTLS << 250-ENHANCEDSTATUSCODES << 250-8BITMIME << 250 DSN >> quit << 221 2.0.0 Bye << Connection closed by foreign host. << sysadmin@vmserver01:~$
ClamAV mit Sanesecurity erweitern
0Benötigte Tools installieren
sudo apt-get install rsync wget gunzip
Verzeichnis anlegen
mkdir -p /root/sanesecurity
Script erstellen
nano /root/sanesecurity/get_sane
Script:
cd /root/sanesecurity wget http://www.sanesecurity.co.uk/clamav/scamsigs/scam.ndb.gz wget http://www.sanesecurity.co.uk/clamav/phishsigs/phish.ndb.gz rsync rsync://rsync.mirror.msrbl.com/msrbl/MSRBL-Images.hdb /root/sanesecurity/MSRBL-Images.hdb rsync rsync://rsync.mirror.msrbl.com/msrbl/MSRBL-SPAM.ndb /root/sanesecurity/MSRBL-SPAM.ndb gunzip -f scam.ndb.gz gunzip -f phish.ndb.gz cp -f scam.ndb /var/lib/clamav cp -f phish.ndb /var/lib/clamav cp -f MSRBL-Images.hdb /var/lib/clamav cp -f MSRBL-SPAM.ndb /var/lib/clamav /etc/init.d/clamav-daemon reload-database
Das ganze noch lauffähig machen:
chmod u+x /root/sanesecurity/get_sane
In Crontab einbinden
Updates täglich:
ln -s /root/sanesecurity/get_sane /etc/cron.daily/01get_sane
oder stündlich:
ln -s /root/sanesecurity/get_sane /etc/cron.hourly/01get_sane
Testlauf
/root/sanesecurity/get_sane
Testmail verschicken ob die Sanepatterns auch funktionieren
Mit einem Standardmailclient eine Testmail senden und folgende Testsignaturen verwenden
Einfach mit Copy & Paste einfügen und versenden. Resultat sollte folgendermaßen aussehen:
Nov 26 06:59:25 server amavis[20891]: (20891-03) Blocked INFECTED (Html.Phishing.Sanesecurity.TestSig), [10.72.30.1] [10.
Spamtrap / Blackhole Adresse unter Postfix einrichten
0Eine E-Mail Adresse einrichten die Richtung “/dev/null“ geht.
main.cf
check_recipient_access hash:/etc/postfix/spam_trap sollte hinter reject_unauth_destination kommen. Die Konfig für smtpd_recipient_restrictions ist nur exemplarisch.
smtpd_recipient_restrictions =
reject_non_fqdn_recipient
reject_non_fqdn_sender
reject_unknown_sender_domain
reject_unknown_recipient_domain
permit_mynetworks
permit_sasl_authenticated
reject_unauth_destination
check_recipient_access hash:/etc/postfix/spam_trap
reject_multi_recipient_bounce
/etc/postfix/spam_trap
Inhalt der Datei:
devnull@thoma.cc DISCARD
Diese Datei muss noch ins Postmap Format konvertiert werden.
Hierzu folgenden Befehl ausführen:
postmap /etc/postfix/spam_trap
Postfix reloaden
Zuletzt muss noch Postfix neu gestartet werden. Ein
postfix reload
reicht.
Fertig!
Alle Mails die z.B. an devnull@thoma.cc geschickt werden verschwinden im Nirvana.
Standard POP3 Dialog
0Genauere Definition und Erklärung von POP3 bei Wikipedia
RFCs
Default Ports
-
110 TCP
-
995 TCP für SSL Verbindungen
Standard Kommandos
| Kommando | Beschreibung |
|---|---|
| USER xxx | Auswahl der Benutzerkontos auf dem Server |
| PASS xxx | Sendet das Passwort an den Server (Klartext) |
| STAT | Liefert den Status der Mailbox, u. a. die Anzahl der neuen E-Mails. |
| LIST (n) | Liefert die Anzahl und die Größe der (n-ten) E-Mail(s). |
| RETR n | Holt die n-te E-Mail vom E-Mail-Server. |
| DELE n | Löscht die n-te E-Mail am E-Mail-Server. |
| NOOP | Keine Funktion, der Server antwortet mit +OK. (Wird bei FTP-Servern öfters dazu verwendet die Verbindung offen zu halten.) |
| RSET | Setzt alle DELE-Kommandos zurück. |
| QUIT | Beendet die aktuelle POP3-Sitzung und führt alle DELE-Kommandos durch. |
Der POP3 Dialog
>> sysadmin@vmserver01:~$ telnet pop3.example.net 110 << Trying 10.23.45.10... << Connected to pop3.example.net. << Escape character is '^]'. << +OK POP3 Ready pop3.example.net >> user testuser@example.org << +OK USER testuser@example.org set, mate >> pass 123geheimKENNWORT123 << +OK You are so in >> list << +OK POP3 clients that break here, they violate STD53. << 1 236 >> retr 1 << +OK 236 octets follow. << Date: Mon, 18 Oct 2004 04:11:45 +0200 << From: Someone <someone@example.com> << To: testuser@example.org << Subject: Test-E-Mail << Content-Type: text/plain; charset=us-ascii; format=flowed << Content-Transfer-Encoding: 7bit << << Dies ist eine Test-E-Mail << << . >> quit << +OK Bye-bye. << Connection closed by foreign host. << sysadmin@vmserver01:~$
Standard IMAP Dialog
0Genauere Definition und Erklärung von IMAP bei Wikipedia
RFCs
Default Ports
-
143 TCP
-
993 TCP für SSL Verbindungen
Standard Kommandos
xx – Fortlaufende 2stellige Nummer 01, 02, 03, 04, …
| Kommando | Beschreibung |
|---|---|
| xx LOGIN username passwort | User anmelden |
| xx LIST ““ * | IMAP-Ordner auflisten (zwei Anführungszeichen nach List) |
| xx SELECT yyyy | IMAP-Ordner auswählen |
| xx STATUS yyy (zzz) | Status abfragen von einem IMAP-Ordner Folgende Optionen für zzz sind möglich: MESSAGES, UNSEEN, RECENT, UIDNEXT und UIDVALIDITY |
| xx FETCH n yyy | Nachricht/Kopfzeile abrufen n – Nachrichtenummer oder * für alle Nachrichten yyy – Folgende Werte sind möglich: ALL # Alle IMAP Header FULL # Alle Headers und Body Infos BODY # Body ENVELOPE # Envelope |
| xx UID fetch n:n (UID RFC822.SIZE FLAGS BODY.PEEK[]) | Nachricht komplett Empfangen |
| xx LOGOUT | Ausloggen |
Der IMAP Dialog
>> sysadmin@vmserver01:~$ telnet imap.example.org 143
<< Trying 10.23.45.10...
<< Connected to imap.example.org.
<< Escape character is '^]'.
<< * OK IMAP4 Ready imap.example.org
>> 01 LOGIN testuser@example.net 123geheimKENNWORT123
<< 01 OK You are so in
>> 02 LIST "" *
<< * LIST (\HasNoChildren) "." "INBOX.Junk"
<< * LIST (\HasNoChildren) "." "INBOX.Sent"
<< * LIST (\HasNoChildren) "." "INBOX.Drafts"
<< * LIST (\HasNoChildren) "." "INBOX.Trash"
<< * LIST (\Unmarked \HasChildren) "." "INBOX"
<< 02 OK LIST completed
>> 03 SELECT INBOX
<< * FLAGS (\Draft \Answered \Flagged \Deleted \Seen \Recent)
<< * OK [PERMANENTFLAGS (\* \Draft \Answered \Flagged \Deleted \Seen)] Limited
<< * 1 EXISTS
<< * 0 RECENT
<< * OK [UIDVALIDITY 1196314665] Ok
<< * OK [MYRIGHTS "acdilrsw"] ACL
<< 03 OK [READ-WRITE] Ok
>> 04 STATUS INBOX (MESSAGES)
<< * STATUS "INBOX" (MESSAGES 1)
<< 04 OK STATUS Completed.
>> 05 FETCH 1 ALL
<< * 1 FETCH (FLAGS (\Seen) INTERNALDATE "28-Nov-2007 11:55:53 +0100" RFC822.SIZE 912 ENVELOPE ("Wed, 28 Nov 2007 11:24:08 +0100 (CET)" NIL ((NIL NIL "testuser" "example.net")) ((NIL NIL "testuser" "example.net")) ((NIL NIL "testuser" "example.net")) ((NIL NIL "undisclosed-recipients" NIL)(NIL NIL NIL NIL)) NIL NIL NIL "<20071128102415.7245146C215@mailserver.example.org>"))
<< 05 OK FETCH completed.
>> 06 CLOSE
<< 06 OK mailbox closed.
>> 07 LOGOUT
<< * BYE Courier-IMAP server shutting down
<< 07 OK LOGOUT completed
<< Connection closed by foreign host.
<< sysadmin@vmserver01:~$