diff --git a/Makefile b/Makefile
index 96dac8180..f40559814 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@ PAGER?= less
all:
@cat ${.CURDIR}/README.md | ${PAGER}
-CATEGORIES= devel dns net net-mgmt sysutils security www
+CATEGORIES= devel dns net net-mgmt mail security sysutils www
.for CATEGORY in ${CATEGORIES}
_${CATEGORY}!= ls -1d ${CATEGORY}/*
diff --git a/README.md b/README.md
index 089ad275d..aef68a0be 100644
--- a/README.md
+++ b/README.md
@@ -52,6 +52,8 @@ net-mgmt/snmp -- SNMP Server via bsnmpd
net-mgmt/telegraf -- Agent for collecting metrics and data
net-mgmt/zabbix-agent -- Enterprise-class open source distributed monitoring agent
net-mgmt/zabbix-proxy -- Zabbix-Proxy enables decentralized monitoring
+mail/postfix -- SMTP mail relay
+mail/rspamd -- Protect your network from spam
sysutils/boot-delay -- Apply a persistent 10 second boot delay
sysutils/monit -- Proactive system monitoring
sysutils/smart -- SMART tools
diff --git a/mail/rspamd/Makefile b/mail/rspamd/Makefile
new file mode 100644
index 000000000..7c4519ee4
--- /dev/null
+++ b/mail/rspamd/Makefile
@@ -0,0 +1,8 @@
+PLUGIN_NAME= rspamd
+PLUGIN_VERSION= 0.1
+PLUGIN_COMMENT= Protect your network from spam
+PLUGIN_DEPENDS= rspamd
+PLUGIN_MAINTAINER= franz.fabian.94@gmail.com
+PLUGIN_DEVEL= YES
+
+.include "../../Mk/plugins.mk"
diff --git a/mail/rspamd/pkg-descr b/mail/rspamd/pkg-descr
new file mode 100644
index 000000000..4f1eef47b
--- /dev/null
+++ b/mail/rspamd/pkg-descr
@@ -0,0 +1,3 @@
+Rspamd is fast, modular and lightweight spam filter. It is designed to work
+with big amount of mail and can be easily extended with own filters written in
+lua.
diff --git a/mail/rspamd/src/etc/inc/plugins.inc.d/rspamd.inc b/mail/rspamd/src/etc/inc/plugins.inc.d/rspamd.inc
new file mode 100644
index 000000000..7b5699a7e
--- /dev/null
+++ b/mail/rspamd/src/etc/inc/plugins.inc.d/rspamd.inc
@@ -0,0 +1,62 @@
+general->enabled == '1') {
+ return true;
+ }
+
+ return false;
+}
+
+function rspamd_firewall($fw)
+{
+ if (rspamd_enabled()) {
+ }
+}
+
+function rspamd_services()
+{
+ $services = array();
+
+ if (rspamd_enabled()) {
+ $services[] = array(
+ 'description' => gettext('Rapid Spamfilter Daemon'),
+ 'configd' => array(
+ 'restart' => array('rspamd restart'),
+ 'start' => array('rspamd start'),
+ 'stop' => array('rspamd stop'),
+ ),
+ 'name' => 'rspamd',
+ 'pidfile' => '/var/run/rspamd/rspamd.pid'
+ );
+ }
+ return $services;
+}
diff --git a/mail/rspamd/src/opnsense/mvc/app/controllers/OPNsense/Rspamd/Api/ServiceController.php b/mail/rspamd/src/opnsense/mvc/app/controllers/OPNsense/Rspamd/Api/ServiceController.php
new file mode 100644
index 000000000..ffcc38591
--- /dev/null
+++ b/mail/rspamd/src/opnsense/mvc/app/controllers/OPNsense/Rspamd/Api/ServiceController.php
@@ -0,0 +1,139 @@
+request->isPost()) {
+ $backend = new Backend();
+ $response = $backend->configdRun('rspamd restart');
+ return array('response' => $response);
+ } else {
+ return array('response' => array());
+ }
+ }
+
+ /**
+ * retrieve status of rspamd
+ * @return array
+ * @throws \Exception
+ */
+ public function statusAction()
+ {
+ $backend = new Backend();
+ $rspamd = new RSpamd();
+ $response = $backend->configdRun('rspamd status');
+
+ if (strpos($response, 'not running') > 0) {
+ if ((string)$rspamd->general->enabled == 1) {
+ $status = 'stopped';
+ } else {
+ $status = 'disabled';
+ }
+ } elseif (strpos($response, 'is running') > 0) {
+ $status = 'running';
+ } elseif ((string)$rspamd->general->enabled == 0) {
+ $status = 'disabled';
+ } else {
+ $status = 'unknown';
+ }
+
+
+ return array('status' => $status);
+ }
+
+ /**
+ * reconfigure rspamd, generate config and reload
+ */
+ public function reconfigureAction()
+ {
+ if ($this->request->isPost()) {
+ // close session for long running action
+ $this->sessionClose();
+
+ $rspamd = new RSpamd();
+ $backend = new Backend();
+
+ $this->stopAction();
+
+ // generate template
+ $backend->configdRun('template reload OPNsense/Rspamd');
+
+ // (re)start daemon
+ if ((string)$rspamd->general->enabled == '1') {
+ $this->startAction();
+ }
+
+ return array('status' => 'ok');
+ } else {
+ return array('status' => 'failed');
+ }
+ }
+
+ /**
+ * stop rspamd service
+ * @return array
+ */
+ public function stopAction()
+ {
+ if ($this->request->isPost()) {
+ $backend = new Backend();
+ $response = $backend->configdRun('rspamd stop');
+ return array('response' => $response);
+ } else {
+ return array('response' => array());
+ }
+ }
+ /**
+ * start rspamd service
+ * @return array
+ */
+ public function startAction()
+ {
+ if ($this->request->isPost()) {
+ $backend = new Backend();
+ $response = $backend->configdRun('rspamd start');
+ return array('response' => $response);
+ } else {
+ return array('response' => array());
+ }
+ }
+}
diff --git a/mail/rspamd/src/opnsense/mvc/app/controllers/OPNsense/Rspamd/Api/SettingsController.php b/mail/rspamd/src/opnsense/mvc/app/controllers/OPNsense/Rspamd/Api/SettingsController.php
new file mode 100644
index 000000000..c4b1d8da4
--- /dev/null
+++ b/mail/rspamd/src/opnsense/mvc/app/controllers/OPNsense/Rspamd/Api/SettingsController.php
@@ -0,0 +1,38 @@
+view->clamav_installed = (trim($backend->configdRun('firmware plugin clamav')) == '1');
+ $this->view->title = gettext("Rspamd Mail Protection");
+ $this->view->settings = $this->getForm("settings");
+ $this->view->pick('OPNsense/Rspamd/index');
+ }
+}
diff --git a/mail/rspamd/src/opnsense/mvc/app/controllers/OPNsense/Rspamd/forms/settings.xml b/mail/rspamd/src/opnsense/mvc/app/controllers/OPNsense/Rspamd/forms/settings.xml
new file mode 100644
index 000000000..2fc66828a
--- /dev/null
+++ b/mail/rspamd/src/opnsense/mvc/app/controllers/OPNsense/Rspamd/forms/settings.xml
@@ -0,0 +1,342 @@
+
+ {% for tab in settings['tabs']|default([]) %}
+ {% if tab['subtabs']|default(false) %}
+ {# Tab with dropdown #}
+ {% for subtab in tab['subtabs']|default({})%}
+
+ {{ partial("layout_partials/base_form",['fields':subtab[2],'id':'frm_'~subtab[0],'data_title':subtab[1],'apply_btn_id':'save_'~subtab[0]]) }}
+
+ {% endfor %}
+ {% endif %}
+ {% if tab['subtabs']|default(false)==false %}
+
+ {{ partial("layout_partials/base_form",['fields':tab[2],'id':'frm_'~tab[0],'apply_btn_id':'save_'~tab[0]]) }}
+
+ {% endif %}
+ {% endfor %}
+
diff --git a/mail/rspamd/src/opnsense/scripts/rspamd/setup.sh b/mail/rspamd/src/opnsense/scripts/rspamd/setup.sh
new file mode 100755
index 000000000..aa722d898
--- /dev/null
+++ b/mail/rspamd/src/opnsense/scripts/rspamd/setup.sh
@@ -0,0 +1,9 @@
+#!/bin/sh
+mkdir -p /var/db/rspamd
+mkdir -p /var/log/rspamd
+mkdir -p /var/run/rspamd
+
+chown nobody:nobody /var/db/rspamd
+chown nobody:nobody /var/log/rspamd
+chown nobody:nobody /var/run/rspamd
+
diff --git a/mail/rspamd/src/opnsense/service/conf/actions.d/actions_rspamd.conf b/mail/rspamd/src/opnsense/service/conf/actions.d/actions_rspamd.conf
new file mode 100644
index 000000000..e4711138a
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/conf/actions.d/actions_rspamd.conf
@@ -0,0 +1,23 @@
+[start]
+command:/usr/local/opnsense/scripts/rspamd/setup.sh;/usr/local/etc/rc.d/rspamd start
+parameters:
+type:script
+message:starting rspamd
+
+[stop]
+command:/usr/local/etc/rc.d/rspamd onestop
+parameters:
+type:script
+message:stopping rspamd
+
+[restart]
+command:/usr/local/opnsense/scripts/rspamd/setup.sh;/usr/local/etc/rc.d/rspamd restart
+parameters:
+type:script
+message:restarting rspamd
+
+[status]
+command:/usr/local/etc/rc.d/rspamd status;exit 0
+parameters:
+type:script_output
+message:request rspamd status
diff --git a/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/+TARGETS b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/+TARGETS
new file mode 100644
index 000000000..c18e907b7
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/+TARGETS
@@ -0,0 +1,14 @@
+rspamd:/etc/rc.conf.d/rspamd
+antivirus.wl:/usr/local/etc/rspamd/local.d/antivirus.wl
+antivirus.conf:/usr/local/etc/rspamd/local.d/antivirus.conf
+dkim_signing.conf:/usr/local/etc/rspamd/local.d/dkim_signing.conf
+dkim.conf:/usr/local/etc/rspamd/local.d/dkim.conf
+spf.conf:/usr/local/etc/rspamd/local.d/spf.conf
+spamtrap.conf:/usr/local/etc/rspamd/local.d/spamtrap.conf
+surbl-whitelist.inc.local:/var/db/rspamd/surbl-whitelist.inc.local
+2tld.inc.local:/var/db/rspamd/2tld.inc.local
+greylist.conf:/usr/local/etc/rspamd/local.d/greylist.conf
+phishing.conf:/usr/local/etc/rspamd/local.d/phishing.conf
+mx_check.conf:/usr/local/etc/rspamd/local.d/mx_check.conf
+ratelimit.conf:/usr/local/etc/rspamd/local.d/ratelimit.conf
+spamtrap.map:/usr/local/etc/rspamd/maps.d/spamtrap.map
diff --git a/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/2tld.inc.local b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/2tld.inc.local
new file mode 100644
index 000000000..022d1b7ef
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/2tld.inc.local
@@ -0,0 +1,5 @@
+{% if helpers.exists('OPNsense.Rspamd.general.enabled') and OPNsense.Rspamd.general.enabled == '1' and helpers.exists('OPNsense.Rspamd.surbl') %}
+{% for host in OPNsense.Rspamd.surbl.exceptions.split(',') %}
+{{ host }}
+{% endfor %}
+{% endif %}
diff --git a/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/antivirus.conf b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/antivirus.conf
new file mode 100644
index 000000000..f4b503d15
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/antivirus.conf
@@ -0,0 +1,29 @@
+#
+# Please don't modify this file as your changes might be overwritten with
+# the next update.
+#
+{% if helpers.exists('OPNsense.Rspamd.general.enabled') and OPNsense.Rspamd.general.enabled == '1' and helpers.exists('OPNsense.Rspamd.av') %}
+
+clamav {
+{% if helpers.exists('OPNsense.Rspamd.av.force-reject') and OPNsense.Rspamd.av['force-reject'] == '1' %}
+ action = "reject";
+{% endif %}
+{% if helpers.exists('OPNsense.Rspamd.av.attachments-only') and OPNsense.Rspamd.av['attachments-only'] == '1' %}
+ attachments_only = true;
+{% else %}
+ attachments_only = false;
+{% endif %}
+{% if helpers.exists('OPNsense.Rspamd.av.max-size') and OPNsense.Rspamd.av.max-size != '' %}
+ # If `max_size` is set, messages > n bytes in size are not scanned
+ max_size = {{ OPNsense.Rspamd.av.max-size }};
+{% endif %}
+ symbol = "CLAM_VIRUS";
+ type = "clamav";
+ #log_clean = true;
+
+{% if helpers.exists('OPNsense.clamav.general') and OPNsense.clamav.general.enabled == '1' %}
+ servers = "/var/run/clamav/clamd.sock";
+{% endif %}
+}
+
+{% endif %}
diff --git a/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/antivirus.wl b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/antivirus.wl
new file mode 100644
index 000000000..deaecad68
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/antivirus.wl
@@ -0,0 +1,5 @@
+{% if helpers.exists('OPNsense.Rspamd.general.enabled') and OPNsense.Rspamd.general.enabled == '1' and helpers.exists('OPNsense.Rspamd.av') %}
+{% for host in OPNsense.Rspamd.av.whitelist.split(',') %}
+{{ host }}
+{% endfor %}
+{% endif %}
diff --git a/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/dkim.conf b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/dkim.conf
new file mode 100644
index 000000000..b2c5dd1d7
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/dkim.conf
@@ -0,0 +1,13 @@
+# Please don't modify this file as your changes might be overwritten with
+# the next update.
+#
+
+{% if helpers.exists('OPNsense.Rspamd.general.enabled') and OPNsense.Rspamd.general.enabled == '1' and helpers.exists('OPNsense.Rspamd.dkim') %}
+
+ dkim_cache_size = {{ OPNsense.Rspamd.dkim.cache_size|default('2') }}k;
+ dkim_cache_expire = {{ OPNsense.Rspamd.dkim.cache_expire|default('1') }}d;
+ time_jitter = {{ OPNsense.Rspamd.dkim.time_jitter|default('6') }}h;
+ trusted_only = {% if helpers.exists('OPNsense.Rspamd.dkim.trusted_only') and OPNsense.Rspamd.dkim.trusted_only == '1' %}true{% else %}false{% endif %};
+ skip_multi = {% if helpers.exists('OPNsense.Rspamd.dkim.skip_multi') and OPNsense.Rspamd.dkim.skip_multi == '1' %}true{% else %}false{% endif %};
+
+{% endif %}
diff --git a/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/dkim_signing.conf b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/dkim_signing.conf
new file mode 100644
index 000000000..2e4c20d71
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/dkim_signing.conf
@@ -0,0 +1,22 @@
+# Please don't modify this file as your changes might be overwritten with
+# the next update.
+#
+
+{% if helpers.exists('OPNsense.Rspamd.general.enabled') and OPNsense.Rspamd.general.enabled == '1' and helpers.exists('OPNsense.Rspamd.dkim') %}
+ allow_envfrom_empty = {% if helpers.exists('OPNsense.Rspamd.dkim.allow_envfrom_empty') and OPNsense.Rspamd.dkim.allow_envfrom_empty == '1' %}true{% else %}false{% endif %};
+ allow_hdrfrom_mismatch = {% if helpers.exists('OPNsense.Rspamd.dkim.allow_hdrfrom_mismatch') and OPNsense.Rspamd.dkim.allow_hdrfrom_mismatch == '1' %}true{% else %}false{% endif %};
+ allow_hdrfrom_multiple = {% if helpers.exists('OPNsense.Rspamd.dkim.allow_hdrfrom_multiple') and OPNsense.Rspamd.dkim.allow_hdrfrom_multiple == '1' %}true{% else %}false{% endif %};
+ allow_username_mismatch = {% if helpers.exists('OPNsense.Rspamd.dkim.allow_username_mismatch') and OPNsense.Rspamd.dkim.allow_username_mismatch == '1' %}true{% else %}false{% endif %};
+ auth_only = {% if helpers.exists('OPNsense.Rspamd.dkim.auth_only') and OPNsense.Rspamd.dkim.auth_only == '1' %}true{% else %}false{% endif %};
+ #path = "/var/lib/rspamd/dkim/$domain.$selector.key";
+ selector = "dkim";
+ sign_local = {% if helpers.exists('OPNsense.Rspamd.dkim.sign_local') and OPNsense.Rspamd.dkim.sign_local == '1' %}true{% else %}false{% endif %};
+ symbol = "DKIM_SIGNED";
+ try_fallback = {% if helpers.exists('OPNsense.Rspamd.dkim.try_fallback') and OPNsense.Rspamd.dkim.try_fallback == '1' %}true{% else %}false{% endif %};
+ use_domain = "{{ OPNsense.Rspamd.dkim.use_domain|default("header") }}";
+ use_esld = {% if helpers.exists('OPNsense.Rspamd.dkim.use_esld') and OPNsense.Rspamd.dkim.use_esld == '1' %}true{% else %}false{% endif %};
+ use_redis = false;
+ # Hash for DKIM keys in Redis
+ key_prefix = "DKIM_KEYS";
+
+{% endif %}
diff --git a/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/greylist.conf b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/greylist.conf
new file mode 100644
index 000000000..840138c65
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/greylist.conf
@@ -0,0 +1,13 @@
+# Please don't modify this file as your changes might be overwritten with
+# the next update.
+#
+
+ expire = {{ OPNsense.Rspamd.graylist.expire|default('1') }}d;
+ timeout = {{ OPNsense.Rspamd.graylist.timeout|default('1') }}min; # 5 minutes by default
+ key_prefix = "rg"; # default hash name
+ max_data_len = {{ OPNsense.Rspamd.graylist.max_data_len|default('10') }}k;
+ message = "Try again later";
+ #symbol = "GREYLIST";
+ action = "soft reject"; # default greylisted action
+ ipv4_mask = {{ OPNsense.Rspamd.graylist.ipv4mask|default('19') }};
+ ipv6_mask = {{ OPNsense.Rspamd.graylist.ipv6mask|default('64') }};
diff --git a/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/mx_check.conf b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/mx_check.conf
new file mode 100644
index 000000000..9a1fd9484
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/mx_check.conf
@@ -0,0 +1,7 @@
+# Please don't modify this file as your changes might be overwritten with
+# the next update.
+#
+ timeout = 1.0;
+ expire = {{ OPNsense.Rspamd['mx-check'].expire|default('86400') }};
+
+ enabled = {% if helpers.exists('OPNsense.Rspamd.mx-check.enabled') and OPNsense.Rspamd['mx-check'].enabled == '1' %}true{% else %}false{% endif %};
diff --git a/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/phishing.conf b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/phishing.conf
new file mode 100644
index 000000000..07fb87dcf
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/phishing.conf
@@ -0,0 +1,8 @@
+# Please don't modify this file as your changes might be overwritten with
+# the next update.
+#
+
+ openphish_enabled = {% if helpers.exists('OPNsense.Rspamd.phishing.openphish_enabled') and OPNsense.Rspamd.phishing.openphish_enabled == '1' %}true{% else %}false{% endif %};
+ openphish_premium = {% if helpers.exists('OPNsense.Rspamd.phishing.openphish_premium_enabled') and OPNsense.Rspamd.phishing.openphish_premium_enabled == '1' %}true{% else %}false{% endif %};
+ # Disabled by default
+ phishtank_enabled = {% if helpers.exists('OPNsense.Rspamd.phishing.phishtank_enabled') and OPNsense.Rspamd.phishing.phishtank_enabled == '1' %}true{% else %}false{% endif %};
diff --git a/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/ratelimit.conf b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/ratelimit.conf
new file mode 100644
index 000000000..b58ef5a88
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/ratelimit.conf
@@ -0,0 +1,60 @@
+# Please don't modify this file as your changes might be overwritten with
+# the next update.
+#
+
+
+rates {
+ # Limit for all mail per recipient (rate 2 per minute)
+{% if helpers.exists('OPNsense.Rspamd.rate_limit.per_recipient.count') and OPNsense.Rspamd.rate_limit.per_recipient.count != '' %}
+{% if helpers.exists('OPNsense.Rspamd.rate_limit.per_recipient.time') and OPNsense.Rspamd.rate_limit.per_recipient.time != '' %}
+{% if helpers.exists('OPNsense.Rspamd.rate_limit.per_recipient.time_unit') and OPNsense.Rspamd.rate_limit.per_recipient.time_unit != '' %}
+ to = "{{ OPNsense.Rspamd.rate_limit.per_recipient.count }} / {{ OPNsense.Rspamd.rate_limit.per_recipient.time }}{{ OPNsense.Rspamd.rate_limit.per_recipient.time_unit }}";
+{% endif %}
+{% endif %}
+{% endif %}
+ # Limit for all mail per one source ip
+{% if helpers.exists('OPNsense.Rspamd.rate_limit.per_ip.count') and OPNsense.Rspamd.rate_limit.per_ip.count != '' %}
+{% if helpers.exists('OPNsense.Rspamd.rate_limit.per_ip.time') and OPNsense.Rspamd.rate_limit.per_ip.time != '' %}
+{% if helpers.exists('OPNsense.Rspamd.rate_limit.per_ip.time_unit') and OPNsense.Rspamd.rate_limit.per_ip.time_unit != '' %}
+ to_ip = "{{ OPNsense.Rspamd.rate_limit.per_ip.count }} / {{ OPNsense.Rspamd.rate_limit.per_ip.time }}{{ OPNsense.Rspamd.rate_limit.per_ip.time_unit }}";
+{% endif %}
+{% endif %}
+{% endif %}
+ # Limit for all mail per one source ip and from address (rate 1 per minute)
+{% if helpers.exists('OPNsense.Rspamd.rate_limit.per_ip_from.count') and OPNsense.Rspamd.rate_limit.per_ip_from.count != '' %}
+{% if helpers.exists('OPNsense.Rspamd.rate_limit.per_ip_from.time') and OPNsense.Rspamd.rate_limit.per_ip_from.time != '' %}
+{% if helpers.exists('OPNsense.Rspamd.rate_limit.per_ip_from.time_unit') and OPNsense.Rspamd.rate_limit.per_ip_from.time_unit != '' %}
+ to_ip_from = "{{ OPNsense.Rspamd.rate_limit.per_ip_from.count }} / {{ OPNsense.Rspamd.rate_limit.per_ip_from.time }}{{ OPNsense.Rspamd.rate_limit.per_ip_from.time_unit }}";
+{% endif %}
+{% endif %}
+{% endif %}
+ # Limit for all bounce mail (rate 2 per hour)
+{% if helpers.exists('OPNsense.Rspamd.rate_limit.bounce.count') and OPNsense.Rspamd.rate_limit.bounce.count != '' %}
+{% if helpers.exists('OPNsense.Rspamd.rate_limit.bounce.time') and OPNsense.Rspamd.rate_limit.bounce.time != '' %}
+{% if helpers.exists('OPNsense.Rspamd.rate_limit.bounce.time_unit') and OPNsense.Rspamd.rate_limit.bounce.time_unit != '' %}
+ bounce_to = "{{ OPNsense.Rspamd.rate_limit.bounce.count }} / {{ OPNsense.Rspamd.rate_limit.bounce.time }}{{ OPNsense.Rspamd.rate_limit.bounce.time_unit }}";
+{% endif %}
+{% endif %}
+{% endif %}
+ # Limit for bounce mail per one source ip
+{% if helpers.exists('OPNsense.Rspamd.rate_limit.bounce_ip.count') and OPNsense.Rspamd.rate_limit.bounce_ip.count != '' %}
+{% if helpers.exists('OPNsense.Rspamd.rate_limit.bounce_ip.time') and OPNsense.Rspamd.rate_limit.bounce_ip.time != '' %}
+{% if helpers.exists('OPNsense.Rspamd.rate_limit.bounce_ip.time_unit') and OPNsense.Rspamd.rate_limit.bounce_ip.time_unit != '' %}
+ bounce_to_ip = "{{ OPNsense.Rspamd.rate_limit.bounce_ip.count }} / {{ OPNsense.Rspamd.rate_limit.bounce_ip.time }}{{ OPNsense.Rspamd.rate_limit.bounce_ip.time_unit }}";
+{% endif %}
+{% endif %}
+{% endif %}
+ # Limit for all mail per authenticated user (rate 1 per minute)
+{% if helpers.exists('OPNsense.Rspamd.rate_limit.user.count') and OPNsense.Rspamd.rate_limit.user.count != '' %}
+{% if helpers.exists('OPNsense.Rspamd.rate_limit.user.time') and OPNsense.Rspamd.rate_limit.user.time != '' %}
+{% if helpers.exists('OPNsense.Rspamd.rate_limit.user.time_unit') and OPNsense.Rspamd.rate_limit.user.time_unit != '' %}
+ user = "{{ OPNsense.Rspamd.rate_limit.user.count }} / {{ OPNsense.Rspamd.rate_limit.user.time }}{{ OPNsense.Rspamd.rate_limit.user.time_unit }}";
+{% endif %}
+{% endif %}
+{% endif %}
+}
+# If symbol is specified, then it is inserted instead of setting result
+#symbol = "R_RATELIMIT";
+whitelisted_rcpts = "{{ OPNsense.Rspamd.rate_limit.whitelisted_rcpts|default('postmaster,mailer-daemon') }}";
+max_rcpt = {{ OPNsense.Rspamd.rate_limit.max_rcpt|default('20') }};
+
diff --git a/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/rspamd b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/rspamd
new file mode 100644
index 000000000..bb02f63f2
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/rspamd
@@ -0,0 +1,6 @@
+{% if helpers.exists('OPNsense.Rspamd.general.enabled') and OPNsense.Rspamd.general.enabled == '1' %}
+rspamd_enable="YES"
+rspamd_opnsense_bootup_run="/usr/local/opnsense/scripts/rspamd/setup.sh"
+{% else %}
+rspamd_enable="NO"
+{% endif %}
diff --git a/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/spamtrap.conf b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/spamtrap.conf
new file mode 100644
index 000000000..14edad5b6
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/spamtrap.conf
@@ -0,0 +1,28 @@
+# Please don't modify this file as your changes might be overwritten with
+# the next update.
+#
+
+
+{% if helpers.exists('OPNsense.Rspamd.general.enabled') and OPNsense.Rspamd.general.enabled == '1' and helpers.exists('OPNsense.Rspamd.spamtrap') %}
+ # Optionally set an action
+ #action = "no action";
+ # A map file containing regexp entries for spamtrap emails and domains
+ map = file://$LOCAL_CONFDIR/maps.d/spamtrap.map
+ # Name of the symbol
+ #symbol = "SPAMTRAP";
+ # A score for this module
+ #score = 0.0;
+ # Flag to enable fuzzy learning
+ learn_fuzzy = {% if helpers.exists('OPNsense.Rspamd.spamtrap.fuzzy_learning') and OPNsense.Rspamd.spamtrap.fuzzy_learning == '1' %}true{% else %}false{% endif %};
+ # Flag to enable bayes spam learning
+ learn_spam = {% if helpers.exists('OPNsense.Rspamd.spamtrap.spam_learning') and OPNsense.Rspamd.spamtrap.spam_learning == '1' %}true{% else %}false{% endif %};
+ # Fuzzy flag
+ #fuzzy_flag = 1;
+ # Fuzzy weight
+ #fuzy_weight = 10;
+ # Redis key prefix
+ #key_prefix = 'sptr_';
+
+ # !!! Disabled by default !!!
+ enabled = {% if helpers.exists('OPNsense.Rspamd.spamtrap.enabled') and OPNsense.Rspamd.spamtrap.enabled == '1' %}true{% else %}false{% endif %};
+{% endif %}
diff --git a/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/spamtrap.map b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/spamtrap.map
new file mode 100644
index 000000000..f2cfd0041
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/spamtrap.map
@@ -0,0 +1,5 @@
+{% if helpers.exists('OPNsense.Rspamd.general.enabled') and OPNsense.Rspamd.general.enabled == '1' and helpers.exists('OPNsense.Rspamd.spamtrap.spam_recipients') %}
+{% for recipient in OPNsense.Rspamd.spamtrap.spam_recipients.split(',') %}
+/{{ recipient }}/i
+{% endfor %}
+{% endif %}
diff --git a/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/spf.conf b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/spf.conf
new file mode 100644
index 000000000..155f73973
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/spf.conf
@@ -0,0 +1,6 @@
+# Please don't modify this file as your changes might be overwritten with
+# the next update.
+#
+
+ spf_cache_size = {{ OPNsense.Rspamd.spf.spf_cache_size|default("2") }}k;
+ spf_cache_expire = {{ OPNsense.Rspamd.spf.spf_cache_expire|default("1") }}d;
diff --git a/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/surbl-whitelist.inc.local b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/surbl-whitelist.inc.local
new file mode 100644
index 000000000..b5164bc77
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/surbl-whitelist.inc.local
@@ -0,0 +1,5 @@
+{% if helpers.exists('OPNsense.Rspamd.general.enabled') and OPNsense.Rspamd.general.enabled == '1' and helpers.exists('OPNsense.Rspamd.surbl') %}
+{% for host in OPNsense.Rspamd.surbl.whitelist.split(',') %}
+{{ host }}
+{% endfor %}
+{% endif %}