Zum Inhalt

SpamAssassin

Inhalt

  • SpamAssassin 4.0.2
  • SpamAss-Milter 0.4.0
  • spamd als Dienst
  • PostgreSQL für Bayes und TxRep
  • Regelupdates über sa-update
  • kompilierte Regeln über sa-compile

Einleitung

Dieses HowTo beschreibt die Installation und Konfiguration von SpamAssassin auf FreeBSD 15+ für ein Mailsetup mit Postfix.

Die aktuelle FreeBSD-Portbasis ist mail/spamassassin 4.0.2. Der Port installiert unter anderem spamd, spamc, sa-update, sa-compile sowie die Sample-Dateien local.cf.sample und init.pre.sample. Für die Anbindung an Postfix wird in diesem HowTo zusätzlich mail/spamass-milter 0.4.0 verwendet. (FreshPorts)

Dieses Setup verwendet PostgreSQL für die Bayes-Datenbank und für TxRep. Für PostgreSQL ist bei Bayes nicht das generische SQL-Backend die richtige Wahl, sondern ausdrücklich Mail::SpamAssassin::BayesStore::PgSQL. Für TxRep erwartet SpamAssassin standardmäßig eine Tabelle mit dem Namen txrep. (spamassassin.apache.org)


Voraussetzungen

Zu den Voraussetzungen für dieses HowTo siehe bitte: Hosting System

Zusätzlich gilt für dieses HowTo:

  • PostgreSQL ist bereits installiert und erreichbar.
  • Postfix ist bereits installiert.
  • Für die spätere Milter-Anbindung ist Postfix bereits für Milter vorbereitet.
  • Für dieses HowTo wird PostgreSQL für Bayes und TxRep verwendet.
  • Dieses HowTo richtet keinen Content-Filter-Proxydienst ein, sondern spamd plus spamass-milter.

Vorbereitungen

DNS Records

Für dieses HowTo sind keine zusätzlichen DNS-Records erforderlich.

Gruppen / Benutzer / Passwörter

Für dieses HowTo müssen keine zusätzlichen Systemgruppen oder Systembenutzer manuell angelegt werden.

Der Port bringt den Systembenutzer spamd selbst mit; im Paketinhalt ist außerdem das Laufzeitverzeichnis /var/run/spamd bereits mit Eigentümer spamd:spamd vorgesehen. (GitHub)

Für dieses HowTo muss zuvor folgendes Passwort angelegt werden, sofern es noch nicht existiert, oder entsprechend geändert werden, sofern es bereits existiert.

Bash

Verzeichnisse / Dateien

Für dieses HowTo müssen zuvor folgende Verzeichnisse angelegt werden, sofern sie noch nicht existieren, oder entsprechend geändert werden, sofern sie bereits existieren.

Bash


Installation

Wir installieren mail/spamassassin und dessen Abhängigkeiten.

Für PostgreSQL-Bayes ist die Portoption PGSQL relevant. Der aktuelle Port bietet außerdem unter anderem die Optionen SPF_QUERY, DKIM, DMARC und GNUPG2. (FreshPorts)

Bash
mkdir -p /var/db/ports/databases_p5-DBD-SQLite
cat <<'EOF' > /var/db/ports/databases_p5-DBD-SQLite/options
_OPTIONS_READ=p5-DBD-SQLite-1.76
_FILE_COMPLETE_OPTIONS_LIST=BUNDLED_SQLITE
OPTIONS_FILE_UNSET+=BUNDLED_SQLITE

EOF

mkdir -p /var/db/ports/databases_p5-DBIx-Simple
cat <<'EOF' > /var/db/ports/databases_p5-DBIx-Simple/options
_OPTIONS_READ=p5-DBIx-Simple-1.37
_FILE_COMPLETE_OPTIONS_LIST=DBIX_XHTML_TABLE SQL_ABSTRACT SQL_INTERP TEXT_TABLE
OPTIONS_FILE_UNSET+=DBIX_XHTML_TABLE
OPTIONS_FILE_UNSET+=SQL_ABSTRACT
OPTIONS_FILE_UNSET+=SQL_INTERP
OPTIONS_FILE_UNSET+=TEXT_TABLE

EOF

mkdir -p /var/db/ports/dns_p5-Net-DNS
cat <<'EOF' > /var/db/ports/dns_p5-Net-DNS/options
_OPTIONS_READ=p5-Net-DNS-1.53
_FILE_COMPLETE_OPTIONS_LIST=IDN IDN2 IPV6 SSHFP TSIG
OPTIONS_FILE_SET+=IDN
OPTIONS_FILE_SET+=IDN2
OPTIONS_FILE_SET+=IPV6
OPTIONS_FILE_UNSET+=SSHFP
OPTIONS_FILE_SET+=TSIG

