1
0
mirror of https://github.com/mailcow/mailcow-dockerized.git synced 2025-12-20 21:31:32 +00:00

[Rspamd] use python bootstrapper to start RSPAMD container

This commit is contained in:
FreddleSpl0it
2025-05-21 09:40:38 +02:00
parent 2efea9c832
commit b8888521f1
16 changed files with 416 additions and 333 deletions

View File

@@ -1,7 +1,14 @@
import os import os
import sys import sys
import signal
def handle_sigterm(signum, frame):
print("Received SIGTERM, exiting gracefully...")
sys.exit(0)
def main(): def main():
signal.signal(signal.SIGTERM, handle_sigterm)
container_name = os.getenv("CONTAINER_NAME") container_name = os.getenv("CONTAINER_NAME")
if container_name == "sogo-mailcow": if container_name == "sogo-mailcow":
@@ -12,6 +19,8 @@ def main():
from modules.BootstrapPostfix import Bootstrap from modules.BootstrapPostfix import Bootstrap
elif container_name == "dovecot-mailcow": elif container_name == "dovecot-mailcow":
from modules.BootstrapDovecot import Bootstrap from modules.BootstrapDovecot import Bootstrap
elif container_name == "rspamd-mailcow":
from modules.BootstrapRspamd import Bootstrap
else: else:
print(f"No bootstrap handler for container: {container_name}", file=sys.stderr) print(f"No bootstrap handler for container: {container_name}", file=sys.stderr)
sys.exit(1) sys.exit(1)

View File

@@ -13,6 +13,7 @@ import redis
import hashlib import hashlib
import json import json
from pathlib import Path from pathlib import Path
import dns.resolver
import mysql.connector import mysql.connector
from jinja2 import Environment, FileSystemLoader from jinja2 import Environment, FileSystemLoader
@@ -395,6 +396,29 @@ class BootstrapBase:
result = sock.connect_ex((host, port)) result = sock.connect_ex((host, port))
return result == 0 return result == 0
def resolve_docker_dns_record(self, hostname, record_type="A"):
"""
Resolves DNS A or AAAA records for a given hostname.
Args:
hostname (str): The domain to query.
record_type (str): "A" for IPv4, "AAAA" for IPv6. Default is "A".
Returns:
list[str]: A list of resolved IP addresses.
Raises:
Exception: If resolution fails or no results are found.
"""
try:
resolver = dns.resolver.Resolver()
resolver.nameservers = ["127.0.0.11"]
answers = resolver.resolve(hostname, record_type)
return [answer.to_text() for answer in answers]
except Exception as e:
raise Exception(f"Failed to resolve {record_type} record for {hostname}: {e}")
def kill_proc(self, process): def kill_proc(self, process):
""" """
Sends a SIGTERM signal to all processes matching the given name using `killall`. Sends a SIGTERM signal to all processes matching the given name using `killall`.

View File

