Welcome to the IPFire Wiki

This wiki is a community-maintained resource about everything there is to know about IPFire. Join us and help us improving it!

Looking for something?

Use the search and find answers to everything about IPFire. If you cannot find what you are looking for, join our community and talk to fellow IPFire users, developers and everybody else involved in the project.

IPFire Community

Mailproxy with IPFire

This wiki describes how you can setup a mail proxy with Postfix on IPFire, which filters virus and spam mails in forefield hence infested mails won´t belong to your internal mailserver.

Required addons

Findable in Pakfire


Additional addons:

razor-agents-2.84     # Ist abhängig zu razor-agents-sdk

Installation of Postfix

The installation can easily be done with Pakfire over the IPFire WUI.

Configuration of Posfix

Postfix should be installed with the presetting Internet with smarthost. After the basic installation has been done, the appropriate properties on main.cf should be indicated. A backup of the original file are strongly recommended!

main.cf for IPFire

queue_directory = /var/spool/postfix
command_directory = /usr/sbin
daemon_directory = /usr/lib/postfix
manpage_directory = /usr/share/man
sample_directory = /etc/postfix
html_directory = no
readme_directory = no

sendmail_path = /usr/sbin/sendmail
newaliases_path = /usr/bin/newaliases
mailq_path = /usr/bin/mailq

strict_rfc821_envelopes = yes
biff = no
append_dot_mydomain = no
readme_directory = no

mail_owner = postfix
setgid_group = postdrop

myhostname = my.ipfire.domain
myorigin = $myhostname

inet_interfaces = all

proxy_interfaces =

mydestination = $myhostname, localhost, mysql:/etc/postfix/mysql-mydestination.cf

mydestination = my.mailserver.domain, my.domain

relay_domains = my.mailserver.domain, my.domain

unknown_local_recipient_reject_code = 550
local_recipient_maps =
local_transport = error:local mail delivery is disabled
transport_maps = hash:/etc/postfix/transport

mynetworks_style = host
mynetworks =, 192.168.xxx.xxx/24, 192.168.xxx.xxx/24

relay_domains = $mydestination

relayhost = $mydomain

relayhost = [gateway.my.domain]

relayhost = [mailserver.isp.tld]

relayhost = uucphost

relayhost = [an.ip.add.ress]

relay_recipient_maps = hash:/etc/postfix/relay_recipients

in_flow_delay = 1s

alias_maps = hash:/etc/aliases
alias_database = hash:/etc/aliases
recipient_delimiter = +

mailbox_size_limit = 0
message_size_limit = 10240000

mailbox_transport = cyrus


header_checks = regexp:/etc/postfix/header_checks

fast_flush_domains = $relay_domains

smtpd_banner = $myhostname ESMTP $mail_name ($mail_version)

local_destination_concurrency_limit = 2

default_destination_concurrency_limit = 20

virtual_alias_maps = mysql:/etc/postfix/mysql-virtual.cf

sender_canonical_maps = mysql:/etc/postfix/mysql-canonical.cf

smtpd_sasl_type = cyrus

smtpd_sasl_auth_enable = yes

smtpd_sasl_security_options = noanonymous

smtpd_sasl_local_domain = $myhostname

broken_sasl_auth_clients = yes

smtp_sasl_password_maps = hash:/etc/postfix/sasl_passwd

soft_bounce = yes

smtpd_helo_required = yes
smtpd_recipient_restrictions =
#reject_rbl_client zombie.dnsbl.sorbs.net,
#reject_rbl_client opm.blitzed.org,
#reject_rbl_client list.dsbl.org,
#reject_rbl_client sbl.spamhaus.org,

smtpd_helo_restrictions =

smtpd_client_restrictions =
reject_rbl_client ix.dnsbl.manitu.net,
reject_rbl_client zombie.dnsbl.sorbs.net,
reject_rbl_client list.dsbl.org,
reject_rbl_client sbl.spamhaus.org,

Adjust the lookup-table /etc/postfix/transport to announce the destination:

