BSNSoft · Domain Reputation
Integration

Integrate the reputation service in your mail filter

Classic DNSBL at dnsbl.bsnsoft-hostings.de. HTTP API available as an alternative transport.

Classification

The service answers with one of five classifications, encoded in the DNS return code:

ScoreClassSymbolDNS returnRecommended weight
9TOPBSN_REP_TOP127.0.0.0−2.0 (bonus)
7–8HIGHBSN_REP_HIGH127.0.0.[1-2]−0.5 (small bonus)
4–6MEDIUMBSN_REP_MEDIUM127.0.0.[3-5]+1.0
2–3LOWBSN_REP_LOW127.0.0.[6-7]+4.0
0–1BADBSN_REP_BAD127.0.0.[8-9]+6.0

With an RSpamD reject threshold of 12, our maximum contribution (+6.0) stays below half – our signal alone never rejects a message; only in combination with another indicator (DMARC fail, Bayes, IP RBL, …).

RSpamD · Lua plugin (recommended)

Sender-domain lookup via our DNSBL. Small Lua file, loaded once, runs for every message. This is the cleanest integration for sender-domain reputation.

Step 1 – save as /etc/rspamd/local.d/bsn_reputation.lua:

local rspamd_logger = require "rspamd_logger"
local N = "bsn_reputation"
local RBL = "dnsbl.bsnsoft-hostings.de"

local function reputation_cb(task)
  local from = task:get_from("smtp")
  if not from or not from[1] then from = task:get_from("mime") end
  if not from or not from[1] then return false end
  local domain = (from[1]["domain"] or ""):lower()
  if domain == "" then return false end

  local resolver = task:get_resolver()
  if not resolver then return false end

  resolver:resolve_a({
    task = task,
    name = domain .. "." .. RBL,
    callback = function(_, _, results, err)
      if err or not results or #results == 0 then return end
      local last = tonumber(tostring(results[1]):match("%.(%d+)$"))
      if not last then return end
      local sym
      if     last == 0 then sym = "BSN_REP_TOP"
      elseif last <= 2 then sym = "BSN_REP_HIGH"
      elseif last <= 5 then sym = "BSN_REP_MEDIUM"
      elseif last <= 7 then sym = "BSN_REP_LOW"
      else                  sym = "BSN_REP_BAD"
      end
      task:insert_result(sym, 1.0, domain)
    end,
  })
  return false
end

local id = rspamd_config:register_symbol({
  name = "BSN_REPUTATION_CHECK", type = "callback", callback = reputation_cb,
})
for _, sub in ipairs({"BSN_REP_TOP","BSN_REP_HIGH","BSN_REP_MEDIUM","BSN_REP_LOW","BSN_REP_BAD"}) do
  rspamd_config:register_symbol({ name = sub, type = "virtual", parent = id, group = N })
end
rspamd_logger.infox(rspamd_config, "%s: loaded (zone=%s)", N, RBL)

Step 2 – activate it by adding this line to /etc/rspamd/rspamd.local.lua:

dofile("/etc/rspamd/local.d/bsn_reputation.lua")

Step 3 – set weights in /etc/rspamd/local.d/scores.conf:

BSN_REP_TOP    = -2.0;
BSN_REP_HIGH   = -0.5;
BSN_REP_MEDIUM =  1.0;
BSN_REP_LOW    =  4.0;
BSN_REP_BAD    =  6.0;

Step 4 – systemctl restart rspamd. Verify with:

printf "From: t@heise.de\nTo: r@x.de\nSubject: t\n\nbody\n" | \
  rspamc -F "t@heise.de" - | grep BSN_REP

RSpamD · rbl module (optional, for body-URLs)

The Lua plugin above covers sender-domain reputation. If you additionally want to match domains that appear as URLs inside the body text, add the native rbl module on top. Be careful not to set weights that double-count between sender-based (Lua) and URL-based (rbl) hits.

File /etc/rspamd/local.d/rbl.conf:

rbls {
  bsnsoft_reputation_urls {
    symbol = "BSN_URL_REPUTATION";
    rbl = "dnsbl.bsnsoft-hostings.de";
    checks = ["urls", "content_urls"];
    emails_domainonly = true;
    unknown = false;
    returncodes {
      BSN_URL_TOP    = "127.0.0.0";
      BSN_URL_HIGH   = "127.0.0.[1-2]";
      BSN_URL_MEDIUM = "127.0.0.[3-5]";
      BSN_URL_LOW    = "127.0.0.[6-7]";
      BSN_URL_BAD    = "127.0.0.[8-9]";
    }
  }
}

Separate symbol names (BSN_URL_*) prevent double-scoring with the Lua sender-check (BSN_REP_*). Typical weights: use smaller numbers for URL hits, since most legit mails contain URLs to mid-reputation domains (shorteners, trackers).

Weights in /etc/rspamd/local.d/scores.conf:

symbols {
  "BSN_REP_TOP"    { score = -2.0; description = "Top reputation"; }
  "BSN_REP_HIGH"   { score = -0.5; description = "Strong reputation"; }
  "BSN_REP_MEDIUM" { score =  1.0; description = "Neutral / unclear reputation"; }
  "BSN_REP_LOW"    { score =  4.0; description = "Weak reputation"; }
  "BSN_REP_BAD"    { score =  6.0; description = "Throwaway / spam infrastructure"; }
}

Reload after changes: systemctl reload rspamd.

Postfix · postfwd policy

If RSpamD is not in use, a policy daemon such as postfwd can query the DNSBL:

id=BSN_REP_REJECT ; client_address!~=^127\. ; sender_domain_in_dnsbl==dnsbl.bsnsoft-hostings.de ; action=REJECT Poor sender reputation

HTTP API

Single lookup:

curl https://dnsbl.bsnsoft-hostings.de/api/score/example.com

Response format:

{
  "rawInput": "example.com",
  "normalizedDomain": "example.com",
  "score": 8,
  "classification": "HIGH",
  "status": "INDEXED"
}

Status values:

StatusMeaning
INDEXEDScore available in score / classification
INVALID_DOMAINInput is not a valid domain

Set generous client timeouts (at least 90 seconds).

Questions or enterprise usage?

Contact: info@bsnsoft.de · bsnsoft.de