===== 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 ?