Configure Spam Filtering#

Improve spam detection by configuring rspamd overrides and DNS-based blocklists.

Problem#

The default rspamd configuration provides basic spam filtering, but does not include:

  • DNS-based Real-time Blackhole Lists (RBLs) like Spamhaus

  • Remote fuzzy hash checking against known spam databases

  • Optimized scoring thresholds

In Kubernetes environments, RBL lookups fail because kube-dns forwards queries to public resolvers (e.g. Cloudflare, Google) which are blocked by RBL providers like Spamhaus.

Solution Overview#

cdk8s-mailu addresses this with:

  1. Unbound DNS sidecar - A recursive DNS resolver running alongside rspamd that sends queries directly to authoritative DNS servers

  2. Rspamd configuration overrides - Stricter thresholds, RBL lists, and remote fuzzy servers via ConfigMap

How It Works#

The RspamdConstruct automatically deploys:

  • An Unbound container as a sidecar in the rspamd pod

  • ConfigMaps with rspamd overrides mounted at /overrides/

  • A DNS override (options.inc) pointing rspamd to 127.0.0.1:53 (Unbound)

        graph LR
    A[Incoming Mail] --> B[Postfix]
    B -->|milter| C[rspamd]
    C -->|DNS queries| D[Unbound Sidecar]
    D -->|recursive| E[Spamhaus]
    D -->|recursive| F[Barracuda]
    D -->|recursive| G[Spamcop]
    D -->|forward| H[kube-dns]
    H -->|.cluster.local| I[K8s Services]
    

DNS Configuration#

The Unbound sidecar can be configured via the dns config option:

dns:
  kubeDnsIp: "10.43.0.10"      # kube-dns ClusterIP (K3S default)
  unboundImage: "mvance/unbound:1.22.0"  # Pinned image version

Default Behavior#

Without explicit dns configuration, Unbound uses:

  • 10.43.0.10 as kube-dns for .cluster.local forwarding

  • Recursive resolution for all other queries (including RBL lookups)

  • QNAME minimization disabled (required by Spamhaus)

Rspamd Overrides#

The following overrides are applied automatically:

Scoring Thresholds (actions.conf)#

reject = 12     # Reject messages with score >= 12 (default: 15)
add_header = 5  # Add X-Spam header at score >= 5 (default: 6)
greylist = 3    # Greylist at score >= 3 (default: 4)

Bayes Auto-Learning (classifier-bayes.conf)#

autolearn {
  spam_threshold = 5.0   # Learn as spam at score >= 5 (default: 6)
  ham_threshold = -1.0   # Learn as ham at score <= -1 (default: -0.5)
}

Remote Fuzzy Servers (fuzzy_check.conf)#

Checks incoming mail hashes against rspamd.com’s global fuzzy database of known spam.

RBL Blocklists (rbl.conf)#

  • Spamhaus ZEN (SBL + XBL + PBL) - IP-based blocklist

  • Spamhaus DBL - Domain-based blocklist (DKIM, URLs, emails)

  • Barracuda RBL - IP-based blocklist

  • Spamcop - IP-based blocklist

Troubleshooting#

RBL Lookups Not Working#

Verify Unbound is running and resolving correctly:

# Check both containers are running
kubectl get pods -n mailu -l app.kubernetes.io/component=rspamd

# Test RBL resolution via Unbound
kubectl exec -n mailu deploy/rspamd -c unbound -- \
  drill @127.0.0.1 2.0.0.127.zen.spamhaus.org

Too Many False Positives#

If legitimate mail is being flagged:

  1. Increase the reject threshold (e.g. to 15)

  2. Lower the greylist threshold sensitivity

  3. Check rspamd web UI for which symbols triggered

Kubernetes DNS Still Working#

Verify that .cluster.local resolution works through Unbound:

kubectl exec -n mailu deploy/rspamd -c rspamd -- \
  nslookup redis.mailu.svc.cluster.local 127.0.0.1

See Also#