===== Introduction ====
antispam rspamd + antivirus clamav ; with rmilter as multiplexor, from :
*http://philpep.org/blog/rspamd-un-antispam-performant
*http://www.stefan-seelmann.de/wiki/mailserver-postfix-dovecot#clamav
*http://www.perlhipster.com/2014/05/how-to-compile-install-rmiler-clamav.html
===== Installation : rspamd, rmilter, clamav and tools =====
# stop mail services
for SERVICE_ in postfix dovecot; do service $SERVICE_ stop; done
apt-get install rspamd rmilter arj bzip2 cabextract cpio file gzip lhasa jlha-utils lzop nomarch p7zip pax rar rpm unrar unzip zip zoo dovecot-antispam clamav clamav-daemon sudo
===== Change clamav user to _rmilter =====
There is no other way as rmilter does not change permission/owner/group over temporary message files.
service rmilter stop
service clamav-daemon stop
service clamav-freshclam stop
chown -R _rmilter: /var/log/clamav/ /var/run/clamav/ /var/lib/clamav/
sed -i 's|create .*$|create 640 _rmilter _rmilter|g' /etc/logrotate.d/clamav-daemon
sed -i 's|create .*$|create 640 _rmilter _rmilter|g' /etc/logrotate.d/clamav-freshclam
===== Configure clamd =====
cp -a /etc/clamav/clamd.conf /etc/clamav/clamd.conf_
sed -i 's|^LocalSocketGroup .*$|LocalSocketGroup _rmilter|g' /etc/clamav/clamd.conf
sed -i 's|^User clamav.*$|User _rmilter|g' /etc/clamav/clamd.conf
cp -a /etc/clamav/freshclam.conf /etc/clamav/freshclam.conf_
sed -i 's|^DatabaseOwner .*$|DatabaseOwner _rmilter|g' /etc/clamav/freshclam.conf
Renew clamav database and start clamav :
freshclam
service clamav-daemon start
service clamav-freshclam start
===== Configure rmilter =====
cp -a /etc/rmilter.conf /etc/rmilter.conf_
cat << 'EOF' > /etc/rmilter.conf
# .include - directive to include other config file
#.include ./rmilter-grey.conf
pidfile = /run/rmilter.pid;
bind_socket = inet:11000@localhost;
whitelist = admin, hostmaster, webmaster, postmaster, abuse;
tempdir = /tmp;
max_size = 64M;
# use_dcc - whether use or not dcc system
use_dcc = no;
# spf_domains - path to file that contains hash of spf domains, ex : spf_domains = example.com;
clamav {
servers = /var/run/clamav/clamd.ctl;
};
spamd {
servers = r:localhost:11333;
connect_timeout = 1s;
results_timeout = 20s;
error_time = 10;
dead_time = 300;
maxerrors = 10;
reject_message = "Spam detecte et rejete. Si ce n'est pas un spam, contactez votre administrateur de messagerie SVP. Spam message rejected; If this is not spam contact abuse";
whitelist = 127.0.0.1/32;
extended_spam_headers = yes;
};
# rule definition:
# rule {
# accept|discard|reject|tempfail|quarantine "[message]"; <- action definition
# [not] connect ; <- conditions
# helo ;
# envfrom ;
# envrcpt ;
# header ;
# body ;
# };
dkim {
# Sample for dkim specific keys
# domain {
# key = /etc/dkim/dkim_example.key;
# domain = "example.com";
# selector = "dkim";
# };
# domain {
# key = /etc/dkim/dkim_test.key;
# domain = "test.com";
# selector = "dkim";
# };
# Universal selector, keys will be checked for pattern /etc/dkim/..key
domain {
key = /etc/dkim;
domain = "*";
selector = "dkim";
};
header_canon = relaxed;
body_canon = relaxed;
sign_alg = sha256;
auth_only = no;
};
EOF
===== configure postfix with new milter =====
cat << 'EOF' >> /etc/postfix/main.cf
################################################
# Milters (antispam/antivirus)
smtpd_milters = inet:localhost:11000
milter_protocol = 6
milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}
milter_rcpt_macros = i {rcpt_addr}
milter_default_action = accept
EOF
Verify you have incoming mail milter in postfix master.cf (/etc/postfix/master.cf):
submission inet n - n - - smtpd
-o syslog_name=postfix/submission
-o smtpd_tls_security_level=encrypt
-o smtpd_sasl_auth_enable=yes
-o smtpd_sasl_type=dovecot
-o smtpd_sasl_path=private/auth
-o smtpd_sasl_security_options=noanonymous
-o smtpd_sasl_local_domain=$myhostname
-o smtpd_client_restrictions=permit_sasl_authenticated,reject
-o smtpd_sender_restrictions=reject_sender_login_mismatch
-o smtpd_recipient_restrictions=reject_non_fqdn_recipient,reject_unknown_recipient_domain,permit_sasl_authenticated,reject
-o smtpd_milters=inet:localhost:11000
-o milter_protocol=6
-o milter_default_action=accept
**Mind the smtpd milter for incoming mails with submission !**
===== configure dovecot for Spam/Ham autolearn =====
==== dovecot reaction to Junk folder ====
sed -i 's|plugin {|plugin {\
mailbox_alias_old = Sent\
mailbox_alias_new = Sent Messages\
mailbox_alias_old2 = Sent\
mailbox_alias_new2 = Sent Items\
mailbox_alias_old3 = Junk\
mailbox_alias_new3 = Spam\
sieve_extensions = +imapflags\
antispam_backend = pipe\
antispam_pipe_program = /usr/bin/rspamc\
antispam_pipe_program_args =\
antispam_pipe_program_spam_arg = learn_spam\
antispam_pipe_program_notspam_arg = learn_ham\
antispam_spam_pattern_ignorecase = junk*;spam*\
antispam_trash_pattern_ignorecase = trash;deleted*|g' /etc/dovecot/conf.d/90-plugin.conf
==== dovecot sieve "before" for putting spams in Junk folder ====
sed -i 's|#sieve_before =.*$|sieve_before = /etc/dovecot/sieve_before/rspamd.sieve|g' /etc/dovecot/conf.d/90-sieve.conf
mkdir /etc/dovecot/sieve_before/
chown vmail:vmail /etc/dovecot/sieve_before/
chmod 755 /etc/dovecot/sieve_before/
cat << 'EOF' > /etc/dovecot/sieve_before/rspamd.sieve
require ["fileinto","imapflags"];
if header :is "X-Spam" "Yes" {
setflag "\\seen";
fileinto "Junk";
stop;
} else {
# The rest goes into INBOX
# default is "implicit keep", we do it explicitly here
keep;
}
EOF
chown vmail:vmail /etc/dovecot/sieve_before/rspamd.sieve
chmod 640 /etc/dovecot/sieve_before/rspamd.sieve
sudo -u vmail sievec /etc/dovecot/sieve_before/rspamd.sieve
===== Autoconfigured mailboxes and virtual ones =====
Autoconf of mailboxes
cat << 'EOF' > /etc/dovecot/conf.d/15-mailboxes.conf
namespace inbox {
mailbox Drafts {
auto = no
special_use = \Drafts
}
mailbox Trash {
auto = no
special_use = \Trash
}
mailbox Sent {
auto = subscribe
special_use = \Sent
}
mailbox Junk {
auto = subscribe
special_use = \Junk
}
}
namespace search {
mailbox All {
auto = subscribe
special_use = \All
}
mailbox New {
auto = subscribe
}
mailbox Star {
auto = subscribe
special_use = \Flagged
}
}
EOF
Virtual mailboxes enabling in dovecot config :
sed -i 's/#mail_plugins =.*$/mail_plugins = $mail_plugins antispam quota sieve virtual mailbox_alias/g' /etc/dovecot/conf.d/15-lda.conf
sed -i 's/#mail_plugins =.*$/mail_plugins = $mail_plugins antispam quota virtual mailbox_alias/g' /etc/dovecot/conf.d/20-imap.conf
cat << '__EOF__' > /etc/dovecot/conf.d/20-lmtp.conf
lmtp_save_to_detail_mailbox = yes
recipient_delimiter = _
protocol lmtp {
postmaster_address = __POSTMASTER_ADDRESS__
mail_plugins = $mail_plugins antispam quota sieve virtual mailbox_alias
}
__EOF__
sed -i "s|__POSTMASTER_ADDRESS__|postmaster@${DEFAULT_MAIL_DOMAIN}|g" /etc/dovecot/conf.d/20-lmtp.conf
Virtual folders auto creation and declaration (via IMAP post login script) :
sed -i 's|service imap {$|service imap {\
executable = imap imap-postlogin\
}\
\
service imap-postlogin {\
executable = script-login /etc/dovecot/imap-postlogin.bash\
user = vmail\
unix_listener imap-postlogin {\
}\
|g' /etc/dovecot/conf.d/10-master.conf
Script for giving virtual mailboxes configuration per user
cat << '__EOF__' > /etc/dovecot/imap-postlogin.bash
#!/bin/sh
__VDIR="${HOME}/search"
__MAILLOCATION="/data/vmail"
__DOMAIN=$(echo ${USER} | cut -d "@" -f 2)
#__USER=$(echo ${USER} | cut -d "@" -f 1)
# creation of domain folder
if [ ! -d ${__MAILLOCATION}/${__DOMAIN} ]; then
mkdir -p ${__MAILLOCATION}/${__DOMAIN}
chown vmail:vmail ${__MAILLOCATION}/${__DOMAIN}
chmod 700 ${__MAILLOCATION}/${__DOMAIN}
fi
# creation of $HOME folder
if [ ! -d ${HOME} ]; then
mkdir -p ${HOME}
chown vmail:vmail ${HOME}
chmod 700 ${HOME}
fi
# creation of $HOME/virtual folder
if [ ! -d ${__VDIR} ]; then
mkdir -p ${__VDIR}
chown vmail:vmail ${__VDIR}
chmod 700 ${__VDIR}
fi
if [ ! -d ${__VDIR}/All ]; then
mkdir ${__VDIR}/All
chown vmail:vmail ${__VDIR}/All
chmod 700 ${__VDIR}/All
cat << "EOF" > ${__VDIR}/All/dovecot-virtual
*
-Trash
-Trash/*
-Junk
-Junk/*
all
EOF
chown vmail:vmail ${__VDIR}/All/dovecot-virtual
chmod 600 ${__VDIR}/All/dovecot-virtual
fi
if [ ! -d ${__VDIR}/Star ]; then
mkdir ${__VDIR}/Star
chown vmail:vmail ${__VDIR}/Star
chmod 700 ${__VDIR}/Star
cat << "EOF" > ${__VDIR}/Star/dovecot-virtual
*
flagged
EOF
chown vmail:vmail ${__VDIR}/Star/dovecot-virtual
chmod 600 ${__VDIR}/Star/dovecot-virtual
fi
if [ ! -d ${__VDIR}/New ]; then
mkdir ${__VDIR}/New
chown vmail:vmail ${__VDIR}/New
chmod 700 ${__VDIR}/New
cat << "EOF" > ${__VDIR}/New/dovecot-virtual
*
-Trash
-Trash/*
-Junk
-Junk/*
unseen
EOF
chown vmail:vmail ${__VDIR}/New/dovecot-virtual
chmod 600 ${__VDIR}/New/dovecot-virtual
fi
#set > /tmp/dovecot-environment
#touch ~/_last_login
exec "$@"
__EOF__
chown root:root /etc/dovecot/imap-postlogin.bash
chmod 755 /etc/dovecot/imap-postlogin.bash
Namespaces configuration with virtual new one :
cat << 'EOF' > /etc/dovecot/conf.d/10-mail.conf
mail_location = maildir:~/maildir:LAYOUT=fs:DIRNAME=__maildir__
mail_privileged_group = vmail
mailbox_list_index = yes
namespace inbox {
prefix =
type = private
subscriptions = yes
separator = /
inbox = yes
}
namespace search {
prefix = search/
type = private
separator = /
location = virtual:~/search:LAYOUT=fs:INDEX=MEMORY
}
EOF
===== DKIM with rmilter =====
==== script that generates DKIM keys and SPF/DMARC entries =====
if [ ! -d /opt/admin_scripts/ ]; then
mkdir -p /opt/admin_scripts/
chown root:root /opt/admin_scripts/
chmod 750 /opt/admin_scripts/
fi
cat << '__EOF__' > /opt/admin_scripts/make_dkim_keys.bash
#!/bin/bash
if [ $# -ne 1 ]; then
echo "illegal number of parameters"
echo "$0 "
exit -1
fi
if [ ! -d /etc/dkim/ ]; then
mkdir -p /etc/dkim/
chown root:ssl-cert /etc/dkim/
chmod 640 /etc/dkim/
fi
DOMAIN=${1}
TIMESTAMP=$(date +"%Y%m%d%H%M")
SELECTOR="$TIMESTAMP"
PRIVKEY="/etc/dkim/dkim_private_${SELECTOR}._domainkey.${DOMAIN}.pem.key"
PUBKEY="/etc/dkim/dkim_public_${SELECTOR}._domainkey.${DOMAIN}.pem.key"
RMILTERLNK="/etc/dkim/${DOMAIN}.${SELECTOR}.key"
openssl genrsa -out ${PRIVKEY} 1024 -outform PEM
openssl rsa -in ${PRIVKEY} -out ${PUBKEY} -pubout -outform PEM
ln ${PRIVKEY} ${RMILTERLNK}
chown root:ssl-cert ${PRIVKEY} ${PUBKEY} ${RMILTERLNK}
chmod 640 ${PRIVKEY} ${RMILTERLNK}
chmod 664 ${PUBKEY}
DNSDKIM=$(cat ${PUBKEY} | egrep -v "^-----.*-----$" | tr -d "\n" | tr -d "\r")
echo "================================================================================"
echo "-> New DNS DKIM for $DOMAIN. Selector is : $SELECTOR"
echo " PRIVATE key path : $PRIVKEY"
echo " $RMILTERLNK"
echo " PUBLIC key path : $PUBKEY"
echo "-> DNS entries to add:"
echo " DKIM : $SELECTOR._domainkey IN 1800 TXT \"v=DKIM1; k=rsa; p=${DNSDKIM}"\"
echo " SPF : @ 1800 IN SPF \"v=spf1 mx ?all\""
echo " : @ 1800 IN TXT \"v=spf1 mx ?all\""
echo " DMARC LIGHT : _dmarc 1800 IN TXT \"v=DMARC1; p=none; rua=mailto:postmaster@$DOMAIN; ruf=mailto:postmaster@$DOMAIN; fo=1; adkim=r; aspf=r; rf=afrf; ri=1800\""
echo " DMARC REJECT : _dmarc 1800 IN TXT \"v=DMARC1; p=reject; rua=mailto:postmaster@$DOMAIN; ruf=mailto:postmaster@$DOMAIN; fo=1; adkim=r; aspf=r; rf=afrf; ri=1800\""
echo "-> rmilter.conf :"
echo " domain {"
echo " key = $RMILTERLNK;"
echo " domain = \"$DOMAIN\";"
echo " selector = \"$SELECTOR\";"
echo " };"
echo "================================================================================"
__EOF__
chown root:root /opt/admin_scripts/make_dkim_keys.bash
chmod 750 /opt/admin_scripts/make_dkim_keys.bash
/opt/admin_scripts/make_dkim_keys.bash
chown -R _rmilter:_rmilter /etc/dkim
chmod 550 /etc/dkim
chmod 640 /etc/dkim/*
**RQ : DKIM for domain AND subdomains, declare the domain in conf AND duplicate for the subdomain.
RQ : for SPF and DMARC, please add entries for the subdomain**
root@mail:~# /opt/admin_scripts/make_dkim_keys.bash
illegal number of parameters
/opt/admin_scripts/make_dkim_keys.bash
root@mail:~# /opt/admin_scripts/make_dkim_keys.bash alocean.com
Generating RSA private key, 1024 bit long modulus
................................................................................++++++
................++++++
e is 65537 (0x10001)
writing RSA key
================================================================================
-> New DNS DKIM for alocean.com. Selector is : 201511301053
PRIVATE key path : /etc/dkim/dkim_private_201511301053._domainkey.alocean.com.pem.key
/etc/dkim/alocean.com.201511301053.key
PUBLIC key path : /etc/dkim/dkim_public_201511301053._domainkey.alocean.com.pem.key
-> DNS entries to add:
DKIM : 201511301053._domainkey IN 1800 TXT "v=DKIM1; k=rsa; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQDe9mpx9bG904HdYt1s74jV/kqGKp5XP3PhX2cB+so89SHCgFw9Wu1mJBBIdDB2mi46DCgrK4MCwZtHYhbegJgdq1X6H1ifZHBjOtprEb8T+vY4ZDPotFkzHtk8TENVhWbbpHY/fsyY/YgFAyQO69NaCKmfrOOCLOpW8aTv/CkMGQIDAQAB"
SPF : alocean.com 1800 IN SPF "v=spf1 mx ?all"
: alocean.com 1800 IN TXT "v=spf1 mx ?all"
DMARC LIGHT : _dmarc 1800 IN TXT "v=DMARC1; p=none; rua=mailto:postmaster@alocean.com; ruf=mailto:postmaster@alocean.com; fo=1; adkim=r; aspf=r; rf=afrf; ri=1800"
DMARC REJECT : _dmarc 1800 IN TXT "v=DMARC1; p=reject; rua=mailto:postmaster@alocean.com; ruf=mailto:postmaster@alocean.com; fo=1; adkim=r; aspf=r; rf=afrf; ri=1800"
-> rmilter.conf :
domain {
key = /etc/dkim/alocean.com.201511301053.key;
domain = "liberasys.com";
selector = "201511301053";
};
================================================================================
chown -R _rmilter:_rmilter /etc/dkim
chmod 550 /etc/dkim
chmod 640 /etc/dkim/*
service rmilter stop && sleep 2 && service rmilter start
===== Configure rspamd =====
Verify you have scoring decisions matching your spam policy. For example :
vi /etc/rspamd/metrics.conf
metric {
name = "default";
actions {
reject = 100;
add_header = 6;
greylist = 4;
};
===== Final stop/start =====
In order to check everything is good, we do a full stop/start of the complete chain (in the good order!)
# tail logs
tail -f /var/log/syslog /var/log/mail.{err,info,log,warn} &
# stop mail services
for SERVICE_ in postfix dovecot rmilter clamav-freshclam clamav-daemon rspamd; do service $SERVICE_ stop; done
# start mail services
for SERVICE_ in rspamd clamav-daemon clamav-freshclam rmilter dovecot postfix; do service $SERVICE_ start; done
fg
TODO : copy spams to a specific admin mailbox/folder ?