EOF

mkdir -p /var/db/ports/dns_libidn
cat <<'EOF' > /var/db/ports/dns_libidn/options
_OPTIONS_READ=libidn-1.43
_FILE_COMPLETE_OPTIONS_LIST=DOCS NLS
OPTIONS_FILE_UNSET+=DOCS
OPTIONS_FILE_SET+=NLS

EOF

mkdir -p /var/db/ports/devel_p5-Parse-RecDescent
cat <<'EOF' > /var/db/ports/devel_p5-Parse-RecDescent/options
_OPTIONS_READ=p5-Parse-RecDescent-1.967015
_FILE_COMPLETE_OPTIONS_LIST=DOCS
OPTIONS_FILE_UNSET+=DOCS

EOF

mkdir -p /var/db/ports/net_p5-Net-Server
cat <<'EOF' > /var/db/ports/net_p5-Net-Server/options
_OPTIONS_READ=p5-Net-Server-2.014
_FILE_COMPLETE_OPTIONS_LIST=IPV6
OPTIONS_FILE_SET+=IPV6

EOF

mkdir -p /var/db/ports/devel_p5-Test-NoWarnings
cat <<'EOF' > /var/db/ports/devel_p5-Test-NoWarnings/options
_OPTIONS_READ=p5-Test-NoWarnings-1.06
_FILE_COMPLETE_OPTIONS_LIST=DEVEL_STACKTRACE
OPTIONS_FILE_UNSET+=DEVEL_STACKTRACE

EOF

mkdir -p /var/db/ports/www_p5-CGI
cat <<'EOF' > /var/db/ports/www_p5-CGI/options
_OPTIONS_READ=p5-CGI-4.71
_FILE_COMPLETE_OPTIONS_LIST=EXAMPLES
OPTIONS_FILE_UNSET+=EXAMPLES

EOF

mkdir -p /var/db/ports/www_p5-HTTP-Tiny
cat <<'EOF' > /var/db/ports/www_p5-HTTP-Tiny/options
_OPTIONS_READ=p5-HTTP-Tiny-0.090
_FILE_COMPLETE_OPTIONS_LIST=CERTS COOKIE HTTPS IO_SOCKET_IP
OPTIONS_FILE_SET+=CERTS
OPTIONS_FILE_SET+=COOKIE
OPTIONS_FILE_SET+=HTTPS
OPTIONS_FILE_SET+=IO_SOCKET_IP

EOF

mkdir -p /var/db/ports/mail_spamassassin
cat <<'EOF' > /var/db/ports/mail_spamassassin/options
_OPTIONS_READ=spamassassin-4.0.2
_FILE_COMPLETE_OPTIONS_LIST=AS_ROOT DOCS SSL GNUPG_NONE GNUPG GNUPG2 MYSQL PGSQL DCC DKIM DMARC PYZOR RAZOR RELAY_COUNTRY RLIMIT SPF_QUERY
OPTIONS_FILE_SET+=AS_ROOT
OPTIONS_FILE_UNSET+=DOCS
OPTIONS_FILE_SET+=SSL
OPTIONS_FILE_UNSET+=GNUPG_NONE
OPTIONS_FILE_UNSET+=GNUPG
OPTIONS_FILE_SET+=GNUPG2
OPTIONS_FILE_SET+=MYSQL
OPTIONS_FILE_SET+=PGSQL
OPTIONS_FILE_UNSET+=DCC
OPTIONS_FILE_SET+=DKIM
OPTIONS_FILE_SET+=DMARC
OPTIONS_FILE_UNSET+=PYZOR
OPTIONS_FILE_UNSET+=RAZOR
OPTIONS_FILE_UNSET+=RELAY_COUNTRY
OPTIONS_FILE_UNSET+=RLIMIT
OPTIONS_FILE_SET+=SPF_QUERY

EOF

portmaster -w -B -g -U --force-config mail/spamassassin -n

Dienst in rc.conf eintragen

Der Dienst wird mittels sysrc in der rc.conf eingetragen und dadurch beim Systemstart automatisch gestartet.

Das rc.d-Skript heißt sa-spamd, die passende rc.conf-Variable bleibt aber spamd_enable. (FreshPorts)

Bash
sysrc spamd_enable="YES"
sysrc spamd_flags="--create-prefs --max-children 5 --helper-home-dir --nouser-config --virtual-config-dir=/var/vmail/%d/%l/spamassassin --username=vmail"

Konfiguration

Konfigurationsdateien

Der Port liefert die Sample-Dateien local.cf.sample und init.pre.sample bereits mit. Für dieses Setup ist das die saubere Basis. (FreshPorts)

local.cf einrichten

