Come configurare un server di posta su linux
Con Postfix, Dovecot, OpenDmarc, SPF, DKIM, SpamAssassin
Aggiornata a giugno 2026
In questa guida ripeterò passo passo le operazioni che ho eseguito per l'installazione di un server di posta su Debian 13.
Diciamo più un memorandum che una guida, ma magari, chissà, tornerà utile a qualcun'altro.
Il mio server di posta attuale dispone di :
- Postfix: ricezione e dell'invio delle email.
- Dovecot: gestione della posta tramite IMAP.
- SPF: autorizzata quali server possono spedire email da un dominio.
- DKIM: firma digitalmente i messaggi per garantirne l'autenticità.
- DMARC: coordina SPF e DKIM definendo le politiche di sicurezza.
- SpamAssassin: controlla i messaggi ed applica un punteggio di SPAM.
- Fail2Ban: blocca gli ip che eseguono determinate operazioni
- RoundCube: webmail accessibile da browser.
Tengo a precisare che nella lista dei comandi utilizzerò aptitude, più per fisima mia personale, ma se volete usare apt va bene ugualmente.
La guida dovrebbe funzionare anche su Ubuntu, se invece siete su alte distro con altri package manager (dnf, pacman, yum, ecc)... Usate un briciolo di immaginazione e convertite i comandi :)
Configurazione DNS
Per prima cosa bisogna impostare i DNS con il record MX, che dice al mondo "la posta per questo dominio consegnatela a questo server". Subito dopo, e qui si scorda quasi sempre, va impostato il PTR (reverse DNS) presso il provider/hosting che ci dà l'ip: è la corrispondenza inversa ip → hostname e deve combaciare col nome del server. Senza un PTR coerente metà dei provider (Gmail in testa) ci sbatte la posta in spam a prescindere da tutto il resto.
Gli altri record DNS (SPF, DKIM, DMARC) li vediamo man mano nelle rispettive sezioni, perché dipendono dalle chiavi e dalle configurazioni che genereremo.
Postfix
Iniziamo con l'installazione di del MTA Postfix. Perché Postfix e non Exim? Sinceramente l'ho scelto per la prima prima volta 15 anni fa semplicemente perché... non mi piaceva il nome Exim XD.
aptitude install -y postfix postfix-psql
Verrà chiesto che tipologia di configurazione, scegliere "Internet Site", successivamente inserire il nome host desiderato, ad esempio example.com
La prima cosa che consiglio di fare è inserire un alias di root al proprio utente: in /etc/aliases:
root:mio_username
E quindi poi lanciare
newaliases
In /etc/postfix/main.cf aggiungere (ci sono già le configurazioni per spamassassin e dovecot):
# Posizione delle chiavi (generate con letsencrypt, acme.sh o altro)
smtpd_tls_cert_file = /path/chiave_dominio.fullchain.pem
smtpd_tls_key_file = /path/chiave_dominio.privkey.pem
# hostname e domini che riceveranno la posta
myhostname = dominio.com
# Domini consegnati localmente (le mailbox vere e proprie su questa macchina)
mydestination = $myhostname, dominio.com, altrodominio.com
# Indirizzi ip considerati "fidati" (possono spedire senza autenticarsi).
# Lasciare quelli locali e aggiungere eventualmente l'ip pubblico del server
mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 123.123.123.123 1234:1234:1234:1234::1
# Delimitatori per indirizzi alias (esempio indirizzo+alias@.. = indirzzo@..)
recipient_delimiter = +_
# Dimensione massima di un messaggio in byte (qui 50 MB)
message_size_limit = 52428800
# --- Mailbox virtuali ---
# Domini gestiti come "virtuali", cioè senza un utente di sistema dedicato
virtual_mailbox_domains = virtual.dominio.com
# Cartella base dove vengono salvate le mailbox virtuali
virtual_mailbox_base = /var/mail/vmail
# uid/gid di sistema con cui vengono scritte le mailbox virtuali (utente vmail)
virtual_gid_maps = static:5000
virtual_uid_maps = static:5000
# uid minimo accettato: protezione per non scrivere come root o utenti di sistema
virtual_minimum_uid = 100
# Tabella degli alias virtuali (da indirizzo -> a destinatario reale)
virtual_alias_maps = hash:/etc/postfix/virtual/addresses
# Consegna dei virtuali tramite il socket LMTP di Dovecot
virtual_transport = lmtp:unix:private/dovecot-lmtp
# --- Autenticazione SASL (delegata a Dovecot) ---
# Abilita l'autenticazione SMTP: senza questo nessuno può spedire da fuori
smtpd_sasl_auth_enable = yes
# Pretende STARTTLS prima dell'AUTH: niente credenziali in chiaro (es. sulla 25)
smtpd_tls_auth_only = yes
# Chi gestisce l'autenticazione: Dovecot, così username/password sono uno solo
smtpd_sasl_type = dovecot
# Socket condiviso con Dovecot per verificare le credenziali
smtpd_sasl_path = private/auth
# Formato mailbox per gli utenti locali: Maildir (un file per email)
home_mailbox = Maildir/
# Consegna locale tramite Dovecot LDA (così sieve e quota funzionano)
mailbox_command = /usr/lib/dovecot/dovecot-lda -f "$SENDER" -a "$RECIPIENT"
# --- Milter DKIM / DMARC / SpamAssassin ---
# Versione del protocollo milter (la 6 è quella moderna)
milter_protocol = 6
# Se un milter non risponde, accetta comunque la mail invece di scartarla:
# meglio una mail non firmata che un server che rifiuta tutto perché un
# demone è morto
milter_default_action = accept
# Catena dei milter: ogni mail in arrivo passa per DKIM, DMARC e SpamAssassin
smtpd_milters = local:opendkim/opendkim.sock,local:opendmarc/opendmarc.sock,local:spamass/spamass.sock
# Stessi milter anche per la posta generata localmente (es. firma DKIM in uscita)
non_smtpd_milters = $smtpd_milters
# Timeout (secondi) per la verifica SPF
policyd-spf_time_limit = 3600
# TLS "opportunistico": usalo se disponibile, ma non rifiutare se manca.
# In ricezione (smtpd) e in invio (smtp)
smtpd_tls_security_level = may
smtp_tls_security_level = may
# IPv6 in uscita: vincolalo all'indirizzo che ha il PTR corretto, altrimenti il
# kernel potrebbe usare un IPv6 "privacy" senza reverse -> Gmail lo penalizza
smtp_bind_address6 = 1234:1234:1234:1234::1
# Livello di log del TLS (1 = essenziale, utile per debug dei certificati)
smtpd_tls_loglevel = 1
# Pretende un HELO/EHLO prima di accettare posta (molti bot lo saltano)
smtpd_helo_required = yes
# Disabilita il comando VRFY: impedisce di "indovinare" gli indirizzi validi
disable_vrfy_command = yes
# Restrizioni applicate sul destinatario: vengono valutate in ordine, la prima
# che decide "vince". Mettendo per primi i due permit chi arriva dalle reti
# fidate o è autenticato salta i controlli
smtpd_recipient_restrictions =
permit_mynetworks, # ok se proviene dalle reti fidate
permit_sasl_authenticated, # ok se l'utente si è autenticato
reject_unknown_recipient_domain, # dominio destinatario inesistente
reject_unauth_pipelining, # client che "spara" comandi senza aspettare
reject_invalid_hostname, # HELO/EHLO con hostname malformato
reject_non_fqdn_sender, # indirizzo mittente non-FQDN (malformato)
reject_non_fqdn_recipient, # indirizzo destinatario non-FQDN
reject_unknown_sender_domain, # dominio del mittente inesistente
reject_unauth_destination, # niente open relay verso domini altrui
reject_rbl_client zen.spamhaus.org,# rifiuta ip in blacklist Spamhaus
check_policy_service unix:private/policyd-spf # verifica SPF
# Forza almeno TLSv1.2: disabilita i protocolli vecchi e ormai insicuri.
# Vale sia in ricezione (smtpd) sia in invio (smtp)
smtpd_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtpd_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_mandatory_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
smtp_tls_protocols = !SSLv2, !SSLv3, !TLSv1, !TLSv1.1
# --- Dovecot ---
# Consegna finale tramite LMTP di Dovecot
mailbox_transport = lmtp:unix:private/dovecot-lmtp
# --- SpamAssassin ---
# Tabelle di filtri PCRE su header e corpo (vedi sezione SpamAssassin)
header_checks = pcre:/etc/postfix/header_checks
body_checks = pcre:/etc/postfix/body_checks
# Pulizia degli header in uscita (toglie gli X-Spam interni)
smtp_header_checks = pcre:/etc/postfix/smtp_header_checks
# --- PostScreen: il buttafuori davanti alla porta 25 ---
# Prima di passare a smtpd postscreen osserva il comportamento
# del client e lo confronta con le blacklist: i bot già segnalati
# vengono bloccati all'inizio (va abilitato anche in master.cf)
postscreen_greet_action = enforce
# Blacklist DNS pesate: Spamhaus vale 3, SpamCop 2
postscreen_dnsbl_sites = zen.spamhaus.org*3, bl.spamcop.net*2
# Somma dei pesi oltre la quale scatta l'azione
postscreen_dnsbl_threshold = 3
# Cosa fare oltre soglia: enforce = rifiuta la connessione
postscreen_dnsbl_action = enforce
# --- Limiti per singolo client ---
# Max connessioni al minuto dallo stesso ip
smtpd_client_connection_rate_limit = 20
# Max connessioni simultanee dallo stesso ip
smtpd_client_connection_count_limit = 10
# Dopo un errore rallenta il client di 5s (fa perdere tempo ai bot)
smtpd_error_sleep_time = 5s
# Errori "soft" tollerati prima di iniziare a rallentare
smtpd_soft_error_limit = 3
# Errori oltre i quali si chiude la connessione
smtpd_hard_error_limit = 10
In /etc/postfix/master.cf aggiungere. Qui si definiscono i "servizi", cioè le porte su cui Postfix ascolta e come si comporta su ciascuna. Le righe -o sono override delle impostazioni globali di main.cf, validi solo per quel servizio: la stessa configurazione, ma più stretta o più larga a seconda della porta.
# Porta 25 (smtp): server-to-server, qui arriva la posta. Davanti ci mettiamo
# postscreen: si commenta la riga smtpd e si abilitano postscreen più
# i servizi di supporto (smtpd via "pass", dnsblog per le blacklist, tlsproxy)
#smtp inet n - - - - smtpd
smtp inet n - - - 1 postscreen
smtpd pass - - - - - smtpd
dnsblog unix - - - - 0 dnsblog
tlsproxy unix - - - - 0 tlsproxy
# Porta 587 (submission): qui si autenticano i tuoi client (Thunderbird, ecc.)
submission inet n - - - - smtpd
-o syslog_name = postfix/submission # etichetta nei log
-o smtpd_tls_security_level = encrypt # TLS obbligatorio, niente chiaro
-o smtpd_sasl_auth_enable = yes # richiede login
-o smtpd_client_restrictions = permit_sasl_authenticated,reject # solo autenticati
-o smtpd_relay_restrictions = permit_sasl_authenticated,reject # niente relay anonimo
-o smtpd_recipient_restrictions = permit_mynetworks,permit_sasl_authenticated,reject
-o smtpd_sasl_type = dovecot # autenticazione via Dovecot
-o smtpd_sasl_path = private/auth
-o milter_macro_daemon_name = ORIGINATING # posta "nostra": i milter la firmano (DKIM) invece di verificarla
# Porta 465 (smtps): submission con TLS "implicito" (wrappermode), per i
# client che si aspettano la connessione già cifrata fin dal primo byte
smtps inet n - - - - smtpd
-o syslog_name = postfix/smtps
-o smtpd_tls_wrappermode = yes # TLS subito, senza STARTTLS
-o smtpd_sasl_auth_enable = yes
-o smtpd_client_restrictions = permit_sasl_authenticated,reject
-o smtpd_relay_restrictions = permit_sasl_authenticated,reject # niente relay anonimo
-o smtpd_sasl_type = dovecot
-o smtpd_sasl_path = private/auth
-o milter_macro_daemon_name = ORIGINATING # posta in uscita: firmata dai milter
# Servizio che esegue il controllo SPF (richiamato da check_policy_service)
policyd-spf unix - n n - 0 spawn
user = policyd-spf argv=/usr/bin/policyd-spf
Per aggiungere utenti virtuali (ovvero alias "da → a") modificare il file /etc/postfix/virtual/addresses. A sinistra l'indirizzo che riceve, a destra dove finisce davvero la posta: può essere un utente locale oppure un indirizzo esterno qualsiasi, comodissimo per inoltrare verso altri utenti o altre email senza creare una mailbox vera.
root@domain.com user # la posta per root@ va all'utente locale
user2@domain.com user2 # alias verso un altro utente locale
mario@domain.com mario@altraemail.com # inoltro verso un indirizzo esterno
E aggiungere i domini a /etc/postfix/virtual/domains, uno per riga: sono i domini per cui Postfix accetta di gestire questi alias.
localhost
domain.com
domain2.com
Poi eseguire i comandi per rigenerare:
postmap /etc/postfix/virtual
postmap /etc/postfix/virtual/addresses
Vi consiglio però di stare attenti a inoltrare email a provider esterni per evitare di compromettere la vostra reputazione: se avete un indirizzo spammato e rigirate tutto lo spam a gmail, poi gmail se la prende con voi e vi marca l'ip come spam
Dovecot
Dovecot è il programma che consente agli utenti di poter accedere alla propria posta, gestendo l'accesso tramite i protocolli IMAP e/o POP3. Per installare dovecot:
aptitude install dovecot-core dovecot-imapd dovecot-pop3d dovecot-lmtpd
Modificare le impostazioni per abilitare i protocolli in /etc/dovecot/dovecot.conf:
# imap: accesso alla posta dai client, lmtp: consegna da Postfix a Dovecot.
# Aggiungere pop3 a questa riga se si vuole anche il POP3
protocols = imap lmtp
!include_try /usr/share/dovecot/protocols.d/*.protocol
Poi impostare il driver e il percorso della posta, io preferisco maildir rispetto a mbox, meglio avere un file per email che tutte le mail in un unico, corruttibile, file. Modificare quindi il file /etc/dovecot/conf.d/10-mail.conf
mail_driver = maildir # un file per email invece di un unico mbox
mail_path = ~/Maildir # posizione della posta, dentro la home di ogni utente
Poi impostare i permessi e i servizo in /etc/dovecot/conf.d/10-master.conf. Qui si creano i due socket che fanno dialogare Postfix e Dovecot: uno per la consegna della posta (lmtp) e uno per l'autenticazione (auth); I socket vivono dentro la spool di Postfix proprio perché è Postfix a doverli leggere, da qui i permessi assegnati all'utente postfix:
service lmtp {
# Socket su cui Postfix consegna la posta a Dovecot
unix_listener /var/spool/postfix/private/dovecot-lmtp {
mode = 0600 # leggibile solo dal proprietario
user = postfix # è Postfix che ci scrive dentro
group = postfix
}
}
service auth {
# Socket condiviso per la verifica delle credenziali SASL
unix_listener /var/spool/postfix/private/auth {
mode = 0660 # leggibile da proprietario e gruppo
user = postfix
group = postfix
}
}
Poi impostare come gli utenti possono effettuare il login in /etc/dovecot/conf.d/10-auth.conf:
# Normalizza lo username: toglie l'eventuale @dominio e lo abbassa a minuscolo,
# così "Mario@dominio.com" e "mario" finiscono sullo stesso account
auth_username_format = %{user|username|lower}
# Meccanismi di login accettati. plain e login vanno bene perché tanto la
# connessione è già cifrata da TLS (vedi submission/smtps)
auth_mechanisms = plain login
Ed impostare le chiavi in /etc/dovecot/conf.d/10-ssl.conf (stesso certificato usato da Postfix). Attenzione: in Dovecot 2.4 le direttive si chiamano ssl_server_cert_file e ssl_server_key_file e vogliono il percorso nudo (in 2.3 erano ssl_cert/ssl_key con una < davanti, ora la < non serve più 1 ):
ssl_server_cert_file = /path/chiave_dominio.fullchain.pem # certificato + catena
ssl_server_key_file = /path/chiave_dominio.privkey.pem # chiave privata
ssl = required # TLS obbligatorio: niente IMAP/POP3 in chiaro
Abilitare sieve in /etc/dovecot/conf.d/15-lda.conf ed impostare i delimitatori uguali a quelli di Postfix (altrimenti nome+etichetta@ non viene riconosciuto allo stesso modo dai due programmi). Sieve serve per le regole di filtraggio, tipo spostare lo spam nel cestino, lo useremo più avanti:
recipient_delimiter = +_ # stessi delimitatori di main.cf
protocol lda {
# Boolean list of plugins to load
mail_plugins {
sieve = yes # abilita il filtraggio sieve in consegna
}
}
Utile anche abilitare il log rotate con /etc/logrotate.d/dovecot, così il log non cresce all'infinito mangiandosi il disco:
/var/log/dovecot.log {
weekly # ruota una volta a settimana
rotate 4 # tiene 4 archivi storici (circa un mese)
missingok # niente errore se il log manca
notifempty # non ruota se è vuoto
maxsize 500M # ruota comunque se supera i 500M prima della settimana
minsize 50M # ma non ruotare se è ancora sotto i 50M
compress # comprime i vecchi log
delaycompress # rimanda la compressione di un giro (l'ultimo resta leggibile)
sharedscripts # esegue lo script sotto una volta sola
postrotate
doveadm log reopen # dice a Dovecot di riaprire il file dopo la rotazione
endscript
}
A questo punto far ripartire i servizi postfix e dovecot con systemctl restart postfix dovecot
Meglio anche impostare che il servizio dovecot riparta in caso di errore: si crea un file di "drop-in" che sovrascrive un pezzo dell'unit di systemd senza toccare quella originale (che a un aggiornamento verrebbe sovrascritta):
mkdir -p /etc/systemd/system/dovecot.service.d/
nano /etc/systemd/system/dovecot.service.d/restart.conf
[Service]
Restart = always # riavvia sempre, qualunque sia il motivo dell'uscita
RestartSec = 5s # aspetta 5 secondi prima di riprovare
E ricaricare quindi i servizi : systemctl daemon-reload
Un certificato per ogni dominio (SNI)
Fino a qui abbiamo usato un certificato per tutto. Va bene se i client si collegano sempre allo stesso nome (es. mail.dominio.com) e quel nome è dentro il certificato. Due accortezze però:
- Il certificato deve combaciare col nome con cui ti raggiungono. Sulla porta 25 in particolare conta l'
HELO(cioèmyhostname): se il certificato è permail.dominio.comma l'HELO èsrv.dominio.com, alcuni provider lo segnalano. Un certificato wildcard*.dominio.comcopre entrambi ed è la via più comoda. - Se gestisci più domini e vuoi che ognuno abbia il suo
mail.altrodominio.comcol certificato corretto, serve SNI: il server sceglie il certificato in base al nome richiesto dal client nell'handshake TLS. Postfix (>= 3.4) e Dovecot lo supportano entrambi ed i nomi non elencati ricadono sul certificato di default già configurato prima.
Per Dovecot basta inserire un blocco local_name per ogni nome, in un file dedicato tipo /etc/dovecot/conf.d/11-ssl-sni.conf:
local_name mail.dominio.com {
ssl_server_cert_file = /path/dominio.com.fullchain.pem
ssl_server_key_file = /path/dominio.com.privkey.pem
}
local_name mail.altrodominio.it {
ssl_server_cert_file = /path/altrodominio.it.fullchain.pem
ssl_server_key_file = /path/altrodominio.it.privkey.pem
}
Per Postfix (submission/smtps) serve una mappa nome → chiave + catena, in /etc/postfix/sni.map (prima la chiave privata, poi il fullchain):
mail.dominio.com /path/dominio.com.privkey.pem /path/dominio.com.fullchain.pem
mail.altrodominio.it /path/altrodominio.it.privkey.pem /path/altrodominio.it.fullchain.pem
Si compila con postmap -F (Postfix genera dei .db contenenti i PEM, non linka i nomi dei file) e si attiva in main.cf:
postmap -F hash:/etc/postfix/sni.map
postconf -e 'tls_server_sni_maps = hash:/etc/postfix/sni.map'
systemctl reload postfix
Occhio ai rinnovi: siccome postmap -F copia il contenuto dei certificati dentro il .db, dopo un rinnovo (in cui i file cambiano) il .db resta con la vecchia versione finché non lo si rigenera. Dovecot invece rilegge i file ad ogni reload.
Per Postfix basta uno script cron che ricompila e rigenera il db solo se qualcosa è cambiato, per esempio /etc/cron.daily/postfix-sni-refresh :
#!/bin/sh
MAP=/etc/postfix/sni.map
OLD=$(cksum "$MAP.db" 2>/dev/null)
postmap -F "hash:$MAP"
[ "$OLD" != "$(cksum "$MAP.db" 2>/dev/null)" ] && systemctl reload postfix
exit 0
Prova finale: chiedendo un nome via SNI deve tornare il certificato giusto.
openssl s_client \
-connect mail.dominio.com:465 \
-servername mail.dominio.com
</dev/null 2>/dev/null \
| openssl x509 -noout -subject
SPF
Per spf basta installare postfix-policyd-spf-python e poi riavviare postfix
aptitude install postfix-policyd-spf-python
systemctl restart postfix
Ora bisogna impostare i campi TXT dei domini con v=spf1 mx ~all. Tradotto: mx autorizza a spedire i server elenati nei record MX del dominio (cioè il nostro), mentre ~all è un "softfail" che dice ai destinatari «tutto il resto è sospetto, ma non scartarlo a priori». Quando si è sicuri che tutto funziona si può irrigidire in -all (hardfail), che invece invita a rifiutare chiunque non sia autorizzato.
OpenDmarc
DMARC è il vigile che mette d'accordo SPF e DKIM: controlla che almeno uno dei due sia valido e allineato col mittente, e decide cosa fare di chi non passa. Durante l'installazione viene chiesto se configurare un database per i report: rispondo "No", a me i report aggregati non interessano.
aptitude install opendmarc
-> No # niente database per i report
systemctl enable opendmarc
Modificare quindi la configurazione in /etc/opendmarc.conf:
# Socket condiviso con Postfix (dev'essere lo stesso indicato in smtpd_milters)
Socket local:/var/spool/postfix/opendmarc/opendmarc.sock
# Non controllare chi si è già autenticato da noi (è posta nostra in uscita)
IgnoreAuthenticatedClients true
# Pretende From, Date, ecc.: una mail senza header obbligatori è da buttare
RequiredHeaders true
# Verifica SPF da solo, senza appoggiarsi a un milter SPF esterno
SPFSelfValidate true
# Lista di host da non sottoporre a DMARC (vedi sotto)
IgnoreHosts /etc/opendmarc/ignore.hosts
# Crea la cartella del socket dentro la spool di Postfix e dà i permessi giusti
mkdir -p /var/spool/postfix/opendmarc
chown opendmarc:opendmarc /var/spool/postfix/opendmarc -R
chmod 750 /var/spool/postfix/opendmarc/ -R
Aggiunge postfix al gruppo opendmarc così può leggere il socket
adduser postfix opendmarc
systemctl restart opendmarc
systemctl restart postfix
Infine va pubblicato il record DNS di DMARC: un TXT su _dmarc.dominio.com con un valore tipo v=DMARC1; p=quarantine; rua=mailto:postmaster@dominio.com. La p è la politica che chiediamo agli altri server di applicare alla nostra posta che non passa i controlli: none osserva soltanto, quarantine la manda in spam, reject la rifiuta del tutto. Consiglio di partire da none finché non si è certi che SPF e DKIM siano allineati, e solo dopo stringere.
DKIM
DKIM aggiunge a ogni mail in uscita una firma crittografica negli header: il destinatario la verifica con la chiave pubblica pubblicata nel nostro DNS e sa così che il messaggio è davvero nostro e non di chissà chis sia. Noi teniamo la chiave privata sul server, la pubblica va nel DNS.
aptitude install opendkim opendkim-tools
gpasswd -a postfix opendkim # postfix nel gruppo opendkim, per il socket
systemctl restart postfix
mkdir -p /etc/opendkim/keys
chown -R opendkim:opendkim /etc/opendkim
chmod go-rw /etc/opendkim/keys # le chiavi non devono essere leggibili da altri
mkdir /var/spool/postfix/opendkim
chown opendkim:postfix /var/spool/postfix/opendkim
Configurare /etc/opendkim.conf:
# Come "normalizzare" header e corpo prima di firmare: relaxed tollera piccoli
# ritocchi sugli header (spazi, a capo), simple è rigido sul corpo
Canonicalization relaxed/simple
# s = sign (firma in uscita), v = verify (verifica in entrata). "sv" = entrambi
Mode sv
# Non firmare automaticamente i sottodomini con la chiave del dominio padre
SubDomains no
# DNS usati da OpenDKIM per pubblicare/verificare le chiavi
Nameservers 8.8.8.8,1.1.1.1
# Socket condiviso con Postfix (lo stesso indicato in smtpd_milters)
Socket local:/var/spool/postfix/opendkim/opendkim.sock
# Quale chiave usare per firmare (KeyTable) e a quali mittenti applicarla
# (SigningTable). "refile": file con caratteri jolly
KeyTable refile:/etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
# Host esterni di cui fidarsi senza verificarne la firma
ExternalIgnoreList refile:/etc/opendkim/TrustedHosts
# Host "interni": la loro posta viene firmata, non verificata
InternalHosts refile:/etc/opendkim/TrustedHosts
La SigningTable in /etc/opendkim/SigningTable associa i mittenti al selettore della chiave da usare (*@dominio.com = chiunque su quel dominio):
*@dominio.com mail._domainkey.dominio.com
*@altrodominio.com mail._domainkey.altrodominio.com
La KeyTable /etc/opendkim/KeyTable dice dove sta fisicamente la chiave privata, nel formato dominio:selettore:percorso_chiave:
mail._domainkey.dominio.com dominio.com:mail:/etc/opendkim/keys/dominio.com/mail.private
mail._domainkey.altrodominio.com altrodominio.com:mail:/etc/opendkim/keys/altrodominio.com/mail.private
Per generare le chiavi (una coppia pubblica/privata per dominio) basta usare opendkim-genkey, che genera già il record spezzato nel formato corretto. Con -b 2048 si sceglie la lunghezza: 2048 bit è lo standard odierno (Gmail e altri segnalano ormai le chiavi a 1024 come deboli). Scendi a -b 1024 solo se il tuo pannello DNS proprio non accetta record TXT lunghi, ma ormai è raro.
mkdir /etc/opendkim/keys/dominio.com
opendkim-genkey -b 2048 -d dominio.com -D /etc/opendkim/keys/dominio.com -s mail -v
# Permessi: la chiave privata deve appartenere a opendkim e basta
chmod 700 /etc/opendkim/keys/dominio.com
chmod 600 /etc/opendkim/keys/dominio.com/*
chown -R opendkim:opendkim /etc/opendkim/keys/dominio.com
Il comando crea anche un file mail.txt contenente il TXT da copiare nel record DNS su mail._domainkey.dominio.com (è la chiave pubblica che i destinatari useranno per verificare la firma). Una volta inserito il record nel DNS controllare che funzioni con:
opendkim-testkey -d dominio.com -s mail -vvv # deve dire "key OK"
dig +short TXT mail._domainkey.dominio.com # mostra la chiave
In /etc/opendkim/TrustedHosts gli host la cui posta va firmata invece che verificata (tipicamente il server stesso e i propri domini):
127.0.0.1
::1
localhost
123.123.123.123
1234:1234:1234:1234::1
.dominio.com
.domaltrodominioinio.com
Infine in /etc/default/opendkim indicare lo stesso socket della configurazione, così il demone parte ascoltando dove Postfix si aspetta di trovarlo:
SOCKET="local:/var/spool/postfix/opendkim/opendkim.sock"
E poi basta far ripartire il tutto:
systemctl restart opendkim postfix
Ruotare o rinnovare una chiave DKIM
Prima o poi capita: vuoi passare a una chiave più robusta (da 1024 a 2048 bit) oppure semplicemente ruotarla per maniacali livelli di sicurezza. Va quindi generata una coppia nuova (nuova privata sul server, nuova pubblica nel DNS).
L'unica insidia è la propagazione DNS. Se riscrivi il TXT dello stesso selettore mail, finché in giro c'è ancora in cache la vecchia chiave pubblica (fino al TTL del record) la posta firmata con la nuova privata fallisce la verifica DKIM dai destinatari. Per evitarlo del tutto si usa un selettore nuovo (es. mail2, o datato tipo 2026a) e si fa il cambio in quest'ordine, senza nessun istante scoperto. Lascia il vecchio TXT mail._domainkey nel DNS ancora qualche giorno: la posta già spedita e firmata con la vecchia chiave deve poter essere verificata da chi la riceve in ritardo. Dopo 3-7 giorni puoi rimuoverlo e cancellare la vecchia mail.private.
Se proprio vuoi riusare lo stesso selettore mail, abbassa il TTL di quel record (es. a 300s) un TTL prima della sostituzione: riduce la finestra di fallimento, ma usare un selettore nuovo resta la strada più facile ed indolore.
SpamAssassin
SpamAssassin è il vero cuore antispam: assegna a ogni mail un punteggio sommando decine di regole, e oltre una certa soglia la marchia (o la rifiuta). Prima di scomodarlo, però, conviene far scartare a Postfix la robaccia più ovvia con due filtri PCRE secchi, così non sprechiamo nemmeno il tempo di analizzarla.
aptitude install postfix-pcre
In /etc/postfix/header_checks le regole sugli header. DISCARD finge di accettare la mail ma la butta nel nulla, senza dare allo spammer la soddisfazione di un errore:
/free mortgage quote/ DISCARD # classico spam di mutui
/repair your credit/ DISCARD
/To:.*<>/ DISCARD # destinatario vuoto, mail malformata
/From:.*<>/ DISCARD # mittente vuoto
E in /etc/postfix/body_checks le stesse regole, ma sul corpo del messaggio:
/free mortgage quote/ DISCARD
/repair your credit/ DISCARD
I file di mappa vanno compilati con postmap ogni volta che si modificano:
postmap /etc/postfix/header_checks
postmap /etc/postfix/body_checks
systemctl restart postfix
Ora SpamAssassin vero e proprio, col suo milter per agganciarlo a Postfix:
aptitude install spamassassin spamc
systemctl enable spamd
systemctl start spamd
aptitude install spamass-milter
In /etc/default/spamass-milter alcune opzioni utili:
# -I = NON scansionare la posta autenticata (submission/smtps con login):
# il milter gira anche in uscita, quindi senza questo una mail inviata
# che totalizza un punteggio alto verrebbe marcata come spam o
# addirittura bloccata
OPTIONS="${OPTIONS} -I"
# -r 8 = rifiuta subito le mail con punteggio >= 8 (spam conclamato),
# invece di consegnarle solo marchiate
OPTIONS="${OPTIONS} -r 8"
# Non analizzare mail oltre i ~5 MB: sarebbero lente da scansionare e
# raramente lo spam è così pesante
OPTIONS="${OPTIONS} -- --max-size=5120000"
systemctl restart postfix spamass-milter
Abilitare anche l'aggiornamento automatico delle regole, così le firme antispam restano fresche senza pensarci:
nano /etc/cron.daily/spamassassin
CRON=1 # abilita l'aggiornamento giornaliero via cron
systemctl enable --now spamassassin-maintenance.timer
Veniamo al pezzo più divertente, /etc/spamassassin/local.cf, dove si ritoccano i punteggi delle regole. Ogni riga score cambia quanti punti vale una certa regola: numeri positivi spingono verso lo spam, negativi verso il "buono". Questi valori sono frutto di anni di aggiustamenti sulla mia posta, prendeteli come spunto e tarateli sui falsi positivi che capitano a voi:
score MISSING_FROM 5.0 # manca l'header From : , quasi sempre spam
score MISSING_DATE 5.0 # manca la data
score PDS_FROM_2_EMAILS 3.0 # due indirizzi diversi nel From
score EMPTY_MESSAGE 5.0 # messaggio vuoto
score FREEMAIL_DISPTO 2.0 # display-to verso un freemail sospetto
score FREEMAIL_FORGED_REPLYTO 3.5 # Reply-To falsificato su un freemail
score DKIM_ADSP_NXDOMAIN 5.0 # dominio firmatario inesistente
score FORGED_GMAIL_RCVD 2.5 # finto Received "da Gmail"
score FROM_FMBLA_NEWDOM 4.0 # mittente su dominio registrato da poco
score FROM_FMBLA_NEWDOM14 3.5 # ...meno di 14 giorni
score FROM_FMBLA_NEWDOM28 3.0 # ...meno di 28 giorni
score RCVD_IN_DNSWL_HI -1.0 # in una whitelist DNS affidabile : -1, è buona
score RCVD_IN_BL_SPAMCOP_NET 3.0 # presente nella blacklist SpamCop
score HTML_FONT_LOW_CONTRAST 0.7 # testo a basso contrasto (trucco da spam)
score NORDNS_LOW_CONTRAST 3.0
score HTML_IMAGE_RATIO_02 2.0 # tutta immagine e poco testo
score HTML_IMAGE_ONLY_20 2.0 # solo immagine, niente testo
score URIBL_BLACK 3.0 # link verso domini in blacklist URIBL
# Reputazione Mailspike: H = buona (punteggi negativi), L = cattiva (positivi)
score RCVD_IN_MSPIKE_H2 -1.0
score RCVD_IN_MSPIKE_H3 -1.5
score RCVD_IN_MSPIKE_H4 -2.0
score RCVD_IN_MSPIKE_H5 -2.5
score RCVD_IN_MSPIKE_L3 1.9
score RCVD_IN_MSPIKE_L4 3.7
score RCVD_IN_MSPIKE_L5 4.5
score T_SPF_PERMERROR 2.0 # record SPF rotto/malformato
score URIBL_ABUSE_SURBL 4.5 # link verso domini nella blacklist abusi SURBL
score MAILING_LIST_MULTI 2.0 # più header da mailing list diverse nello stesso messaggio
Oltre a ritoccare i punteggi, si possono scrivere regole proprie (qui prefissate CHA_ per riconoscerle a colpo d'occhio). Una regola è fatta di tre righe: header/full definisce la condizione, describe la spiega e score le dà il peso:
# Mittente uguale al destinatario: un trucco vecchio ma ancora in giro
header CHA_FROM_SAME_AS_TO ALL =~ /\nFrom: ([^\n]+)\nTo: \1/sm
describe CHA_FROM_SAME_AS_TO From address is the same as To address.
score CHA_FROM_SAME_AS_TO 2.0
# Return-Path vuoto <>
header CHA_EMPTY_RETURN_PATH ALL =~ /<>/i
describe CHA_EMPTY_RETURN_PATH empty address in the Return Path header.
score CHA_EMPTY_RETURN_PATH 3.0
# La mail ha fallito il controllo DMARC (lo leggo dall'header che scrive OpenDmarc)
header CHA_CUSTOM_DMARC_FAIL Authentication-Results =~ /dmarc=fail/
describe CHA_CUSTOM_DMARC_FAIL This email failed DMARC check
score CHA_CUSTOM_DMARC_FAIL 3.0
# Domini delle TLD da cui mi arriva solo spazzatura (regolatevi sulla vostra)
header CHA_SPAM_DOMINI From =~ /@[a-z0-9\-\.]+\.(club|icu|xyz|online|ru|su)/i
describe CHA_SPAM_DOMINI Spam da domini sospetti o russi
score CHA_SPAM_DOMINI 4.5
# Charset cirillici: se non ricevete posta in russo, è quasi sempre spam
full CHA_CHARSET_BLOCKED /charset=(3D|")(koi8-r|windows-1251)(3D|")/is
score CHA_CHARSET_BLOCKED 4.5
describe CHA_CHARSET_BLOCKED Email con charset russi
# Spam che rimbalza su Google Groups: liste con alias di 2 caratteri e relay
# Google, in cui non sei il vero destinatario. Qui entra in gioco `meta`, che
# combina più condizioni con && e ||: le regole che iniziano con `__` non
# servono solo per creare il meta finale
header __CHA_GG_GROUP_ID exists:X-Google-Group-Id
header __CHA_LIST_ID_2CHAR List-Id =~ /<[a-z0-9]{2}\./i
body __CHA_UNSUB_2CHAR_BODY /(?:^|[\s:<"'(])[a-z0-9]{2}\+unsubscribe@[a-z0-9.-]+/i
meta CHA_SPAM_SHORT_LIST ((__CHA_UNSUB_2CHAR_BODY || __CHA_LIST_ID_2CHAR) && __CHA_GG_GROUP_ID)
describe CHA_SPAM_SHORT_LIST Lista con alias di 2 caratteri, relay Google Groups, non destinatario
score CHA_SPAM_SHORT_LIST 4.0
Un esempio più ricco è la meta anti-phishing che intercetta l'impersonazione dei brand famosi (PayPal, Amazon, Poste, banche, Microsoft…). L'idea: se il From rivendica un brand ma il dominio reale del mittente non è quello ufficiale, è quasi sempre phishing. Per ogni brand due sotto-regole __: una rileva il nome nel From, l'altra riconosce i domini legittimi; la meta le combina come «brand citato e dominio non ufficiale»:
# nome del brand rivendicato nel From (display-name o indirizzo)
header __CHA_DN_PAYPAL From =~ /paypal/i
header __CHA_DN_AMAZON From =~ /amazon/i
header __CHA_DN_POSTE From =~ /(poste\s*italiane|posteitaliane|bancoposta)/i
header __CHA_DN_RCUBE From =~ /(roundcube|webmail)/i
# domini realmente ufficiali del brand (la parte dopo la @)
header __CHA_OK_PAYPAL From:addr =~ /@([a-z0-9-]+\.)*paypal\.[a-z.]{2,}$/i
header __CHA_OK_AMAZON From:addr =~ /@([a-z0-9-]+\.)*(amazon\.[a-z.]{2,}|amazonses\.com)$/i
header __CHA_OK_POSTE From:addr =~ /@([a-z0-9-]+\.)*(poste\.it|posteitaliane\.it)$/i
# per roundcube/webmail il mittente "legittimo" è uno dei TUOI domini
header __CHA_OK_RCUBE From:addr =~ /@([a-z0-9-]+\.)*(dominio\.com|altrodominio\.it)$/i
# scatta se un brand è citato ma il suo dominio ufficiale NON combacia
meta CHA_FAKE_DOMAINS ((__CHA_DN_PAYPAL && !__CHA_OK_PAYPAL) || (__CHA_DN_AMAZON && !__CHA_OK_AMAZON) || (__CHA_DN_POSTE && !__CHA_OK_POSTE) || (__CHA_DN_RCUBE && !__CHA_OK_RCUBE))
describe CHA_FAKE_DOMAINS Brand noto nel mittente ma dominio non ufficiale (phishing)
score CHA_FAKE_DOMAINS 4.5
Sullo stesso schema si aggiungono gli altri brand (Intesa Sanpaolo, UniCredit, Nexi, Aruba, Banca d'Italia, OpenAI, Netflix…): basta una coppia __CHA_DN_x / __CHA_OK_x e un'altra clausola nella meta. Il punteggio 4.5 da solo non marca spam (così un brand citato per caso in posta legittima non fa danni), ma sommato a qualunque altro segnale supera la soglia. Occhio ai nomi generici (intesa, aruba sono anche parole comuni): se salta fuori un falso positivo su un mittente preciso, un welcomelist_auth lo mette al riparo.
Sempre nello stesso file, una whitelist e una blacklist "manuali": la prima mette al riparo i mittenti fidati dai falsi positivi, la seconda fulmina chi non si vuole proprio vedere.
Occhio a un tranello grosso: **whitelist_from guarda solo il From, che è banalmente falsificabile.** Uno spammer scrive From: qualcuno@tuodominio (o un qualsiasi indirizzo che hai in whitelist) e si becca il -100 di USER_IN_WELCOMELIST, scavalcando ogni altro controllo. Per questo conviene welcomelist_auth: identico, ma il bonus vale **solo se il mittente è autenticato* (SPF pass* o DKIM valido e allineati col dominio). La posta forgiata fallisce l'auth e non prende niente.
welcomelist_auth *@dominio.com # bonus solo se SPF/DKIM autenticano
welcomelist_auth mario@altrodominio.it
blacklist_from spam@example.com
blacklist_from *@example.org
Un'idea comoda è auto-generare la whitelist dai propri corrispondenti: gli indirizzi a cui hai scritto sono quasi sempre gente vera. Un mini-script che pesca i destinatari dalle cartelle .Sent:
grep -rhEi '^(To|cc|bcc):' /home/*/Maildir/.Sent/*/* |
grep -oEi '[a-z0-9_.+-]+@([a-z0-9-]+\.)+[a-z]{2,}' |
tr 'A-Z' 'a-z' | sort -u |
xargs -n1 echo welcomelist_auth > /etc/spamassassin/whitelist.generator.cf
Nota bene: usa welcomelist_auth, non whitelist_from — altrimenti ognuno di quegli indirizzi diventa un buco, basta che uno spammer lo forgi nel From per auto-whitelistarsi.
Dopo ogni modifica conviene controllare la sintassi con --lint (se non dice nulla, è tutto a posto) e riavviare:
spamassassin --lint
systemctl restart spamd
A questo punto SpamAssassin marchia lo spam, ma la mail finisce comunque nella Posta in arrivo. Per spostarla in automatico nella cartella Junk uso Sieve, che filtra al momento della consegna:
aptitude install dovecot-sieve dovecot-managesieved
Abilitare il plugin sieve sia in consegna locale (lda) sia via lmtp, in /etc/dovecot/conf.d/15-lda.conf
protocol lda {
mail_plugins {
sieve = yes
}
}
e in /etc/dovecot/conf.d/20-lmtp.conf
protocol lmtp {
mail_plugins {
sieve = yes
}
}
In /etc/dovecot/conf.d/90-sieve.conf si registra uno script "globale" che gira prima degli eventuali filtri dell'utente (type = before):
sieve_script move2spam {
type = before
path = /var/mail/SpamToJunk.sieve
}
E lo script vero e proprio, /var/mail/SpamToJunk.sieve: se SpamAssassin ha marcato la mail (X-Spam-Flag: YES), spostala in Junk e ferma l'elaborazione:
require "fileinto";
if header :contains "X-Spam-Flag" "YES"
{
fileinto "Junk";
stop;
}
Gli script sieve vanno compilati con sievec, poi si riavvia Dovecot:
sievec /var/mail/SpamToJunk.sieve
systemctl restart dovecot
Attenzione a un cambiamento di Dovecot 2.4: gli script dei singoli utenti non sono abilitati di default, busogna impostare anche la posizione dello * script personale*, Basta decommentare in 90-sieve.conf:
sieve_script personal {
driver = file
path = ~/sieve
active_path = ~/.dovecot.sieve
}
Un modo veloce per verificare che un filtro personale funzione basta eseguire sieve-test, che simula la consegna senza toccare la mailbox:
sieve-test -u utente /home/utente/.dovecot.sieve messaggio.eml
Occhio anche alla differenza tra header :is "from" "tizio@example.com" e address :is "from" "tizio@example.com": il primo pretende che l'header From sia esattamente quella stringa e quindi non filtra "Nome" <tizio@example.com> Per confrontare il solo indirizzo (ignorando il nome) va usato address. In Roundcube questo si fa nelle impostazioni della regola (l'ingranaggio a destra) apparirà un sottomenu in cui si può cambiare il modificatore, impostandolo ad "indirizzo" (address) invece di "nessuno" (header). Anche qui bestemmie e tempo perso a capire perché i filtri non funzionavano...
Già che ci siamo, un po' di pulizia sulle porte di Dovecot. Se non usi POP3, disattiva i suoi listener invece di lasciarli in ascolto: in /etc/dovecot/conf.d/10-master.conf metti port = 0:
service pop3-login {
inet_listener pop3 { port = 0 }
inet_listener pop3s { port = 0 }
}
E ManageSieve (porta 4190) di solito serve solo alla webmail sulla stessa macchina: conviene legarlo a localhost invece di esporlo su tutte le interfacce. In /etc/dovecot/conf.d/20-managesieve.conf:
service managesieve-login {
listen = 127.0.0.1, ::1 # solo locale (la webmail ci arriva da localhost)
inet_listener sieve {
port = 4190
}
inet_listener sieve_deprecated {
port = 0 # vecchia porta 2000: spenta
}
}
Ovviamente resta sempre valida la regola d'aver un firewall davanti che lascia aperte solo le porte che servono davvero, a scanso di equivoci...
Un'ultima accortezza: nascondere gli header X-Spam-* in uscita, così non sveliamo al mondo come è tarato il nostro antispam. In /etc/postfix/smtp_header_checks (IGNORE toglie l'header solo dalla posta inviata, lasciandolo su quella in arrivo):
/^X-Spam-Status:/ IGNORE
/^X-Spam-Checker-Version:/ IGNORE
postmap /etc/postfix/smtp_header_checks
systemctl reload postfix
Come ciliegina, Spamhaus offre un set di regole DQS (Data Query Service) gratuito per uso personale: bisogna registrarsi sul loro sito per ottenere una chiave, e in cambio si hanno blacklist molto più reattive di quelle pubbliche.
git clone https://github.com/spamhaus/spamassassin-dqs
cd spamassassin-dqs/4.0.0+
sh hbltest.sh # verifica che le query DNS verso Spamhaus funzionino
# Inserisce la propria chiave DQS al posto del placeholder
sed -i -e 's/your_DQS_key/chiave_generata_da_spamhaus/g' sh.cf
nano sh.pre
>> loadplugin Mail::SpamAssassin::Plugin::SH /etc/spamassassin/SH.pm
# Copia plugin, regole e punteggi tra le configurazioni di SpamAssassin
cp SH.pm /etc/spamassassin/
cp sh.cf /etc/spamassassin/
cp sh_scores.cf /etc/spamassassin/
cp sh.pre /etc/spamassassin/
spamassassin --lint
Attivare i Bayes
Una delle cose più efficaci di SpamAssassin è il filtro bayesiano: invece di regole fisse, impara statisticamente dai messaggi che gli dai in pasto e riconosce così anche lo spam "nuovo" (i phishing freschi che non sono ancora in nessuna blacklist). C'è un però: all'inizio è disabilitato. Finché non ha imparato almeno ~200 email di spam e ~200 email validei (bayes_min_spam_num/bayes_min_ham_num) non assegna alcun punteggio.
Il tranello del database condiviso. Qui si perde un pomeriggio se non lo si sa: spamd scansiona la posta girando come root (a livello di sistema), mentre l'addestramento (sa-learn, lanciato da voi o dalla webmail) gira con un altro utente. Di default ognuno usa il proprio database in ~/.spamassassin/bayes: risultato, addestri un DB e ne scansioni un altro, e il training non serve a niente. La soluzione mia è un unico DB condiviso su cui convergono tutti (mi fido dei miei utenti). In /etc/spamassassin/local.cf:
bayes_path /var/lib/spamassassin/bayes/bayes # DB in posizione fissa e condivisa
bayes_file_mode 0660 # leggibile/scrivibile dal gruppo
e si prepara la cartella con un gruppo comune (qui users) per chi scansiona e per chi addestra, con lo setgid così i file nuovi ereditano il gruppo giusto:
mkdir -p /var/lib/spamassassin/bayes
chgrp users /var/lib/spamassassin/bayes # gruppo condiviso: spamd + chi addestra
chmod 2770 /var/lib/spamassassin/bayes # il "2" e' setgid: i nuovi file ereditano il gruppo
Adesso lo si deve addestrare. Come? Dandogli in pasto lo spam dalla cartella Junk e la posta valida dalla Inbox. Più esempi si danno in pasto (e più sono significativi) meglio funziona:
# come root (o un utente del gruppo condiviso), così scrive nel DB condiviso
sa-learn --spam /home/*/Maildir/.Junk/{cur,new}
sa-learn --ham /home/*/Maildir/{cur,new}
# verifica: nspam e nham devono superare 200 perché il Bayes si attivi
sa-learn --dump magic
Da qui in poi il Bayes contribuisce con le regole BAYES_* (da BAYES_00, "quasi certamente buono" e con punteggio negativo, fino a BAYES_99, sicuramente spam).
L'opzione bayes_auto_learn (attiva di default) continua poi ad auto-allenarlo sui messaggi che gli altri controlli giudicano nettamente spam o ham.
Per allenarlo manualmente marcando "spam / non spam" dalla webmail vedremo markasjunk nella guida qui sotto di RoundCube.
le regole KAM
Le regole che arrivano con SpamAssassin sono poche e generiche. Si possono aggiungere canali di regole di terze parti, aggiornati via sa-update con firma GPG. Il più usato e ben mantenuto è KAM della McGrail Foundation: gratis, senza registrazione.
Per impostarle per prima cosa si scarica la chiave GPG del canale e si importa con sa-update:
wget https://mcgrail.com/downloads/kam.sa-channels.mcgrail.com.key
sa-update --import kam.sa-channels.mcgrail.com.key
sa-update --gpgkey 24C063D8 --channel kam.sa-channels.mcgrail.com
Su Debian l'aggiornamento automatico gira come debian-spamd con un keyring dedicato in /var/lib/spamassassin/sa-update-keys. Se importate la chiave come root finisce nel keyring sbagliato e il cron giornaliero fallirà. Meglio fare tutto in quel keyring condiviso, eseguendo come debian-spamd:
runuser -u debian-spamd -- sa-update --gpghomedir /var/lib/spamassassin/sa-update-keys --import kam.sa-channels.mcgrail.com.key
runuser -u debian-spamd -- sa-update --gpghomedir /var/lib/spamassassin/sa-update-keys --gpgkey 24C063D8 --channel kam.sa-channels.mcgrail.com
spamassassin --lint # controlla che tutte le regole (KAM incluse) siano valide
systemctl reload spamd
Il timer standard di Debian (spamassassin-maintenance.timer) lancia sa-update senza --channel, quindi aggiorna solo il canale di default: le regole KAM resterebbero non aggiornate. Serve un service+timer dedicato, creaundo un file /etc/systemd/system/sa-update-kam.service:
[Unit]
Description = Aggiorna il canale KAM di SpamAssassin (McGrail Foundation)
After = network-online.target
Wants = network-online.target
ConditionACPower = true
[Service]
Type = oneshot
# sa-update ritorna 0 = aggiornato, 1 = nessun update, >1 = errore.
# Solo su 0 rifacciamo lint e ricarichiamo spamd; su 1 usciamo puliti.
ExecStart = /bin/sh -c 'runuser -u debian-spamd -- sa-update --gpghomedir /var/lib/spamassassin/sa-update-keys --gpgkey 24C063D8 --channel kam.sa-channels.mcgrail.com; rc=$?; if [ "$rc" -eq 0 ]; then runuser -u debian-spamd -- spamassassin --lint && systemctl reload spamd; elif [ "$rc" -eq 1 ]; then exit 0; else exit "$rc"; fi'
E in /etc/systemd/system/sa-update-kam.timer:
[Unit]
Description = Aggiornamento giornaliero del canale KAM di SpamAssassin
[Timer]
OnCalendar = daily
RandomizedDelaySec = 1h
Persistent = true
[Install]
WantedBy = timers.target
systemctl daemon-reload
systemctl enable --now sa-update-kam.timer
systemctl start sa-update-kam.service # test: deve dare Result=success
KAM può essere un filo aggressivo: se una singola regola vi dà falsi positivi, non serve toccare il canale, basta abbassarne il punteggio come visto sopra in /etc/spamassassin/local.cf con score KAM_NOME_REGOLA <valore>.
Server DNS
Se riceverete errori di URIBL_BLOCKED e RCVD_IN_DNSWL_BLOCKED in spamassassin vuol dire che state utilizzanod DNS pubblici, che vengono bloccati per evitare utilizzi massicci da parte degli utenti.
Per ovviare al problema è succiciente installare un server DNS locale come unbound. Ciò rallenta un po' le richieste dns ma si guadagna aanche in privacy :)
aptitude install unbound
# Attivazione servizio
systemctl enable --now unbound
# Modificarei dns
nano /etc/systemd/resolved.conf
# aggiungere/modificare
# DNS=127.0.0.1
# e anche in opedkim (opzionale, solo per privacy)
nano /etc/opendkim.conf
# aggiungere/modificare
# nameservers 127.0.0.1
# Ricaricare il gestore DNS:
systemctl restart systemd-resolved
Un errore frequente: impostare DNS=127.0.0.1 in resolved.conf non basta se la scheda di rete continua a impostare dei DNS come fallback (spesso ce li mette netplan/cloud-init o il DHCP). Con systemd-resolved quei resolver restano in lista e se una richiesta passa a quelli, Spamhaus & co. tornano a dare errore (URIBL_BLOCKED). I DNS vanno tolti alla fonte, ovvero nel netplan si lascia come unico nameserver dell'interfaccia il resolver locale:
# /etc/netplan/*.yaml - dentro la sezione dell'interfaccia
nameservers:
addresses:
- 127.0.0.1
E si impedisce a cloud-init di rigenerare il file al reboot (reinserendo i DNS del provider):
echo 'network: {config: disabled}' > /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg
netplan apply
Per controllare: resolvectl status deve mostrare solo 127.0.0.1 come DNS. Da qui ogni query passa esclusivamente per unbound, che è ricorsivo e non dipende da nessun dns esterno.
Testare il tutto
Per testare il tutto accedete con quello che volete alla vostra casella di posta (thunderbird, app sul telefono, RoundCube, ecc) ed inviate una email all'indirizzo che vi fornisce mail-tester.com. Vi restituirà un punteggio da 1 a 10 con il dettaglio di cosa va e cosa no: SPF, DKIM, DMARC, reverse DNS, presenza in blacklist e via dicendo. L'obiettivo è il fatidico 10/10, ma anche un onesto 9 fa arrivare la posta a destinazione.
Qualche altra verifica utile:
- Spedite una mail verso una casella Gmail, apritela e dal menu "Mostra originale" controllate che SPF, DKIM e DMARC risultino tutti
PASS: è il giudice più severo che abbiate a disposizione. - Tenete d'occhio i log mentre provate, sono la prima cosa che vi dirà cosa c'è non va:
journalctl -u postfix -u dovecot -f(oppuretail -f /var/log/mail.log). - Per i record DNS pubblici (MX, SPF, DKIM, DMARC, PTR) un giro su https://mxtoolbox.com/ vi dice al volo se manca o è sbagliato qualcosa.
Se il primo invio finisce in spam non disperate: spesso è solo questione di "reputazione" del vostro ip, che migliora col tempo mandando posta legittima. E mi raccomando... non madare email con solo "ok" o poche parole... Sicuramente vanno in spam. Trucco utile: Aggiungete una firma automatica ai messaggi di uscita con qualche dato utile, non è necessaria, non aiuta molto, ma male non fa.
Extra: fail2ban
Fail2Ban guarda i log dei servizi e, quando vede troppi tentativi falliti dallo stesso ip (login SSH sbagliati, bot che cercano pagine inesistenti, ecc.), lo banna a livello di firewall per un po'. Da solo non ferma uno spammer determinato, ma toglie di mezzo il rumore di fondo dei bot, che è già tantissimo. Così poi è possibile visualizzare i log con effettivamente quello che serve per migliorare i filtri
aptitude install fail2ban
nano /etc/fail2ban/jail.d/chalda.conf
Ogni [sezione] è una "jail": un servizio da sorvegliare con le sue soglie. I valori chiave sono sempre gli stessi: maxretry quanti errori tollerare, findtime in quanto tempo devono accadere perché scatti il ban, bantime quanto dura il ban. La sezione [DEFAULT] fissa i valori di default (ma va) per tutte le jail (chee ovviamnete si possono sovrassrivere)
[DEFAULT]
ignoreip = 127.0.0.1/8 dominio.com # ip da non bannare mai (te stesso!)
bantime = 5d # ban di 5 giorni di default
findtime = 1d # finestra entro cui contare i tentativi
maxretry = 3 # 3 errori e sei fuori
# La jail "recidive" banna più a lungo chi è già finito in una jail più volte:
[recidive]
enabled = true
bantime = 4w # un mese di ban per i recidivi
findtime = 2w # se colleziona più ban in 2 settimane
[sshd]
enabled = true
port = XX # la porta SSH (di default 22, cambiatela!)
maxretry = 2
findtime = 7d
# Chi prova a loggarsi con utenti inesistenti: tolleranza zero, basta 1 colpo
[sshd-invaliduser]
enabled = true
maxretry = 1
port = XX # Inserire la porta ssh (di default 22, cambiala!)
logpath = %(sshd_log)s # path del log SSH, preso dai default distro
backend = %(sshd_backend)s
bantime = 5d
# Tentativi di login falliti sulla webmail RoundCube
[roundcube-auth]
enabled = true
logpath = /var/dir_di_roundcube/public/logs/errors.log
maxretry = 5
# Abusi su Postfix: protocollo SMTP malformato, relay tentati, ecc.
[postfix]
enabled = true
port = smtp,465,submission
# Login SMTP (SASL) sbagliati: chi prova a indovinare le password di invio
[postfix-sasl]
enabled = true
port = smtp,465,submission
# Login IMAP/POP3 falliti su Dovecot
[dovecot]
enabled = true
port = pop3,pop3s,imap,imaps,submission,465,sieve
Extra: RoudCube
Per vedere la propria posta online io installo RoundCuhe, ha tutto ciò che occorre e sinceramente è abbastanza fatto bene.
Per installarlo basta scaricare il pacchetto da roundcube.net ed estraetelo in una cartella:
version=1.7.2
wget -q --show-progress -O "roundcubemail-${version}-complete.tar.gz" "https://github.com/roundcube/roundcubemail/releases/download/$version/roundcubemail-$version-complete.tar.gz"
tar -xf "roundcubemail-${version}-complete.tar.gz"
Ora c'è una cartella roundcubemail-1.7.x/. Per installarlo (ed eventualmente aggiornarlo) basta eseguire lo script bin/installTo.sh, specificando tutti i dati necessari:
Poi la configurazione vera e propria sta in config/config.inc.php, le voci essenziali:
class="c_keyword"><?php
// IMAP e SMTP: puntano al NOSTRO server di posta, con TLS
$config['imap_host'] = 'ssl://mail.dominio.com:993'; // TLS implicito su 993
$config['smtp_host'] = 'tls://mail.dominio.com:587'; // submission con STARTTLS
// Database. Attenzione: i caratteri speciali della password vanno URL-encoded
// nella stringa DSN (es. '$' -> %24, spazio -> %20, '@' -> %40)
$config['db_dsnw'] = 'mysql://roundcube:password_segreta@localhost/roundcubemail';
// Chiave per cifrare la password IMAP in sessione: 32 caratteri casuali.
// L'installer ne genera una; se la scrivi a mano, CAMBIALA (non lasciare questa)
$config['des_key'] = 'metti_qui_32_caratteri_casuali__';
$config['product_name'] = 'Webmail';
// Plugin attivi (nella cartella plugins/)
$config['plugins'] = [
'managesieve', // filtri/regole sieve dall'interfaccia (sposta lo spam, ecc.)
'password', // cambio password dalla webmail
'markasjunk', // pulsante "spam / non spam": ri-addestra il Bayes (vedi bayes sorpra)
'zipdownload', // scarica più allegati in un unico zip
'contextmenu', // opzioni sulla posta con il tasto destro
'show_additional_headers',
];
Da Roundcube 1.7 in poi il document root del web server va puntato alla sottocartella public_html/ (non più alla radice): quindi il virtualhost Apache/Nginx deve servire .../roundcubemail-1.7.x/public_html/.
Commenti
Nessun commento ancora. Sii il primo!
Lascia un commento