Mail Server: openSUSE, Postfix, Dovecot, MariaDB e Squirrel Mail – Part 3

Published by Lello on

Installiamo e configuriamo Amavis con SpamAssassin e ClamAV

  • Spamassassin
  • ClamAV
  • Amavis

Spamassassin

Installare spamassassin è molto semplice; scarichiamo anche lo script di creazione del DB dei dati bayesiani e i package per gestire il database razor (che permette di utilizzare gli stessi DB dei clienti commerciali Cloudmark; tale database è un HashSharingSystem)

# mkdir /root/tmp
# wget http://svn.apache.org/repos/asf/spamassassin/tags/spamassassin_release_3_3_1/sql/bayes_mysql.sql -O /root/tmp/bayes_mysql.sql
# sed -i 's/TYPE=MyISAM;/ENGINE=InnoDB DEFAULT CHARSET=utf8;/' /root/tmp/bayes_mysql.sql
# zypper -n in spamassassin perl-Mail-SpamAssassin perl-DBI razor-agents perl-razor-agents clamav libfreshclam2 amavisd-new arc arj cabextract lz4 lzop p7zip-full tnef unrar zoo unzip
# zypper in https://download.opensuse.org/repositories/Archiving/openSUSE_Leap_15.2/x86_64/lrzip-0.641-archiving.1.1.x86_64.rpm

Creiamo e configuriamo il database che ospiterà i dati bayesiani:

# mysql -p
MariaDB [(none)]> CREATE DATABASE sa_bayes_db;
Query OK, 1 row affected (0.001 sec)
MariaDB [(none)]> GRANT SELECT, INSERT, UPDATE, DELETE ON sa_bayes_db.* TO sa_bayes_user@localhost IDENTIFIED BY 'sa_bayes_passwd';
MariaDB [(none)]> USE sa_bayes_db;
Database changed
MariaDB [(sa_bayes_db)]>  SOURCE /root/tmp/bayes_mysql.sql;
..........
MariaDB [(none)]> FLUSH PRIVILEGES;
MariaDB [sa_bayes_db]> exit

Modifichiamo il file di configurazione /etc/mail/spamassassin/local.cf di spamassassin:

required_score  4.00
rewrite_header  Subject ===== SPAM (_SCORE_) =====

report_safe     1
lock_method     flock
ok_locales      it en
dns_available   yes

#
# Bayesian support
use_bayes          1
bayes_auto_learn   1
bayes_auto_expire  1
bayes_learn_during_report 1
bayes_auto_learn_threshold_nonspam 0.1
bayes_auto_learn_threshold_spam 4.5

# Store bayesian data in MySQL.
bayes_store_module Mail::SpamAssassin::BayesStore::MySQL
bayes_sql_dsn      DBI:mysql:sa_bayes_db:127.0.0.1:3306
# SQL username and password.
bayes_sql_username sa_bayes_user
bayes_sql_password sa_bayes_passwd

# Increase score for message which contains blacklisted or phishing URI
# URIBL
#score URIBL_SBL 3
# dbl.spamhaus.org
#score URIBL_DBL_SPAM 3
#score URIBL_DBL_PHISH 3
#score URIBL_DBL_MALWARE 3
#score URIBL_DBL_BOTNETCC 3
#score URIBL_DBL_ABUSE_SPAM 3
#score URIBL_DBL_ABUSE_REDIR 3
#score URIBL_DBL_ABUSE_PHISH 3
#score URIBL_DBL_ABUSE_MALW 3
#score URIBL_DBL_ABUSE_BOTCC 3
#score URIBL_DBL_ERROR 0
# multi.surbl.org
#score URIBL_WS_SURBL 3
#score URIBL_PH_SURBL 3
#score URIBL_MW_SURBL 3
#score URIBL_CR_SURBL 3
#score URIBL_ABUSE_SURBL 3
#score SURBL_BLOCKED 0
# multi.urlbl.com
#score URIBL_BLACK 3
#score URIBL_RED 3
#score URIBL_BLOCKED 0

# DNSBL
#score RCVD_IN_SBL 5
#score RCVD_IN_XBL 5
#score RCVD_IN_PBL 5

# Turn off ALL DNSBL (DNS Blocklists)
#skip_rbl_checks  1
# Turn off DNSBL: rhsbl.ahbl.org.
# Check /usr/share/spamassassin/20_dnsbl_teest.cf to see the rule name.
score DNS_FROM_AHBL_RHSBL 0