Bash
cat <<'EOF' > /usr/local/etc/mail/spamassassin/local.cf
# ============================================================================
# SPAMASSASSIN 4.0.2+ OPTIMIZED CONFIGURATION
# Verified: 2026-03-21
# ============================================================================
# Reference: https://spamassassin.apache.org/full/4.0.x/doc/Mail_SpamAssassin_Conf.txt
# Reference: https://wiki.apache.org/spamassassin/ImportantInitialConfigItems
# ============================================================================

# ----------------------------------------------------------------------------
# CORE SETTINGS
# ----------------------------------------------------------------------------
lock_method             flock
required_score          7.0
report_safe             0
skip_rbl_checks         1
skip_uribl_checks       1
normalize_charset       1
version_tag             20260321
enable_compat           welcomelist_blocklist

# ----------------------------------------------------------------------------
# DNS CONFIGURATION (Critical for network rules)
# ----------------------------------------------------------------------------
dns_available           yes
dns_server              127.0.0.1

# ----------------------------------------------------------------------------
# NETWORK TRUST & RELAYS (Must match Postfix/Amavisd mynetworks)
# ----------------------------------------------------------------------------
clear_internal_networks
internal_networks       10.0.0.0/8 [fe80::]/10
internal_networks       __IPADDR4__/32 [__IPADDR6__]/64

clear_trusted_networks
trusted_networks        10.0.0.0/8 [fe80::]/10
trusted_networks        __IPADDR4__/32 [__IPADDR6__]/64

# ----------------------------------------------------------------------------
# BAYES DATABASE (PostgreSQL with SSL via Unix Socket)
# ----------------------------------------------------------------------------
use_bayes               1
use_bayes_rules         1
bayes_auto_learn        1
bayes_expiry_max_db_size 150000
bayes_sql_override_username 0

bayes_store_module      Mail::SpamAssassin::BayesStore::PgSQL
bayes_sql_dsn           DBI:Pg:dbname=mail_bayes;host=localhost
bayes_sql_username      spamass
bayes_sql_password      __PASSWORD_SPAMASS__

bayes_ignore_header     X-Bogosity
bayes_ignore_header     X-Spam-Flag
bayes_ignore_header     X-Spam-Status

# ----------------------------------------------------------------------------
# TXREP (Replaces deprecated AWL - SpamAssassin 4.0.2+)
# ----------------------------------------------------------------------------
use_txrep               1
txrep_factory           Mail::SpamAssassin::SQLBasedAddrList
user_awl_dsn            DBI:Pg:dbname=mail_txrep;host=localhost
user_awl_sql_username   spamass
user_awl_sql_password   __PASSWORD_SPAMASS__

# ----------------------------------------------------------------------------
# HEADER ADDITIONS (SpamAssassin 4.0.2+ Compliant)
# ----------------------------------------------------------------------------
clear_headers
add_header all Flag           _YESNOCAPS_
add_header all Level          _STARS(*)_
add_header all Status         _YESNO_, score=_SCORE_ required=_REQD_ autolearn=_AUTOLEARN_ tests=_TESTSSCORES(,)_
add_header all Report         _REPORT_
add_header all RBLs           _RBL_
add_header all Checker-Version SpamAssassin _VERSION_ (_SUBVERSION_) on _HOSTNAME_

# ----------------------------------------------------------------------------
# REPORT CONFIGURATION
# ----------------------------------------------------------------------------
report_contact          postmaster@example.com
report_hostname         mail.example.com

# ----------------------------------------------------------------------------
# SCORE ADJUSTMENTS (Reduce false positives)
# ----------------------------------------------------------------------------
score RCVD_IN_MSBL       0    # Often false positive
score FORGED_GMAIL_RCVD  0    # Common false positive
score HTML_IMAGE_ONLY_00 0    # Adjust as needed

# SPF Scores
score SPF_FAIL          2.0
score SPF_SOFTFAIL      1.0
score SPF_PASS         -1.0
score SPF_HELO_FAIL     1.0

# DMARC / DKIM Scores
score DMARC_FAIL_REJECT 3.0
score DMARC_FAIL_QUAR   2.0
score DKIM_INVALID      2.0
score DKIM_VALID       -0.5
score DKIM_VALID_AU    -0.5

# Spoofing & Phishing Adjustments
score FROM_NAME_SPOOF   2.0
score PHISHING          3.0
score FREEMAIL_FROM     1.5
score OLEVBMACRO        3.5

# RBL/DNSBL Adjustments (Using SA's pre-compiled Native Rules)
score RCVD_IN_SBL       3.0
score RCVD_IN_XBL       3.0
score RCVD_IN_PBL       2.5
score RCVD_IN_ZEN       3.5
score RCVD_IN_BLSPAMCOP_NET 2.0
score RCVD_IN_BARRACUDA 2.5