@@ -0,0 +1,137 @@
from jinja2 import Environment, FileSystemLoader
from modules.BootstrapBase import BootstrapBase
from pathlib import Path
import os
import sys
import time
import platform
class Bootstrap(BootstrapBase):
def bootstrap(self):
# Connect to MySQL
self.connect_mysql()
# Connect to MySQL
self.connect_redis()
# get dovecot ips
dovecot_v4 = []
dovecot_v6 = []
while not dovecot_v4 and not dovecot_v6:
try:
dovecot_v4 = self.resolve_docker_dns_record("dovecot-mailcow", "A")
dovecot_v6 = self.resolve_docker_dns_record("dovecot-mailcow", "AAAA")
except Exception as e:
print(e)
if not dovecot_v4 and not dovecot_v6:
print("Waiting for Dovecot IPs...")
time.sleep(3)
# get rspamd ips
rspamd_v4 = []
rspamd_v6 = []
while not rspamd_v4 and not rspamd_v6:
try:
rspamd_v4 = self.resolve_docker_dns_record("rspamd-mailcow", "A")
rspamd_v6 = self.resolve_docker_dns_record("rspamd-mailcow", "AAAA")
except Exception:
print(e)
if not rspamd_v4 and not rspamd_v6:
print("Waiting for Rspamd IPs...")
time.sleep(3)
# wait for Services
services = [
["php-fpm-mailcow", 9001],
["php-fpm-mailcow", 9002]
]
for service in services:
while not self.is_port_open(service[0], service[1]):
print(f"Waiting for {service[0]} on port {service[1]}...")
time.sleep(1)
print(f"Service {service[0]} on port {service[1]} is ready!")
for dir_path in ["/etc/rspamd/plugins.d", "/etc/rspamd/custom"]:
Path(dir_path).mkdir(parents=True, exist_ok=True)
for file_path in ["/etc/rspamd/rspamd.conf.local", "/etc/rspamd/rspamd.conf.override"]:
Path(file_path).touch(exist_ok=True)
self.set_permissions("/var/lib/rspamd", 0o755)
# Setup Jinja2 Environment and load vars
self.env = Environment(
loader=FileSystemLoader('./etc/rspamd/config_templates'),
keep_trailing_newline=True,
lstrip_blocks=True,
trim_blocks=True
)
extra_vars = {
"DOVECOT_V4": dovecot_v4[0],
"DOVECOT_V6": dovecot_v6[0],
"RSPAMD_V4": rspamd_v4[0],
"RSPAMD_V6": rspamd_v6[0],
}
self.env_vars = self.prepare_template_vars('/overwrites.json', extra_vars)
print("Set Timezone")
self.set_timezone()
print("Render config")
self.render_config("mailcow_networks.map.j2", "/etc/rspamd/custom/mailcow_networks.map")
self.render_config("dovecot_trusted.map.j2", "/etc/rspamd/custom/dovecot_trusted.map")
self.render_config("rspamd_trusted.map.j2", "/etc/rspamd/custom/rspamd_trusted.map")
self.render_config("external_services.conf.j2", "/etc/rspamd/local.d/external_services.conf")
self.render_config("redis.conf.j2", "/etc/rspamd/local.d/redis.conf")
self.render_config("dqs-rbl.conf.j2", "/etc/rspamd/custom/dqs-rbl.conf")
self.render_config("worker-controller-password.inc.j2", "/etc/rspamd/override.d/worker-controller-password.inc")
# Fix missing default global maps, if any
# These exists in mailcow UI and should not be removed
files = [
"/etc/rspamd/custom/global_mime_from_blacklist.map",
"/etc/rspamd/custom/global_rcpt_blacklist.map",
"/etc/rspamd/custom/global_smtp_from_blacklist.map",
"/etc/rspamd/custom/global_mime_from_whitelist.map",
"/etc/rspamd/custom/global_rcpt_whitelist.map",
"/etc/rspamd/custom/global_smtp_from_whitelist.map",
"/etc/rspamd/custom/bad_languages.map",
"/etc/rspamd/custom/sa-rules",
"/etc/rspamd/custom/dovecot_trusted.map",
"/etc/rspamd/custom/rspamd_trusted.map",
"/etc/rspamd/custom/mailcow_networks.map",
"/etc/rspamd/custom/ip_wl.map",
"/etc/rspamd/custom/fishy_tlds.map",
"/etc/rspamd/custom/bad_words.map",
"/etc/rspamd/custom/bad_asn.map",
"/etc/rspamd/custom/bad_words_de.map",
"/etc/rspamd/custom/bulk_header.map",
"/etc/rspamd/custom/bad_header.map"
]
for file in files:
path = Path(file)
path.parent.mkdir(parents=True, exist_ok=True)
path.touch(exist_ok=True)
# Fix permissions
paths_rspamd = [
"/var/lib/rspamd",
"/etc/rspamd/local.d",
"/etc/rspamd/override.d",
"/etc/rspamd/rspamd.conf.local",
"/etc/rspamd/rspamd.conf.override",
"/etc/rspamd/plugins.d"
]
for path in paths_rspamd:
self.set_owner(path, "_rspamd", "_rspamd", recursive=True)
self.set_owner("/etc/rspamd/custom", "_rspamd", "_rspamd")
self.set_permissions("/etc/rspamd/custom", 0o755)
custom_path = Path("/etc/rspamd/custom")
for child in custom_path.iterdir():
if child.is_file():
self.set_owner(child, 82, 82)
self.set_permissions(child, 0o644)
# Provide additional lua modules
arch = platform.machine()
self.run_command(["ln", "-s", f"/usr/lib/{arch}-linux-gnu/liblua5.1-cjson.so.0.0.0", "/usr/lib/rspamd/cjson.so"], check=False)

View File

@@ -118,7 +118,8 @@ RUN addgroup -g 5000 vmail \
RUN pip install --break-system-packages \ RUN pip install --break-system-packages \
mysql-connector-python \ mysql-connector-python \
jinja2 \ jinja2 \
redis redis \
dnspython
COPY data/Dockerfiles/bootstrap /bootstrap COPY data/Dockerfiles/bootstrap /bootstrap

View File

@@ -43,7 +43,8 @@ RUN groupadd -g 102 postfix \
RUN pip install --break-system-packages \ RUN pip install --break-system-packages \
mysql-connector-python \ mysql-connector-python \
jinja2 \ jinja2 \
redis redis \
dnspython
COPY data/Dockerfiles/bootstrap /bootstrap COPY data/Dockerfiles/bootstrap /bootstrap
COPY data/Dockerfiles/postfix/supervisord.conf /etc/supervisor/supervisord.conf COPY data/Dockerfiles/postfix/supervisord.conf /etc/supervisor/supervisord.conf

View File

