mirror of
https://github.com/mailcow/mailcow-dockerized.git
synced 2026-06-17 03:50:30 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ae531fd7b0 | |||
| 3507ff2773 | |||
| 4132f6bd48 |
@@ -12,7 +12,7 @@ jobs:
|
|||||||
with:
|
with:
|
||||||
fetch-depth: 0
|
fetch-depth: 0
|
||||||
- name: Run the Action
|
- name: Run the Action
|
||||||
uses: devops-infra/action-pull-request@v0.6.1
|
uses: devops-infra/action-pull-request@v1.0.2
|
||||||
with:
|
with:
|
||||||
github_token: ${{ secrets.PRTONIGHTLY_ACTION_PAT }}
|
github_token: ${{ secrets.PRTONIGHTLY_ACTION_PAT }}
|
||||||
title: Automatic PR to nightly from ${{ github.event.repository.updated_at}}
|
title: Automatic PR to nightly from ${{ github.event.repository.updated_at}}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
FROM php:8.2-fpm-alpine3.21
|
FROM php:8.4-fpm-alpine3.22
|
||||||
|
|
||||||
LABEL maintainer = "The Infrastructure Company GmbH <info@servercow.de>"
|
LABEL maintainer="The Infrastructure Company GmbH <info@servercow.de>"
|
||||||
|
|
||||||
# renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
# renovate: datasource=github-tags depName=krakjoe/apcu versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
||||||
ARG APCU_PECL_VERSION=5.1.27
|
ARG APCU_PECL_VERSION=5.1.27
|
||||||
@@ -14,8 +14,12 @@ ARG MEMCACHED_PECL_VERSION=3.3.0
|
|||||||
ARG REDIS_PECL_VERSION=6.2.0
|
ARG REDIS_PECL_VERSION=6.2.0
|
||||||
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced extractVersion=(?<version>.*)$
|
# renovate: datasource=github-tags depName=composer/composer versioning=semver-coerced extractVersion=(?<version>.*)$
|
||||||
ARG COMPOSER_VERSION=2.8.6
|
ARG COMPOSER_VERSION=2.8.6
|
||||||
|
# renovate: datasource=github-tags depName=php/pecl-text-pspell versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
||||||
|
ARG PSPELL_PECL_VERSION=1.0.1
|
||||||
|
# renovate: datasource=github-tags depName=php/pecl-mail-imap versioning=semver-coerced extractVersion=^v(?<version>.*)$
|
||||||
|
ARG IMAP_PECL_VERSION=1.0.3
|
||||||
|
|
||||||
RUN apk add -U --no-cache autoconf \
|
RUN apk update && apk add -U --no-cache autoconf \
|
||||||
aspell-dev \
|
aspell-dev \
|
||||||
aspell-libs \
|
aspell-libs \
|
||||||
bash \
|
bash \
|
||||||
@@ -68,7 +72,9 @@ RUN apk add -U --no-cache autoconf \
|
|||||||
&& pecl install mailparse-${MAILPARSE_PECL_VERSION} \
|
&& pecl install mailparse-${MAILPARSE_PECL_VERSION} \
|
||||||
&& pecl install memcached-${MEMCACHED_PECL_VERSION} \
|
&& pecl install memcached-${MEMCACHED_PECL_VERSION} \
|
||||||
&& pecl install redis-${REDIS_PECL_VERSION} \
|
&& pecl install redis-${REDIS_PECL_VERSION} \
|
||||||
&& docker-php-ext-enable apcu imagick memcached mailparse redis \
|
&& pecl install pspell-${PSPELL_PECL_VERSION} \
|
||||||
|
&& pecl install --configureoptions='with-kerberos="no" with-imap="yes" with-imap-ssl="yes"' imap-${IMAP_PECL_VERSION} \
|
||||||
|
&& docker-php-ext-enable apcu imagick memcached mailparse redis pspell imap \
|
||||||
&& pecl clear-cache \
|
&& pecl clear-cache \
|
||||||
&& docker-php-ext-configure intl \
|
&& docker-php-ext-configure intl \
|
||||||
&& docker-php-ext-configure exif \
|
&& docker-php-ext-configure exif \
|
||||||
@@ -77,9 +83,7 @@ RUN apk add -U --no-cache autoconf \
|
|||||||
--with-webp \
|
--with-webp \
|
||||||
--with-xpm \
|
--with-xpm \
|
||||||
--with-avif \
|
--with-avif \
|
||||||
&& docker-php-ext-install -j 4 exif gd gettext intl ldap opcache pcntl pdo pdo_mysql pspell soap sockets zip bcmath gmp \
|
&& docker-php-ext-install -j 4 exif gd gettext intl ldap opcache pcntl pdo pdo_mysql soap sockets zip bcmath gmp \
|
||||||
&& docker-php-ext-configure imap --with-imap --with-imap-ssl \
|
|
||||||
&& docker-php-ext-install -j 4 imap \
|
|
||||||
&& curl --silent --show-error https://getcomposer.org/installer | php -- --version=${COMPOSER_VERSION} \
|
&& curl --silent --show-error https://getcomposer.org/installer | php -- --version=${COMPOSER_VERSION} \
|
||||||
&& mv composer.phar /usr/local/bin/composer \
|
&& mv composer.phar /usr/local/bin/composer \
|
||||||
&& chmod +x /usr/local/bin/composer \
|
&& chmod +x /usr/local/bin/composer \
|
||||||
|
|||||||
@@ -146,171 +146,8 @@ rspamd_config:register_symbol({
|
|||||||
return false
|
return false
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Helper function to parse IPv6 into 8 segments
|
|
||||||
local function ipv6_to_segments(ip_str)
|
|
||||||
-- Remove zone identifier if present (e.g., %eth0)
|
|
||||||
ip_str = ip_str:gsub("%%.*$", "")
|
|
||||||
|
|
||||||
local segments = {}
|
|
||||||
|
|
||||||
-- Handle :: compression
|
|
||||||
if ip_str:find('::') then
|
|
||||||
local before, after = ip_str:match('^(.*)::(.*)$')
|
|
||||||
before = before or ''
|
|
||||||
after = after or ''
|
|
||||||
|
|
||||||
local before_parts = {}
|
|
||||||
local after_parts = {}
|
|
||||||
|
|
||||||
if before ~= '' then
|
|
||||||
for seg in before:gmatch('[^:]+') do
|
|
||||||
table.insert(before_parts, tonumber(seg, 16) or 0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if after ~= '' then
|
|
||||||
for seg in after:gmatch('[^:]+') do
|
|
||||||
table.insert(after_parts, tonumber(seg, 16) or 0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Add before segments
|
|
||||||
for _, seg in ipairs(before_parts) do
|
|
||||||
table.insert(segments, seg)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Add compressed zeros
|
|
||||||
local zeros_needed = 8 - #before_parts - #after_parts
|
|
||||||
for i = 1, zeros_needed do
|
|
||||||
table.insert(segments, 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Add after segments
|
|
||||||
for _, seg in ipairs(after_parts) do
|
|
||||||
table.insert(segments, seg)
|
|
||||||
end
|
|
||||||
else
|
|
||||||
-- No compression
|
|
||||||
for seg in ip_str:gmatch('[^:]+') do
|
|
||||||
table.insert(segments, tonumber(seg, 16) or 0)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Ensure we have exactly 8 segments
|
|
||||||
while #segments < 8 do
|
|
||||||
table.insert(segments, 0)
|
|
||||||
end
|
|
||||||
|
|
||||||
return segments
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Generate all common IPv6 notations
|
|
||||||
local function get_ipv6_variants(ip_str)
|
|
||||||
local variants = {}
|
|
||||||
local seen = {}
|
|
||||||
|
|
||||||
local function add_variant(v)
|
|
||||||
if v and not seen[v] then
|
|
||||||
table.insert(variants, v)
|
|
||||||
seen[v] = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- For IPv4, just return the original
|
|
||||||
if not ip_str:find(':') then
|
|
||||||
add_variant(ip_str)
|
|
||||||
return variants
|
|
||||||
end
|
|
||||||
|
|
||||||
local segments = ipv6_to_segments(ip_str)
|
|
||||||
|
|
||||||
-- 1. Fully expanded form (all zeros shown as 0000)
|
|
||||||
local expanded_parts = {}
|
|
||||||
for _, seg in ipairs(segments) do
|
|
||||||
table.insert(expanded_parts, string.format('%04x', seg))
|
|
||||||
end
|
|
||||||
add_variant(table.concat(expanded_parts, ':'))
|
|
||||||
|
|
||||||
-- 2. Standard form (no leading zeros, but all segments present)
|
|
||||||
local standard_parts = {}
|
|
||||||
for _, seg in ipairs(segments) do
|
|
||||||
table.insert(standard_parts, string.format('%x', seg))
|
|
||||||
end
|
|
||||||
add_variant(table.concat(standard_parts, ':'))
|
|
||||||
|
|
||||||
-- 3. Find all possible :: compressions
|
|
||||||
-- RFC 5952: compress the longest run of consecutive zeros
|
|
||||||
-- But we need to check all possibilities since Redis might have any form
|
|
||||||
|
|
||||||
-- Find all zero runs
|
|
||||||
local zero_runs = {}
|
|
||||||
local in_run = false
|
|
||||||
local run_start = 0
|
|
||||||
local run_length = 0
|
|
||||||
|
|
||||||
for i = 1, 8 do
|
|
||||||
if segments[i] == 0 then
|
|
||||||
if not in_run then
|
|
||||||
in_run = true
|
|
||||||
run_start = i
|
|
||||||
run_length = 1
|
|
||||||
else
|
|
||||||
run_length = run_length + 1
|
|
||||||
end
|
|
||||||
else
|
|
||||||
if in_run then
|
|
||||||
if run_length >= 1 then -- Allow single zero compression too
|
|
||||||
table.insert(zero_runs, {start = run_start, length = run_length})
|
|
||||||
end
|
|
||||||
in_run = false
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Don't forget the last run
|
|
||||||
if in_run and run_length >= 1 then
|
|
||||||
table.insert(zero_runs, {start = run_start, length = run_length})
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Generate variant for each zero run compression
|
|
||||||
for _, run in ipairs(zero_runs) do
|
|
||||||
local parts = {}
|
|
||||||
|
|
||||||
-- Before compression
|
|
||||||
for i = 1, run.start - 1 do
|
|
||||||
table.insert(parts, string.format('%x', segments[i]))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- The compression
|
|
||||||
if run.start == 1 then
|
|
||||||
table.insert(parts, '')
|
|
||||||
table.insert(parts, '')
|
|
||||||
elseif run.start + run.length - 1 == 8 then
|
|
||||||
table.insert(parts, '')
|
|
||||||
table.insert(parts, '')
|
|
||||||
else
|
|
||||||
table.insert(parts, '')
|
|
||||||
end
|
|
||||||
|
|
||||||
-- After compression
|
|
||||||
for i = run.start + run.length, 8 do
|
|
||||||
table.insert(parts, string.format('%x', segments[i]))
|
|
||||||
end
|
|
||||||
|
|
||||||
local compressed = table.concat(parts, ':'):gsub('::+', '::')
|
|
||||||
add_variant(compressed)
|
|
||||||
end
|
|
||||||
|
|
||||||
return variants
|
|
||||||
end
|
|
||||||
|
|
||||||
local from_ip_string = tostring(ip)
|
local from_ip_string = tostring(ip)
|
||||||
local ip_check_table = {}
|
ip_check_table = {from_ip_string}
|
||||||
|
|
||||||
-- Add all variants of the exact IP
|
|
||||||
for _, variant in ipairs(get_ipv6_variants(from_ip_string)) do
|
|
||||||
table.insert(ip_check_table, variant)
|
|
||||||
end
|
|
||||||
|
|
||||||
local maxbits = 128
|
local maxbits = 128
|
||||||
local minbits = 32
|
local minbits = 32
|
||||||
@@ -318,18 +155,10 @@ rspamd_config:register_symbol({
|
|||||||
maxbits = 32
|
maxbits = 32
|
||||||
minbits = 8
|
minbits = 8
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Add all CIDR notations with variants
|
|
||||||
for i=maxbits,minbits,-1 do
|
for i=maxbits,minbits,-1 do
|
||||||
local masked_ip = ip:apply_mask(i)
|
local nip = ip:apply_mask(i):to_string() .. "/" .. i
|
||||||
local cidr_base = masked_ip:to_string()
|
table.insert(ip_check_table, nip)
|
||||||
|
|
||||||
for _, variant in ipairs(get_ipv6_variants(cidr_base)) do
|
|
||||||
local cidr = variant .. "/" .. i
|
|
||||||
table.insert(ip_check_table, cidr)
|
|
||||||
end
|
end
|
||||||
end
|
|
||||||
|
|
||||||
local function keep_spam_cb(err, data)
|
local function keep_spam_cb(err, data)
|
||||||
if err then
|
if err then
|
||||||
rspamd_logger.infox(rspamd_config, "keep_spam query request for ip %s returned invalid or empty data (\"%s\") or error (\"%s\")", ip, data, err)
|
rspamd_logger.infox(rspamd_config, "keep_spam query request for ip %s returned invalid or empty data (\"%s\") or error (\"%s\")", ip, data, err)
|
||||||
@@ -337,15 +166,12 @@ rspamd_config:register_symbol({
|
|||||||
else
|
else
|
||||||
for k,v in pairs(data) do
|
for k,v in pairs(data) do
|
||||||
if (v and v ~= userdata and v == '1') then
|
if (v and v ~= userdata and v == '1') then
|
||||||
rspamd_logger.infox(rspamd_config, "found ip %s (checked as: %s) in keep_spam map, setting pre-result accept", from_ip_string, ip_check_table[k])
|
rspamd_logger.infox(rspamd_config, "found ip in keep_spam map, setting pre-result")
|
||||||
task:set_pre_result('accept', 'ip matched with forward hosts', 'keep_spam')
|
task:set_pre_result('accept', 'ip matched with forward hosts', 'keep_spam')
|
||||||
task:set_flag('no_stat')
|
|
||||||
return
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
table.insert(ip_check_table, 1, 'KEEP_SPAM')
|
table.insert(ip_check_table, 1, 'KEEP_SPAM')
|
||||||
local redis_ret_user = rspamd_redis_make_request(task,
|
local redis_ret_user = rspamd_redis_make_request(task,
|
||||||
redis_params, -- connect params
|
redis_params, -- connect params
|
||||||
@@ -384,7 +210,6 @@ rspamd_config:register_symbol({
|
|||||||
rspamd_config:register_symbol({
|
rspamd_config:register_symbol({
|
||||||
name = 'TAG_MOO',
|
name = 'TAG_MOO',
|
||||||
type = 'postfilter',
|
type = 'postfilter',
|
||||||
flags = 'ignore_passthrough',
|
|
||||||
callback = function(task)
|
callback = function(task)
|
||||||
local util = require("rspamd_util")
|
local util = require("rspamd_util")
|
||||||
local rspamd_logger = require "rspamd_logger"
|
local rspamd_logger = require "rspamd_logger"
|
||||||
@@ -393,6 +218,9 @@ rspamd_config:register_symbol({
|
|||||||
local rcpts = task:get_recipients('smtp')
|
local rcpts = task:get_recipients('smtp')
|
||||||
local lua_util = require "lua_util"
|
local lua_util = require "lua_util"
|
||||||
|
|
||||||
|
local tagged_rcpt = task:get_symbol("TAGGED_RCPT")
|
||||||
|
local mailcow_domain = task:get_symbol("RCPT_MAILCOW_DOMAIN")
|
||||||
|
|
||||||
local function remove_moo_tag()
|
local function remove_moo_tag()
|
||||||
local moo_tag_header = task:get_header('X-Moo-Tag', false)
|
local moo_tag_header = task:get_header('X-Moo-Tag', false)
|
||||||
if moo_tag_header then
|
if moo_tag_header then
|
||||||
@@ -403,81 +231,32 @@ rspamd_config:register_symbol({
|
|||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Check if we have exactly one recipient
|
if tagged_rcpt and tagged_rcpt[1].options and mailcow_domain then
|
||||||
if not (rcpts and #rcpts == 1) then
|
local tag = tagged_rcpt[1].options[1]
|
||||||
rspamd_logger.infox("TAG_MOO: not exactly one rcpt (%s), removing moo tag", rcpts and #rcpts or 0)
|
rspamd_logger.infox("found tag: %s", tag)
|
||||||
remove_moo_tag()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local rcpt_addr = rcpts[1]['addr']
|
|
||||||
local rcpt_user = rcpts[1]['user']
|
|
||||||
local rcpt_domain = rcpts[1]['domain']
|
|
||||||
|
|
||||||
-- Check if recipient has a tag (contains '+')
|
|
||||||
local tag = nil
|
|
||||||
if rcpt_user:find('%+') then
|
|
||||||
local base_user, tag_part = rcpt_user:match('^(.-)%+(.+)$')
|
|
||||||
if base_user and tag_part then
|
|
||||||
tag = tag_part
|
|
||||||
rspamd_logger.infox("TAG_MOO: found tag in recipient: %s (base: %s, tag: %s)", rcpt_addr, base_user, tag)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if not tag then
|
|
||||||
rspamd_logger.infox("TAG_MOO: no tag found in recipient %s, removing moo tag", rcpt_addr)
|
|
||||||
remove_moo_tag()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Optional: Check if domain is a mailcow domain
|
|
||||||
-- When KEEP_SPAM is active, RCPT_MAILCOW_DOMAIN might not be set
|
|
||||||
-- If the mail is being delivered, we can assume it's valid
|
|
||||||
local mailcow_domain = task:get_symbol("RCPT_MAILCOW_DOMAIN")
|
|
||||||
if not mailcow_domain then
|
|
||||||
rspamd_logger.infox("TAG_MOO: RCPT_MAILCOW_DOMAIN not set (possibly due to pre-result), proceeding anyway for domain %s", rcpt_domain)
|
|
||||||
end
|
|
||||||
|
|
||||||
local action = task:get_metric_action('default')
|
local action = task:get_metric_action('default')
|
||||||
rspamd_logger.infox("TAG_MOO: metric action: %s", action)
|
rspamd_logger.infox("metric action now: %s", action)
|
||||||
|
|
||||||
-- Check if we have a pre-result (e.g., from KEEP_SPAM or POSTMASTER_HANDLER)
|
if action ~= 'no action' and action ~= 'greylist' then
|
||||||
local allow_processing = false
|
rspamd_logger.infox("skipping tag handler for action: %s", action)
|
||||||
|
|
||||||
if task.has_pre_result then
|
|
||||||
local has_pre, pre_action = task:has_pre_result()
|
|
||||||
if has_pre then
|
|
||||||
rspamd_logger.infox("TAG_MOO: pre-result detected: %s", tostring(pre_action))
|
|
||||||
if pre_action == 'accept' then
|
|
||||||
allow_processing = true
|
|
||||||
rspamd_logger.infox("TAG_MOO: pre-result is accept, will process")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Allow processing for mild actions or when we have pre-result accept
|
|
||||||
if not allow_processing and action ~= 'no action' and action ~= 'greylist' then
|
|
||||||
rspamd_logger.infox("TAG_MOO: skipping tag handler for action: %s", action)
|
|
||||||
remove_moo_tag()
|
remove_moo_tag()
|
||||||
return true
|
return true
|
||||||
end
|
end
|
||||||
|
|
||||||
rspamd_logger.infox("TAG_MOO: processing allowed")
|
|
||||||
|
|
||||||
local function http_callback(err_message, code, body, headers)
|
local function http_callback(err_message, code, body, headers)
|
||||||
if body ~= nil and body ~= "" then
|
if body ~= nil and body ~= "" then
|
||||||
rspamd_logger.infox(rspamd_config, "TAG_MOO: expanding rcpt to \"%s\"", body)
|
rspamd_logger.infox(rspamd_config, "expanding rcpt to \"%s\"", body)
|
||||||
|
|
||||||
local function tag_callback_subject(err, data)
|
local function tag_callback_subject(err, data)
|
||||||
if err or type(data) ~= 'string' or data == '' then
|
if err or type(data) ~= 'string' then
|
||||||
rspamd_logger.infox(rspamd_config, "TAG_MOO: subject tag handler rcpt %s returned invalid or empty data (\"%s\") or error (\"%s\") - trying subfolder tag handler...", body, data, err)
|
rspamd_logger.infox(rspamd_config, "subject tag handler rcpt %s returned invalid or empty data (\"%s\") or error (\"%s\") - trying subfolder tag handler...", body, data, err)
|
||||||
|
|
||||||
local function tag_callback_subfolder(err, data)
|
local function tag_callback_subfolder(err, data)
|
||||||
if err or type(data) ~= 'string' or data == '' then
|
if err or type(data) ~= 'string' then
|
||||||
rspamd_logger.infox(rspamd_config, "TAG_MOO: subfolder tag handler for rcpt %s returned invalid or empty data (\"%s\") or error (\"%s\")", body, data, err)
|
rspamd_logger.infox(rspamd_config, "subfolder tag handler for rcpt %s returned invalid or empty data (\"%s\") or error (\"%s\")", body, data, err)
|
||||||
remove_moo_tag()
|
remove_moo_tag()
|
||||||
else
|
else
|
||||||
rspamd_logger.infox("TAG_MOO: User wants subfolder tag, adding X-Moo-Tag header")
|
rspamd_logger.infox("Add X-Moo-Tag header")
|
||||||
task:set_milter_reply({
|
task:set_milter_reply({
|
||||||
add_headers = {['X-Moo-Tag'] = 'YES'}
|
add_headers = {['X-Moo-Tag'] = 'YES'}
|
||||||
})
|
})
|
||||||
@@ -493,13 +272,13 @@ rspamd_config:register_symbol({
|
|||||||
{'RCPT_WANTS_SUBFOLDER_TAG', body} -- arguments
|
{'RCPT_WANTS_SUBFOLDER_TAG', body} -- arguments
|
||||||
)
|
)
|
||||||
if not redis_ret_subfolder then
|
if not redis_ret_subfolder then
|
||||||
rspamd_logger.infox(rspamd_config, "TAG_MOO: cannot make request to load tag handler for rcpt")
|
rspamd_logger.infox(rspamd_config, "cannot make request to load tag handler for rcpt")
|
||||||
remove_moo_tag()
|
remove_moo_tag()
|
||||||
end
|
end
|
||||||
|
|
||||||
else
|
else
|
||||||
rspamd_logger.infox("TAG_MOO: user wants subject modified for tagged mail")
|
rspamd_logger.infox("user wants subject modified for tagged mail")
|
||||||
local sbj = task:get_header('Subject') or ''
|
local sbj = task:get_header('Subject')
|
||||||
new_sbj = '=?UTF-8?B?' .. tostring(util.encode_base64('[' .. tag .. '] ' .. sbj)) .. '?='
|
new_sbj = '=?UTF-8?B?' .. tostring(util.encode_base64('[' .. tag .. '] ' .. sbj)) .. '?='
|
||||||
task:set_milter_reply({
|
task:set_milter_reply({
|
||||||
remove_headers = {
|
remove_headers = {
|
||||||
@@ -520,32 +299,33 @@ rspamd_config:register_symbol({
|
|||||||
{'RCPT_WANTS_SUBJECT_TAG', body} -- arguments
|
{'RCPT_WANTS_SUBJECT_TAG', body} -- arguments
|
||||||
)
|
)
|
||||||
if not redis_ret_subject then
|
if not redis_ret_subject then
|
||||||
rspamd_logger.infox(rspamd_config, "TAG_MOO: cannot make request to load tag handler for rcpt")
|
rspamd_logger.infox(rspamd_config, "cannot make request to load tag handler for rcpt")
|
||||||
remove_moo_tag()
|
remove_moo_tag()
|
||||||
end
|
end
|
||||||
else
|
|
||||||
rspamd_logger.infox("TAG_MOO: alias expansion returned empty body")
|
|
||||||
remove_moo_tag()
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
local rcpt_split = rspamd_str_split(rcpt_addr, '@')
|
if rcpts and #rcpts == 1 then
|
||||||
|
for _,rcpt in ipairs(rcpts) do
|
||||||
|
local rcpt_split = rspamd_str_split(rcpt['addr'], '@')
|
||||||
if #rcpt_split == 2 then
|
if #rcpt_split == 2 then
|
||||||
if rcpt_split[1]:match('^postmaster') then
|
if rcpt_split[1] == 'postmaster' then
|
||||||
rspamd_logger.infox(rspamd_config, "TAG_MOO: not expanding postmaster alias")
|
rspamd_logger.infox(rspamd_config, "not expanding postmaster alias")
|
||||||
remove_moo_tag()
|
remove_moo_tag()
|
||||||
else
|
else
|
||||||
rspamd_logger.infox("TAG_MOO: requesting alias expansion for %s", rcpt_addr)
|
|
||||||
rspamd_http.request({
|
rspamd_http.request({
|
||||||
task=task,
|
task=task,
|
||||||
url='http://nginx:8081/aliasexp.php',
|
url='http://nginx:8081/aliasexp.php',
|
||||||
body='',
|
body='',
|
||||||
callback=http_callback,
|
callback=http_callback,
|
||||||
headers={Rcpt=rcpt_addr},
|
headers={Rcpt=rcpt['addr']},
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
else
|
else
|
||||||
rspamd_logger.infox("TAG_MOO: invalid rcpt format")
|
|
||||||
remove_moo_tag()
|
remove_moo_tag()
|
||||||
end
|
end
|
||||||
end,
|
end,
|
||||||
@@ -555,7 +335,6 @@ rspamd_config:register_symbol({
|
|||||||
rspamd_config:register_symbol({
|
rspamd_config:register_symbol({
|
||||||
name = 'BCC',
|
name = 'BCC',
|
||||||
type = 'postfilter',
|
type = 'postfilter',
|
||||||
flags = 'ignore_passthrough',
|
|
||||||
callback = function(task)
|
callback = function(task)
|
||||||
local util = require("rspamd_util")
|
local util = require("rspamd_util")
|
||||||
local rspamd_http = require "rspamd_http"
|
local rspamd_http = require "rspamd_http"
|
||||||
@@ -584,13 +363,11 @@ rspamd_config:register_symbol({
|
|||||||
local email_content = tostring(task:get_content())
|
local email_content = tostring(task:get_content())
|
||||||
email_content = string.gsub(email_content, "\r\n%.", "\r\n..")
|
email_content = string.gsub(email_content, "\r\n%.", "\r\n..")
|
||||||
-- send mail
|
-- send mail
|
||||||
local from_smtp = task:get_from('smtp')
|
|
||||||
local from_addr = (from_smtp and from_smtp[1] and from_smtp[1].addr) or 'mailer-daemon@localhost'
|
|
||||||
lua_smtp.sendmail({
|
lua_smtp.sendmail({
|
||||||
task = task,
|
task = task,
|
||||||
host = os.getenv("IPV4_NETWORK") .. '.253',
|
host = os.getenv("IPV4_NETWORK") .. '.253',
|
||||||
port = 591,
|
port = 591,
|
||||||
from = from_addr,
|
from = task:get_from(stp)[1].addr,
|
||||||
recipients = bcc_dest,
|
recipients = bcc_dest,
|
||||||
helo = 'bcc',
|
helo = 'bcc',
|
||||||
timeout = 20,
|
timeout = 20,
|
||||||
@@ -620,41 +397,27 @@ rspamd_config:register_symbol({
|
|||||||
end
|
end
|
||||||
|
|
||||||
local action = task:get_metric_action('default')
|
local action = task:get_metric_action('default')
|
||||||
rspamd_logger.infox("BCC: metric action: %s", action)
|
rspamd_logger.infox("metric action now: %s", action)
|
||||||
|
|
||||||
-- Check for pre-result accept (e.g., from KEEP_SPAM)
|
|
||||||
local allow_bcc = false
|
|
||||||
if task.has_pre_result then
|
|
||||||
local has_pre, pre_action = task:has_pre_result()
|
|
||||||
if has_pre and pre_action == 'accept' then
|
|
||||||
allow_bcc = true
|
|
||||||
rspamd_logger.infox("BCC: pre-result accept detected, will send BCC")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Allow BCC for mild actions or when we have pre-result accept
|
|
||||||
if not allow_bcc and action ~= 'no action' and action ~= 'add header' and action ~= 'rewrite subject' then
|
|
||||||
rspamd_logger.infox("BCC: skipping for action: %s", action)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local function rcpt_callback(err_message, code, body, headers)
|
local function rcpt_callback(err_message, code, body, headers)
|
||||||
if err_message == nil and code == 201 and body ~= nil then
|
if err_message == nil and code == 201 and body ~= nil then
|
||||||
rspamd_logger.infox("BCC: sending BCC to %s for rcpt match", body)
|
if action == 'no action' or action == 'add header' or action == 'rewrite subject' then
|
||||||
send_mail(task, body)
|
send_mail(task, body)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
local function from_callback(err_message, code, body, headers)
|
local function from_callback(err_message, code, body, headers)
|
||||||
if err_message == nil and code == 201 and body ~= nil then
|
if err_message == nil and code == 201 and body ~= nil then
|
||||||
rspamd_logger.infox("BCC: sending BCC to %s for from match", body)
|
if action == 'no action' or action == 'add header' or action == 'rewrite subject' then
|
||||||
send_mail(task, body)
|
send_mail(task, body)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
if rcpt_table then
|
if rcpt_table then
|
||||||
for _,e in ipairs(rcpt_table) do
|
for _,e in ipairs(rcpt_table) do
|
||||||
rspamd_logger.infox(rspamd_config, "BCC: checking bcc for rcpt address %s", e)
|
rspamd_logger.infox(rspamd_config, "checking bcc for rcpt address %s", e)
|
||||||
rspamd_http.request({
|
rspamd_http.request({
|
||||||
task=task,
|
task=task,
|
||||||
url='http://nginx:8081/bcc.php',
|
url='http://nginx:8081/bcc.php',
|
||||||
@@ -667,7 +430,7 @@ rspamd_config:register_symbol({
|
|||||||
|
|
||||||
if from_table then
|
if from_table then
|
||||||
for _,e in ipairs(from_table) do
|
for _,e in ipairs(from_table) do
|
||||||
rspamd_logger.infox(rspamd_config, "BCC: checking bcc for from address %s", e)
|
rspamd_logger.infox(rspamd_config, "checking bcc for from address %s", e)
|
||||||
rspamd_http.request({
|
rspamd_http.request({
|
||||||
task=task,
|
task=task,
|
||||||
url='http://nginx:8081/bcc.php',
|
url='http://nginx:8081/bcc.php',
|
||||||
@@ -678,7 +441,7 @@ rspamd_config:register_symbol({
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
-- Don't return true to avoid symbol being logged
|
return true
|
||||||
end,
|
end,
|
||||||
priority = 20
|
priority = 20
|
||||||
})
|
})
|
||||||
|
|||||||
+1
-1
@@ -117,7 +117,7 @@ services:
|
|||||||
- rspamd
|
- rspamd
|
||||||
|
|
||||||
php-fpm-mailcow:
|
php-fpm-mailcow:
|
||||||
image: ghcr.io/mailcow/phpfpm:1.94
|
image: ghcr.io/mailcow/phpfpm:8.4
|
||||||
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
|
command: "php-fpm -d date.timezone=${TZ} -d expose_php=0"
|
||||||
depends_on:
|
depends_on:
|
||||||
- redis-mailcow
|
- redis-mailcow
|
||||||
|
|||||||
+1
-1
@@ -296,7 +296,7 @@ SKIP_LETS_ENCRYPT=n
|
|||||||
# Create separate certificates for all domains - y/n
|
# Create separate certificates for all domains - y/n
|
||||||
# this will allow adding more than 100 domains, but some email clients will not be able to connect with alternative hostnames
|
# this will allow adding more than 100 domains, but some email clients will not be able to connect with alternative hostnames
|
||||||
# see https://doc.dovecot.org/admin_manual/ssl/sni_support
|
# see https://doc.dovecot.org/admin_manual/ssl/sni_support
|
||||||
ENABLE_SSL_SNI=y
|
ENABLE_SSL_SNI=n
|
||||||
|
|
||||||
# Skip IPv4 check in ACME container - y/n
|
# Skip IPv4 check in ACME container - y/n
|
||||||
SKIP_IP_CHECK=n
|
SKIP_IP_CHECK=n
|
||||||
|
|||||||
Reference in New Issue
Block a user