Perché usare dspam
invece di SpamAssassin?
Il problema è che SpamAssassin impiega molto tempo per analizzare ciascun messaggio di mail. Ad esempio su un Pentium4 3.2 GHz con 2Gb RAM impiega in media 3.43 secondi per messaggio su un campione di 1116 messaggi ricevuti nell'arco di 24 ore.
Questo significa che nei momenti di burst si incappa facilmente nei limiti del MTA, ad esempio Exim smette temporaneamente di ricevere con l'errore SMTP:
421 Too many concurrent SMTP connections: please try again later.
oppure SpamAssassin non riesce ad effettuare il lock su un file utente e genera l'errore:
spamd[1259]: bayes: cannot open bayes databases /home/.spamassassin/bayes_* R/W: lock failed: Interrupted system call
oppure ancora si incappa nell'errore
spamd[7122]: prefork: server reached --max-children setting, consider raising it
Da approfondire:
greylistd-setup-exim4
Installati i pacchetti
Per far funzionare il frontend web ci vuole l'Apache suexec perché lo script CGI deve essere esguito a nome dell'utente dspam
. Il modulo Apache PAM serve a fare l'autenticazione web, necessaria per ovvi motivi di sicurezza e per fornire il nome utente all'interfaccia web.
Il pacchetto libapache2-mod-auth-pam richiede che www-data
appartenga al gruppo shadow
, in alternativa valutare il pacchetto libapache2-mod-auth-shadow (che non c'è in Lenny, ma è facile farne il backport).
Debian mette il front-end web all'indirizzo http://<host>/dspam/
.
/etc/dspam/dspam.conf
# DSPAM storage. Home /var/spool/dspam StorageDriver /usr/lib/dspam/libhash_drv.so # Exim4 integration. TrustedDeliveryAgent "/usr/sbin/exim4 -oi" # Default filtering: active (user can opt-out). Opt out # Daemon configuration. ServerPort 10024 ServerPass.Relay1 "8d0b79d1f6a3" # Client configuration. ClientHost 127.0.0.1 ClientPort 10024 ClientIdent "8d0b79d1f6a3@Relay1"
Per testare il funzionamento del sistema dspam
senza passare dal MTA si può usare un comando del genere:
cat spam_message.txt \ | /usr/bin/dspam --deliver=innocent,spam --user niccolo --stdout
Il messaggio filtrato viene mostrato a video, indipendentemente dal fatto che sia identificato come spam o meno. L'operazione viene conteggiata nelle statistiche, compare nella history dell'interfaccia web, interessa la quarantena in caso di --deliver=innocent
(vedi sotto), ecc.
Per default viene usato Hash-Based Driver, una buona alternativa è PostgreSQL. Con l'Hash-Based Driver vengono creati i file con le statistiche per ogni utente in una struttura di directory /var/spool/dspam/data/local/<login>/
.
Se lo storage è un PostgreSQL c'è il vantaggio che l'alias per l'invio di spam/nospam può essere unico per tutto il dominio di posta, invece di averne uno per ogni utente.
In condizioni normali viene invocata un'istanza di dspam
per ogni messaggio da filtrare. In alternativa può risultare conveniente attivare una sola istanza daemon di dspam
ed eseguire il filtro con l'opzione --client
.
Volendo far girare il demone senza i privilegi di superutente bisogna scegliere una porta > 1024, impostando il parametro ServerPort
(quella predefinita è la TCP 24). Per consentire ai client di passare al daemon i parametri del comando (viene usato il protocollo proprietario DLMTP) è necessaria una autenticazione client/server, il modo più semplice è impostare il parametro ServerPass.<relay>
.
Infine si imposta START=yes
in /etc/default/dspam
e si avvia il demone con /etc/init.d/dspam start
.
Ogni invocazione di dspam
dovrà includere l'opzione --client
e dovrà trovare nel file di configurazione /etc/dspam/dspam.conf
i tre parametri ClientHost, ClientPort e ClientIdent. In caso contrario dspam
verrà eseguito silenziosamente in modalità stand-alone. Per il massimo dell'efficienza esiste anche il thin-client dspamc
, che include nell'eseguibile solo le funzioni di client.
L'azione predefinita di dspam
- quando identifica un messaggio di spam - è metterlo in quarantena, cioè memorizzarlo nel suo storage. Tramite l'interfaccia web l'utente può vedere i messaggi in quarantena, cancellarli oppure farli recapitare normalmente.
Se l'utente decide di recapitare normalmente un messaggio presente in quarantena, questo viene considerato non-spam e va ad istruire il filtro Bayesiano opportunamente. Nella history verrà evidenziato come Retrained.
Se dspam
è configurato come filtro nel MTA e viene invocato con l'opzione --deliver=innocent,spam
, il messaggio di spam viene recapitato al destinatario senza essere messo nella quarantena. Viene tuttavia aggiunto l'header X-DSPAM-Result: Spam
.
Se si avvia dspam
con l'opzione --debug
, viene creato il file /var/log/dspam/dspam.debug
, oltre al normale dspam.messages
.
Le impostazioni utente sono modificabili dalla scheda Preferences dell'interfaccia web. L'amministratore può impostare i valori predefiniti modificando le Preference in /etc/dspam/dspam.conf
:
Preference "spamAction=tag" Preference "signatureLocation=headers" Preference "showFactors=on" Preference "spamSubject=SPAM"
Il superutente può chiedere le statistiche sul filtraggio dei messsaggi con il comando:
dspam_stats -H
Configurare TrustedDeliveryAgent
in /etc/dspam/dspam.conf
:
TrustedDeliveryAgent "/usr/sbin/exim4 -oi"
Aggiungere i seguenti file alla configurazione di Exim4:
/etc/exim4/conf.d/main/00_local
# Allow dspam user to re-enqueue mails setting the protocol to # spam-scanned (-oMr option) and setting the sender (-f option). # Setting the sender can be granted also with untrusted_set_sender. MAIN_TRUSTED_USERS = dspam
/etc/exim4/conf.d/router/550_local_dspam
Oltre al router che instrada i messaggi al filtro dspam, vengono definiti due alias per ogni utente: login-spam
e login-nospam
a cui l'utente invierà i falsi negativi e i falsi positivi rispettivamente.
Fare attenzione che questo non funziona in caso di alias diversi dal login (usati in genere con i domini virtuali), cioè non funziona niccolo.rigacci-spam@example.org
, ma funziona solo niccolo-spam@example.org
.
### router/550_local_dspam ################################# # Route non-local mail to the dspam filter. # Dspam will re-queue filtered messages to Exim using the # "spam-scanned" protocol: we avoid filtering them twice. dspam_route_check: no_verify check_local_user condition = "${if \ and {{!eq {$received_protocol}{spam-scanned}} \ {!eq {$received_protocol}{local}} } \ {1}{0}}" driver = accept transport = dspam_check headers_add = "X-My-Dspam: scanned by $primary_hostname, $tod_full" require_files = /var/spool/dspam:+/usr/bin/dspam # Feed false negatives and false positives forwarded by # the users to dspam, for learning. dspam_route_spam: no_verify check_local_user driver = accept local_part_suffix = -spam transport = dspam_class_spam dspam_route_innocent: no_verify check_local_user driver = accept local_part_suffix = -nospam transport = dspam_class_innocent
/etc/exim4/conf.d/transport/40_local_dspam
Il transport di Exim4 è configurato come driver = pipe
, cioè il messaggio viene passato via standard input al comando dspam
, che viene eseguito con lo UID e GID specificati (dspam:dspam
). Con questa configurazione dspam
passa la mail filtrata nuovamente ad Exim, quindi non ha bisogno dei privilegi di superutente.
### transport/40_local_dspam ################################# # Filter the message with dspam and feed the result to its TrustedDeliveryAgent. # That will be Exim again, with pass-through arguments -f, -oMr and recipient. dspam_check: driver = pipe command = "/usr/bin/dspam --deliver=innocent --user ${lc:$local_part} -f '$sender_address' -oMr spam-scanned -- %u" user = dspam group = dspam return_path_add = false # Write the first line of command output to the Exim log. log_output = true # If command fails, return the command output in the bounce. return_fail_output = true message_prefix = "" message_suffix = "" # Send false negatives and false positives to dspam for learning. dspam_class_spam: driver = pipe command = "/usr/bin/dspam --debug --source=error --class=spam --user ${lc:$local_part}" user = dspam group = dspam return_path_add = false log_output = true return_fail_output = true home_directory = "/var/spool/dspam" current_directory = "/var/spool/dspam" message_prefix = "" message_suffix = "" dspam_class_innocent: driver = pipe command = "/usr/bin/dspam --debug --source=error --class=innocent --user ${lc:$local_part}" return_path_add = false return_fail_output = true log_output = true home_directory = "/var/spool/dspam" current_directory = "/var/spool/dspam" user = dspam group = dspam message_prefix = "" message_suffix = ""
Con la configurazione vista sopra ciascun messaggio passa per due volte nella mail queue: prima e dopo essere stato filtrato da dspam
. Questo consente di processare ulteriormente il messaggio filtrato con gli strumenti di Exim: instradamento verso altri host, espansione del destinatario tramite alias, applicazione di ACL, ecc.
In alternativa si può utilizzare dspam
come filtro di procmail
. In tal caso dspam può essere attivato sulla singola mailbox, può essere messo in serie ad altri filtri per il dirottamento dello spam su apposita maildir oppure per il controllo antivirus, ecc.
Uno svantaggio è che questo metodo può essere usato solo per utenti di sistema locali, non funziona quindi con sistemi di virtualizzazione delle mailbox oppure se la mailbox risiede su altro host. Inoltre con il filtraggio tramite procmail
non si hanno a disposizione gli alias di posta a cui inoltrare i messaggi non correttamente classificati, funziona solo l'interfaccia web.
Ecco un esempio di file $HOME/.procmailrc
che lavora su Maildir, filtra i messaggi e salva lo spam in apposita cartella:
#------------------------------------------------------------------------- # $HOME/.procmailrc #------------------------------------------------------------------------- PATH=/usr/local/bin:/usr/bin:/bin SHELL=/bin/bash UMASK=007 # Use user's Maildir instead of /var/mail/. MAILDIR=$HOME/Maildir/ ORGMAIL=$MAILDIR DEFAULT=$MAILDIR #LOGFILE=$HOME/procmail.log #VERBOSE=yes #------------------------------------------------------------------------- # Filter messages (below 512 Kb) with dspam. #------------------------------------------------------------------------- :0 fw * < 524288 | /usr/bin/dspam --debug --user niccolo --deliver=innocent,spam --stdout #------------------------------------------------------------------------- # Spam messages marked by dspam. #------------------------------------------------------------------------- :0 : * ^X-DSPAM-Result: spam .Spam/