# URIBL Native Adjustments
score URIBL_SBL         3.0
score URIBL_XBL         3.0
score URIBL_PBL         2.5
score URIBL_SPAMHAUS    3.5
score URIBL_BARRACUDA   2.5

# Internal Mail Fix (Whitelist internal auth sending)
header   AUTHENTICATED_SENDER  Received =~ /Authenticated\s+sender:.*by\s+mail\.example\.com/
describe AUTHENTICATED_SENDER  Header 'Received:' contains 'Authenticated sender:'
score    AUTHENTICATED_SENDER  -3.0

score RP_MATCHES_RCVD   0

# USER RULES ENABLED
allow_user_rules        1
EOF

init.pre einrichten

Bash
cat <<'EOF' > /usr/local/etc/mail/spamassassin/init.pre
# ============================================================================
# SPAMASSASSIN 4.0.2+ PLUGIN INITIALIZATION
# Verified: 2026-03-21
# ============================================================================
# Reference: https://spamassassin.apache.org/full/4.0.x/doc/Mail_SpamAssassin.html
# ============================================================================

# ----------------------------------------------------------------------------
# CORE PLUGINS (Load first - required for basic functionality)
# Priority: -200 to -100 (before network lookups)
# ----------------------------------------------------------------------------
loadplugin Mail::SpamAssassin::Plugin::Check
loadplugin Mail::SpamAssassin::Plugin::Shortcircuit
loadplugin Mail::SpamAssassin::Plugin::Bayes

# ----------------------------------------------------------------------------
# EVALUATION PLUGINS (Message analysis)
# Priority: -100 to 0 (standard evaluation)
# ----------------------------------------------------------------------------
loadplugin Mail::SpamAssassin::Plugin::BodyEval
loadplugin Mail::SpamAssassin::Plugin::DNSEval
loadplugin Mail::SpamAssassin::Plugin::HTMLEval
loadplugin Mail::SpamAssassin::Plugin::HeaderEval
loadplugin Mail::SpamAssassin::Plugin::MIMEEval
loadplugin Mail::SpamAssassin::Plugin::RelayEval
loadplugin Mail::SpamAssassin::Plugin::URIEval
loadplugin Mail::SpamAssassin::Plugin::WLBLEval

# ----------------------------------------------------------------------------
# DNS/RBL PLUGINS (Network-based checks)
# Priority: 0 to 100 (network lookups start at -100)
# ----------------------------------------------------------------------------
loadplugin Mail::SpamAssassin::Plugin::URIDNSBL
loadplugin Mail::SpamAssassin::Plugin::HashBL
loadplugin Mail::SpamAssassin::Plugin::AskDNS
loadplugin Mail::SpamAssassin::Plugin::SPF

# ----------------------------------------------------------------------------
# AUTHENTICATION PLUGINS (DKIM, DMARC, etc.)
# ----------------------------------------------------------------------------
loadplugin Mail::SpamAssassin::Plugin::DKIM
loadplugin Mail::SpamAssassin::Plugin::DMARC
loadplugin Mail::SpamAssassin::Plugin::AuthRes

# ----------------------------------------------------------------------------
# REPUTATION PLUGINS (TxRep replaces deprecated AWL)
# ----------------------------------------------------------------------------
loadplugin Mail::SpamAssassin::Plugin::TxRep

# ----------------------------------------------------------------------------
# OPTIONAL PLUGINS (Load last - feature enhancements)
# ----------------------------------------------------------------------------
loadplugin Mail::SpamAssassin::Plugin::ExtractText
loadplugin Mail::SpamAssassin::Plugin::ImageInfo
loadplugin Mail::SpamAssassin::Plugin::MIMEHeader
loadplugin Mail::SpamAssassin::Plugin::ReplaceTags
loadplugin Mail::SpamAssassin::Plugin::TextCat
loadplugin Mail::SpamAssassin::Plugin::FromNameSpoof
loadplugin Mail::SpamAssassin::Plugin::FreeMail
loadplugin Mail::SpamAssassin::Plugin::HTTPSMismatch
loadplugin Mail::SpamAssassin::Plugin::WelcomeListSubject
loadplugin Mail::SpamAssassin::Plugin::VBounce

# ----------------------------------------------------------------------------
# PERFORMANCE PLUGINS
# ----------------------------------------------------------------------------
loadplugin Mail::SpamAssassin::Plugin::Rule2XSBody

# ----------------------------------------------------------------------------
# EXCLUSIVE SA 4.0.x MODERN PROTECTION PLUGINS
# ----------------------------------------------------------------------------
loadplugin Mail::SpamAssassin::Plugin::Phishing
loadplugin Mail::SpamAssassin::Plugin::DecodeShortURLs
#loadplugin Mail::SpamAssassin::Plugin::OLEVBMacro

