Postfix & Co.

Postfix: Mail-Relay mit SMTP-Auth via Submission/TLS für ausgehende Mails

0

Ein 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.

Download

#!/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

1

Fü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

0

MySQL 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

0

Trotz 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

0

Genauere 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

0

Benö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

0

Eine 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

0

Genauere 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

0

Genauere 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:~$
nach oben