score URIBL_AB_SURBL 0 0.3306 0 0.3812
score URIBL_JP_SURBL 0 0.3360 0 0.4087
score URIBL_OB_SURBL 0 0.2617 0 0.3008
score URIBL_PH_SURBL 0 0.2240 0 0.2800
score URIBL_SBL 0 0.1094 0 0.1639
score URIBL_SC_SURBL 0 0.3600 0 0.4498
score URIBL_WS_SURBL 0 0.1533 0 0.2140

# Trusted networks. Examples:
#   192.168/16            # all in 192.168.*.*
#   212.17.35.15          # just that host
#   !10.0.1.5 10.0.1/24   # all in 10.0.1.* but not 10.0.1.5
#   DEAD:BEEF::/32        # all in that ipv6 prefix
# Local host (127.0.0.1) will automatically be trusted implicitly.
trusted_networks 192.168.100.0/24

# Whitelist from SPF.
#whitelist_from_spf joe@example.com fred@example.com
#whitelist_from_spf *@example.com
#whitelist_to    joe@example.com fred@example.com
#whitelist_from  joe@example.com fred@example.com
#blacklist_from  joe@example.com fred@example.com
#blacklist_to  joe@example.com fred@example.com

# Whitelist from DKIM.
loadplugin Mail::SpamAssassin::Plugin::DKIM
whitelist_from_dkim *@paypal.com
whitelist_from_dkim *@linkedin.com
whitelist_from_dkim *@twitter.com
whitelist_from_dkim *@bounce.twitter.com

# Whitelist domains.
# Reference: http://wiki.apache.org/spamassassin/ManualWhitelist
#whitelist_from         *@gmail.com

# Locales.
ok_locales          all

# Checks if domain name of an envelope sender address matches the domain name
# of the first untrusted relay (if any), or any trusted relay otherwise.
# https://wiki.apache.org/spamassassin/Rules/RP_MATCHES_RCVD
score RP_MATCHES_RCVD 0

# SPF mismatch
score SPF_FAIL 5

#Razor
use_razor2 1
razor_config /etc/mail/spamassassin/razor.conf
score RAZOR2_CHECK 3.000

# Pyzor
#pyzor_path /usr/bin/pyzor

score PYZOR_CHECK 3.000

score BAYES_99 2.0
score BAYES_999 4.0

Creiamo il file di configurazione /etc/mail/spamassassin/razor.conf per razor:

Ricordiamoci inoltre di aprire in uscita sul firewall la porta TCP/2703 (altrimenti razor non funzionerà).

debuglevel             = 1
identity               = identity
ignorelist             = 0
listfile_catalogue     = servers.catalogue.lst
listfile_discovery     = servers.discovery.lst
listfile_nomination    = servers.nomination.lst
logfile                = razor-agent.log
logic_method           = 4
min_cf                 = ac
razordiscovery         = discovery.razor.cloudmark.com
razorhome              = /var/lib/razor
rediscovery_wait       = 172800
report_headers         = 1
turn_off_discovery     = 0
use_engines            = 4,8
whitelist              = razor-whitelist

Inizializziamo il database e cambiamo l’owner della cartella razorhome assegnadola all’utente vscan.

# razor-admin -conf=/etc/mail/spamassassin/razor.conf -create
# razor-admin -conf=/etc/mail/spamassassin/razor.conf -register
   Register successful.  Identity stored in /var/lib/razor/identity-XXXXXXX
# razor-admin -conf=/etc/mail/spamassassin/razor.conf -discover
# chown -R vscan.vscan /var/lib/razor

Aggiorniamo le regole e le configurazione di Spamassassin:

# sa-update

ClamAV

Modifichiamo il file di configurazione /etc/clamd.conf di ClamAV:

# Use system logger.
LogSyslog yes

# Specify the type of syslog messages - please refer to 'man syslog'
# for facility names.
LogFacility LOG_MAIL

# This option allows you to save a process identifier of the listening
# daemon (main thread).
PidFile /var/run/clamav/clamd.pid

# Remove stale socket after unclean shutdown.
# Default: disabled
FixStaleSocket yes

# Run as a selected user (clamd must be started by root).
User vscan

# Path to a local socket file the daemon will listen on.
LocalSocket /var/run/clamav/clamd.socket
AllowSupplementaryGroups true

Aggiorniamo il database di ClamAV, abilitiamo ed avviamo il servizio:

# freshclam
# systemctl enable --now clamd

Amavis

Cominciamo con il generare i certificati DKIM da associare ai domini da cui invieremo e riceveremo posta.

# mkdir /var/lib/dkim
# cd /var/lib/dkim
# amavisd genrsa anthesia.lan.pem 2048
# chown -R vscan.vscan /var/lib/dkim
# chmod 400 /var/lib/dkim/*

