Table of Contents

Dspam

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:

Installazione su Debian Lenny

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/.

Configurazione

/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"

Test

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.

Storage

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.

Demone

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.

Quarantena

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.

Debug

Se si avvia dspam con l'opzione --debug, viene creato il file /var/log/dspam/dspam.debug, oltre al normale dspam.messages.

Impostazioni system-wide

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"

Statistiche

Il superutente può chiedere le statistiche sul filtraggio dei messsaggi con il comando:

dspam_stats -H

Integrazione con Exim4

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

Alternativa procmail

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/