@@ -14,10 +14,11 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
dnsutils \ dnsutils \
netcat-traditional \ netcat-traditional \
wget \ wget \
redis-tools \ redis-tools \
procps \ procps \
nano \ nano \
lua-cjson \ lua-cjson \
python3 python3-pip \
&& arch=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) \ && arch=$(arch | sed s/aarch64/arm64/ | sed s/x86_64/amd64/) \
&& wget -P /tmp https://rspamd.com/apt-stable/pool/main/r/rspamd/${RSPAMD_VER}~${CODENAME}_${arch}.deb\ && wget -P /tmp https://rspamd.com/apt-stable/pool/main/r/rspamd/${RSPAMD_VER}~${CODENAME}_${arch}.deb\
&& apt install -y /tmp/${RSPAMD_VER}~${CODENAME}_${arch}.deb \ && apt install -y /tmp/${RSPAMD_VER}~${CODENAME}_${arch}.deb \
@@ -29,12 +30,19 @@ RUN apt-get update && apt-get install -y --no-install-recommends \
&& echo 'alias ll="ls -la --color"' >> ~/.bashrc \ && echo 'alias ll="ls -la --color"' >> ~/.bashrc \
&& sed -i 's/#analysis_keyword_table > 0/analysis_cat_table.macro_exist == "M"/g' /usr/share/rspamd/lualib/lua_scanners/oletools.lua && sed -i 's/#analysis_keyword_table > 0/analysis_cat_table.macro_exist == "M"/g' /usr/share/rspamd/lualib/lua_scanners/oletools.lua
COPY settings.conf /etc/rspamd/settings.conf RUN pip install --break-system-packages \
COPY set_worker_password.sh /set_worker_password.sh mysql-connector-python \
COPY docker-entrypoint.sh /docker-entrypoint.sh jinja2 \
redis \
dnspython
COPY data/Dockerfiles/bootstrap /bootstrap
COPY data/Dockerfiles/rspamd/settings.conf /etc/rspamd/settings.conf
COPY data/Dockerfiles/rspamd/set_worker_password.sh /set_worker_password.sh
COPY data/Dockerfiles/rspamd/docker-entrypoint.sh /docker-entrypoint.sh
ENTRYPOINT ["/docker-entrypoint.sh"]
STOPSIGNAL SIGTERM STOPSIGNAL SIGTERM
ENTRYPOINT ["/docker-entrypoint.sh"]
CMD ["/usr/bin/rspamd", "-f", "-u", "_rspamd", "-g", "_rspamd"] CMD ["/usr/bin/rspamd", "-f", "-u", "_rspamd", "-g", "_rspamd"]

View File