# ----------------------------------------------------------------------------
# DEPRECATED - DO NOT LOAD (SpamAssassin 4.0.2+)
# ----------------------------------------------------------------------------
# loadplugin Mail::SpamAssassin::Plugin::AWL                   # Replaced by TxRep
# loadplugin Mail::SpamAssassin::Plugin::AutoLearnThreshold    # Use bayes_auto_learn
# loadplugin Mail::SpamAssassin::Plugin::Razor2                # Deprecated
# loadplugin Mail::SpamAssassin::Plugin::Pyzor                 # Deprecated
EOF

Für PostgreSQL-Bayes muss in local.cf das PostgreSQL-spezifische Backend verwendet werden. Das generische SQL-Backend ist für PostgreSQL ausdrücklich nicht das richtige Modul. Für TxRep gilt zusätzlich: Standardmäßig wird die SQL-Tabelle txrep erwartet. (spamassassin.apache.org)

Platzhalter in local.cf ersetzen

Bash
# 1. Get Default Interface
DEF_IF="$(route -n get -inet default | awk '/interface:/ {print $2}')"

# 2. Get IPv4 IP
IP4="$(ifconfig "$DEF_IF" inet | awk '/inet / && $2 !~ /^127\./ {print $2}' | head -n 1)"
[ -n "$IP4" ] && sed -e "s|__IPADDR4__|$IP4|g" -i '' /usr/local/etc/mail/spamassassin/local.cf

# 3. Get IPv6 IP
IP6="$(ifconfig "$DEF_IF" inet6 | awk '/inet6 / && $2 !~ /^fe80:/ && $2 !~ /^::1/ {print $2}' | head -n 1)"
[ -n "$IP6" ] && sed -e "s|__IPADDR6__|$IP6|g" -i '' /usr/local/etc/mail/spamassassin/local.cf

cat /var/db/passwords/postgresql_user_spamass | xargs -I % \
  sed -e "s|__PASSWORD_SPAMASS__|%|g" -i '' /usr/local/etc/mail/spamassassin/local.cf

Regelupdates und kompilierte Regeln

sa-update lädt und installiert aktualisierte Regeln. Der Standardkanal ist updates.spamassassin.org. sa-update startet spamd danach nicht neu. sa-compile kompiliert Regeln, lädt sie aber ebenfalls nicht automatisch neu; zusätzlich muss dafür das Plugin Rule2XSBody aktiv sein. Auf FreeBSD gilt außerdem der aktuelle Port-Hinweis: Nach einem Port-Update zuerst sa-update laufen lassen und danach erst sa-spamd neu starten. (spamassassin.apache.org)

Bash
/usr/local/bin/sa-update --channel updates.spamassassin.org --refreshmirrors --verbose
/usr/local/bin/sa-update --channel updates.spamassassin.org --verbose
/usr/local/bin/sa-compile --quiet

Zusätzliche Drittanbieter-Regelkanäle sind möglich, gehören aber nicht in dieses Basis-HowTo. Der Port verweist dafür allgemein auf zusätzliche Drop-in-Regelsätze, SpamAssassin selbst behandelt updates.spamassassin.org als Standardkanal. (FreshPorts)

Wartungsscript für Updates

Bash
cat <<'EOF' > /usr/local/sbin/update-spamassassin
EOF
chmod 755 /usr/local/sbin/update-spamassassin

Konfiguration prüfen

Vor dem ersten Start sollte die Konfiguration immer geprüft werden.

Bash
spamassassin --lint
service sa-spamd start
sockstat -4 -6 -l | egrep 'spamd|783'

Datenbanken

PostgreSQL-Benutzer spamass anlegen

Für SpamAssassin reicht ein normaler Login-Benutzer. Zusätzliche Rechte wie CREATEROLE oder CREATEDB sind dafür nicht erforderlich.

Bash
# Passwort für PostgreSQL-Benutzer "spamass" erzeugen und
# in /var/db/passwords/postgresql_user_spamass speichern
install -b -m 0600 -o postgres -g postgres /dev/null /var/db/passwords/postgresql_user_spamass
openssl rand -hex 64 | openssl passwd -5 -stdin | tr -cd '[[:print:]]' | \
  cut -c 2-17 | tee /var/db/passwords/postgresql_user_spamass

# PostgreSQL-Benutzer "spamass" mit Passwort anlegen
su -l postgres -c "psql <<'EOF'
\set content `cat /var/db/passwords/postgresql_user_spamass`
DROP ROLE IF EXISTS \"spamass\";
CREATE ROLE \"spamass\";
ALTER ROLE \"spamass\" WITH NOSUPERUSER INHERIT CREATEROLE CREATEDB LOGIN NOREPLICATION NOBYPASSRLS PASSWORD :'content';
\unset content
EOF"