Al momento in cui scrivo questo articolo, il package altermime è disponibile solo per openSUSE Leap 15.2; installeremo pertanto tale package (supportato anche nella 15.3):

# zypper in https://download.opensuse.org/repositories/home:/aledr/openSUSE_Leap_15.2/x86_64/altermime-0.3.10-lp152.1.1.x86_64.rpm

Per creare il database di Amavis, partendo dallo script mysql “/usr/share/doc/packages/amavisd-new-docs/README_FILES/README.sql-mysql“, creaimo il file /root/tmp/amavis_db.sql ed inseriamo il seguente codice:

-- local users
CREATE TABLE users (
  id         int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,  -- unique id
  priority   integer      NOT NULL DEFAULT '7',  -- sort field, 0 is low prior.
  policy_id  integer unsigned NOT NULL DEFAULT '1',  -- JOINs with policy.id
  email      varbinary(255) NOT NULL UNIQUE,
  fullname   varchar(255) DEFAULT NULL    -- not used by amavisd-new
  -- local   char(1)      -- Y/N  (optional field, see note further down)
);

-- any e-mail address (non- rfc2822-quoted), external or local,
-- used as senders in wblist
CREATE TABLE mailaddr (
  id         int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
  priority   integer      NOT NULL DEFAULT '7',  -- 0 is low priority
  email      varbinary(255) NOT NULL UNIQUE
);

-- per-recipient whitelist and/or blacklist,
-- puts sender and recipient in relation wb  (white or blacklisted sender)
CREATE TABLE wblist (
  rid        integer unsigned NOT NULL,  -- recipient: users.id
  sid        integer unsigned NOT NULL,  -- sender: mailaddr.id
  wb         varchar(10)  NOT NULL,  -- W or Y / B or N / space=neutral / score
  PRIMARY KEY (rid,sid)
);

CREATE TABLE policy (
  id  int unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
                                    -- 'id' this is the _only_ required field
  policy_name      varchar(32),     -- not used by amavisd-new, a comment

  virus_lover           char(1) default NULL,     -- Y/N
  spam_lover            char(1) default NULL,     -- Y/N
  unchecked_lovers_maps char(1) default NULL,     -- Y/N
  banned_files_lover    char(1) default NULL,     -- Y/N
  bad_header_lover      char(1) default NULL,     -- Y/N

  bypass_virus_checks   char(1) default NULL,     -- Y/N
  bypass_spam_checks    char(1) default NULL,     -- Y/N
  bypass_banned_checks  char(1) default NULL,     -- Y/N
  bypass_header_checks  char(1) default NULL,     -- Y/N

  spam_modifies_subj    char(1) default NULL,     -- Y/N

  virus_quarantine_to      varchar(64) default NULL,
  spam_quarantine_to       varchar(64) default NULL,
  banned_quarantine_to     varchar(64) default NULL,
  unchecked_quarantine_to  varchar(64) default NULL,
  bad_header_quarantine_to varchar(64) default NULL,
  clean_quarantine_to      varchar(64) default NULL,
  archive_quarantine_to    varchar(64) default NULL,

  spam_tag_level  float default NULL, -- higher score inserts spam info headers
  spam_tag2_level float default NULL, -- inserts 'declared spam' header fields
  spam_tag3_level float default NULL, -- inserts 'blatant spam' header fields
  spam_kill_level float default NULL, -- higher score triggers evasive actions
                                      -- e.g. reject/drop, quarantine, ...
                                     -- (subject to final_spam_destiny setting)
  spam_dsn_cutoff_level        float default NULL,
  spam_quarantine_cutoff_level float default NULL,

  addr_extension_virus      varchar(64) default NULL,
  addr_extension_spam       varchar(64) default NULL,
  addr_extension_banned     varchar(64) default NULL,
  addr_extension_bad_header varchar(64) default NULL,

  warnvirusrecip      char(1)     default NULL, -- Y/N
  warnbannedrecip     char(1)     default NULL, -- Y/N
  warnbadhrecip       char(1)     default NULL, -- Y/N
  newvirus_admin      varchar(64) default NULL,
  virus_admin         varchar(64) default NULL,
  banned_admin        varchar(64) default NULL,
  bad_header_admin    varchar(64) default NULL,
  spam_admin          varchar(64) default NULL,
  spam_subject_tag    varchar(64) default NULL,
  spam_subject_tag2   varchar(64) default NULL,
  spam_subject_tag3   varchar(64) default NULL,
  message_size_limit  integer     default NULL, -- max size in bytes, 0 disable
  banned_rulenames    varchar(64) default NULL, -- comma-separated list of ...
        -- names mapped through %banned_rules to actual banned_filename tables
  disclaimer_options  varchar(64) default NULL,
  forward_method      varchar(64) default NULL,
  sa_userconf         varchar(64) default NULL,
  sa_username         varchar(64) default NULL
);

