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
# 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
# Livello di log del TLS (1 = essenziale, utile per debug dei certificati)
smtpd_tls_loglevel = 1
# Restrizioni applicate sul destinatario: vengono valutate in ordine,
# la prima che decide "vince". L'idea è scartare presto le mail palesemente
# sbagliate e poi lasciar passare solo chi è almeno autenticato
smtpd_recipient_restrictions =
reject_unknown_recipient_domain, # dominio destinatario inesistente
reject_unauth_pipelining, # client che "spara" comandi senza aspettare
reject_invalid_hostname, # HELO/EHLO con hostname malformato
permit_mynetworks, # ok se proviene dalle reti fidate
permit_sasl_authenticated, # ok se l'utente si è autenticato
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
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
smtp inet n - - - - smtpd
# 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
# 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_sasl_type = dovecot
-o smtpd_sasl_path = private/auth
# 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 tutto su Gmail 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@gmail.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
Dovecot
Dovecot è il programma che consentee 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
Modifificare 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 le direttive si chiamano ssl_cert e ssl_key, con la < davanti al percorso che dice a Dovecot di leggere il file:
ssl_cert = </path/chiave_dominio.fullchain.pem # certificato + catena
ssl_key = </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
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
nano
Modificare il file /etc/opendmarc/ignore.hosts {^Sel la cartella /etc/opendmarc non esiste, createla} inserendo localhost e ip fidati: la posta che parte da qui non va filtrata.
127.0.0.1
::1
123.123.123.123
1234:1234:1234:1234::1
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 rivalutato a ogni richiesta
KeyTable refile:/etc/opendkim/KeyTable
SigningTable refile:/etc/opendkim/SigningTable
# Host esterni di cui fidarsi senza verificarne la firma
ExternalIgnoreList /etc/opendkim/trusted.hosts
# 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). 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 è raro, opendkim-genkey genera già il record spezzato nel formato corretto:
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 con il record DNS da copiare: va pubblicato come TXT 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:
ndkim-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 conf, così il demone parte ascoltando dove Postfix si aspetta di trovarlo:
SOCKET="local:/var/spool/postfix/opendkim/opendkim.sock"
E 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 due opzioni utili:
# -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
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
Sempre nello stesso file, una whitelist e una blacklist "manuali": la prima mette al riparo i propri domini dai falsi positivi, la seconda fulmina mittenti che non si vogliono proprio vedere. Attenzione che whitelist_from si basa sul From, quindi è falsificabile: utile per i propri domini, non per fidarsi di sconosciuti.
whitelist_from *@dominio.com
whitelist_from *@altrodominio.com
blacklist_from spam@example.com
blacklist_from *@example.org
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
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
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 = 2 # 2 errori e sei fuori
[sshd]
enabled = true
port = 12345 # 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 = 12345 # 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
# Bot che scandagliano Apache in cerca di pannelli/exploit
[apache-botsearch]
enabled = true
port = http,https
maxretry = 2
logpath = /var/log/apache*/*error.log
/var/www/*/logs/error.log
# Richieste a script/file inesistenti (tipico scanning automatizzato)
[apache-noscript]
enabled = true
bantime = 1h
findtime = 1h
maxretry = 3
logpath = /var/www/*/logs/error.log
/var/log/apache*/*error.log
# Tentativi di login falliti sulla webmail RoundCube
[roundcube-auth]
enabled = true
logpath = /var/www/domino.com/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 installare e aggiornare RoundCube io utilizzo questa piccolp trucchetto, tengo separate le cartelle config, plugins, skins e le inserisco nella cartella di RoundCube come link simbolici. Poi in apache creo un virtualhost che punta alla cartella public che in realtà è anchessa un link simbolico alla cartella dell'ultima vesione di RoundCube:
<VirtualHost *:443>
ServerName mail.dominio.com
# DocumentRoot punta al symlink "public": così cambiando versione basta
# rifare il link, senza toccare la configurazione di Apache
DocumentRoot /var/www/mail.dominio.com/public
<Directory /var/www/mail.dominio.com/public>
Options -Indexes +FollowSymLinks # serve a far seguire i symlink
AllowOverride All # lascia agire il .htaccess di RoundCube
Require all granted
</Directory>
# Certificato (lo stesso usato da Postfix/Dovecot)
SSLEngine on
SSLCertificateFile /path/chiave_dominio.fullchain.pem
SSLCertificateKeyFile /path/chiave_dominio.privkey.pem
ErrorLog /var/www/mail.dominio.com/logs/error.log
CustomLog /var/www/mail.dominio.com/logs/access.log combined
</VirtualHost>
In questo modo ad ogni aggiornamento non devo far altro che scaricare la nuova versione in una nuova cartella, aggiungere i link simbolici alle 3 cartelle config, plugins e skins, e poi ricreare il link simbolico a public. Così son certo che le mie impostazioni e plugin non vengono mai sostituiti e soprattutto in caso di errore è facile fare il rollback.
Esempio di come si presenta la cartella:
.
├── config
├── public -> roundcubemail-1.7.1/public_html
├── plugins
├── roundcubemail-1.7.1
│ ├── vendor
│ ├── temp
│ ├── SQL
│ ├── skins -> ../skins
│ ├── public_html
│ ├── program
│ ├── plugins -> ../plugins
│ ├── logs
│ ├── installer
│ ├── docs
│ ├── config -> ../config
├── roundcubemail-1.6.7
│ ├── vendor
│ ├── temp
│ ├── SQL
│ ├── skins -> ../skins
│ ├── public_html
│ ├── program
│ ├── plugins -> ../plugins
│ ├── logs
│ ├── installer
│ ├── docs
│ ├── config -> ../config
├── skins
└── update-roundcube.sh
Per fare tutto ciò in automatico ho creato un file update-roundcube.sh che automatizza il tutto:
#!/bin/bash
set -euo pipefail
DIR="/var/www/mail.dominio.com"
PUBLIC="${DIR}/roundcube"
# 1. Trova l'ultima versione disponibile
echo "Ricerca ultima versione di Roundcube..."
LATEST_TAG=$(curl -s https://api.github.com/repos/roundcube/roundcubemail/releases/latest \
| grep '"tag_name"' \
| sed 's/.*"tag_name": *"\([^"]*\)".*/\1/')
if [ -z "${LATEST_TAG}" ]; then
echo "Errore: impossibile ottenere la versione più recente." >&2
exit 1
fi
VERSION="${LATEST_TAG#v}"
TARBALL="roundcubemail-${VERSION}-complete.tar.gz"
EXTRACT_DIR="${DIR}/roundcubemail-${VERSION}"
DOWNLOAD_URL="https://github.com/roundcube/roundcubemail/releases/download/${LATEST_TAG}/${TARBALL}"
echo "Ultima versione: ${LATEST_TAG}"
# Controlla se la versione è già installata
if [ -d "${EXTRACT_DIR}" ]; then
echo "La versione ${VERSION} è già installata in ${EXTRACT_DIR}."
read -rp "Vuoi reinstallarla comunque? [s/N] " RISPOSTA
case "${RISPOSTA}" in
[sS]) rm -rf "${EXTRACT_DIR}" ;;
*) echo "Aggiornamento annullato."; exit 0 ;;
esac
fi
# Controlla che le directory persistenti esistano (config, plugins, skins)
for PERSISTENT_DIR in config plugins skins; do
if [ ! -d "${DIR}/${PERSISTENT_DIR}" ]; then
echo "Errore: ${DIR}/${PERSISTENT_DIR} non trovato." >&2
echo "Crea le directory persistenti (config, plugins, skins) prima di procedere." >&2
exit 1
fi
done
# 2. Scarica il tarball in DIR
echo "Download di ${TARBALL}..."
wget -q --show-progress -O "${DIR}/${TARBALL}" "${DOWNLOAD_URL}"
# 3. Estrai il tarball in DIR (crea automaticamente roundcubemail-X.Y.Z/)
echo "Estrazione in ${EXTRACT_DIR}..."
tar -xzf "${DIR}/${TARBALL}" -C "${DIR}"
rm -f "${DIR}/${TARBALL}"
# 4. Symlink delle directory persistenti nella directory appena estratta
# (config, plugins e skins non devono essere sovrascritti ad ogni aggiornamento)
for PERSISTENT_DIR in config plugins skins; do
rm -rf "${EXTRACT_DIR}/${PERSISTENT_DIR}"
ln -sf "../${PERSISTENT_DIR}" "${EXTRACT_DIR}/${PERSISTENT_DIR}"
echo "Symlink ${PERSISTENT_DIR} -> ../${PERSISTENT_DIR} creato."
done
# 5. Fix permessi
chown -R www-data:www-data "${EXTRACT_DIR}"
# 6. Migrazione DB
echo "Migrazione database..."
cd "${EXTRACT_DIR}"
php bin/update.sh --version="${VERSION}"
# 7. Determina il document root
# Dalla versione 1.7 il webserver deve puntare a public_html/
if [ -d "${EXTRACT_DIR}/public_html" ]; then
WEBROOT="${EXTRACT_DIR}/public_html"
echo "Struttura 1.7+: document root impostato su public_html/"
else
WEBROOT="${EXTRACT_DIR}"
fi
# 8. Rimuovi il symlink htdocs precedente
if [ -L "${PUBLIC}" ]; then
rm -f "${PUBLIC}"
fi
# 9. Crea il nuovo symlink htdocs -> webroot della nuova versione
ln -s "${WEBROOT}" "${PUBLIC}"
echo "Symlink htdocs -> ${WEBROOT} creato."
echo ""
echo "Aggiornamento completato: Roundcube ${LATEST_TAG}"
Testare il tutto
Per testare il tutto loggatevi nella vostro RoundCube con utente e password ed inviate una email all'indirizzo che vi fornisce mail-tester.com. Vi restituirà un punteggio su 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 vostra 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 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
Commenti
Nessun commento ancora. Sii il primo!
Lascia un commento