Das Passwort bitte sicher notieren, du wirst es bei jeder externen Verbindung über TCP benötigen.

PostgreSQL-Datenbank mail_bayes für spamass anlegen

Für PostgreSQL-Bayes ist das PostgreSQL-spezifische Bayes-Backend zuständig. Das ist genau für BYTEA-basierte Token-Speicherung in PostgreSQL gedacht. (spamassassin.apache.org)

Bash
su -l postgres -c "psql <<'EOF'
DROP DATABASE IF EXISTS \"mail_bayes\";
CREATE DATABASE \"mail_bayes\";
ALTER DATABASE \"mail_bayes\" OWNER TO \"spamass\";
EOF"

Schema für mail_bayes einspielen

Bash
su -l postgres -c "psql <<'EOF'
\connect \"mail_bayes\"

CREATE OR REPLACE FUNCTION \"public\".\"greatest_int\"(integer, integer) RETURNS integer
    LANGUAGE \"sql\" IMMUTABLE STRICT
    AS \$_\$SELECT CASE WHEN \$1 < \$2 THEN \$2 ELSE \$1 END;\$_\$;

ALTER FUNCTION \"public\".\"greatest_int\"(integer, integer) OWNER TO \"spamass\";

CREATE OR REPLACE FUNCTION \"public\".\"least_int\"(integer, integer) RETURNS integer
    LANGUAGE \"sql\" IMMUTABLE STRICT
    AS \$_\$SELECT CASE WHEN \$1 < \$2 THEN \$1 ELSE \$2 END;\$_\$;

ALTER FUNCTION \"public\".\"least_int\"(integer, integer) OWNER TO \"spamass\";

CREATE OR REPLACE FUNCTION \"public\".\"put_tokens\"(integer, \"bytea\"[], integer, integer, integer) RETURNS \"void\"
    LANGUAGE \"plpgsql\"
    AS \$_\$
DECLARE
  inuserid      ALIAS FOR \$1;
  intokenary    ALIAS FOR \$2;
  inspam_count  ALIAS FOR \$3;
  inham_count   ALIAS FOR \$4;
  inatime       ALIAS FOR \$5;
  _token BYTEA;
  new_tokens INTEGER := 0;
BEGIN
  for i in array_lower(intokenary, 1) .. array_upper(intokenary, 1)
  LOOP
    _token := intokenary[i];
    UPDATE bayes_token
       SET spam_count = greatest_int(spam_count + inspam_count, 0),
           ham_count = greatest_int(ham_count + inham_count, 0),
           atime = greatest_int(atime, inatime)
     WHERE id = inuserid
       AND token = _token;
    IF NOT FOUND THEN
      IF NOT (inspam_count < 0 OR inham_count < 0) THEN
        INSERT INTO bayes_token (id, token, spam_count, ham_count, atime)
        VALUES (inuserid, _token, inspam_count, inham_count, inatime);
        IF FOUND THEN
          new_tokens := new_tokens + 1;
        END IF;
      END IF;
    END IF;
  END LOOP;

  IF new_tokens > 0 AND inatime > 0 THEN
    UPDATE bayes_vars
       SET token_count = token_count + new_tokens,
           newest_token_age = greatest_int(newest_token_age, inatime),
           oldest_token_age = least_int(oldest_token_age, inatime)
     WHERE id = inuserid;
  ELSIF new_tokens > 0 AND NOT inatime > 0 THEN
    UPDATE bayes_vars
       SET token_count = token_count + new_tokens
     WHERE id = inuserid;
  ELSIF NOT new_tokens > 0 AND inatime > 0 THEN
    UPDATE bayes_vars
       SET newest_token_age = greatest_int(newest_token_age, inatime),
           oldest_token_age = least_int(oldest_token_age, inatime)
     WHERE id = inuserid;
  END IF;
  RETURN;
END;
\$_\$;

ALTER FUNCTION \"public\".\"put_tokens\"(integer, \"bytea\"[], integer, integer, integer) OWNER TO \"spamass\";

CREATE TABLE \"public\".\"bayes_expire\" (
    \"id\" integer DEFAULT 0 NOT NULL,
    \"runtime\" integer DEFAULT 0 NOT NULL
);

ALTER TABLE \"public\".\"bayes_expire\" OWNER TO \"spamass\";

CREATE TABLE \"public\".\"bayes_global_vars\" (
    \"variable\" character varying(30) DEFAULT ''::character varying NOT NULL,
    \"value\" character varying(200) DEFAULT ''::character varying NOT NULL
);

ALTER TABLE \"public\".\"bayes_global_vars\" OWNER TO \"spamass\";