-- provide unique id for each e-mail address, avoids storing copies
CREATE TABLE maddr (
  partition_tag integer      DEFAULT 0, -- see $partition_tag
  id         bigint unsigned NOT NULL AUTO_INCREMENT PRIMARY KEY,
  email      varbinary(255)  NOT NULL,  -- full mail address
  domain     varchar(255)    NOT NULL,  -- only domain part of the email address
                                        -- with subdomain fields in reverse
  CONSTRAINT part_email UNIQUE (partition_tag,email)
) ENGINE=InnoDB;

CREATE TABLE msgs (
  partition_tag integer     DEFAULT 0,   -- see $partition_tag
  mail_id     varbinary(16) NOT NULL,    -- long-term unique mail id, dflt 12 ch
  secret_id   varbinary(16) DEFAULT '',  -- authorizes release of mail_id, 12 ch
  am_id       varchar(20)   NOT NULL,    -- id used in the log
  time_num    integer unsigned NOT NULL, -- rx_time: seconds since Unix epoch
  time_iso    char(16)      NOT NULL,    -- rx_time: ISO8601 UTC ascii time
  sid         bigint unsigned NOT NULL,  -- sender: maddr.id
  policy      varchar(255)  DEFAULT '',  -- policy bank path (like macro %p)
  client_addr varchar(255)  DEFAULT '',  -- SMTP client IP address (IPv4 or v6)
  size        integer unsigned NOT NULL, -- message size in bytes
  originating char(1) DEFAULT ' ' NOT NULL,  -- sender from inside or auth'd
  content     char(1),                   -- content type: V/B/U/S/Y/M/H/O/T/C
    -- virus/banned/unchecked/spam(kill)/spammy(tag2)/
    -- /bad-mime/bad-header/oversized/mta-err/clean
    -- is NULL on partially processed mail
    -- (prior to 2.7.0 the CC_SPAMMY was logged as 's', now 'Y' is used;
    -- to avoid a need for case-insenstivity in queries)
  quar_type  char(1),                   -- quarantined as: ' '/F/Z/B/Q/M/L
                                        --  none/file/zipfile/bsmtp/sql/
                                        --  /mailbox(smtp)/mailbox(lmtp)
  quar_loc   varbinary(255) DEFAULT '', -- quarantine location (e.g. file)
  dsn_sent   char(1),                   -- was DSN sent? Y/N/q (q=quenched)
  spam_level float,                     -- SA spam level (no boosts)
  message_id varchar(255)  DEFAULT '',  -- mail Message-ID header field
  from_addr  varchar(255)  CHARACTER SET utf8 COLLATE utf8_bin  DEFAULT '',
                                        -- mail From header field,    UTF8
  subject    varchar(255)  CHARACTER SET utf8 COLLATE utf8_bin  DEFAULT '',
                                        -- mail Subject header field, UTF8
  host       varchar(255)  NOT NULL,    -- hostname where amavisd is running
  PRIMARY KEY (partition_tag,mail_id)
-- FOREIGN KEY (sid) REFERENCES maddr(id) ON DELETE RESTRICT
) ENGINE=InnoDB;
CREATE INDEX msgs_idx_sid      ON msgs (sid);
CREATE INDEX msgs_idx_mess_id  ON msgs (message_id); -- useful with pen pals
CREATE INDEX msgs_idx_time_num ON msgs (time_num);
-- alternatively when purging based on time_iso (instead of msgs_idx_time_num):
-- CREATE INDEX msgs_idx_time_iso ON msgs (time_iso);

