diff --git a/Makefile b/Makefile
index 70c0b65a5..8902b8d02 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@ PAGER?= less
all:
@cat ${.CURDIR}/README.md | ${PAGER}
-CATEGORIES= databases devel dns net-mgmt net security sysutils www
+CATEGORIES= databases devel dns mail net-mgmt net security sysutils www
.for CATEGORY in ${CATEGORIES}
_${CATEGORY}!= ls -1d ${CATEGORY}/*
diff --git a/README.md b/README.md
index f64394386..8fc960ce7 100644
--- a/README.md
+++ b/README.md
@@ -33,6 +33,8 @@ devel/debug -- Debugging Tools
devel/helloworld -- A sample framework application
dns/dyndns -- Dynamic DNS Support
dns/rfc2136 -- RFC-2136 Support
+mail/postfix -- SMTP mail relay
+mail/rspamd -- Protect your network from spam
net-mgmt/collectd -- Collect system and application performance metrics periodically
net-mgmt/snmp -- SNMP Server via bsnmpd
net-mgmt/telegraf -- Agent for collecting metrics and data
diff --git a/mail/postfix/Makefile b/mail/postfix/Makefile
new file mode 100644
index 000000000..fb4cdc37b
--- /dev/null
+++ b/mail/postfix/Makefile
@@ -0,0 +1,8 @@
+PLUGIN_NAME= postfix
+PLUGIN_VERSION= 0.1
+PLUGIN_COMMENT= SMTP mail relay
+PLUGIN_DEPENDS= postfix-sasl
+PLUGIN_MAINTAINER= m.muenz@gmail.com
+PLUGIN_DEVEL= yes
+
+.include "../../Mk/plugins.mk"
diff --git a/mail/postfix/pkg-descr b/mail/postfix/pkg-descr
new file mode 100644
index 000000000..1e96d0531
--- /dev/null
+++ b/mail/postfix/pkg-descr
@@ -0,0 +1,5 @@
+Postfix attempts to be fast, easy to administer, and secure.
+The outside has a definite Sendmail-ish flavor, but the inside
+is completely different.
+
+WWW: http://www.postfix.org/
diff --git a/mail/postfix/src/etc/inc/plugins.inc.d/postfix.inc b/mail/postfix/src/etc/inc/plugins.inc.d/postfix.inc
new file mode 100644
index 000000000..0ff66f9de
--- /dev/null
+++ b/mail/postfix/src/etc/inc/plugins.inc.d/postfix.inc
@@ -0,0 +1,49 @@
+ gettext('Postfix'),
+ 'configd' => array(
+ 'restart' => array('postfix restart'),
+ 'start' => array('postfix start'),
+ 'stop' => array('postfix stop'),
+ ),
+ 'name' => 'postfix',
+ 'pidfile' => '/var/spool/postfix/pid/master.pid'
+ );
+ }
+
+ return $services;
+}
diff --git a/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/Api/AntispamController.php b/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/Api/AntispamController.php
new file mode 100644
index 000000000..c9896d712
--- /dev/null
+++ b/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/Api/AntispamController.php
@@ -0,0 +1,76 @@
+request->isGet()) {
+ $mdlAntispam = new Antispam();
+ $result['antispam'] = $mdlAntispam->getNodes();
+ }
+ return $result;
+ }
+
+ public function setAction()
+ {
+ $result = array("result"=>"failed");
+ if ($this->request->isPost()) {
+ // load model and update with provided data
+ $mdlAntispam = new Antispam();
+ $mdlAntispam->setNodes($this->request->getPost("antispam"));
+
+ // perform validation
+ $valMsgs = $mdlAntispam->performValidation();
+ foreach ($valMsgs as $field => $msg) {
+ if (!array_key_exists("validations", $result)) {
+ $result["validations"] = array();
+ }
+ $result["validations"]["antispam.".$msg->getField()] = $msg->getMessage();
+ }
+
+ // serialize model to config and save
+ if ($valMsgs->count() == 0) {
+ $mdlAntispam->serializeToConfig();
+ Config::getInstance()->save();
+ $result["result"] = "saved";
+ }
+ }
+ return $result;
+ }
+}
diff --git a/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/Api/DomainController.php b/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/Api/DomainController.php
new file mode 100644
index 000000000..f449b9245
--- /dev/null
+++ b/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/Api/DomainController.php
@@ -0,0 +1,205 @@
+request->isGet()) {
+ $mdlDomain = new Domain();
+ $result['domain'] = $mdlDomain->getNodes();
+ }
+ return $result;
+ }
+
+ public function setAction()
+ {
+ $result = array("result"=>"failed");
+ if ($this->request->isPost()) {
+ // load model and update with provided data
+ $mdlDomain = new Domain();
+ $mdlDomain->setNodes($this->request->getPost("domain"));
+ // perform validation
+ $valMsgs = $mdlDomain->performValidation();
+ foreach ($valMsgs as $field => $msg) {
+ if (!array_key_exists("validations", $result)) {
+ $result["validations"] = array();
+ }
+ $result["validations"]["domain.".$msg->getField()] = $msg->getMessage();
+ }
+ // serialize model to config and save
+ if ($valMsgs->count() == 0) {
+ $mdlDomain->serializeToConfig();
+ Config::getInstance()->save();
+ $result["result"] = "saved";
+ }
+ }
+ return $result;
+ }
+
+ public function searchDomainAction()
+ {
+ $this->sessionClose();
+ $mdlDomain = $this->getModel();
+ $grid = new UIModelGrid($mdlDomain->domains->domain);
+ return $grid->fetchBindRequest(
+ $this->request,
+ array("enabled", "domainname", "destination" )
+ );
+ }
+
+ public function getDomainAction($uuid = null)
+ {
+ $mdlDomain = $this->getModel();
+ if ($uuid != null) {
+ $node = $mdlDomain->getNodeByReference('domains.domain.' . $uuid);
+ if ($node != null) {
+ // return node
+ return array("domain" => $node->getNodes());
+ }
+ } else {
+ $node = $mdlDomain->domains->domain->add();
+ return array("domain" => $node->getNodes());
+ }
+ return array();
+ }
+
+ public function addDomainAction()
+ {
+ $result = array("result" => "failed");
+ if ($this->request->isPost() && $this->request->hasPost("domain")) {
+ $result = array("result" => "failed", "validations" => array());
+ $mdlDomain = $this->getModel();
+ $node = $mdlDomain->domains->domain->Add();
+ $node->setNodes($this->request->getPost("domain"));
+ $valMsgs = $mdlDomain->performValidation();
+ foreach ($valMsgs as $field => $msg) {
+ $fieldnm = str_replace($node->__reference, "domain", $msg->getField());
+ $result["validations"][$fieldnm] = $msg->getMessage();
+ }
+ if (count($result['validations']) == 0) {
+ unset($result['validations']);
+ // save config if validated correctly
+ $mdlDomain->serializeToConfig();
+ Config::getInstance()->save();
+ unset($result['validations']);
+ $result["result"] = "saved";
+ }
+ }
+ return $result;
+ }
+
+ public function delDomainAction($uuid)
+ {
+ $result = array("result" => "failed");
+ if ($this->request->isPost()) {
+ $mdlDomain = $this->getModel();
+ if ($uuid != null) {
+ if ($mdlDomain->domains->domain->del($uuid)) {
+ $mdlDomain->serializeToConfig();
+ Config::getInstance()->save();
+ $result['result'] = 'deleted';
+ } else {
+ $result['result'] = 'not found';
+ }
+ }
+ }
+ return $result;
+ }
+
+ public function setDomainAction($uuid)
+ {
+ if ($this->request->isPost() && $this->request->hasPost("domain")) {
+ $mdlSetting = $this->getModel();
+ if ($uuid != null) {
+ $node = $mdlSetting->getNodeByReference('domains.domain.' . $uuid);
+ if ($node != null) {
+ $result = array("result" => "failed", "validations" => array());
+ $domainInfo = $this->request->getPost("domain");
+ $node->setNodes($domainInfo);
+ $valMsgs = $mdlSetting->performValidation();
+ foreach ($valMsgs as $field => $msg) {
+ $fieldnm = str_replace($node->__reference, "domain", $msg->getField());
+ $result["validations"][$fieldnm] = $msg->getMessage();
+ }
+ if (count($result['validations']) == 0) {
+ // save config if validated correctly
+ $mdlSetting->serializeToConfig();
+ Config::getInstance()->save();
+ $result = array("result" => "saved");
+ }
+ return $result;
+ }
+ }
+ }
+ return array("result" => "failed");
+ }
+
+ public function toggle_handler($uuid, $elements, $element)
+ {
+ $result = array("result" => "failed");
+ if ($this->request->isPost()) {
+ $mdlSetting = $this->getModel();
+ if ($uuid != null) {
+ $node = $mdlSetting->getNodeByReference($elements . '.'. $element .'.' . $uuid);
+ if ($node != null) {
+ if ($node->enabled->__toString() == "1") {
+ $result['result'] = "Disabled";
+ $node->enabled = "0";
+ } else {
+ $result['result'] = "Enabled";
+ $node->enabled = "1";
+ }
+ // if item has toggled, serialize to config and save
+ $mdlSetting->serializeToConfig();
+ Config::getInstance()->save();
+ }
+ }
+ }
+ return $result;
+ }
+
+ public function toggleDomainAction($uuid)
+ {
+ return $this->toggle_handler($uuid, 'domains', 'domain');
+ }
+}
diff --git a/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/Api/GeneralController.php b/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/Api/GeneralController.php
new file mode 100644
index 000000000..b01f39fea
--- /dev/null
+++ b/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/Api/GeneralController.php
@@ -0,0 +1,76 @@
+request->isGet()) {
+ $mdlGeneral = new General();
+ $result['general'] = $mdlGeneral->getNodes();
+ }
+ return $result;
+ }
+
+ public function setAction()
+ {
+ $result = array("result"=>"failed");
+ if ($this->request->isPost()) {
+ // load model and update with provided data
+ $mdlGeneral = new General();
+ $mdlGeneral->setNodes($this->request->getPost("general"));
+
+ // perform validation
+ $valMsgs = $mdlGeneral->performValidation();
+ foreach ($valMsgs as $field => $msg) {
+ if (!array_key_exists("validations", $result)) {
+ $result["validations"] = array();
+ }
+ $result["validations"]["general.".$msg->getField()] = $msg->getMessage();
+ }
+
+ // serialize model to config and save
+ if ($valMsgs->count() == 0) {
+ $mdlGeneral->serializeToConfig();
+ Config::getInstance()->save();
+ $result["result"] = "saved";
+ }
+ }
+ return $result;
+ }
+}
diff --git a/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/Api/ServiceController.php b/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/Api/ServiceController.php
new file mode 100644
index 000000000..269363bfe
--- /dev/null
+++ b/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/Api/ServiceController.php
@@ -0,0 +1,159 @@
+configdRun("firmware plugin rspamd");
+ return $response;
+ }
+
+ /**
+ * start postfix service (in background)
+ * @return array
+ */
+ public function startAction()
+ {
+ if ($this->request->isPost()) {
+ $backend = new Backend();
+ $response = $backend->configdRun('postfix start');
+ return array("response" => $response);
+ } else {
+ return array("response" => array());
+ }
+ }
+
+ /**
+ * stop postfix service
+ * @return array
+ */
+ public function stopAction()
+ {
+ if ($this->request->isPost()) {
+ $backend = new Backend();
+ $response = $backend->configdRun("postfix stop");
+ return array("response" => $response);
+ } else {
+ return array("response" => array());
+ }
+ }
+
+ /**
+ * restart postfix service
+ * @return array
+ */
+ public function restartAction()
+ {
+ if ($this->request->isPost()) {
+ $backend = new Backend();
+ $response = $backend->configdRun("postfix restart");
+ return array("response" => $response);
+ } else {
+ return array("response" => array());
+ }
+ }
+
+ /**
+ * retrieve status of postfix
+ * @return array
+ * @throws \Exception
+ */
+ public function statusAction()
+ {
+ $backend = new Backend();
+ $mdlGeneral = new General();
+ $response = $backend->configdRun("postfix status");
+
+ if (strpos($response, "not running") > 0) {
+ if ($mdlGeneral->enabled->__toString() == 1) {
+ $status = "stopped";
+ } else {
+ $status = "disabled";
+ }
+ } elseif (strpos($response, "is running") > 0) {
+ $status = "running";
+ } elseif ($mdlGeneral->enabled->__toString() == 0) {
+ $status = "disabled";
+ } else {
+ $status = "unkown";
+ }
+
+
+ return array("status" => $status);
+ }
+
+ /**
+ * reconfigure postfix, generate config and reload
+ */
+ public function reconfigureAction()
+ {
+ if ($this->request->isPost()) {
+ // close session for long running action
+ $this->sessionClose();
+
+ $mdlGeneral = new General();
+ $backend = new Backend();
+
+ $runStatus = $this->statusAction();
+
+ // stop postfix if it is running or not
+ $this->stopAction();
+
+ // generate template
+ $backend->configdRun('template reload OPNsense/Postfix');
+ $backend->configdRun('postfix make-transport');
+
+ // (res)start daemon
+ if ($mdlGeneral->enabled->__toString() == 1) {
+ $this->startAction();
+ }
+
+ return array("status" => "ok");
+ } else {
+ return array("status" => "failed");
+ }
+ }
+}
diff --git a/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/DomainController.php b/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/DomainController.php
new file mode 100644
index 000000000..1f4e7b844
--- /dev/null
+++ b/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/DomainController.php
@@ -0,0 +1,34 @@
+view->title = gettext("Postfix Domains");
+ $this->view->formDialogEditPostfixDomain = $this->getForm("dialogEditPostfixDomain");
+ $this->view->pick('OPNsense/Postfix/domain');
+ }
+}
diff --git a/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/GeneralController.php b/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/GeneralController.php
new file mode 100644
index 000000000..3714878b5
--- /dev/null
+++ b/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/GeneralController.php
@@ -0,0 +1,39 @@
+view->title = gettext("Postfix Settings");
+ $this->view->generalForm = $this->getForm("general");
+ $this->view->antispamForm = $this->getForm("antispam");
+ $this->view->pick('OPNsense/Postfix/general');
+ }
+}
diff --git a/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/forms/antispam.xml b/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/forms/antispam.xml
new file mode 100644
index 000000000..9db8d64b3
--- /dev/null
+++ b/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/forms/antispam.xml
@@ -0,0 +1,8 @@
+
diff --git a/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/forms/dialogEditPostfixDomain.xml b/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/forms/dialogEditPostfixDomain.xml
new file mode 100644
index 000000000..ce4c6b020
--- /dev/null
+++ b/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/forms/dialogEditPostfixDomain.xml
@@ -0,0 +1,20 @@
+
diff --git a/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/forms/general.xml b/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/forms/general.xml
new file mode 100644
index 000000000..2e974c035
--- /dev/null
+++ b/mail/postfix/src/opnsense/mvc/app/controllers/OPNsense/Postfix/forms/general.xml
@@ -0,0 +1,112 @@
+
diff --git a/mail/postfix/src/opnsense/mvc/app/models/OPNsense/Postfix/ACL/ACL.xml b/mail/postfix/src/opnsense/mvc/app/models/OPNsense/Postfix/ACL/ACL.xml
new file mode 100644
index 000000000..65c0078e5
--- /dev/null
+++ b/mail/postfix/src/opnsense/mvc/app/models/OPNsense/Postfix/ACL/ACL.xml
@@ -0,0 +1,9 @@
+
+
+ Services: Postfix
+
+ ui/postfix/*
+ api/postfix/*
+
+
+
diff --git a/mail/postfix/src/opnsense/mvc/app/models/OPNsense/Postfix/Antispam.php b/mail/postfix/src/opnsense/mvc/app/models/OPNsense/Postfix/Antispam.php
new file mode 100644
index 000000000..50598c8ca
--- /dev/null
+++ b/mail/postfix/src/opnsense/mvc/app/models/OPNsense/Postfix/Antispam.php
@@ -0,0 +1,34 @@
+
+ //OPNsense/postfix/antispam
+ Postfix Antispam configuration
+ 1.0.0
+
+
+ 0
+ Y
+
+
+
diff --git a/mail/postfix/src/opnsense/mvc/app/models/OPNsense/Postfix/Domain.php b/mail/postfix/src/opnsense/mvc/app/models/OPNsense/Postfix/Domain.php
new file mode 100644
index 000000000..2bcab6c9e
--- /dev/null
+++ b/mail/postfix/src/opnsense/mvc/app/models/OPNsense/Postfix/Domain.php
@@ -0,0 +1,30 @@
+
+ //OPNsense/postfix/domain
+ Postfix domain configuration
+ 1.0.0
+
+
+
+
+ 1
+ Y
+
+
+
+ Y
+
+
+
+ Y
+
+
+
+
+
diff --git a/mail/postfix/src/opnsense/mvc/app/models/OPNsense/Postfix/General.php b/mail/postfix/src/opnsense/mvc/app/models/OPNsense/Postfix/General.php
new file mode 100644
index 000000000..5fe1c41cd
--- /dev/null
+++ b/mail/postfix/src/opnsense/mvc/app/models/OPNsense/Postfix/General.php
@@ -0,0 +1,34 @@
+
+ //OPNsense/postfix/general
+ Postfix configuration
+ 1.0.0
+
+
+ 0
+ Y
+
+
+
+ N
+
+
+
+ N
+
+
+
+ N
+
+
+ all
+ Y
+
+
+ 127.0.0.0/8,[::ffff:127.0.0.0]/104,[::1]/128
+ Y
+
+
+
+ N
+
+
+ 51200000
+ Y
+
+
+ 1
+ Y
+
+
+ 1
+ Y
+
+
+
+
+ 1
+ Y
+
+
+
+
+ 1
+ Y
+
+
+ 1
+ Y
+
+
+ 1
+ Y
+
+
+ 1
+ Y
+
+
+ 1
+ Y
+
+
+ 1
+ Y
+
+
+ 1
+ Y
+
+
+ 1
+ Y
+
+
+
diff --git a/mail/postfix/src/opnsense/mvc/app/models/OPNsense/Postfix/Menu/Menu.xml b/mail/postfix/src/opnsense/mvc/app/models/OPNsense/Postfix/Menu/Menu.xml
new file mode 100644
index 000000000..6c9e95893
--- /dev/null
+++ b/mail/postfix/src/opnsense/mvc/app/models/OPNsense/Postfix/Menu/Menu.xml
@@ -0,0 +1,8 @@
+
+
+
+
+
+
+
+
diff --git a/mail/postfix/src/opnsense/mvc/app/views/OPNsense/Postfix/domain.volt b/mail/postfix/src/opnsense/mvc/app/views/OPNsense/Postfix/domain.volt
new file mode 100644
index 000000000..ccb964315
--- /dev/null
+++ b/mail/postfix/src/opnsense/mvc/app/views/OPNsense/Postfix/domain.volt
@@ -0,0 +1,110 @@
+{#
+
+OPNsense® is Copyright © 2014 – 2017 by Deciso B.V.
+Copyright (C) 2017 Michael Muenz
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+this list of conditions and the following disclaimer in the documentation
+and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+#}
+
+
+
+
+
+
+
+
+
+ {{ lang._('Enabled') }}
+ {{ lang._('Domain') }}
+ {{ lang._('Destination') }}
+ {{ lang._('ID') }}
+ {{ lang._('Commands') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {{ lang._('Apply') }}
+
+
+
+
+{{ partial("layout_partials/base_dialog",['fields':formDialogEditPostfixDomain,'id':'dialogEditPostfixDomain','label':lang._('Edit Domain')])}}
diff --git a/mail/postfix/src/opnsense/mvc/app/views/OPNsense/Postfix/general.volt b/mail/postfix/src/opnsense/mvc/app/views/OPNsense/Postfix/general.volt
new file mode 100644
index 000000000..318606e52
--- /dev/null
+++ b/mail/postfix/src/opnsense/mvc/app/views/OPNsense/Postfix/general.volt
@@ -0,0 +1,100 @@
+{#
+
+OPNsense® is Copyright © 2014 – 2017 by Deciso B.V.
+This file is Copyright © 2017 by Michael Muenz
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES,
+INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGE.
+
+#}
+
+
+
+
+ {{ partial("layout_partials/base_form",['fields':generalForm,'id':'frm_general_settings'])}}
+
+
+ {{ lang._('Save') }}
+
+
+
+
+
+
+
{{ lang._('No Rspamd plugin found, please install and come back.')}}
+
+ {{ partial("layout_partials/base_form",['fields':antispamForm,'id':'frm_antispam_settings'])}}
+
+
+ {{ lang._('Save') }}
+
+
+
+
+
+
diff --git a/mail/postfix/src/opnsense/scripts/OPNsense/Postfix/setup.sh b/mail/postfix/src/opnsense/scripts/OPNsense/Postfix/setup.sh
new file mode 100755
index 000000000..5a153a51e
--- /dev/null
+++ b/mail/postfix/src/opnsense/scripts/OPNsense/Postfix/setup.sh
@@ -0,0 +1,32 @@
+#!/bin/sh
+
+mkdir -p /var/spool/postfix/
+chown root:wheel /var/spool/postfix
+chmod 755 /var/spool/postfix
+
+# Set defaults
+POSTFIX_DIRS="/var/spool/postfix/active /var/spool/postfix/bounce /var/spool/postfix/corrupt /var/spool/postfix/defer /var/spool/postfix/deferred /var/spool/postfix/flush /var/spool/postfix/hold /var/spool/postfix/incoming /var/spool/postfix/private /var/spool/postfix/saved /var/spool/postfix/trace /var/db/postfix"
+POSTFIX_USER=postfix
+POSTFIX_GROUP=wheel
+
+for DIR in ${POSTFIX_DIRS}; do
+ mkdir -p ${DIR}
+ chmod -R 700 ${DIR}
+ chown -R ${POSTFIX_USER}:${POSTFIX_GROUP} ${DIR}
+done
+
+# Some folders need special attention
+mkdir -p /var/spool/postfix/maildrop
+mkdir -p /var/spool/postfix/public
+mkdir -p /var/spool/postfix/pid
+chmod -R 730 /var/spool/postfix/maildrop
+chmod -R 710 /var/spool/postfix/public
+chmod -R 755 /var/spool/postfix/pid
+chown -R postfix:maildrop /var/spool/postfix/maildrop
+chown -R postfix:maildrop /var/spool/postfix/public
+chown -R root:postfix /var/spool/postfix/pid
+
+# Create Transporttable
+postmap /usr/local/etc/postfix/transport
+postmap /usr/local/etc/postfix/recipient_access
+postmap /usr/local/etc/postfix/sender_access
diff --git a/mail/postfix/src/opnsense/service/conf/actions.d/actions_postfix.conf b/mail/postfix/src/opnsense/service/conf/actions.d/actions_postfix.conf
new file mode 100644
index 000000000..f26c0d74e
--- /dev/null
+++ b/mail/postfix/src/opnsense/service/conf/actions.d/actions_postfix.conf
@@ -0,0 +1,35 @@
+[start]
+command:/usr/local/opnsense/scripts/OPNsense/Postfix/setup.sh;/usr/local/etc/rc.d/postfix start
+parameters:
+type:script
+message:starting Postfix
+
+[stop]
+command:/usr/local/etc/rc.d/postfix stop; exit 0
+parameters:
+type:script
+message:stopping Postfix
+
+[restart]
+command:/usr/local/opnsense/scripts/OPNsense/Postfix/setup.sh;/usr/local/etc/rc.d/postfix restart
+parameters:
+type:script
+message:restarting Postfix
+
+[reconfigure]
+command:/usr/local/opnsense/scripts/OPNsense/Postfix/setup.sh;/usr/local/etc/rc.d/postfix reload
+parameters:
+type:script
+message:reconfigure Postfix
+
+[status]
+command:/usr/local/etc/rc.d/postfix status;exit 0
+parameters:
+type:script_output
+message:request Postfix status
+
+[make-transport]
+command:postmap /usr/local/etc/postfix/transport
+parameters:
+type:script_output
+message:rebuilding transport table
diff --git a/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/+TARGETS b/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/+TARGETS
new file mode 100644
index 000000000..3cbe5f21d
--- /dev/null
+++ b/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/+TARGETS
@@ -0,0 +1,6 @@
+main.cf:/usr/local/etc/postfix/main.cf
+master.cf:/usr/local/etc/postfix/master.cf
+postfix:/etc/rc.conf.d/postfix
+transport:/usr/local/etc/postfix/transport
+recipient_access:/usr/local/etc/postfix/recipient_access
+sender_access:/usr/local/etc/postfix/sender_access
diff --git a/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/main.cf b/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/main.cf
new file mode 100644
index 000000000..b9f248b5b
--- /dev/null
+++ b/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/main.cf
@@ -0,0 +1,129 @@
+{% if helpers.exists('OPNsense.postfix.general.enabled') and OPNsense.postfix.general.enabled == '1' %}
+
+##########################
+# START SYSTEM DEFAULTS
+##########################
+compatibility_level = 2
+queue_directory = /var/spool/postfix
+command_directory = /usr/local/sbin
+daemon_directory = /usr/local/libexec/postfix
+data_directory = /var/db/postfix
+mail_owner = postfix
+unknown_local_recipient_reject_code = 550
+mynetworks_style = host
+debug_peer_level = 2
+debugger_command =
+ PATH=/bin:/usr/bin:/usr/local/bin:/usr/X11R6/bin
+ ddd $daemon_directory/$process_name $process_id & sleep 5
+
+sendmail_path = /usr/local/sbin/sendmail
+newaliases_path = /usr/local/bin/newaliases
+mailq_path = /usr/local/bin/mailq
+setgid_group = maildrop
+html_directory = no
+manpage_directory = /usr/local/man
+sample_directory = /usr/local/etc/postfix
+readme_directory = no
+inet_protocols = all
+meta_directory = /usr/local/libexec/postfix
+shlib_directory = /usr/local/lib/postfix
+relay_domains = hash:/usr/local/etc/postfix/transport
+transport_maps = hash:/usr/local/etc/postfix/transport
+##########################
+# END SYSTEM DEFAULTS
+##########################
+
+{% if helpers.exists('OPNsense.postfix.general.myhostname') and OPNsense.postfix.general.myhostname != '' %}
+myhostname = {{ OPNsense.postfix.general.myhostname }}
+{% else %}
+myhostname = {{ system.hostname }}
+{% endif %}
+{% if helpers.exists('OPNsense.postfix.general.mydomain') and OPNsense.postfix.general.mydomain != '' %}
+mydomain = {{ OPNsense.postfix.general.mydomain }}
+{% else %}
+mydomain = {{ system.domain }}
+{% endif %}
+{% if helpers.exists('OPNsense.postfix.general.myorigin') and OPNsense.postfix.general.myorigin != '' %}
+myorigin = {{ OPNsense.postfix.general.myorigin }}
+{% else %}
+myorigin = $myhostname
+{% endif %}
+{% if helpers.exists('OPNsense.postfix.general.inet_interfaces') and OPNsense.postfix.general.inet_interfaces != '' %}
+inet_interfaces = {{ OPNsense.postfix.general.inet_interfaces }}
+{% else %}
+inet_interfaces = all
+{% endif %}
+{% if helpers.exists('OPNsense.postfix.general.mynetworks') and OPNsense.postfix.general.mynetworks != '' %}
+mynetworks = {{ OPNsense.postfix.general.mynetworks.replace(',', ' ') }}
+{% else %}
+mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128
+{% endif %}
+{% if helpers.exists('OPNsense.postfix.general.banner') and OPNsense.postfix.general.banner != '' %}
+smtpd_banner = {{ OPNsense.postfix.general.banner }}
+{% else %}
+smtpd_banner = $myhostname ESMTP Postfix
+{% endif %}
+{% if helpers.exists('OPNsense.postfix.general.message_size_limit') and OPNsense.postfix.general.message_size_limit != '' %}
+message_size_limit = {{ OPNsense.postfix.general.message_size_limit }}
+{% endif %}
+{% if helpers.exists('OPNsense.postfix.general.disable_ssl') and OPNsense.postfix.general.disable_ssl == '1' %}
+smtpd_tls_mandatory_protocols=!SSLv2,!SSLv3
+smtp_tls_mandatory_protocols=!SSLv2,!SSLv3
+smtpd_tls_protocols=!SSLv2,!SSLv3
+smtp_tls_protocols=!SSLv2,!SSLv3
+{% endif %}
+{% if helpers.exists('OPNsense.postfix.general.disable_weak_ciphers') and OPNsense.postfix.general.disable_weak_ciphers == '1' %}
+smtpd_tls_exclude_ciphers = aNULL, eNULL, EXPORT, DES, RC4, MD5, PSK, aECDH, EDH-DSS-DES-CBC3-SHA, EDH-RSA-DES-CDC3-SHA, KRB5-DE5, CBC3-SHA
+{% endif %}
+
+{% if helpers.exists('OPNsense.postfix.antispam.enable_rspamd') and OPNsense.postfix.antispam.enable_rspamd == '1' %}
+smtpd_milters = inet:localhost:11332
+non_smtpd_milters = inet:localhost:11332
+milter_protocol = 6
+milter_mail_macros = i {mail_addr} {client_addr} {client_name} {auth_authen}
+milter_default_action = accept
+{% endif %}
+
+{# Sender Restrictions #}
+{% set smtpd_recipient_restrictions=[] %}
+{% if helpers.exists('OPNsense.postfix.general.check_recipient_access') %}
+{% do smtpd_recipient_restrictions.append('check_recipient_access hash:/usr/local/etc/postfix/recipient_access') %}
+{% endif %}
+{% if helpers.exists('OPNsense.postfix.general.reject_unauth_pipelining') and OPNsense.postfix.general.reject_unauth_pipelining == '1' %}
+{% do smtpd_recipient_restrictions.append('reject_unauth_pipelining') %}
+{% endif %}
+{% if helpers.exists('OPNsense.postfix.general.check_sender_access') %}
+{% do smtpd_recipient_restrictions.append('check_sender_access hash:/usr/local/etc/postfix/sender_access') %}
+{% endif %}
+{% if helpers.exists('OPNsense.postfix.general.reject_unknown_sender_domain') and OPNsense.postfix.general.reject_unknown_sender_domain == '1' %}
+{% do smtpd_recipient_restrictions.append('reject_unknown_sender_domain') %}
+{% endif %}
+{% if helpers.exists('OPNsense.postfix.general.reject_unknown_recipient_domain') and OPNsense.postfix.general.reject_unknown_recipient_domain == '1' %}
+{% do smtpd_recipient_restrictions.append('reject_unknown_recipient_domain') %}
+{% endif %}
+{% if helpers.exists('OPNsense.postfix.general.reject_non_fqdn_sender') and OPNsense.postfix.general.reject_non_fqdn_sender == '1' %}
+{% do smtpd_recipient_restrictions.append('reject_non_fqdn_sender') %}
+{% endif %}
+{% if helpers.exists('OPNsense.postfix.general.reject_non_fqdn_recipient') and OPNsense.postfix.general.reject_non_fqdn_recipient == '1' %}
+{% do smtpd_recipient_restrictions.append('reject_non_fqdn_recipient') %}
+{% endif %}
+{% if helpers.exists('OPNsense.postfix.general.permit_sasl_authenticated') and OPNsense.postfix.general.permit_sasl_authenticated == '1' %}
+{% do smtpd_recipient_restrictions.append('permit_sasl_authenticated') %}
+{% endif %}
+{% if helpers.exists('OPNsense.postfix.general.permit_tls_clientcerts') and OPNsense.postfix.general.permit_tls_clientcerts == '1' %}
+{% do smtpd_recipient_restrictions.append('permit_tls_clientcerts') %}
+{% endif %}
+{% if helpers.exists('OPNsense.postfix.general.permit_mynetworks') and OPNsense.postfix.general.permit_mynetworks == '1' %}
+{% do smtpd_recipient_restrictions.append('permit_mynetworks') %}
+{% endif %}
+{% if helpers.exists('OPNsense.postfix.general.reject_unauth_destination') and OPNsense.postfix.general.reject_unauth_destination == '1' %}
+{% do smtpd_recipient_restrictions.append('reject_unauth_destination') %}
+{% endif %}
+
+{% if smtpd_recipient_restrictions|length >= 1 %}
+smtpd_recipient_restrictions = {{ smtpd_recipient_restrictions | join(', ') }}
+{% endif %}
+
+smtpd_helo_required = yes
+
+{% endif %}
diff --git a/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/master.cf b/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/master.cf
new file mode 100644
index 000000000..4fb709e7f
--- /dev/null
+++ b/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/master.cf
@@ -0,0 +1,135 @@
+{% if helpers.exists('OPNsense.postfix.general.enabled') and OPNsense.postfix.general.enabled == '1' %}
+#
+# Postfix master process configuration file. For details on the format
+# of the file, see the master(5) manual page (command: "man 5 master" or
+# on-line: http://www.postfix.org/master.5.html).
+#
+# Do not forget to execute "postfix reload" after editing this file.
+#
+# ==========================================================================
+# service type private unpriv chroot wakeup maxproc command + args
+# (yes) (yes) (no) (never) (100)
+# ==========================================================================
+smtp inet n - n - - smtpd
+#smtp inet n - n - 1 postscreen
+#smtpd pass - - n - - smtpd
+#dnsblog unix - - n - 0 dnsblog
+#tlsproxy unix - - n - 0 tlsproxy
+#submission inet n - n - - smtpd
+# -o syslog_name=postfix/submission
+# -o smtpd_tls_security_level=encrypt
+# -o smtpd_sasl_auth_enable=yes
+# -o smtpd_tls_auth_only=yes
+# -o smtpd_reject_unlisted_recipient=no
+# -o smtpd_client_restrictions=$mua_client_restrictions
+# -o smtpd_helo_restrictions=$mua_helo_restrictions
+# -o smtpd_sender_restrictions=$mua_sender_restrictions
+# -o smtpd_recipient_restrictions=
+# -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
+# -o milter_macro_daemon_name=ORIGINATING
+#smtps inet n - n - - smtpd
+# -o syslog_name=postfix/smtps
+# -o smtpd_tls_wrappermode=yes
+# -o smtpd_sasl_auth_enable=yes
+# -o smtpd_reject_unlisted_recipient=no
+# -o smtpd_client_restrictions=$mua_client_restrictions
+# -o smtpd_helo_restrictions=$mua_helo_restrictions
+# -o smtpd_sender_restrictions=$mua_sender_restrictions
+# -o smtpd_recipient_restrictions=
+# -o smtpd_relay_restrictions=permit_sasl_authenticated,reject
+# -o milter_macro_daemon_name=ORIGINATING
+#628 inet n - n - - qmqpd
+pickup unix n - n 60 1 pickup
+cleanup unix n - n - 0 cleanup
+qmgr unix n - n 300 1 qmgr
+#qmgr unix n - n 300 1 oqmgr
+tlsmgr unix - - n 1000? 1 tlsmgr
+rewrite unix - - n - - trivial-rewrite
+bounce unix - - n - 0 bounce
+defer unix - - n - 0 bounce
+trace unix - - n - 0 bounce
+verify unix - - n - 1 verify
+flush unix n - n 1000? 0 flush
+proxymap unix - - n - - proxymap
+proxywrite unix - - n - 1 proxymap
+smtp unix - - n - - smtp
+relay unix - - n - - smtp
+# -o smtp_helo_timeout=5 -o smtp_connect_timeout=5
+showq unix n - n - - showq
+error unix - - n - - error
+retry unix - - n - - error
+discard unix - - n - - discard
+local unix - n n - - local
+virtual unix - n n - - virtual
+lmtp unix - - n - - lmtp
+anvil unix - - n - 1 anvil
+scache unix - - n - 1 scache
+#
+# ====================================================================
+# Interfaces to non-Postfix software. Be sure to examine the manual
+# pages of the non-Postfix software to find out what options it wants.
+#
+# Many of the following services use the Postfix pipe(8) delivery
+# agent. See the pipe(8) man page for information about ${recipient}
+# and other message envelope options.
+# ====================================================================
+#
+# maildrop. See the Postfix MAILDROP_README file for details.
+# Also specify in main.cf: maildrop_destination_recipient_limit=1
+#
+#maildrop unix - n n - - pipe
+# flags=DRhu user=vmail argv=/usr/local/bin/maildrop -d ${recipient}
+#
+# ====================================================================
+#
+# Recent Cyrus versions can use the existing "lmtp" master.cf entry.
+#
+# Specify in cyrus.conf:
+# lmtp cmd="lmtpd -a" listen="localhost:lmtp" proto=tcp4
+#
+# Specify in main.cf one or more of the following:
+# mailbox_transport = lmtp:inet:localhost
+# virtual_transport = lmtp:inet:localhost
+#
+# ====================================================================
+#
+# Cyrus 2.1.5 (Amos Gouaux)
+# Also specify in main.cf: cyrus_destination_recipient_limit=1
+#
+#cyrus unix - n n - - pipe
+# user=cyrus argv=/cyrus/bin/deliver -e -r ${sender} -m ${extension} ${user}
+#
+# ====================================================================
+#
+# Old example of delivery via Cyrus.
+#
+#old-cyrus unix - n n - - pipe
+# flags=R user=cyrus argv=/cyrus/bin/deliver -e -m ${extension} ${user}
+#
+# ====================================================================
+#
+# See the Postfix UUCP_README file for configuration details.
+#
+#uucp unix - n n - - pipe
+# flags=Fqhu user=uucp argv=uux -r -n -z -a$sender - $nexthop!rmail ($recipient)
+#
+# ====================================================================
+#
+# Other external delivery methods.
+#
+#ifmail unix - n n - - pipe
+# flags=F user=ftn argv=/usr/lib/ifmail/ifmail -r $nexthop ($recipient)
+#
+#bsmtp unix - n n - - pipe
+# flags=Fq. user=bsmtp argv=/usr/local/sbin/bsmtp -f $sender $nexthop $recipient
+#
+#scalemail-backend unix - n n - 2 pipe
+# flags=R user=scalemail argv=/usr/lib/scalemail/bin/scalemail-store
+# ${nexthop} ${user} ${extension}
+#
+#mailman unix - n n - - pipe
+# flags=FR user=list argv=/usr/lib/mailman/bin/postfix-to-mailman.py
+# ${nexthop} ${user}
+
+
+{% endif %}
diff --git a/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/postfix b/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/postfix
new file mode 100644
index 000000000..cf34c5829
--- /dev/null
+++ b/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/postfix
@@ -0,0 +1,6 @@
+{% if helpers.exists('OPNsense.postfix.general.enabled') and OPNsense.postfix.general.enabled == '1' %}
+postfix_opnsense_bootup_run="/usr/local/opnsense/scripts/OPNsense/Postfix/setup.sh"
+postfix_enable="YES"
+{% else %}
+postfix_enable="NO"
+{% endif %}
diff --git a/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/recipient_access b/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/recipient_access
new file mode 100644
index 000000000..c82b63b8b
--- /dev/null
+++ b/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/recipient_access
@@ -0,0 +1,4 @@
+{% if helpers.exists('OPNsense.postfix.general.enabled') and OPNsense.postfix.general.enabled == '1' %}
+{% if helpers.exists('OPNsense.postfix.general.check_recipient_access') %}
+{% endif %}
+{% endif %}
diff --git a/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/sender_access b/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/sender_access
new file mode 100644
index 000000000..c82b63b8b
--- /dev/null
+++ b/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/sender_access
@@ -0,0 +1,4 @@
+{% if helpers.exists('OPNsense.postfix.general.enabled') and OPNsense.postfix.general.enabled == '1' %}
+{% if helpers.exists('OPNsense.postfix.general.check_recipient_access') %}
+{% endif %}
+{% endif %}
diff --git a/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/transport b/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/transport
new file mode 100644
index 000000000..8c058a078
--- /dev/null
+++ b/mail/postfix/src/opnsense/service/templates/OPNsense/Postfix/transport
@@ -0,0 +1,9 @@
+{% if helpers.exists('OPNsense.postfix.general.enabled') and OPNsense.postfix.general.enabled == '1' %}
+{% if helpers.exists('OPNsense.postfix.domain.domains.domain') %}
+{% for domain in helpers.toList('OPNsense.postfix.domain.domains.domain') %}
+{% if domain.enabled == '1' %}
+{{ domain.domainname }} smtp:{{ domain.destination }}
+{% endif %}
+{% endfor %}
+{% endif %}
+{% endif %}
diff --git a/mail/rspamd/Makefile b/mail/rspamd/Makefile
new file mode 100644
index 000000000..fc1f4ea2d
--- /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..468707216
--- /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->redis_installed = (trim($backend->configdRun('firmware plugin redis')) == '1');
+ $this->view->redis_plugin_enabled = ((string)((new RSpamd())->general->enable_redis_plugin)) == '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..a26568758
--- /dev/null
+++ b/mail/rspamd/src/opnsense/mvc/app/controllers/OPNsense/Rspamd/forms/settings.xml
@@ -0,0 +1,348 @@
+
diff --git a/mail/rspamd/src/opnsense/mvc/app/models/OPNsense/Rspamd/ACL/ACL.xml b/mail/rspamd/src/opnsense/mvc/app/models/OPNsense/Rspamd/ACL/ACL.xml
new file mode 100644
index 000000000..f75c79bef
--- /dev/null
+++ b/mail/rspamd/src/opnsense/mvc/app/models/OPNsense/Rspamd/ACL/ACL.xml
@@ -0,0 +1,9 @@
+
+
+ antispam
+
+ ui/rspamd/*
+ api/rspamd/*
+
+
+
diff --git a/mail/rspamd/src/opnsense/mvc/app/models/OPNsense/Rspamd/Menu/Menu.xml b/mail/rspamd/src/opnsense/mvc/app/models/OPNsense/Rspamd/Menu/Menu.xml
new file mode 100644
index 000000000..a7e9b27fb
--- /dev/null
+++ b/mail/rspamd/src/opnsense/mvc/app/models/OPNsense/Rspamd/Menu/Menu.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/mail/rspamd/src/opnsense/mvc/app/models/OPNsense/Rspamd/RSpamd.php b/mail/rspamd/src/opnsense/mvc/app/models/OPNsense/Rspamd/RSpamd.php
new file mode 100644
index 000000000..633e88ed1
--- /dev/null
+++ b/mail/rspamd/src/opnsense/mvc/app/models/OPNsense/Rspamd/RSpamd.php
@@ -0,0 +1,34 @@
+
+ //OPNsense/Rspamd
+ rspamd anti spam filter
+
+
+
+ 0
+ Y
+
+
+ 0
+ Y
+
+
+
+
+
+ N
+ 1
+
+
+ N
+ 1
+
+
+ N
+
+
+ 1
+ N
+ 32
+ A valid IPv4 mask must be between 1 and 32 bits.
+ 19
+
+
+ 1
+ N
+ 128
+ 64
+ A valid IPv6 mask must be between 1 and 128 bits. 64 bits are recommended as this is the recommended subnet size in IPv6.
+
+
+
+
+
+ 1
+ N
+ A valid cache size must be set.
+
+
+ 1
+ N
+ A valid cache expiration must be set.
+
+
+ 1
+ N
+ A valid time jitter must be set.
+
+
+ 0
+ Y
+
+
+ 0
+ Y
+
+
+
+ 1
+ Y
+
+
+ 0
+ Y
+
+
+ 0
+ Y
+
+
+ 0
+ Y
+
+
+ 1
+ Y
+
+
+ 1
+ Y
+
+
+ 0
+ Y
+
+
+ header
+ Y
+
+
+ Envelope
+
+
+
+ 1
+ Y
+
+
+
+
+
+ 0
+ Y
+
+
+ 1
+ N
+ 86400
+ A valid cache expiration must be set.
+
+
+
+
+
+ 0
+ Y
+
+
+ 0
+ Y
+
+
+ 0
+ Y
+
+
+
+
+
+
+ 1
+ N
+ The count value must be a positive number.
+
+
+ 1
+ N
+ The time must be a positive integer.
+
+
+ m
+ Y
+
+ Seconds
+ Minutes
+ Hours
+
+
+
+
+
+ 1
+ N
+ The count value must be a positive number.
+
+
+ 1
+ N
+ The time must be a positive integer.
+
+
+ m
+ Y
+
+ Seconds
+ Minutes
+ Hours
+
+
+
+
+
+ 1
+ N
+ The count value must be a positive number.
+
+
+ 1
+ N
+ The time must be a positive integer.
+
+
+ m
+ Y
+
+ Seconds
+ Minutes
+ Hours
+
+
+
+
+
+ 1
+ N
+ The count value must be a positive number.
+
+
+ 1
+ N
+ The time must be a positive integer.
+
+
+ m
+ Y
+
+ Seconds
+ Minutes
+ Hours
+
+
+
+
+
+ 1
+ N
+ The count value must be a positive number.
+
+
+ 1
+ N
+ The time must be a positive integer.
+
+
+ m
+ Y
+
+ Seconds
+ Minutes
+ Hours
+
+
+
+
+
+ 1
+ N
+ The count value must be a positive number.
+
+
+ 1
+ N
+ The time must be a positive integer.
+
+
+ m
+ Y
+
+ Seconds
+ Minutes
+ Hours
+
+
+
+
+ postmaster,mailer-daemon
+
+
+ 1
+ Y
+ 20
+
+
+
+
+
+ 0
+ Y
+
+
+ 0
+ Y
+
+
+ 1
+ Y
+
+
+ N
+
+
+
+
+
+ 1
+ N
+ 2
+ A valid cache size in kilobytes must be set.
+
+
+ 1
+ N
+ A valid expiration time must be set.
+
+
+
+
+
+ 1
+ Y
+
+
+ 1
+ Y
+
+
+ 1
+ 20000000
+ N
+ A valid maximum size in bytes must be set.
+
+
+ N
+
+
+
+
+
+ N
+
+
+ N
+
+
+
+
diff --git a/mail/rspamd/src/opnsense/mvc/app/views/OPNsense/Rspamd/index.volt b/mail/rspamd/src/opnsense/mvc/app/views/OPNsense/Rspamd/index.volt
new file mode 100644
index 000000000..53dd4d589
--- /dev/null
+++ b/mail/rspamd/src/opnsense/mvc/app/views/OPNsense/Rspamd/index.volt
@@ -0,0 +1,166 @@
+{#
+
+ Copyright (C) 2017 Fabian Franz
+ OPNsense® is Copyright © 2014 – 2015 by Deciso B.V.
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions are met:
+
+ 1. Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+
+ THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ POSSIBILITY OF SUCH DAMAGE.
+
+
+#}
+
+
+
+{% if !clamav_installed %}
+
+
{{ lang._('No ClamAV plugin found, please install via %sSystem > Firmware > Plugins%s. If the plugin is not installed and enabled, mails cannot be scanned for malware.')|format('
',' ')}}
+
+{% endif %}
+
+
{{ lang._('The Redis plugin is configured to use but it is not installed. Please install it via %sSystem > Firmware > Plugins%s.')|format('
',' ')}}
+
+
+
+{% for tab in settings['tabs']|default([]) %}
+ {% if tab['subtabs']|default(false) %}
+ {# Tab with dropdown #}
+
+ {# Find active subtab #}
+ {% set active_subtab="" %}
+ {% for subtab in tab['subtabs']|default({}) %}
+ {% if subtab[0]==settings['activetab']|default("") %}
+ {% set active_subtab=subtab[0] %}
+ {% endif %}
+ {% endfor %}
+
+
+
+
+
+ {{ tab[1] }}
+
+
+ {% else %}
+ {# Standard Tab #}
+
+
+ {{ tab[1] }}
+
+
+ {% endif %}
+{% endfor %}
+ {# add custom content
+ {{ lang._('Remote Access Control Lists') }}
+ #}
+
+
+
+ {% 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..97f90dbfd
--- /dev/null
+++ b/mail/rspamd/src/opnsense/scripts/rspamd/setup.sh
@@ -0,0 +1,8 @@
+#!/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..0f83e7ba9
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/+TARGETS
@@ -0,0 +1,15 @@
+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
+redis.conf:/usr/local/etc/rspamd/local.d/redis.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..7784d1b17
--- /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.exceptions') and OPNsense.Rspamd.surbl.exceptions != '' %}
+{% 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..3f191b265
--- /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..234b45174
--- /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.whitelist') and OPNsense.Rspamd.av.whitelist != '' %}
+{% 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..2af6f1e15
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/greylist.conf
@@ -0,0 +1,14 @@
+# 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.graylist') %}
+ 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') }};
+{% endif %}
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..bb3069acb
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/mx_check.conf
@@ -0,0 +1,9 @@
+# Please don't modify this file as your changes might be overwritten with
+# the next update.
+#
+
+{% if helpers.exists('OPNsense.Rspamd.mx-check.enabled') %}
+ 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 %};
+{% 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..e40fe63fa
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/phishing.conf
@@ -0,0 +1,9 @@
+# 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.phishing') %}
+ 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 %};
+{% 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..4caa7069b
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/ratelimit.conf
@@ -0,0 +1,62 @@
+# 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.rate_limit') %}
+
+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') }};
+
+{% endif %}
diff --git a/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/redis.conf b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/redis.conf
new file mode 100644
index 000000000..e3b39b780
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/redis.conf
@@ -0,0 +1,28 @@
+# Please don't modify this file as your changes might be overwritten with
+# the next update.
+#
+# You can modify '$LOCAL_CONFDIR/rspamd.conf.local.override' to redefine
+# parameters defined on the top level
+#
+# You can modify '$LOCAL_CONFDIR/rspamd.conf.local' to add
+# parameters defined on the top level
+#
+# For specific modules or configuration you can also modify
+# '$LOCAL_CONFDIR/local.d/file.conf' - to add your options or rewrite defaults
+# '$LOCAL_CONFDIR/override.d/file.conf' - to override the defaults
+#
+# See https://rspamd.com/doc/configuration/redis.html
+
+{% if helpers.exists('OPNsense.Rspamd.general.enable_redis_plugin') and OPNsense.Rspamd.general.enable_redis_plugin == '1' %}
+{% if helpers.exists('OPNsense.redis.general.enabled') and OPNsense.redis.general.enabled == '1' %}
+
+servers = "::1";
+write_servers = "::1";
+timeout = 10s;
+#db = "0";
+{% if helpers.exists('OPNsense.redis.security.password') and OPNsense.redis.security.password != '' %}
+password = "{{ OPNsense.redis.security.password.replace('"',"\\\"") }}";
+{% endif %}
+
+{% endif %}
+{% endif %}
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..0a81e7a87
--- /dev/null
+++ b/mail/rspamd/src/opnsense/service/templates/OPNsense/Rspamd/spf.conf
@@ -0,0 +1,9 @@
+# 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.spf') %}
+
+ spf_cache_size = {{ OPNsense.Rspamd.spf.spf_cache_size|default("2") }}k;
+ spf_cache_expire = {{ OPNsense.Rspamd.spf.spf_cache_expire|default("1") }}d;
+
+{% endif %}
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..2098ba554
--- /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.whitelist') and OPNsense.Rspamd.surbl.whitelist != '' %}
+{% for host in OPNsense.Rspamd.surbl.whitelist.split(',') %}
+{{ host }}
+{% endfor %}
+{% endif %}