CREATE TABLE \"public\".\"bayes_seen\" (
    \"id\" integer DEFAULT 0 NOT NULL,
    \"msgid\" character varying(200) DEFAULT ''::character varying NOT NULL,
    \"flag\" character(1) DEFAULT ''::\"bpchar\" NOT NULL
);

ALTER TABLE \"public\".\"bayes_seen\" OWNER TO \"spamass\";

CREATE TABLE \"public\".\"bayes_token\" (
    \"id\" integer DEFAULT 0 NOT NULL,
    \"token\" \"bytea\" DEFAULT '\\x'::\"bytea\" NOT NULL,
    \"spam_count\" integer DEFAULT 0 NOT NULL,
    \"ham_count\" integer DEFAULT 0 NOT NULL,
    \"atime\" integer DEFAULT 0 NOT NULL
)
WITH (\"fillfactor\"='95');

ALTER TABLE \"public\".\"bayes_token\" OWNER TO \"spamass\";

CREATE TABLE \"public\".\"bayes_vars\" (
    \"id\" integer NOT NULL,
    \"username\" character varying(200) DEFAULT ''::character varying NOT NULL,
    \"spam_count\" integer DEFAULT 0 NOT NULL,
    \"ham_count\" integer DEFAULT 0 NOT NULL,
    \"token_count\" integer DEFAULT 0 NOT NULL,
    \"last_expire\" integer DEFAULT 0 NOT NULL,
    \"last_atime_delta\" integer DEFAULT 0 NOT NULL,
    \"last_expire_reduce\" integer DEFAULT 0 NOT NULL,
    \"oldest_token_age\" integer DEFAULT 2147483647 NOT NULL,
    \"newest_token_age\" integer DEFAULT 0 NOT NULL
);

ALTER TABLE \"public\".\"bayes_vars\" OWNER TO \"spamass\";

CREATE SEQUENCE \"public\".\"bayes_vars_id_seq\"
    AS integer
    START WITH 1
    INCREMENT BY 1
    NO MINVALUE
    NO MAXVALUE
    CACHE 1;

ALTER SEQUENCE \"public\".\"bayes_vars_id_seq\" OWNER TO \"spamass\";
ALTER SEQUENCE \"public\".\"bayes_vars_id_seq\" OWNED BY \"public\".\"bayes_vars\".\"id\";

ALTER TABLE ONLY \"public\".\"bayes_vars\" ALTER COLUMN \"id\" SET DEFAULT \"nextval\"('\"public\".\"bayes_vars_id_seq\"'::\"regclass\");
SELECT pg_catalog.setval('\"public\".\"bayes_vars_id_seq\"', 1, false);

ALTER TABLE ONLY \"public\".\"bayes_global_vars\"
    ADD CONSTRAINT \"bayes_global_vars_pkey\" PRIMARY KEY (\"variable\");

ALTER TABLE ONLY \"public\".\"bayes_seen\"
    ADD CONSTRAINT \"bayes_seen_pkey\" PRIMARY KEY (\"id\", \"msgid\");

ALTER TABLE ONLY \"public\".\"bayes_token\"
    ADD CONSTRAINT \"bayes_token_pkey\" PRIMARY KEY (\"id\", \"token\");

ALTER TABLE ONLY \"public\".\"bayes_vars\"
    ADD CONSTRAINT \"bayes_vars_pkey\" PRIMARY KEY (\"id\");

CREATE INDEX \"bayes_expire_idx1\" ON \"public\".\"bayes_expire\" USING \"btree\" (\"id\");
CREATE INDEX \"bayes_token_idx1\" ON \"public\".\"bayes_token\" USING \"btree\" (\"token\");
CREATE UNIQUE INDEX \"bayes_vars_idx1\" ON \"public\".\"bayes_vars\" USING \"btree\" (\"username\");

INSERT INTO \"bayes_global_vars\" VALUES ('VERSION','3');
EOF"

Verbindung als spamass testen

Bash
psql -h 127.0.0.1 -U spamass -d mail_bayes -c 'SELECT current_user, current_database();'

PostgreSQL-Datenbank mail_txrep für spamass anlegen

TxRep verwendet dieselbe SQL-Architektur wie das frühere AWL-Plugin, erwartet aber standardmäßig eine Tabelle mit dem Namen txrep. Für PostgreSQL beschreibt die offizielle Dokumentation dazu den Import über ein PostgreSQL-Schema und empfiehlt außerdem, alte Einträge regelmäßig zu bereinigen. (spamassassin.apache.org)

Bash
su -l postgres -c "psql <<'EOF'
DROP DATABASE IF EXISTS \"mail_txrep\";
CREATE DATABASE \"mail_txrep\";
ALTER DATABASE \"mail_txrep\" OWNER TO \"spamass\";
EOF"