my.domain smtp:192.168.xxx.xxx

To write the changes into the database, the following command should be executed:

postmap /etc/postfix/transport

Now restart Postfix:

/etc/init.d/postfix restart

Optimize Postfix

Since Postfix is a extension and no standard on IPFire there is currently no logrotate configured for Postfix logs (they can be found under /var/log/mail ) which should be done subsequently. This prevents a fast increase of the mail logs.

The following lines should be deposit under /etc/logrotate.conf.


/var/log/mail {
rotate 4

Here the same, a backup of the original files are your best friend! ;-)

Installation of ClamAV

The installation can also be done over IPFires WUI via Pakfire. At this point there is for now nothing else to be done.

Installation of Amavis and SpamAssassin

The installation of Amavis and SpamAssassin (which is a part of the Amavis installation) are provided also over Pakfire .

Configure Amavis

After installation of Amavis, the configuration file which is located under /etc/amavisd.conf should be adapted.

Content of amavisd.conf:

use strict;

a minimalistic configuration file for amavisd-new with all necessary settings

see amavisd.conf-default for a list of all variables with their defaults;

see amavisd.conf-sample for a traditional-style commented file;

for more details see documentation in INSTALL, README_FILES/*

and at http://www.ijs.si/software/amavisd/amavisd-new-docs.html


@bypass_virus_checks_maps = (

%bypass_virus_checks, @bypass_virus_checks_acl, $bypass_virus_checks_re); # controls running of anti-virus code

@bypass_spam_checks_maps = (

%bypass_spam_checks, @bypass_spam_checks_acl, $bypass_spam_checks_re); # controls running of anti-spam code

$bypass_decode_parts = 1; # controls running of decoders&dearchivers

$max_servers = 2; # num of pre-forked children (2..15 is common), -m
$daemon_user = 'amavis'; # (no default; customary: vscan or amavis), -u
$daemon_group = 'amavis'; # (no default; customary: vscan or amavis), -g

$mydomain = 'my.local.domain'; # a convenient default for other settings

$MYHOME = '/var/amavis'; # a convenient default for other settings, -H

$TEMPBASE = "$MYHOME/tmp"; # working directory, needs to exist, -T
$ENV{TMPDIR} = $TEMPBASE; # environment variable TMPDIR, used by SA, etc.
$QUARANTINEDIR = '/var/virusmails'; # -Q

$quarantine_subdir_levels = 1; # add level of subdirs to disperse quarantine

$daemon_chroot_dir = $MYHOME; # chroot directory or undef, -R

$db_home = "$MYHOME/db"; # dir for bdb nanny/cache/snmp databases, -D

$helpers_home = "$MYHOME/var"; # working directory for SpamAssassin, -S

$lock_file = "$MYHOME/var/amavisd.lock"; # -L

$pid_file = "$MYHOME/var/amavisd.pid"; # -P

NOTE: create directories $MYHOME/tmp, $MYHOME/var, $MYHOME/db manually

$log_level = 2; # verbosity 0..5, -d
$log_recip_templ = undef; # disable by-recipient level-0 log entries
$DO_SYSLOG = 1; # log via syslogd (preferred)
$syslog_facility = 'mail'; # Syslog facility as a string
# e.g.: mail, daemon, user, local0, ... local7
$syslog_priority = 'debug'; # Syslog base (minimal) priority as a string,
# choose from: emerg, alert, crit, err, warning, notice, info, debug

$enable_db = 1; # enable use of BerkeleyDB/libdb (SNMP and nanny)
$enable_global_cache = 1; # enable use of libdb-based cache if $enable_db=1
$nanny_details_level = 2; # nanny verbosity: 1: traditional, 2: detailed

@local_domains_maps = ( ".$mydomain" ); # list of all local domains

@mynetworks = qw( 172.16.xxx.xxx/12 192.168.xxx.xxx/16 );

$unix_socketname = "$MYHOME/amavisd.sock"; # amavisd-release or amavis-milter
# option(s) -p overrides $inet_socket_port and $unix_socketname

$inet_socket_port = 10024; # listen on this local TCP port(s)

$inet_socket_port = [10024,10026]; # listen on multiple TCP ports

$policy_bank{'MYNETS'} = { # mail originating from @mynetworks
originating => 1, # is true in MYNETS by default, but let's make it explicit
os_fingerprint_method => undef, # don't query p0f for internal clients

it is up to MTA to re-route mail from authenticated roaming users or

from internal hosts to a dedicated TCP port (such as 10026) for filtering

$interface_policy{'10026'} = 'ORIGINATING';

$policy_bank{'ORIGINATING'} = { # mail supposedly originating from our users
originating => 1, # declare that mail was submitted by our smtp client
allow_disclaimers => 1, # enables disclaimer insertion if available
# notify administrator of locally originating malware
virus_admin_maps => ["virusalert\@$mydomain"],
spam_admin_maps => ["spamalert\@$mydomain"],
warnbadhsender => 1,
# forward to a smtpd service providing DKIM signing service
forward_method => 'smtp:[]:10027',
# force MTA conversion to 7-bit (e.g. before DKIM signing)
smtpd_discard_ehlo_keywords => ['8BITMIME'],
bypass_banned_checks_maps => [1], # allow sending any file names and types
terminate_dsn_on_notify_success => 0, # don't remove NOTIFY=SUCCESS option

$interface_policy{'SOCK'} = 'AM.PDP-SOCK'; # only applies with $unix_socketname

Use with amavis-release over a socket or with Petr Rehor's amavis-milter.c

(with amavis-milter.c from this package or old amavis.c client use 'AM.CL'):

$policy_bank{'AM.PDP-SOCK'} = {
protocol => 'AM.PDP',
auth_required_release => 0, # do not require secret_id for amavisd-release

$sa_tag_level_deflt = undef; # 2.0 add spam info headers if at, or above that level
$sa_tag2_level_deflt = 4.2; # 6.2 add 'spam detected' headers at that level
$sa_kill_level_deflt = 6.9; # 6.9 triggers spam evasive actions (e.g. blocks mail)
$sa_dsn_cutoff_level = 18; # 10 spam level beyond which a DSN is not sent

$sa_quarantine_cutoff_level = 25; # spam level beyond which quarantine is off

$penpals_bonus_score = 8; # (no effect without a @storage_sql_dsn database)
$penpals_threshold_high = $sa_kill_level_deflt; # don't waste time on hi spam

$sa_mail_body_size_limit = 400*1024; # don't waste time on SA if mail is larger
$sa_local_tests_only = 0; # only tests which do not require internet access?

$virus_admin = "virusalert\@$mydomain"; # notifications recip.

$mailfrom_notify_admin = "virusalert\@$mydomain"; # notifications sender
$mailfrom_notify_recip = "virusalert\@$mydomain"; # notifications sender
$mailfrom_notify_spamadmin = "spam.police\@$mydomain"; # notifications sender
$mailfrom_to_quarantine = ''; # null return path; uses original sender if undef

@addr_extension_virus_maps = ('virus');
@addr_extension_banned_maps = ('banned');
@addr_extension_spam_maps = ('spam');
@addr_extension_bad_header_maps = ('badh');

$recipient_delimiter = '+'; # undef disables address extensions altogether

when enabling addr extensions do also Postfix/main.cf: recipient_delimiter=+

$path = '/usr/local/sbin:/usr/local/bin:/usr/sbin:/sbin:/usr/bin:/bin';

$dspam = 'dspam';

$MAXFILES = 1500;
$MIN_EXPANSION_QUOTA = 1001024; # bytes (default undef, not enforced)
1024*1024; # bytes (default undef, not enforced)

$sa_spam_subject_tag = 'SPAM ';
$defang_virus = 1; # MIME-wrap passed infected mail
$defang_banned = 1; # MIME-wrap passed mail containing banned name

for defanging bad headers only turn on certain minor contents categories:

$defang_by_ccat{+CC_BADH.",3"} = 1; # NUL or CR character in header
$defang_by_ccat{+CC_BADH.",5"} = 1; # header line longer than 998 characters
$defang_by_ccat{+CC_BADH.",6"} = 1; # header field syntax error

OTHER MORE COMMON SETTINGS (defaults may suffice):

$myhostname = 'my.ipfire.local'; # must be a fully-qualified domain name!

$notify_method = 'smtp:[]:10025';

$forward_method = 'smtp:[]:10025'; # set to undef with milter!

$final_virus_destiny = D_DISCARD;
$final_banned_destiny = D_BOUNCE;
$final_spam_destiny = D_PASS;
$final_bad_header_destiny = D_PASS;

Notify virus sender? Bloß nicht!

$warnvirussender = 0;

Notify spam sender? Bloß nicht!

$warnspamsender = 0;

Notify sender of banned files? Kann man machen.

$warnbannedsender = 0;

Notify sender of syntactically invalid header containing non-ASCII characters? Bloß nicht!

$warnbadsender = 0;

Notify virus (or banned files) RECIPIENT? Wie man möchte, ich finde es sinnvoll.

$warnvirusrecip = 1;
$warnbannedrecip = 1;
$warnbadhrecip = 1;

SOME OTHER VARIABLES WORTH CONSIDERING (see amavisd.conf-default for all)


$warnvirusrecip, $warnbannedrecip, $warnbadhrecip, (or @warn*recip_maps)

@bypass_virus_checks_maps, @bypass_spam_checks_maps,

@bypass_banned_checks_maps, @bypass_header_checks_maps,

@virus_lovers_maps, @spam_lovers_maps,

@banned_files_lovers_maps, @bad_header_lovers_maps,

@blacklist_sender_maps, @score_sender_maps,

$clean_quarantine_method, $virus_quarantine_to, $banned_quarantine_to,

$bad_header_quarantine_to, $spam_quarantine_to,

$defang_bad_header, $defang_undecipherable, $defang_spam


@keep_decoded_original_maps = (new_RE(

qr'^MAIL$', # retain full original message for virus checking (can be slow)

qr'^MAIL-UNDECIPHERABLE$', # recheck full mail if it contains undecipherables
qr'^(ASCII(?! cpio)|text|uuencoded|xxencoded|binhex)'i,

qr'^Zip archive data', # don't trust Archive::Zip


for $banned_namepath_re (a new-style of banned table) see amavisd.conf-sample

$banned_filename_re = new_RE(


qr'^UNDECIPHERABLE$', # is or contains any undecipherable components

qr'^\.(exe-ms|dll)$', # banned file(1) types, rudimentary

qr'^.(exe|lha|tnef|cab|dll)$', # banned file(1) types


[ qr'^.(gz|bz2)$' => 0 ], # allow any in gzip or bzip2

[ qr'^.(rpm|cpio|tar)$' => 0 ], # allow any in Unix-type archives

qr'.\.(pif|scr)$'i, # banned extensions - rudimentary

qr'^.zip$', # block zip type


[ qr'^.(zip|rar|arc|arj|zoo)$'=> 0 ], # allow any within these archives

qr'^application/x-msdownload$'i, # block these MIME types

qr'^message/partial$'i, # rfc2046 MIME type

qr'^message/external-body$'i, # rfc2046 MIME type

qr'^(application/x-msmetafile|image/x-wmf)$'i, # Windows Metafile MIME type

qr'^.wmf$', # Windows Metafile file(1) type

# block certain double extensions in filenames

qr'{[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}}?'i, # Class ID CLSID, strict

qr'{[0-9a-z]{4,}(-[0-9a-z]{4,}){0,7}}?'i, # Class ID extension CLSID, loose

qr'.\.(exe|vbs|pif|scr|cpl)$'i, # banned extension - basic

qr'..(exe|vbs|pif|scr|cpl|bat|cmd|com)$'i, # banned extension - basic+cmd




wmf|wsc|wsf|wsh)$'ix, # banned ext - long

qr'..(ani|cur|ico)$'i, # banned cursors and icons filename

qr'^.ani$', # banned animated cursor file(1) type

qr'..(mim|b64|bhx|hqx|xxe|uu|uue)$'i, # banned extension - WinZip vulnerab.


See http://support.microsoft.com/default.aspx?scid=kb;EN-US;q262631

and http://www.cknow.com/vtutor/vtextensions.htm


@score_sender_maps = ({ # a by-recipient hash lookup table,
# results from all matching recipient tables are summed

## per-recipient personal tables (NOTE: positive: black, negative: white)

'user1@example.com' => [{'bla-mobile.press@example.com' => 10.0}],

'user3@example.com' => [{'.ebay.com' => -3.0}],

'user4@example.com' => [{'cleargreen@cleargreen.com' => -7.0,

'.cleargreen.com' => -5.0}],

## site-wide opinions about senders (the '.' matches any recipient)
'.' => [ # the first matching sender determines the score boost

new_RE( # regexp-type lookup table, just happens to be all soft-blacklist
[qr'^(bulkmail|offers|cheapbenefits|earnmoney|foryou)@'i => 5.0],
[qr'^(greatcasino|investments|lose_weight_today|market.alert)@'i=> 5.0],
[qr'^(money2you|MyGreenCard|new.tld.registry|opt-out|opt-in)@'i=> 5.0],
[qr'^(optin|saveonlsmoking2002k|specialoffer|specialoffers)@'i => 5.0],
[qr'^(stockalert|stopsnoring|wantsome|workathome|yesitsfree)@'i => 5.0],
[qr'^(your_friend|greatoffers)@'i => 5.0],
[qr'^(inkjetplanet|marketopt|MakeMoney)\d*@'i => 5.0],


{ # a hash-type lookup table (associative array)
'nobody@cert.org' => -3.0,
'cert-advisory@us-cert.gov' => -3.0,
'owner-alert@iss.net' => -3.0,
'slashdot@slashdot.org' => -3.0,
'securityfocus.com' => -3.0,
'ntbugtraq@listserv.ntbugtraq.com' => -3.0,
'security-alerts@linuxsecurity.com' => -3.0,
'mailman-announce-admin@python.org' => -3.0,
'amavis-user-admin@lists.sourceforge.net'=> -3.0,
'amavis-user-bounces@lists.sourceforge.net' => -3.0,
'spamassassin.apache.org' => -3.0,
'notification-return@lists.sophos.com' => -3.0,
'owner-postfix-users@postfix.org' => -3.0,
'owner-postfix-announce@postfix.org' => -3.0,
'owner-sendmail-announce@lists.sendmail.org' => -3.0,
'sendmail-announce-request@lists.sendmail.org' => -3.0,
'donotreply@sendmail.org' => -3.0,
'ca+envelope@sendmail.org' => -3.0,
'noreply@freshmeat.net' => -3.0,
'owner-technews@postel.acm.org' => -3.0,
'ietf-123-owner@loki.ietf.org' => -3.0,
'cvs-commits-list-admin@gnome.org' => -3.0,
'rt-users-admin@lists.fsck.com' => -3.0,
'clp-request@comp.nus.edu.sg' => -3.0,
'surveys-errors@lists.nua.ie' => -3.0,
'emailnews@genomeweb.com' => -5.0,
'yahoo-dev-null@yahoo-inc.com' => -3.0,
'returns.groups.yahoo.com' => -3.0,
'clusternews@linuxnetworx.com' => -3.0,
lc('lvs-users-admin@LinuxVirtualServer.org') => -3.0,
lc('owner-textbreakingnews@CNNIMAIL12.CNN.COM') => -5.0,

# soft-blacklisting (positive score)
'sender@example.net' => 3.0,
'.example.net' => 1.0,

], # end of site-wide tables

@decoders = (
['mail', \&do_mime_decode],
['asc', \&do_ascii],
['uue', \&do_ascii],
['hqx', \&do_ascii],
['ync', \&do_ascii],
['F', \&do_uncompress, ['unfreeze','freeze -d','melt','fcat'] ],
['Z', \&do_uncompress, ['uncompress','gzip -d','zcat'] ],
['gz', \&do_uncompress, 'gzip -d'],
['gz', \&do_gunzip],
['bz2', \&do_uncompress, 'bzip2 -d'],
['lzo', \&do_uncompress, 'lzop -d'],
['rpm', \&do_uncompress, ['rpm2cpio.pl','rpm2cpio'] ],
['cpio', \&do_pax_cpio, ['pax','gcpio','cpio'] ],
['tar', \&do_pax_cpio, ['pax','gcpio','cpio'] ],
['deb', \&do_ar, 'ar'],

['a', \&do_ar, 'ar'], # unpacking .a seems an overkill

['zip', \&do_unzip],
['7z', \&do_7zip, ['7zr','7za','7z'] ],
['rar', \&do_unrar, ['rar','unrar'] ],
['arj', \&do_unarj, ['arj','unarj'] ],
['arc', \&do_arc, ['nomarch','arc'] ],
['zoo', \&do_zoo, ['zoo','unzoo'] ],
['lha', \&do_lha, 'lha'],

['doc', \&do_ole, 'ripole'],

['cab', \&do_cabextract, 'cabextract'],
['tnef', \&do_tnef_ext, 'tnef'],
['tnef', \&do_tnef],

['sit', \&do_unstuff, 'unstuff'], # broken/unsafe decoder

['exe', \&do_executable, ['rar','unrar'], 'lha', ['arj','unarj'] ],

@av_scanners = (

### http://www.clamav.net/


\&ask_daemon, ["CONTSCAN {}\n", "/var/run/clamav/clamd"],
qr/\bOK$/, qr/\bFOUND$/,
qr/^.?: (?!Infected Archive)(.) FOUND$/ ],

# NOTE: run clamd under the same user as amavisd, or run it under its own

# uid such as clamav, add user clamav to the amavis group, and then add

# AllowSupplementaryGroups to clamd.conf;

# NOTE: match socket name (LocalSocket) in clamav.conf to the socket name in

# this entry; when running chrooted one may prefer socket "$MYHOME/clamd".

### http://www.f-prot.com/

['FRISK F-Prot Daemon',


["GET {}/*?-dumb%20-archive%20-packed HTTP/1.0\r\n\r\n",


'',''] ],



qr/(?i)(.+)<\/name>/ ],

### http://www.kaspersky.com/ (kav4mailservers)
['KasperskyLab AVP - aveclient',
'-p /var/run/aveserver -s {}/*',
# NOTE: one may prefer [0],[2,3,4,5], depending on how suspicious,
# currupted or protected archives are to be handled

### http://www.avira.com/
### Avira AntiVir (formerly H+BEDV) or (old) CentralCommand Vexira Antivirus
['Avira AntiVir', ['antivir','vexira'],
'--allfiles -noboot -nombr -rs -s -z {}', [0], qr/ALERT:|VIRUS:/,
qr/(?x)^\s (?: ALERT: \s (?: [ | [^'] ' ) |
(?i) VIRUS:\ .
?\ virus\ '?) ( [^]\s']+ )/ ],
# NOTE: if you only have a demo version, remove -z and add 214, as in:
# '--allfiles -noboot -nombr -rs -s {}', [0,214], qr/ALERT:|VIRUS:/,

### http://www.avast.com/

['avast! Antivirus daemon',

\&ask_daemon, # greets with 220, terminate with QUIT

["SCAN {}\015\012QUIT\015\012", '/var/run/avast4/mailscanner.sock'],

qr/\t[+]/, qr/\t[L]\t/, qr/\t[L]\t([^[ \t\015\012]+)/ ],

### http://www.avast.com/

['avast! Antivirus - Client/Server Version', 'avastlite',

'-a /var/run/avast4/mailscanner.sock -n {}', [0], [1],

qr/\t[L]\t([^[ \t\015\012]+)/ ],

### http://www.avast.com/
['avast! Antivirus', ['/usr/bin/avastcmd','avastcmd'],
'-a -i -n -t=A {}', [0], [1], qr/\binfected by:\s+([^ \t\n[]]+)/ ],

### http://www.bitdefender.com/
['BitDefender', 'bdc',
'--arc --mail {}', qr/^Infected files :0+(?!\d)/,
qr/^(?:Infected files|Identified viruses|Suspect files)
qr/(?:suspected|infected): (.
)(?:\033|$)/ ],
# consider also: --all --nowarn --alev=15 --flev=15. The --all argument may
# not apply to your version of bdc, check documentation and see 'bdc --help'


@av_scanners_backup = (

### http://www.clamav.net/ - backs up clamd or Mail::ClamAV
['ClamAV-clamscan', 'clamscan',
"--stdout --no-summary -r --tempdir=$TEMPBASE {}",
[0], qr/:.\sFOUND$/, qr/^.?: (?!Infected Archive)(.*) FOUND$/ ],

### http://www.f-prot.com/ - backs up F-Prot Daemon
['FRISK F-Prot Antivirus', ['f-prot','f-prot.sh'],
'-dumb -archive -packed {}', [0,8], [3,6], # or: [0], [3,6,8],
qr/(?:Infection:|security risk named) (.+)|\s+contains\s+(.+)$/ ],

### http://www.kaspersky.com/
['Kaspersky Antivirus v5.5',
'/opt/kav/5.5/kav4mailservers/bin/kavscanner', 'kavscanner'],
'-i0 -xn -xp -mn -R -ePASBME {}/', [0,10,15], [5,20,21,25],
)/ ,

sub {chdir('/opt/kav/bin') or die "Can't chdir to kav: $!"},

sub {chdir($TEMPBASE) or die "Can't chdir back to $TEMPBASE $!"},


always succeeds (uncomment to consider mail clean if all other scanners fail)

['always-clean', sub {0}],


1; # insure a defined return

Proove the ClamAV path

It is important that ClamAV uses the correct path, which will be checked in here:

$myhostname = 'ipfire.localdomain'; # must be a fully-qualified domain name!
# ### http://www.clamav.net/
\&ask_daemon, ["CONTSCAN {}\n", "/var/run/clamav/clamd"],
qr/\bOK$/, qr/\bFOUND$/,
qr/^.?: (?!Infected Archive)(.) FOUND$/ ],

Amavis and ClamAV group assignment

Amavis should have the correct permission for ClamAV:

# usermod -a -G clamav amavis
# usermod -a -G clamav clamav
# usermod -a -G amavis clamav
# usermod -a -G amavis amavis

Adapt SpamAssassin

Spamassassins configuration file are located under /var/ipfire/mail/spamassassin/ in here the following values can be changed if needed:

This is the right place to customize your installation of SpamAssassin.

See 'perldoc Mail::SpamAssassin::Conf' for details of what can be


Only a small subset of options are listed below

add_header all Report REPORT

Add *SPAM*** to the Subject header of spam e-mails

rewrite_header Subject *SPAM***

Save spam messages as a message/rfc822 MIME attachment instead of

modifying the original message (0: off, 2: use text/plain instead)

report_safe 1

Set which networks or hosts are considered 'trusted' by your mail

server (i.e. not spammers)

trusted_networks 192.168.xxx.

Set file-locking method (flock is not safe over NFS, but is faster)

lock_method flock

Set the threshold at which a message is considered spam (default: 5.0)

required_score 5.0

Use Bayesian classifier (default: 1)

use_bayes 1

Bayesian classifier auto-learning (default: 1)

bayes_auto_learn 1

Set headers which may provide inappropriate cues to the Bayesian


bayes_ignore_header X-Bogosity
bayes_ignore_header X-Spam-Flag
bayes_ignore_header X-Spam-Status

Optimize Amavis

To prevent a high amount of temporary files and directories produced by Amavis, the documentation of Amavisd-new recommends to delete dispensable data from time to time.

The following command are an advice for doing this:

find /var/amavis -type d -name 'amavis-20??????T*' \
`  -prune -mtime +1 -exec rm -rf {} \;`

While executing this command, amavisd should be deactivated.

This can also be integrated for example in the START|STOP routines (init script) of Amavis. For this, the following lines can be used in /etc/init.d/amavis.d.

It should be another time said: A backup of the file are your best friend ;-)


Begin $rc_base/init.d/amavisd

Description : Amavisd Init Script

Authors : Michael Tremer (ms@ipfire.org)

Version : 01.00

Notes :

. /etc/sysconfig/rc
. ${rc_functions}

case "${1}" in
boot_mesg "Starting AMaViS Daemon..."
find /var/amavis/tmp -type d -name 'amavis-20??????T*' \
-prune -mtime +1 -exec rm -rf {} \;
loadproc /usr/bin/amavisd

boot_mesg "Stopping AMaViS Daemon..."
killproc /usr/bin/amavisd

${0} stop
sleep 1
${0} start

statusproc /usr/bin/amavisd


echo "Usage: ${0} {start|stop|restart|status}"
exit 1

Adapt IPTables

iptables -A CUSTOMINPUT -p tcp -s 0/0 --sport 1024:65535 -d 192.168.ip.red0 --dport 25 -m state --state NEW,ESTABLISHED -j ACCEPT

Autostart SpamAssassin and Amavis

For a automatic START|STOP|RESTART of SpamAssassin and Amavis, you can set the following symlinks:

ln -s ../init.d/amavisd /etc/rc.d/rc3.d/S38amavisd
ln -s ../init.d/spamassassin /etc/rc.d/rc3.d/S38spamassassin
ln -s ../init.d/amavisd /etc/rc.d/rc0.d/K25amavisd
ln -s ../init.d/amavisd /etc/rc.d/rc6.d/K25amavisd
ln -s ../init.d/spamassassin /etc/rc.d/rc0.d/K25spamassassin
ln -s ../init.d/spamassassin /etc/rc.d/rc6.d/K25spamassassin

Prevent NDR via LDAP-Proofe

First, thanks to botribun for his hint.
Here, you have the possibility to connect the mail proxy against an LDAP to generate a receiver list.

Thereby, the mailsystem can be relieved and a NDR can be prevented. It is your own decision if you´d like to implement this feature.

The following Perl script can be used for this, also a appropriate cronjob should be applied, which updates this list in a configured time.

Integrate the following line in /etc/postfix/main.cf (or remove # if the directives are already present):

relay_recipient_maps = hash:/etc/postfix/relay_recipients

Now integrate the script and adapt it to your needs.

!/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/transport_maps";

Enter the FQDN of your Active Directory domain controllers below


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:


which would be $hqbase="cn=Users,dc=example,dc=com"

Note: You can also use just $hqbase="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:


which would be $user="cn=user,cn=Users,dc=example,dc=com"

Note: You can also use the UPN login: "user\@example.com"


Connecting to Active Directory domain controllers

$ldap = Net::LDAP->new($dc1) or
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)

(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

if ($cookie) {
# We had an abnormal exit, so let the server know we do not want any more
$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;

Prepare the cronjob with the following script which also writes the LDAP list into a database while execution.


cd /etc/postfix
postmap relay_recipients

to execute it continuously, apply the cronjob by typing:

fcrontab -e

Hints & tricks

Adept the mail queue

List the queue:

mailq | cat

Delete mail in queue with the corresponding Mail-ID:

postsuper -d 38A9580045

Further informations

Wiki for Amavis, SpamAssassin und Clamav für Fetchmail .

Infos for smtp_recipient_restrictions .

Examples for Postfix im Ubuntu-Wiki

Infos for NDR

Edit Page ‐ Yes, you can edit!

Older Revisions • September 18, 2018 at 11:25 pm • Jon