Table des matières

Introduction

antispam rspamd + antivirus clamav ; with rmilter as multiplexor, from :

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 <regexp> <regexp>; <- conditions
#       helo <regexp>;
#       envfrom <regexp>;
#       envrcpt <regexp>;
#       header <regexp> <regexp>;
#       body <regexp>;
# };
 
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/<domain>.<selector>.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 <domain name>"
  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 <your_domain>
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

example
root@mail:~# /opt/admin_scripts/make_dkim_keys.bash
illegal number of parameters
/opt/admin_scripts/make_dkim_keys.bash <domain name>
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 ?