@@ -1,144 +1,5 @@
#!/bin/bash #!/bin/bash
until nc phpfpm 9001 -z; do
echo "Waiting for PHP on port 9001..."
sleep 3
done
until nc phpfpm 9002 -z; do
echo "Waiting for PHP on port 9002..."
sleep 3
done
mkdir -p /etc/rspamd/plugins.d \
/etc/rspamd/custom
touch /etc/rspamd/rspamd.conf.local \
/etc/rspamd/rspamd.conf.override
chmod 755 /var/lib/rspamd
[[ ! -f /etc/rspamd/override.d/worker-controller-password.inc ]] && echo '# Autogenerated by mailcow' > /etc/rspamd/override.d/worker-controller-password.inc
echo ${IPV4_NETWORK}.0/24 > /etc/rspamd/custom/mailcow_networks.map
echo ${IPV6_NETWORK} >> /etc/rspamd/custom/mailcow_networks.map
DOVECOT_V4=
DOVECOT_V6=
until [[ ! -z ${DOVECOT_V4} ]]; do
DOVECOT_V4=$(dig a dovecot +short)
DOVECOT_V6=$(dig aaaa dovecot +short)
[[ ! -z ${DOVECOT_V4} ]] && break;
echo "Waiting for Dovecot..."
sleep 3
done
echo ${DOVECOT_V4}/32 > /etc/rspamd/custom/dovecot_trusted.map
if [[ ! -z ${DOVECOT_V6} ]]; then
echo ${DOVECOT_V6}/128 >> /etc/rspamd/custom/dovecot_trusted.map
fi
RSPAMD_V4=
RSPAMD_V6=
until [[ ! -z ${RSPAMD_V4} ]]; do
RSPAMD_V4=$(dig a rspamd +short)
RSPAMD_V6=$(dig aaaa rspamd +short)
[[ ! -z ${RSPAMD_V4} ]] && break;
echo "Waiting for Rspamd..."
sleep 3
done
echo ${RSPAMD_V4}/32 > /etc/rspamd/custom/rspamd_trusted.map
if [[ ! -z ${RSPAMD_V6} ]]; then
echo ${RSPAMD_V6}/128 >> /etc/rspamd/custom/rspamd_trusted.map
fi
if [[ ! -z ${REDIS_SLAVEOF_IP} ]]; then
cat <<EOF > /etc/rspamd/local.d/redis.conf
read_servers = "redis:6379";
write_servers = "${REDIS_SLAVEOF_IP}:${REDIS_SLAVEOF_PORT}";
password = "${REDISPASS}";
timeout = 10;
EOF
until [[ $(redis-cli -h redis-mailcow -a ${REDISPASS} --no-auth-warning PING) == "PONG" ]]; do
echo "Waiting for Redis @redis-mailcow..."
sleep 2
done
until [[ $(redis-cli -h ${REDIS_SLAVEOF_IP} -p ${REDIS_SLAVEOF_PORT} -a ${REDISPASS} --no-auth-warning PING) == "PONG" ]]; do
echo "Waiting for Redis @${REDIS_SLAVEOF_IP}..."
sleep 2
done
redis-cli -h redis-mailcow -a ${REDISPASS} --no-auth-warning SLAVEOF ${REDIS_SLAVEOF_IP} ${REDIS_SLAVEOF_PORT}
else
cat <<EOF > /etc/rspamd/local.d/redis.conf
servers = "redis:6379";
password = "${REDISPASS}";
timeout = 10;
EOF
until [[ $(redis-cli -h redis-mailcow -a ${REDISPASS} --no-auth-warning PING) == "PONG" ]]; do
echo "Waiting for Redis slave..."
sleep 2
done
redis-cli -h redis-mailcow -a ${REDISPASS} --no-auth-warning SLAVEOF NO ONE
fi
if [[ "${SKIP_OLEFY}" =~ ^([yY][eE][sS]|[yY])+$ ]]; then
if [[ -f /etc/rspamd/local.d/external_services.conf ]]; then
rm /etc/rspamd/local.d/external_services.conf
fi
else
cat <<EOF > /etc/rspamd/local.d/external_services.conf
oletools {
# default olefy settings
servers = "olefy:10055";
# needs to be set explicitly for Rspamd < 1.9.5
scan_mime_parts = true;
# mime-part regex matching in content-type or filename
# block all macros
extended = true;
max_size = 3145728;
timeout = 20.0;
retransmits = 1;
}
EOF
fi
# Provide additional lua modules
ln -s /usr/lib/$(uname -m)-linux-gnu/liblua5.1-cjson.so.0.0.0 /usr/lib/rspamd/cjson.so
chown -R _rspamd:_rspamd /var/lib/rspamd \
/etc/rspamd/local.d \
/etc/rspamd/override.d \
/etc/rspamd/rspamd.conf.local \
/etc/rspamd/rspamd.conf.override \
/etc/rspamd/plugins.d
# Fix missing default global maps, if any
# These exists in mailcow UI and should not be removed
touch /etc/rspamd/custom/global_mime_from_blacklist.map \
/etc/rspamd/custom/global_rcpt_blacklist.map \
/etc/rspamd/custom/global_smtp_from_blacklist.map \
/etc/rspamd/custom/global_mime_from_whitelist.map \
/etc/rspamd/custom/global_rcpt_whitelist.map \
/etc/rspamd/custom/global_smtp_from_whitelist.map \
/etc/rspamd/custom/bad_languages.map \
/etc/rspamd/custom/sa-rules \
/etc/rspamd/custom/dovecot_trusted.map \
/etc/rspamd/custom/rspamd_trusted.map \
/etc/rspamd/custom/mailcow_networks.map \
/etc/rspamd/custom/ip_wl.map \
/etc/rspamd/custom/fishy_tlds.map \
/etc/rspamd/custom/bad_words.map \
/etc/rspamd/custom/bad_asn.map \
/etc/rspamd/custom/bad_words_de.map \
/etc/rspamd/custom/bulk_header.map \
/etc/rspamd/custom/bad_header.map
# www-data (82) group needs to write to these files
chown _rspamd:_rspamd /etc/rspamd/custom/
chmod 0755 /etc/rspamd/custom/.
chown -R 82:82 /etc/rspamd/custom/*
chmod 644 -R /etc/rspamd/custom/*
# Run hooks # Run hooks
for file in /hooks/*; do for file in /hooks/*; do
if [ -x "${file}" ]; then if [ -x "${file}" ]; then
@@ -147,190 +8,13 @@ for file in /hooks/*; do
fi fi
done done
# If DQS KEY is set in mailcow.conf add Spamhaus DQS RBLs python3 -u /bootstrap/main.py
if [[ ! -z ${SPAMHAUS_DQS_KEY} ]]; then BOOTSTRAP_EXIT_CODE=$?
cat <<EOF > /etc/rspamd/custom/dqs-rbl.conf
# Autogenerated by mailcow. DO NOT TOUCH!
spamhaus {
rbl = "${SPAMHAUS_DQS_KEY}.zen.dq.spamhaus.net";
from = false;
}
spamhaus_from {
from = true;
received = false;
rbl = "${SPAMHAUS_DQS_KEY}.zen.dq.spamhaus.net";
returncodes {
SPAMHAUS_ZEN = [ "127.0.0.2", "127.0.0.3", "127.0.0.4", "127.0.0.5", "127.0.0.6", "127.0.0.7", "127.0.0.9", "127.0.0.10", "127.0.0.11" ];
}
}
spamhaus_authbl_received {
# Check if the sender client is listed in AuthBL (AuthBL is *not* part of ZEN)
rbl = "${SPAMHAUS_DQS_KEY}.authbl.dq.spamhaus.net";
from = false;
received = true;
ipv6 = true;
returncodes {
SH_AUTHBL_RECEIVED = "127.0.0.20"
}
}
spamhaus_dbl {
# Add checks on the HELO string
rbl = "${SPAMHAUS_DQS_KEY}.dbl.dq.spamhaus.net";
helo = true;
rdns = true;
dkim = true;
disable_monitoring = true;
returncodes {
RBL_DBL_SPAM = "127.0.1.2";
RBL_DBL_PHISH = "127.0.1.4";
RBL_DBL_MALWARE = "127.0.1.5";
RBL_DBL_BOTNET = "127.0.1.6";
RBL_DBL_ABUSED_SPAM = "127.0.1.102";
RBL_DBL_ABUSED_PHISH = "127.0.1.104";
RBL_DBL_ABUSED_MALWARE = "127.0.1.105";
RBL_DBL_ABUSED_BOTNET = "127.0.1.106";
RBL_DBL_DONT_QUERY_IPS = "127.0.1.255";
}
}
spamhaus_dbl_fullurls {
ignore_defaults = true;
no_ip = true;
rbl = "${SPAMHAUS_DQS_KEY}.dbl.dq.spamhaus.net";
selector = 'urls:get_host'
disable_monitoring = true;
returncodes {
DBLABUSED_SPAM_FULLURLS = "127.0.1.102";
DBLABUSED_PHISH_FULLURLS = "127.0.1.104";
DBLABUSED_MALWARE_FULLURLS = "127.0.1.105";
DBLABUSED_BOTNET_FULLURLS = "127.0.1.106";
}
}
spamhaus_zrd {
# Add checks on the HELO string also for DQS
rbl = "${SPAMHAUS_DQS_KEY}.zrd.dq.spamhaus.net";
helo = true;
rdns = true;
dkim = true;
disable_monitoring = true;
returncodes {
RBL_ZRD_VERY_FRESH_DOMAIN = ["127.0.2.2", "127.0.2.3", "127.0.2.4"];
RBL_ZRD_FRESH_DOMAIN = [
"127.0.2.5", "127.0.2.6", "127.0.2.7", "127.0.2.8", "127.0.2.9", "127.0.2.10", "127.0.2.11", "127.0.2.12", "127.0.2.13", "127.0.2.14", "127.0.2.15", "127.0.2.16", "127.0.2.17", "127.0.2.18", "127.0.2.19", "127.0.2.20", "127.0.2.21", "127.0.2.22", "127.0.2.23", "127.0.2.24"
];
RBL_ZRD_DONT_QUERY_IPS = "127.0.2.255";
}
}
"SPAMHAUS_ZEN_URIBL" {
enabled = true;
rbl = "${SPAMHAUS_DQS_KEY}.zen.dq.spamhaus.net";
resolve_ip = true;
checks = ['urls'];
replyto = true;
emails = true;
ipv4 = true;
ipv6 = true;
emails_domainonly = true;
returncodes {
URIBL_SBL = "127.0.0.2";
URIBL_SBL_CSS = "127.0.0.3";
URIBL_XBL = ["127.0.0.4", "127.0.0.5", "127.0.0.6", "127.0.0.7"];
URIBL_PBL = ["127.0.0.10", "127.0.0.11"];
URIBL_DROP = "127.0.0.9";
}
}
SH_EMAIL_DBL {
ignore_defaults = true;
replyto = true;
emails_domainonly = true;
disable_monitoring = true;
rbl = "${SPAMHAUS_DQS_KEY}.dbl.dq.spamhaus.net";
returncodes = {
SH_EMAIL_DBL = [
"127.0.1.2",
"127.0.1.4",
"127.0.1.5",
"127.0.1.6"
];
SH_EMAIL_DBL_ABUSED = [
"127.0.1.102",
"127.0.1.104",
"127.0.1.105",
"127.0.1.106"
];
SH_EMAIL_DBL_DONT_QUERY_IPS = [ "127.0.1.255" ];
}
}
SH_EMAIL_ZRD {
ignore_defaults = true;
replyto = true;
emails_domainonly = true;
disable_monitoring = true;
rbl = "${SPAMHAUS_DQS_KEY}.zrd.dq.spamhaus.net";
returncodes = {
SH_EMAIL_ZRD_VERY_FRESH_DOMAIN = ["127.0.2.2", "127.0.2.3", "127.0.2.4"];
SH_EMAIL_ZRD_FRESH_DOMAIN = [
"127.0.2.5", "127.0.2.6", "127.0.2.7", "127.0.2.8", "127.0.2.9", "127.0.2.10", "127.0.2.11", "127.0.2.12", "127.0.2.13", "127.0.2.14", "127.0.2.15", "127.0.2.16", "127.0.2.17", "127.0.2.18", "127.0.2.19", "127.0.2.20", "127.0.2.21", "127.0.2.22", "127.0.2.23", "127.0.2.24"
];
SH_EMAIL_ZRD_DONT_QUERY_IPS = [ "127.0.2.255" ];
}
}
"DBL" {
# override the defaults for DBL defined in modules.d/rbl.conf
rbl = "${SPAMHAUS_DQS_KEY}.dbl.dq.spamhaus.net";
disable_monitoring = true;
}
"ZRD" {
ignore_defaults = true;
rbl = "${SPAMHAUS_DQS_KEY}.zrd.dq.spamhaus.net";
no_ip = true;
dkim = true;
emails = true;
emails_domainonly = true;
urls = true;
returncodes = {
ZRD_VERY_FRESH_DOMAIN = ["127.0.2.2", "127.0.2.3", "127.0.2.4"];
ZRD_FRESH_DOMAIN = ["127.0.2.5", "127.0.2.6", "127.0.2.7", "127.0.2.8", "127.0.2.9", "127.0.2.10", "127.0.2.11", "127.0.2.12", "127.0.2.13", "127.0.2.14", "127.0.2.15", "127.0.2.16", "127.0.2.17", "127.0.2.18", "127.0.2.19", "127.0.2.20", "127.0.2.21", "127.0.2.22", "127.0.2.23", "127.0.2.24"];
}
}
spamhaus_sbl_url {
ignore_defaults = true
rbl = "${SPAMHAUS_DQS_KEY}.sbl.dq.spamhaus.net";
checks = ['urls'];
disable_monitoring = true;
returncodes {
SPAMHAUS_SBL_URL = "127.0.0.2";
}
}
SH_HBL_EMAIL { if [ $BOOTSTRAP_EXIT_CODE -ne 0 ]; then
ignore_defaults = true; echo "Bootstrap failed with exit code $BOOTSTRAP_EXIT_CODE. Not starting Rspamd."
rbl = "_email.${SPAMHAUS_DQS_KEY}.hbl.dq.spamhaus.net"; exit $BOOTSTRAP_EXIT_CODE
emails_domainonly = false;
selector = "from('smtp').lower;from('mime').lower";
ignore_whitelist = true;
checks = ['emails', 'replyto'];
hash = "sha1";
returncodes = {
SH_HBL_EMAIL = [
"127.0.3.2"
];
}
}
spamhaus_dqs_hbl {
symbol = "HBL_FILE_UNKNOWN";
rbl = "_file.${SPAMHAUS_DQS_KEY}.hbl.dq.spamhaus.net.";
selector = "attachments('rbase32', 'sha256')";
ignore_whitelist = true;
ignore_defaults = true;
returncodes {
SH_HBL_FILE_MALICIOUS = "127.0.3.10";
SH_HBL_FILE_SUSPICIOUS = "127.0.3.15";
}
}
EOF
else
rm -rf /etc/rspamd/custom/dqs-rbl.conf
fi fi
echo "Bootstrap succeeded. Starting Rspamd..."
exec "$@" exec "$@"

View File

@@ -46,7 +46,8 @@ RUN echo "Building from repository $SOGO_DEBIAN_REPOSITORY" \
RUN pip install --break-system-packages \ RUN pip install --break-system-packages \
mysql-connector-python \ mysql-connector-python \
jinja2 \ jinja2 \
redis redis \
dnspython
COPY data/Dockerfiles/bootstrap /bootstrap COPY data/Dockerfiles/bootstrap /bootstrap

View File

@@ -0,0 +1,2 @@
{{ DOVECOT_V4 }}/32
{{ DOVECOT_V6 }}/128

View File

@@ -0,0 +1,179 @@
{% if SPAMHAUS_DQS_KEY %}
spamhaus {
rbl = "{{ SPAMHAUS_DQS_KEY }}.zen.dq.spamhaus.net";
from = false;
}
spamhaus_from {
from = true;
received = false;
rbl = "{{ SPAMHAUS_DQS_KEY }}.zen.dq.spamhaus.net";
returncodes {
SPAMHAUS_ZEN = [ "127.0.0.2", "127.0.0.3", "127.0.0.4", "127.0.0.5", "127.0.0.6", "127.0.0.7", "127.0.0.9", "127.0.0.10", "127.0.0.11" ];
}
}
spamhaus_authbl_received {
# Check if the sender client is listed in AuthBL (AuthBL is *not* part of ZEN)
rbl = "{{ SPAMHAUS_DQS_KEY }}.authbl.dq.spamhaus.net";
from = false;
received = true;
ipv6 = true;
returncodes {
SH_AUTHBL_RECEIVED = "127.0.0.20"
}
}
spamhaus_dbl {
# Add checks on the HELO string
rbl = "{{ SPAMHAUS_DQS_KEY }}.dbl.dq.spamhaus.net";
helo = true;
rdns = true;
dkim = true;
disable_monitoring = true;
returncodes {
RBL_DBL_SPAM = "127.0.1.2";
RBL_DBL_PHISH = "127.0.1.4";
RBL_DBL_MALWARE = "127.0.1.5";
RBL_DBL_BOTNET = "127.0.1.6";
RBL_DBL_ABUSED_SPAM = "127.0.1.102";
RBL_DBL_ABUSED_PHISH = "127.0.1.104";
RBL_DBL_ABUSED_MALWARE = "127.0.1.105";
RBL_DBL_ABUSED_BOTNET = "127.0.1.106";
RBL_DBL_DONT_QUERY_IPS = "127.0.1.255";
}
}
spamhaus_dbl_fullurls {
ignore_defaults = true;
no_ip = true;
rbl = "{{ SPAMHAUS_DQS_KEY }}.dbl.dq.spamhaus.net";
selector = 'urls:get_host'
disable_monitoring = true;
returncodes {
DBLABUSED_SPAM_FULLURLS = "127.0.1.102";
DBLABUSED_PHISH_FULLURLS = "127.0.1.104";
DBLABUSED_MALWARE_FULLURLS = "127.0.1.105";
DBLABUSED_BOTNET_FULLURLS = "127.0.1.106";
}
}
spamhaus_zrd {
# Add checks on the HELO string also for DQS
rbl = "{{ SPAMHAUS_DQS_KEY }}.zrd.dq.spamhaus.net";
helo = true;
rdns = true;
dkim = true;
disable_monitoring = true;
returncodes {
RBL_ZRD_VERY_FRESH_DOMAIN = ["127.0.2.2", "127.0.2.3", "127.0.2.4"];
RBL_ZRD_FRESH_DOMAIN = [
"127.0.2.5", "127.0.2.6", "127.0.2.7", "127.0.2.8", "127.0.2.9", "127.0.2.10", "127.0.2.11", "127.0.2.12", "127.0.2.13", "127.0.2.14", "127.0.2.15", "127.0.2.16", "127.0.2.17", "127.0.2.18", "127.0.2.19", "127.0.2.20", "127.0.2.21", "127.0.2.22", "127.0.2.23", "127.0.2.24"
];
RBL_ZRD_DONT_QUERY_IPS = "127.0.2.255";
}
}
"SPAMHAUS_ZEN_URIBL" {
enabled = true;
rbl = "{{ SPAMHAUS_DQS_KEY }}.zen.dq.spamhaus.net";
resolve_ip = true;
checks = ['urls'];
replyto = true;
emails = true;
ipv4 = true;
ipv6 = true;
emails_domainonly = true;
returncodes {
URIBL_SBL = "127.0.0.2";
URIBL_SBL_CSS = "127.0.0.3";
URIBL_XBL = ["127.0.0.4", "127.0.0.5", "127.0.0.6", "127.0.0.7"];
URIBL_PBL = ["127.0.0.10", "127.0.0.11"];
URIBL_DROP = "127.0.0.9";
}
}
SH_EMAIL_DBL {
ignore_defaults = true;
replyto = true;
emails_domainonly = true;
disable_monitoring = true;
rbl = "{{ SPAMHAUS_DQS_KEY }}.dbl.dq.spamhaus.net";
returncodes = {
SH_EMAIL_DBL = [
"127.0.1.2",
"127.0.1.4",
"127.0.1.5",
"127.0.1.6"
];
SH_EMAIL_DBL_ABUSED = [
"127.0.1.102",
"127.0.1.104",
"127.0.1.105",
"127.0.1.106"
];
SH_EMAIL_DBL_DONT_QUERY_IPS = [ "127.0.1.255" ];
}
}
SH_EMAIL_ZRD {
ignore_defaults = true;
replyto = true;
emails_domainonly = true;
disable_monitoring = true;
rbl = "{{ SPAMHAUS_DQS_KEY }}.zrd.dq.spamhaus.net";
returncodes = {
SH_EMAIL_ZRD_VERY_FRESH_DOMAIN = ["127.0.2.2", "127.0.2.3", "127.0.2.4"];
SH_EMAIL_ZRD_FRESH_DOMAIN = [
"127.0.2.5", "127.0.2.6", "127.0.2.7", "127.0.2.8", "127.0.2.9", "127.0.2.10", "127.0.2.11", "127.0.2.12", "127.0.2.13", "127.0.2.14", "127.0.2.15", "127.0.2.16", "127.0.2.17", "127.0.2.18", "127.0.2.19", "127.0.2.20", "127.0.2.21", "127.0.2.22", "127.0.2.23", "127.0.2.24"
];
SH_EMAIL_ZRD_DONT_QUERY_IPS = [ "127.0.2.255" ];
}
}
"DBL" {
# override the defaults for DBL defined in modules.d/rbl.conf
rbl = "{{ SPAMHAUS_DQS_KEY }}.dbl.dq.spamhaus.net";
disable_monitoring = true;
}
"ZRD" {
ignore_defaults = true;
rbl = "{{ SPAMHAUS_DQS_KEY }}.zrd.dq.spamhaus.net";
no_ip = true;
dkim = true;
emails = true;
emails_domainonly = true;
urls = true;
returncodes = {
ZRD_VERY_FRESH_DOMAIN = ["127.0.2.2", "127.0.2.3", "127.0.2.4"];
ZRD_FRESH_DOMAIN = ["127.0.2.5", "127.0.2.6", "127.0.2.7", "127.0.2.8", "127.0.2.9", "127.0.2.10", "127.0.2.11", "127.0.2.12", "127.0.2.13", "127.0.2.14", "127.0.2.15", "127.0.2.16", "127.0.2.17", "127.0.2.18", "127.0.2.19", "127.0.2.20", "127.0.2.21", "127.0.2.22", "127.0.2.23", "127.0.2.24"];
}
}
spamhaus_sbl_url {
ignore_defaults = true
rbl = "{{ SPAMHAUS_DQS_KEY }}.sbl.dq.spamhaus.net";
checks = ['urls'];
disable_monitoring = true;
returncodes {
SPAMHAUS_SBL_URL = "127.0.0.2";
}
}
SH_HBL_EMAIL {
ignore_defaults = true;
rbl = "_email.{{ SPAMHAUS_DQS_KEY }}.hbl.dq.spamhaus.net";
emails_domainonly = false;
selector = "from('smtp').lower;from('mime').lower";
ignore_whitelist = true;
checks = ['emails', 'replyto'];
hash = "sha1";
returncodes = {
SH_HBL_EMAIL = [
"127.0.3.2"
];
}
}
spamhaus_dqs_hbl {
symbol = "HBL_FILE_UNKNOWN";
rbl = "_file.{{ SPAMHAUS_DQS_KEY }}.hbl.dq.spamhaus.net.";
selector = "attachments('rbase32', 'sha256')";
ignore_whitelist = true;
ignore_defaults = true;
returncodes {
SH_HBL_FILE_MALICIOUS = "127.0.3.10";
SH_HBL_FILE_SUSPICIOUS = "127.0.3.15";
}
}
{% endif %}

View File

@@ -0,0 +1,16 @@
{% if SKIP_OLEFY|lower in ['y', 'yes'] %}
# OLEFY deactivated
{% else %}
oletools {
# default olefy settings
servers = "olefy-mailcow:10055";
# needs to be set explicitly for Rspamd < 1.9.5
scan_mime_parts = true;
# mime-part regex matching in content-type or filename
# block all macros
extended = true;
max_size = 3145728;
timeout = 20.0;
retransmits = 1;
}
{% endif %}

View File

@@ -0,0 +1,2 @@
{{ IPV4_NETWORK }}.0/24
{{ IPV6_NETWORK }}

View File

@@ -0,0 +1,10 @@
{% if REDIS_SLAVEOF_IP and REDIS_SLAVEOF_PORT %}
read_servers = "redis-mailcow:6379";
write_servers = "{{ REDIS_SLAVEOF_IP }}:{{ REDIS_SLAVEOF_PORT }}";
password = "{{ REDISPASS }}";
timeout = 10;
{% else %}
servers = "redis-mailcow:6379";
password = "{{ REDISPASS }}";
timeout = 10;
{% endif %}

View File

@@ -0,0 +1,2 @@
{{ RSPAMD_V4 }}/32
{{ RSPAMD_V6 }}/128

View File

@@ -0,0 +1 @@
# Autogenerated by mailcow

View File

@@ -84,12 +84,16 @@ services:
- clamd - clamd
rspamd-mailcow: rspamd-mailcow:
image: ghcr.io/mailcow/rspamd:2.2 image: ghcr.io/mailcow/rspamd:nightly-19052025
stop_grace_period: 30s stop_grace_period: 30s
depends_on: depends_on:
- dovecot-mailcow - dovecot-mailcow
- clamd-mailcow - clamd-mailcow
environment: environment:
- CONTAINER_NAME=rspamd-mailcow
- DBNAME=${DBNAME}
- DBUSER=${DBUSER}
- DBPASS=${DBPASS}
- TZ=${TZ} - TZ=${TZ}
- IPV4_NETWORK=${IPV4_NETWORK:-172.22.1} - IPV4_NETWORK=${IPV4_NETWORK:-172.22.1}
- IPV6_NETWORK=${IPV6_NETWORK:-fd4d:6169:6c63:6f77::/64} - IPV6_NETWORK=${IPV6_NETWORK:-fd4d:6169:6c63:6f77::/64}
@@ -99,6 +103,7 @@ services:
- SPAMHAUS_DQS_KEY=${SPAMHAUS_DQS_KEY:-} - SPAMHAUS_DQS_KEY=${SPAMHAUS_DQS_KEY:-}
volumes: volumes:
- ./data/hooks/rspamd:/hooks:Z - ./data/hooks/rspamd:/hooks:Z
- ./data/conf/rspamd/config_templates/:/etc/rspamd/config_templates:z
- ./data/conf/rspamd/custom/:/etc/rspamd/custom:z - ./data/conf/rspamd/custom/:/etc/rspamd/custom:z
- ./data/conf/rspamd/override.d/:/etc/rspamd/override.d:Z - ./data/conf/rspamd/override.d/:/etc/rspamd/override.d:Z
- ./data/conf/rspamd/local.d/:/etc/rspamd/local.d:Z - ./data/conf/rspamd/local.d/:/etc/rspamd/local.d:Z
@@ -106,6 +111,7 @@ services:
- ./data/conf/rspamd/lua/:/etc/rspamd/lua/:ro,Z - ./data/conf/rspamd/lua/:/etc/rspamd/lua/:ro,Z
- ./data/conf/rspamd/rspamd.conf.local:/etc/rspamd/rspamd.conf.local:Z - ./data/conf/rspamd/rspamd.conf.local:/etc/rspamd/rspamd.conf.local:Z
- ./data/conf/rspamd/rspamd.conf.override:/etc/rspamd/rspamd.conf.override:Z - ./data/conf/rspamd/rspamd.conf.override:/etc/rspamd/rspamd.conf.override:Z
- mysql-socket-vol-1:/var/run/mysqld/
- rspamd-vol-1:/var/lib/rspamd - rspamd-vol-1:/var/lib/rspamd
restart: always restart: always
hostname: rspamd hostname: rspamd