Schema für mail_txrep einspielen

Bash
su -l postgres -c "psql <<'EOF'
\connect \"mail_txrep\"

CREATE OR REPLACE FUNCTION \"public\".\"update_txrep_last_hit\"() RETURNS \"trigger\"
    LANGUAGE \"plpgsql\"
    AS \$\$
BEGIN
  NEW.last_hit = CURRENT_TIMESTAMP;
  RETURN NEW;
END;
\$\$;

ALTER FUNCTION \"public\".\"update_txrep_last_hit\"() OWNER TO \"spamass\";

CREATE TABLE \"public\".\"txrep\" (
    \"username\" character varying(100) DEFAULT ''::character varying NOT NULL,
    \"email\" character varying(255) DEFAULT ''::character varying NOT NULL,
    \"ip\" character varying(40) DEFAULT ''::character varying NOT NULL,
    \"msgcount\" bigint DEFAULT '0'::bigint NOT NULL,
    \"totscore\" double precision DEFAULT '0'::double precision NOT NULL,
    \"signedby\" character varying(255) DEFAULT ''::character varying NOT NULL,
    \"last_hit\" timestamp without time zone DEFAULT CURRENT_TIMESTAMP NOT NULL
)
WITH (\"fillfactor\"='95');

ALTER TABLE \"public\".\"txrep\" OWNER TO \"spamass\";

ALTER TABLE ONLY \"public\".\"txrep\"
    ADD CONSTRAINT \"txrep_pkey\" PRIMARY KEY (\"username\", \"email\", \"signedby\", \"ip\");

CREATE INDEX \"txrep_last_hit\" ON \"public\".\"txrep\" USING \"btree\" (\"last_hit\");

CREATE TRIGGER \"update_txrep_update_last_hit\" BEFORE UPDATE ON \"public\".\"txrep\" FOR EACH ROW EXECUTE FUNCTION \"public\".\"update_txrep_last_hit\"();
EOF"

Verbindung als spamass testen

Bash
psql -h 127.0.0.1 -U spamass -d mail_txrep -c 'SELECT current_user, current_database();'

Zusatzsoftware

Mögliche Zusatzsoftware wird hier installiert und konfiguriert.

Wir installieren mail/spamass-milter und dessen Abhängigkeiten.

mail/spamass-milter bringt auf FreeBSD ein eigenes rc.d-Skript spamass-milter mit. Der aktuelle Portstand ist 0.4.0_5. (FreshPorts)

Bash
mkdir -p /var/db/ports/mail_spamass-milter
cat <<'EOF' > /var/db/ports/mail_spamass-milter/options
_OPTIONS_READ=spamass-milter-0.4.0
_FILE_COMPLETE_OPTIONS_LIST=DOCS LDAP MILTER_PORT
OPTIONS_FILE_UNSET+=DOCS
OPTIONS_FILE_UNSET+=LDAP
OPTIONS_FILE_SET+=MILTER_PORT

EOF

portmaster -w -B -g -U --force-config mail/spamass-milter -n

mkdir -p /var/spool/postfix/spamass
chown spamd:wheel /var/spool/postfix/spamass
chmod 770 /var/spool/postfix/spamass
pw groupmod spamd -m postfix

Dienst in rc.conf eintragen

Bash
sysrc spamass_milter_enable="YES"
sysrc spamass_milter_user="spamd"
sysrc spamass_milter_group="spamd"
sysrc spamass_milter_socket="/var/spool/postfix/spamass/spamass.sock"
sysrc spamass_milter_socket_owner="postfix"
sysrc spamass_milter_socket_group="postfix"
sysrc spamass_milter_socket_mode="660"
sysrc spamass_milter_localflags="-e example.com -u spamd -i 127.0.0.1 -R REJECTED_AS_SPAM -r 10 -- --max-size=5120000"

Zusatzsoftware Konfiguration prüfen

Bash
service spamass-milter start
service spamass-milter status

Aufräumen

Überflüssige oder temporäre Verzeichnisse und Dateien entsorgen.

Zusatzsoftware Installation

Nicht erforderlich.

Zusatzsoftware Konfiguration

Nicht erforderlich.


Abschluss

SpamAssassin kann nun gestartet werden.

Bash
service sa-spamd start
service spamass-milter start

Für spätere Änderungen:

Bash
service sa-spamd restart
service spamass-milter restart

Für Funktionstests danach:

Bash
sockstat -4 -6 -l | egrep 'spamd|spamass-milter'
spamc -R < /path/to/testmail.eml

Nach einem späteren Port-Update von mail/spamassassin gilt auf FreeBSD weiter: erst sa-update, dann sa-spamd neu starten. (FreshPorts)


Referenzen