Browse Source

Use new milter interface

andryyy 8 years ago
parent
commit
663ea7815c

+ 0 - 26
data/Dockerfiles/rmilter/Dockerfile

@@ -1,26 +0,0 @@
-FROM debian:jessie-slim
-LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
-
-ARG DEBIAN_FRONTEND=noninteractive
-ENV LC_ALL C
-
-RUN apt-key adv --fetch-keys http://rspamd.com/apt-stable/gpg.key \
-	&& echo "deb http://rspamd.com/apt-stable/ jessie main" > /etc/apt/sources.list.d/rspamd.list \
-	&& apt-get update && apt-get install -y --force-yes --no-install-recommends \
-		cron \
-		rmilter \
-		supervisor \
-		syslog-ng \
-		syslog-ng-core \
-	&& rm -rf /var/lib/apt/lists/*
-
-COPY supervisord.conf /etc/supervisor/supervisord.conf
-
-EXPOSE 9000
-
-RUN sed -i -E 's/^(\s*)system\(\);/\1unix-stream("\/dev\/log");/' /etc/syslog-ng/syslog-ng.conf
-RUN touch /var/log/mail.log && chmod 640 /var/log/mail.log && chown root:adm /var/log/mail.log
-
-CMD exec /usr/bin/supervisord -c /etc/supervisor/supervisord.conf
-
-RUN rm -rf /tmp/* /var/tmp/*

+ 0 - 18
data/Dockerfiles/rmilter/supervisord.conf

@@ -1,18 +0,0 @@
-[supervisord]
-nodaemon=true
-
-[program:syslog-ng]
-command=/usr/sbin/syslog-ng --foreground  --no-caps
-redirect_stderr=true
-autostart=true
-stdout_syslog=true
-
-[program:rmilter]
-command=/usr/sbin/rmilter -n -c /etc/rmilter.conf.d/rmilter.conf
-user=_rmilter
-autorestart=true
-
-[program:rmilter-syslog]
-command=/usr/bin/tail -f /var/log/mail.log
-stdout_logfile=/dev/fd/1
-stdout_logfile_maxbytes=0

+ 3 - 3
data/Dockerfiles/rspamd/Dockerfile

@@ -4,8 +4,8 @@ LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
 ARG DEBIAN_FRONTEND=noninteractive
 ENV LC_ALL C
 
-RUN apt-key adv --fetch-keys http://rspamd.com/apt-stable/gpg.key \
-	&& echo "deb http://rspamd.com/apt-stable/ jessie main" > /etc/apt/sources.list.d/rspamd.list \
+RUN apt-key adv --fetch-keys http://rspamd.com/apt/gpg.key \
+	&& echo "deb http://rspamd.com/apt/ jessie main" > /etc/apt/sources.list.d/rspamd.list \
 	&& apt-get update && apt-get install -y \
 		ca-certificates \
 		rspamd \
@@ -15,7 +15,7 @@ RUN echo '.include $LOCAL_CONFDIR/local.d/rspamd.conf.local' > /etc/rspamd/rspam
 
 COPY settings.conf /etc/rspamd/modules.d/settings.conf
 COPY antivirus.conf /etc/rspamd/modules.d/antivirus.conf
-COPY dkim_signing.lua /usr/share/rspamd/lua/dkim_signing.lua
+COPY milter_headers.lua /usr/share/rspamd/lua/milter_headers.lua
 
 RUN apt-get autoremove --purge \
 	&& apt-get clean

+ 0 - 286
data/Dockerfiles/rspamd/dkim_signing.lua

@@ -1,286 +0,0 @@
---[[
-Copyright (c) 2016, Andrew Lewis <nerf@judo.za.org>
-Copyright (c) 2016, Vsevolod Stakhov <vsevolod@highsecure.ru>
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-]]--
-
-local rspamd_logger = require "rspamd_logger"
-local rspamd_util = require "rspamd_util"
-
-if confighelp then
-  return
-end
-
-local settings = {
-  allow_envfrom_empty = true,
-  allow_hdrfrom_mismatch = false,
-  allow_hdrfrom_mismatch_local = false,
-  allow_hdrfrom_mismatch_sign_networks = false,
-  allow_hdrfrom_multiple = false,
-  allow_username_mismatch = false,
-  auth_only = true,
-  domain = {},
-  path = string.format('%s/%s/%s', rspamd_paths['DBDIR'], 'dkim', '$domain.$selector.key'),
-  sign_local = true,
-  selector = 'dkim',
-  symbol = 'DKIM_SIGNED',
-  try_fallback = true,
-  use_domain = 'header',
-  use_esld = true,
-  use_redis = false,
-  key_prefix = 'dkim_keys', -- default hash name
-}
-
-local E = {}
-local N = 'dkim_signing'
-local redis_params
-
-local function simple_template(tmpl, keys)
-  local lpeg = require "lpeg"
-
-  local var_lit = lpeg.P { lpeg.R("az") + lpeg.R("AZ") + lpeg.R("09") + "_" }
-  local var = lpeg.P { (lpeg.P("$") / "") * ((var_lit^1) / keys) }
-  local var_braced = lpeg.P { (lpeg.P("${") / "") * ((var_lit^1) / keys) * (lpeg.P("}") / "") }
-
-  local template_grammar = lpeg.Cs((var + var_braced + 1)^0)
-
-  return lpeg.match(template_grammar, tmpl)
-end
-
-local function dkim_signing_cb(task)
-  local is_local, is_sign_networks
-  local auser = task:get_user()
-  local ip = task:get_from_ip()
-  if ip and ip:is_local() then
-    is_local = true
-  end
-  if settings.auth_only and not auser then
-    if (settings.sign_networks and settings.sign_networks:get_key(ip)) then
-      is_sign_networks = true
-      rspamd_logger.debugm(N, task, 'mail is from address in sign_networks')
-    elseif settings.sign_local and is_local then
-      rspamd_logger.debugm(N, task, 'mail is from local address')
-    else
-      rspamd_logger.debugm(N, task, 'ignoring unauthenticated mail')
-      return
-    end
-  end
-  local efrom = task:get_from('smtp')
-  if not settings.allow_envfrom_empty and
-      #(((efrom or E)[1] or E).addr or '') == 0 then
-    rspamd_logger.debugm(N, task, 'empty envelope from not allowed')
-    return false
-  end
-  local hfrom = task:get_from('mime')
-  if not settings.allow_hdrfrom_multiple and (hfrom or E)[2] then
-    rspamd_logger.debugm(N, task, 'multiple header from not allowed')
-    return false
-  end
-  local dkim_domain
-  local hdom = ((hfrom or E)[1] or E).domain
-  local edom = ((efrom or E)[1] or E).domain
-  if hdom then
-    hdom = hdom:lower()
-  end
-  if edom then
-    edom = edom:lower()
-  end
-  if settings.use_domain_sign_networks and is_sign_networks then
-    if settings.use_domain_sign_networks == 'header' then
-      dkim_domain = hdom
-    else
-      dkim_domain = edom
-    end
-  elseif settings.use_domain_local and is_local then
-    if settings.use_domain_local == 'header' then
-      dkim_domain = hdom
-    else
-      dkim_domain = edom
-    end
-  else
-    if settings.use_domain == 'header' then
-      dkim_domain = hdom
-    else
-      dkim_domain = edom
-    end
-  end
-  if not dkim_domain then
-    rspamd_logger.debugm(N, task, 'could not extract dkim domain')
-    return false
-  end
-  if settings.use_esld then
-    dkim_domain = rspamd_util.get_tld(dkim_domain)
-    if settings.use_domain == 'envelope' and hdom then
-      hdom = rspamd_util.get_tld(hdom)
-    elseif settings.use_domain == 'header' and edom then
-      edom = rspamd_util.get_tld(edom)
-    end
-  end
-  if edom and hdom and not settings.allow_hdrfrom_mismatch and hdom ~= edom then
-    if settings.allow_hdrfrom_mismatch_local and is_local then
-      rspamd_logger.debugm(N, task, 'domain mismatch allowed for local IP: %1 != %2', hdom, edom)
-    elseif settings.allow_hdrfrom_mismatch_sign_networks and is_sign_networks then
-      rspamd_logger.debugm(N, task, 'domain mismatch allowed for sign_networks: %1 != %2', hdom, edom)
-    else
-      rspamd_logger.debugm(N, task, 'domain mismatch not allowed: %1 != %2', hdom, edom)
-      return false
-    end
-  end
-  if auser and not settings.allow_username_mismatch then
-    local udom = string.match(auser, '.*@(.*)')
-    if not udom then
-      rspamd_logger.debugm(N, task, 'couldnt find domain in username')
-      return false
-    end
-    if settings.use_esld then
-      udom = rspamd_util.get_tld(udom)
-    end
-    if udom ~= dkim_domain then
-      rspamd_logger.debugm(N, task, 'user domain mismatch')
-      return false
-    end
-  end
-  local p = {}
-  if settings.domain[dkim_domain] then
-    p.selector = settings.domain[dkim_domain].selector
-    p.key = settings.domain[dkim_domain].path
-  end
-  if not (p.key and p.selector) and not
-    (settings.try_fallback or settings.use_redis or settings.selector_map or settings.path_map) then
-    rspamd_logger.debugm(N, task, 'dkim unconfigured and fallback disabled')
-    return false
-  end
-  if not p.key then
-    if not settings.use_redis then
-      p.key = settings.path
-    end
-  end
-  if not p.selector then
-    p.selector = settings.selector
-  end
-  p.domain = dkim_domain
-
-  if settings.selector_map then
-    local data = settings.selector_map:get_key(dkim_domain)
-    if data then
-      p.selector = data
-    end
-  end
-  if settings.path_map then
-    local data = settings.path_map:get_key(dkim_domain)
-    if data then
-      p.key = data
-    end
-  end
-
-  if settings.use_redis then
-    local function try_redis_key(selector)
-      p.key = nil
-      p.selector = selector
-      local rk = string.format('%s.%s', p.selector, p.domain)
-      local function redis_key_cb(err, data)
-        if err or type(data) ~= 'string' then
-          rspamd_logger.infox(rspamd_config, "cannot make request to load DKIM key for %s: %s",
-            rk, err)
-        else
-          p.rawkey = data
-          if rspamd_plugins.dkim.sign(task, p) then
-            task:insert_result(settings.symbol, 1.0)
-          end
-        end
-      end
-      local ret = rspamd_redis_make_request(task,
-        redis_params, -- connect params
-        rk, -- hash key
-        false, -- is write
-        redis_key_cb, --callback
-        'HGET', -- command
-        {settings.key_prefix, rk} -- arguments
-      )
-      if not ret then
-        rspamd_logger.infox(rspamd_config, "cannot make request to load DKIM key for %s", rk)
-      end
-    end
-    if settings.selector_prefix then
-      rspamd_logger.infox(rspamd_config, "Using selector prefix %s for domain %s", settings.selector_prefix, p.domain);
-      local function redis_selector_cb(err, data)
-        if err or type(data) ~= 'string' then
-          rspamd_logger.infox(rspamd_config, "cannot make request to load DKIM selector for domain %s: %s", p.domain, err)
-        else
-          try_redis_key(data)
-        end
-      end
-      local ret = rspamd_redis_make_request(task,
-        redis_params, -- connect params
-        p.domain, -- hash key
-        false, -- is write
-        redis_selector_cb, --callback
-        'HGET', -- command
-        {settings.selector_prefix, p.domain} -- arguments
-      )
-      if not ret then
-        rspamd_logger.infox(rspamd_config, "cannot make request to load DKIM selector for %s", p.domain)
-      end
-    else
-      if not p.selector then
-        rspamd_logger.errx(task, 'No selector specified')
-        return false
-      end
-      try_redis_key(p.selector)
-    end
-  else
-    if (p.key and p.selector) then
-      p.key = simple_template(p.key, {domain = p.domain, selector = p.selector})
-      return rspamd_plugins.dkim.sign(task, p)
-    else
-      rspamd_logger.infox(task, 'key path or dkim selector unconfigured; no signing')
-      return false
-    end
-  end
-end
-
-local opts =  rspamd_config:get_all_opt('dkim_signing')
-if not opts then return end
-for k,v in pairs(opts) do
-  if k == 'sign_networks' then
-    settings[k] = rspamd_map_add(N, k, 'radix', 'DKIM signing networks')
-  elseif k == 'path_map' then
-    settings[k] = rspamd_map_add(N, k, 'map', 'Paths to DKIM signing keys')
-  elseif k == 'selector_map' then
-    settings[k] = rspamd_map_add(N, k, 'map', 'DKIM selectors')
-  else
-    settings[k] = v
-  end
-end
-if not (settings.use_redis or settings.path or settings.domain or settings.path_map or settings.selector_map) then
-  rspamd_logger.infox(rspamd_config, 'mandatory parameters missing, disable dkim signing')
-  return
-end
-if settings.use_redis then
-  redis_params = rspamd_parse_redis_server('dkim_signing')
-
-  if not redis_params then
-    rspamd_logger.errx(rspamd_config, 'no servers are specified, but module is configured to load keys from redis, disable dkim signing')
-    return
-  end
-end
-if settings.use_domain ~= 'header' and settings.use_domain ~= 'envelope' then
-  rspamd_logger.errx(rspamd_config, "Value for 'use_domain' is invalid")
-  settings.use_domain = 'header'
-end
-
-rspamd_config:register_symbol({
-  name = settings['symbol'],
-  callback = dkim_signing_cb
-})

+ 387 - 0
data/Dockerfiles/rspamd/milter_headers.lua

@@ -0,0 +1,387 @@
+--[[
+Copyright (c) 2016, Andrew Lewis <nerf@judo.za.org>
+Copyright (c) 2016, Vsevolod Stakhov <vsevolod@highsecure.ru>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+]]--
+
+if confighelp then
+  return
+end
+
+-- A plugin that provides common header manipulations
+
+local logger = require "rspamd_logger"
+local util = require "rspamd_util"
+local N = 'milter_headers'
+local E = {}
+
+local HOSTNAME = util.get_hostname()
+
+local settings = {
+  skip_local = false,
+  skip_authenticated = false,
+  routines = {
+    ['x-spamd-result'] = {
+      header = 'X-Spamd-Result',
+      remove = 1,
+    },
+    ['x-rspamd-server'] = {
+      header = 'X-Rspamd-Server',
+      remove = 1,
+    },
+    ['x-rspamd-queue-id'] = {
+      header = 'X-Rspamd-Queue-Id',
+      remove = 1,
+    },
+    ['spam-header'] = {
+      header = 'Deliver-To',
+      value = 'Junk',
+      remove = 1,
+    },
+    ['x-virus'] = {
+      header = 'X-Virus',
+      remove = 1,
+      symbols = {}, -- needs config
+    },
+    ['x-spamd-bar'] = {
+      header = 'X-Spamd-Bar',
+      positive = '+',
+      negative = '-',
+      neutral = '/',
+      remove = 1,
+    },
+    ['x-spam-level'] = {
+      header = 'X-Spam-Level',
+      char = '*',
+      remove = 1,
+    },
+    ['x-spam-status'] = {
+      header = 'X-Spam-Status',
+      remove = 1,
+    },
+    ['authentication-results'] = {
+      header = 'Authentication-Results',
+      remove = 1,
+      spf_symbols = {
+        pass = 'R_SPF_ALLOW',
+        fail = 'R_SPF_FAIL',
+        softfail = 'R_SPF_SOFTFAIL',
+        neutral = 'R_SPF_NEUTRAL',
+        temperror = 'R_SPF_DNSFAIL',
+        none = 'R_SPF_NA',
+        permerror = 'R_SPF_PERMFAIL',
+      },
+      dkim_symbols = {
+        pass = 'R_DKIM_ALLOW',
+        fail = 'R_DKIM_REJECT',
+        temperror = 'R_DKIM_TEMPFAIL',
+        none = 'R_DKIM_NA',
+        permerror = 'R_DKIM_PERMFAIL',
+      },
+      dmarc_symbols = {
+        pass = 'DMARC_POLICY_ALLOW',
+        permerror = 'DMARC_BAD_POLICY',
+        temperror = 'DMARC_DNSFAIL',
+        none = 'DMARC_NA',
+        reject = 'DMARC_POLICY_REJECT',
+        softfail = 'DMARC_POLICY_SOFTFAIL',
+        quarantine = 'DMARC_POLICY_QUARANTINE',
+      },
+    },
+  },
+}
+
+local active_routines = {}
+local custom_routines = {}
+
+local function milter_headers(task)
+
+  if settings.skip_local then
+    local ip = task:get_ip()
+    if (ip and ip:is_local()) then return end
+  end
+
+  if settings.skip_authenticated then
+    if task:get_user() ~= nil then return end
+  end
+
+  local routines, common, add, remove = {}, {}, {}, {}
+
+  routines['x-spamd-result'] = function()
+    if not common.symbols then
+      common.symbols = task:get_symbols_all()
+      common['metric_score'] = task:get_metric_score('default')
+      common['metric_action'] = task:get_metric_action('default')
+    end
+    if settings.routines['x-spamd-result'].remove then
+      remove[settings.routines['x-spamd-result'].header] = settings.routines['x-spamd-result'].remove
+    end
+    local buf = {}
+    table.insert(buf, table.concat({
+      'default: ', (common['metric_action'] == 'reject') and 'True' or 'False', ' [',
+      common['metric_score'][1], ' / ', common['metric_score'][2], ']'
+    }))
+    for _, s in ipairs(common.symbols) do
+      if not s.options then s.options = {} end
+      table.insert(buf, table.concat({
+        ' ', s.name, ' (', s.score, ') [', table.concat(s.options, ','), ']',
+      }))
+    end
+    add[settings.routines['x-spamd-result'].header] = table.concat(buf, '\n')
+  end
+
+  routines['x-rspamd-queue-id'] = function()
+    if common.queue_id ~= false then
+      common.queue_id = task:get_queue_id()
+      if not common.queue_id then
+        common.queue_id = false
+      end
+    end
+    if settings.routines['x-rspamd-queue-id'].remove then
+      remove[settings.routines['x-rspamd-queue-id'].header] = settings.routines['x-rspamd-queue-id'].remove
+    end
+    if common.queue_id then
+      add[settings.routines['x-rspamd-queue-id'].header] = common.queue_id
+    end
+  end
+
+  routines['x-rspamd-server'] = function()
+    if settings.routines['x-rspamd-server'].remove then
+      remove[settings.routines['x-rspamd-server'].header] = settings.routines['x-rspamd-server'].remove
+    end
+    add[settings.routines['x-rspamd-server'].header] = HOSTNAME
+  end
+
+  routines['x-spamd-bar'] = function()
+    if not common['metric_score'] then
+      common['metric_score'] = task:get_metric_score('default')
+    end
+    local score = common['metric_score'][1]
+    local spambar
+    if score <= -1 then
+      spambar = string.rep(settings.routines['x-spamd-bar'].negative, score*-1)
+    elseif score >= 1 then
+      spambar = string.rep(settings.routines['x-spamd-bar'].positive, score)
+    else
+      spambar = settings.routines['x-spamd-bar'].neutral
+    end
+    if settings.routines['x-spamd-bar'].remove then
+      remove[settings.routines['x-spamd-bar'].header] = settings.routines['x-spamd-bar'].remove
+    end
+    if spambar ~= '' then
+      add[settings.routines['x-spamd-bar'].header] = spambar
+    end
+  end
+
+  routines['x-spam-level'] = function()
+    if not common['metric_score'] then
+      common['metric_score'] = task:get_metric_score('default')
+    end
+    local score = common['metric_score'][1]
+    if score < 1 then
+      return nil, {}, {}
+    end
+    if settings.routines['x-spam-level'].remove then
+      remove[settings.routines['x-spam-level'].header] = settings.routines['x-spam-level'].remove
+    end
+    add[settings.routines['x-spam-level'].header] = string.rep(settings.routines['x-spam-level'].char, score)
+  end
+
+  routines['spam-header'] = function()
+    if not common['metric_action'] then
+      common['metric_action'] = task:get_metric_action('default')
+    end
+    if settings.routines['spam-header'].remove then
+      remove[settings.routines['spam-header'].header] = settings.routines['spam-header'].remove
+    end
+    local action = common['metric_action']
+    if action ~= 'no action' and action ~= 'greylist' then
+      add[settings.routines['spam-header'].header] = settings.routines['spam-header'].value
+    end
+  end
+
+  routines['x-virus'] = function()
+    if not common.symbols then
+      common.symbols = {}
+    end
+    if settings.routines['x-virus'].remove then
+      remove[settings.routines['x-virus'].header] = settings.routines['x-virus'].remove
+    end
+    local virii = {}
+    for _, sym in ipairs(settings.routines['x-virus'].symbols) do
+      if not (common.symbols[sym] == false) then
+        local s = task:get_symbol(sym)
+        if not s then
+          common.symbols[sym] = false
+        else
+          common.symbols[sym] = s
+          if (((s or E)[1] or E).options or E)[1] then
+            table.insert(virii, s[1].options[1])
+          else
+            table.insert(virii, 'unknown')
+          end
+        end
+      end
+    end
+    if #virii > 0 then
+      add[settings.routines['x-virus'].header] = table.concat(virii, ',')
+    end
+  end
+
+  routines['x-spam-status'] = function()
+    if not common['metric_score'] then
+      common['metric_score'] = task:get_metric_score('default')
+    end
+    if not common['metric_action'] then
+      common['metric_action'] = task:get_metric_action('default')
+    end
+    local score = common['metric_score'][1]
+    local action = common['metric_action']
+    local is_spam
+    local spamstatus
+    if action ~= 'no action' and action ~= 'greylist' then
+      is_spam = 'Yes'
+    else
+      is_spam = 'No'
+    end
+    spamstatus = is_spam .. ', score=' .. string.format('%.2f', score)
+    if settings.routines['x-spam-status'].remove then
+      remove[settings.routines['x-spam-status'].header] = settings.routines['x-spam-status'].remove
+    end
+    add[settings.routines['x-spam-status'].header] = spamstatus
+  end
+
+  routines['authentication-results'] = function()
+    local ar = require "auth_results"
+
+    if settings.routines['authentication-results'].remove then
+      remove[settings.routines['authentication-results'].header] =
+          settings.routines['authentication-results'].remove
+    end
+
+    local res = ar.gen_auth_results(task,
+      settings.routines['authentication-results'])
+
+    if res then
+      add[settings.routines['authentication-results'].header] = res
+    end
+  end
+
+  for _, n in ipairs(active_routines) do
+    local ok, err
+    if custom_routines[n] then
+      local to_add, to_remove, common_in
+      ok, err, to_add, to_remove, common_in = pcall(custom_routines[n], task, common)
+      if ok then
+        for k, v in pairs(to_add) do
+          add[k] = v
+        end
+        for k, v in pairs(to_remove) do
+          add[k] = v
+        end
+        for k, v in pairs(common_in) do
+          if type(v) == 'table' then
+            if not common[k] then
+              common[k] = {}
+            end
+            for kk, vv in pairs(v) do
+              common[k][kk] = vv
+            end
+          else
+            common[k] = v
+          end
+        end
+      end
+    else
+      ok, err = pcall(routines[n])
+    end
+    if not ok then
+      logger.errx(task, 'call to %s failed: %s', n, err)
+    end
+  end
+
+  if not next(add) then add = nil end
+  if not next(remove) then remove = nil end
+  if add or remove then
+    task:set_milter_reply({
+      add_headers = add,
+      remove_headers = remove
+    })
+  end
+end
+
+local opts = rspamd_config:get_all_opt(N) or rspamd_config:get_all_opt('rmilter_headers')
+if not opts then return end
+
+if type(opts['use']) == 'string' then
+  opts['use'] = {opts['use']}
+elseif (type(opts['use']) == 'table' and not opts['use'][1]) then
+  logger.debugm(N, rspamd_config, 'no functions are enabled')
+  return
+end
+if type(opts['use']) ~= 'table' then
+  logger.errx(rspamd_config, 'unexpected type for "use" option: %s', type(opts['use']))
+  return
+end
+if type(opts['custom']) == 'table' then
+  for k, v in pairs(opts['custom']) do
+    local f, err = load(v)
+    if not f then
+      logger.errx(rspamd_config, 'could not load "%s": %s', k, err)
+    else
+      custom_routines[k] = f()
+    end
+  end
+end
+local have_routine = {}
+local function activate_routine(s)
+  if settings.routines[s] or custom_routines[s] then
+    have_routine[s] = true
+    table.insert(active_routines, s)
+    if (opts.routines and opts.routines[s]) then
+      for k, v in pairs(opts.routines[s]) do
+        settings.routines[s][k] = v
+      end
+    end
+  else
+    logger.errx(rspamd_config, 'routine "%s" does not exist', s)
+  end
+end
+if opts['extended_spam_headers'] then
+  activate_routine('x-spamd-result')
+  activate_routine('x-rspamd-server')
+  activate_routine('x-rspamd-queue-id')
+end
+if opts['skip_local'] then
+  settings.skip_local = true
+end
+if opts['skip_authenticated'] then
+  settings.skip_authenticated = true
+end
+for _, s in ipairs(opts['use']) do
+  if not have_routine[s] then
+    activate_routine(s)
+  end
+end
+if (#active_routines < 1) then
+  logger.errx(rspamd_config, 'no active routines')
+  return
+end
+logger.infox(rspamd_config, 'active routines [%s]', table.concat(active_routines, ','))
+rspamd_config:register_symbol({
+  name = 'MILTER_HEADERS',
+  type = 'postfilter',
+  callback = milter_headers,
+  priority = 10
+})

+ 0 - 41
data/conf/rmilter/rmilter.conf

@@ -1,41 +0,0 @@
-bind_socket = inet:9900;
-spamd {
-        servers = r:rspamd:11333;
-        connect_timeout = 1s;
-        results_timeout = 20s;
-        error_time = 10;
-        dead_time = 300;
-        maxerrors = 10;
-        reject_message = "Spam or virus message rejected due to high detection score";
-        whitelist = 127.0.0.1/32, [::1]/128;
-        spamd_soft_fail = yes;
-        rspamd_metric = "default";
-        extended_spam_headers = yes;
-        spam_header = "X-Spam-Flag";
-        spam_header_value = "YES";
-};
-redis {
-        servers_grey = redis:6379;
-        servers_limits = redis:6379;
-        servers_id = redis:6379;
-        id_prefix = "message_id.";
-        grey_prefix = "grey.";
-        white_prefix = "white.";
-        connect_timeout = 1s;
-        error_time = 10;
-        dead_time = 300;
-        maxerrors = 10;
-};
-tempdir = /tmp;
-tempfiles_mode = 00600;
-strict_auth = yes;
-use_dcc = no;
-limits {
-        enable = false;
-};
-greylisting {
-        enable = false;
-}
-dkim {
-        enable = false;
-};