-- per-recipient information related to each processed message;
-- NOTE: records in msgrcpt without corresponding msgs.mail_id record are
--  orphaned and should be ignored and eventually deleted by external utilities
CREATE TABLE msgrcpt (
  partition_tag integer    DEFAULT 0,    -- see $partition_tag
  mail_id    varbinary(16) NOT NULL,     -- (must allow duplicates)
  rseqnum    integer  DEFAULT 0   NOT NULL, -- recip's enumeration within msg
  rid        bigint unsigned NOT NULL,   -- recipient: maddr.id (dupl. allowed)
  is_local   char(1)  DEFAULT ' ' NOT NULL, -- recip is: Y=local, N=foreign
  content    char(1)  DEFAULT ' ' NOT NULL, -- content type V/B/U/S/Y/M/H/O/T/C
  ds         char(1)  NOT NULL,          -- delivery status: P/R/B/D/T
                                         -- pass/reject/bounce/discard/tempfail
  rs         char(1)  NOT NULL,          -- release status: initialized to ' '
  bl         char(1)  DEFAULT ' ',       -- sender blacklisted by this recip
  wl         char(1)  DEFAULT ' ',       -- sender whitelisted by this recip
  bspam_level float,                     -- per-recipient (total) spam level
  smtp_resp  varchar(255)  DEFAULT '',   -- SMTP response given to MTA
  PRIMARY KEY (partition_tag,mail_id,rseqnum)
-- FOREIGN KEY (rid)     REFERENCES maddr(id)     ON DELETE RESTRICT,
-- FOREIGN KEY (mail_id) REFERENCES msgs(mail_id) ON DELETE CASCADE
) ENGINE=InnoDB;
CREATE INDEX msgrcpt_idx_mail_id  ON msgrcpt (mail_id);
CREATE INDEX msgrcpt_idx_rid      ON msgrcpt (rid);

-- mail quarantine in SQL, enabled by $*_quarantine_method='sql:'
-- NOTE: records in quarantine without corresponding msgs.mail_id record are
--  orphaned and should be ignored and eventually deleted by external utilities
CREATE TABLE quarantine (
  partition_tag integer    DEFAULT 0,    -- see $partition_tag
  mail_id    varbinary(16) NOT NULL,     -- long-term unique mail id
  chunk_ind  integer unsigned NOT NULL,  -- chunk number, starting with 1
  mail_text  blob          NOT NULL,     -- store mail as chunks of octets
  PRIMARY KEY (partition_tag,mail_id,chunk_ind)
-- FOREIGN KEY (mail_id) REFERENCES msgs(mail_id) ON DELETE CASCADE
) ENGINE=InnoDB;

Creiamo e popoliamo successivamente il database amavis_db, utilizzando lo script appena creato:

# mysql -p
MariaDB [(none)]> CREATE DATABASE amavis_db;
Query OK, 1 row affected (0.001 sec)
MariaDB [(none)]> GRANT SELECT, INSERT, UPDATE, DELETE ON amavis_db.* TO amavis_user@localhost IDENTIFIED BY 'amavis_passwd';
MariaDB [(none)]> FLUSH PRIVILEGES;
MariaDB [(none)]> USE amavis_db;
MariaDB [(amavis_db)]> SOURCE /root/tmp/amavis_db.sql;
MariaDB [(amavis_db)]> exit

Dobbiamo ora creare la configurazione del file di Amavis; la configurazione è molto articolata e decisamente personalizzata sul sistema su cui stiamo installando il sistema. Di seguito indicherò soltanto alcune linee del file, mentre per uno script completo vi rimando a questo indirizzo; il file dovrà essere alvato nella posizione /etc/amavisd.conf.

Dopo aver creato il file di configurazione, abilitiamo ed avviamo il servizio:

# systemctl enable --now amavis
# systemctl status amavis
● amavis.service - Amavisd-new Virus Scanner interface
      Loaded: loaded (/usr/lib/systemd/system/amavis.service; disabled; vendor preset: disabled)
      Active: active (running) since Thu 2021-06-24 19:47:50 CEST; 4min 31s ago
     Process: 20368 ExecStartPre=/bin/echo Starting virus-scanner (amavisd-new): (code=exited, status=0/SUCCESS)
     Process: 20369 ExecStartPre=/usr/sbin/amavisd-milter.sh start (code=exited, status=0/SUCCESS)
    Main PID: 20370 (/usr/sbin/amavi)
      Status: "Starting child process(es), ready for work."
       Tasks: 3 (limit: 2344)
      CGroup: /system.slice/amavis.service
              ├─20370 /usr/sbin/amavisd (master)
              ├─20373 /usr/sbin/amavisd (virgin child)
              └─20374 /usr/sbin/amavisd (virgin child)
 Jun 24 19:47:48 mailserver amavis[20370]: No decoder for  .......
 Jun 24 19:47:48 mailserver amavis[20370]: Using primary internal av scanner code for clamav-socket
 Jun 24 19:47:48 mailserver amavis[20370]: Found secondary av scanner clamav-clamscan at /usr/bin/clamscan
 Jun 24 19:47:50 mailserver systemd[1]: Started Amavisd-new Virus Scanner interface.

← Part 2

Part 4 →