Skip to content
This repository was archived by the owner on Jun 23, 2026. It is now read-only.

v1.0.0 - Initial release

Latest

Choose a tag to compare

@danielcolquitt danielcolquitt released this 13 Jan 16:03
ea1a77c

Spamhaus DQS Milter (Python)

A small Python milter daemon that queries Spamhaus DQS DNS zones. It can be used with any Mail Transfer Agent (MTA) that supports the milter protocol (Sendmail-compatible milters), such as Postfix and others. A Postfix configuration example is included below.

What it does

SMTP-stage checks:

  • CONNECT: checks the connecting IP against ZEN (optional)
  • HELO/EHLO: checks the HELO domain against DBL + ZRD
  • MAIL FROM: checks the sender domain against DBL + ZRD

Policy (as implemented):

  • DBL: reject only 127.0.1.2127.0.1.99 (“safe to block”)
  • ZRD: reject 127.0.2.2127.0.2.24 (“domain too young”)
  • ZEN: reject if 127.0.0.X where X ∈ {2,3,4,9,10,11} by default
  • 127.255.255.* indicates DQS-side error conditions and is treated as fail-open

You need your own Spamhaus DQS credentials/API key (free for low volume non-commercial use) and must comply with Spamhaus terms. This is an independent, unofficial hobby project and is not affiliated with, endorsed by, or sponsored by The Spamhaus Project. “Spamhaus” and “Spamhaus DQS” are trademarks of their respective owners. All other trademarks are the property of their respective owners.

Repository contents

  • spamhaus-dqs-milter.py — the milter daemon
  • requirements.txt — Python dependencies
  • spamhaus-dqs-milter.service — example systemd unit file (runs the daemon + loads env)

Dependencies:

  • pymilter
  • dnspython

Installation (base dir: /opt/spamhaus-dqs-milter)

These steps install into:

  • Code: /opt/spamhaus-dqs-milter
  • Virtualenv: /opt/spamhaus-dqs-milter/venv
  • Env file: /etc/spamhaus-dqs-milter.env
  • Default listener: inet:11332@localhost

1) Create a dedicated service user

sudo adduser --system --group --no-create-home --shell /usr/sbin/nologin spamhausmilter

2) Install the code

sudo install -d -o root -g root -m 0755 /opt/spamhaus-dqs-milter
sudo git clone <your-repo-url> /opt/spamhaus-dqs-milter
sudo chown -R root:root /opt/spamhaus-dqs-milter
sudo chmod -R a-w /opt/spamhaus-dqs-milter

3) Create a virtual environment in /opt/spamhaus-dqs-milter

sudo python3 -m venv /opt/spamhaus-dqs-milter/venv
sudo /opt/spamhaus-dqs-milter/venv/bin/pip install --upgrade pip
sudo /opt/spamhaus-dqs-milter/venv/bin/pip install -r /opt/spamhaus-dqs-milter/requirements.txt

4) Create the environment file

The systemd unit loads:

  • /etc/spamhaus-dqs-milter.env

Create it (see .env.example for an example):

sudo install -m 0640 -o root -g spamhausmilter /dev/null /etc/spamhaus-dqs-milter.env
sudo nano /etc/spamhaus-dqs-milter.env

5) Install and start the systemd service

sudo cp /opt/spamhaus-dqs-milter/spamhaus-dqs-milter.service /etc/systemd/system/spamhaus-dqs-milter.service
sudo systemctl daemon-reload
sudo systemctl enable --now spamhaus-dqs-milter

Logs:

sudo systemctl status spamhaus-dqs-milter --no-pager
sudo journalctl -u spamhaus-dqs-milter -f

Integrating with an MTA

This daemon implements the milter protocol and listens on SPAMHAUS_DQS_LISTEN
(default inet:11332@localhost). Configure your milter-capable MTA to connect to that socket.

Postfix example

In /etc/postfix/main.cf:

# Milter integration
smtpd_milters = inet:localhost:11332
non_smtpd_milters = $smtpd_milters

# Recommended: keep mail flowing if the milter is unavailable
milter_default_action = accept

# Often used with milters; adjust if you have specific requirements
milter_protocol = 6
milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}

Reload postfix:

sudo postfix check
sudo systemctl reload postfix

Configuration (environment variables)

The daemon reads an env file at startup (defaults to /etc/spamhaus-dqs-milter.env).
Override the env file path with:

  • SPAMHAUS_DQS_ENV_FILE=/path/to/file

Required

  • SPAMHAUS_DQS_API_KEY
    Your Spamhaus DQS key (inserted into the DQS query name).

Core settings

  • SPAMHAUS_DQS_LISTEN (default: inet:11332@localhost)
  • SPAMHAUS_DQS_LOG_LEVEL (default: INFO)
  • SPAMHAUS_DQS_NAMESERVERS (default: 127.0.0.1,::1)

Timeouts (seconds)

  • SPAMHAUS_DQS_MILTER_TIMEOUT (default: 2)
  • SPAMHAUS_DQS_DNS_LIFETIME (default: 1.8)
  • SPAMHAUS_DQS_DNS_PER_TRY (default: 0.7)

The script enforces DNS_LIFETIME < MILTER_TIMEOUT with headroom.

Cache

  • SPAMHAUS_DQS_CACHE_MAX (default: 20000)
  • SPAMHAUS_DQS_CACHE_TTL_LISTED (default: 600)
  • SPAMHAUS_DQS_CACHE_TTL_UNLISTED (default: 300)

ZEN at CONNECT

  • SPAMHAUS_DQS_ZEN_ENABLE (default: 1)
  • SPAMHAUS_DQS_ZEN_REJECT_CODES (default: 2,3,4,9,10,11)

Whitelisting

  • SPAMHAUS_DQS_IP_WHITELIST
    Comma-separated IPs (exact match), e.g. 192.0.2.10,2001:db8::1

  • SPAMHAUS_DQS_DOMAIN_WHITELIST
    Comma-separated exact domains (applies to both HELO and MAIL FROM).

  • SPAMHAUS_DQS_DOMAIN_SUFFIX_WHITELIST
    Comma-separated suffixes; matches example.com and *.example.com.

  • SPAMHAUS_DQS_WHITELIST_SKIP_ALL (default: 0)
    If 1 and the connecting IP is whitelisted, skip all checks for that connection.