mail: add postfix and rspamd

This commit is contained in:
Franco Fichtner 2017-11-19 08:59:43 +01:00
parent c90792fc63
commit 7626e5d047
63 changed files with 3097 additions and 1 deletions

View file

@ -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}/*

View file

@ -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

8
mail/postfix/Makefile Normal file
View file

@ -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"

5
mail/postfix/pkg-descr Normal file
View file

@ -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/

View file

@ -0,0 +1,49 @@
<?php
/*
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.
*/
function postfix_services()
{
global $config;
$services = array();
if (isset($config['OPNsense']['postfix']['general']['enabled']) && $config['OPNsense']['postfix']['general']['enabled'] == 1) {
$services[] = array(
'description' => 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;
}

View file

@ -0,0 +1,76 @@
<?php
/**
* Copyright (C) 2015 - 2017 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.
*
*/
namespace OPNsense\Postfix\Api;
use \OPNsense\Base\ApiControllerBase;
use \OPNsense\Postfix\Antispam;
use \OPNsense\Core\Config;
class AntispamController extends ApiControllerBase
{
public function getAction()
{
// define list of configurable settings
$result = array();
if ($this->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;
}
}

View file

@ -0,0 +1,205 @@
<?php
/**
* Copyright (C) 2015 - 2017 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.
*
*/
namespace OPNsense\Postfix\Api;
use \OPNsense\Postfix\Domain;
use \OPNsense\Core\Config;
use \OPNsense\Base\ApiMutableModelControllerBase;
use \OPNsense\Base\UIModelGrid;
class DomainController extends ApiMutableModelControllerBase
{
static protected $internalModelName = 'Domain';
static protected $internalModelClass = '\OPNsense\Postfix\Domain';
public function getAction()
{
// define list of configurable settings
$result = array();
if ($this->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');
}
}

View file

@ -0,0 +1,76 @@
<?php
/**
* Copyright (C) 2015 - 2017 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.
*
*/
namespace OPNsense\Postfix\Api;
use \OPNsense\Base\ApiControllerBase;
use \OPNsense\Postfix\General;
use \OPNsense\Core\Config;
class GeneralController extends ApiControllerBase
{
public function getAction()
{
// define list of configurable settings
$result = array();
if ($this->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;
}
}

View file

@ -0,0 +1,159 @@
<?php
/*
* Copyright (C) 2015 - 2017 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.
*/
namespace OPNsense\Postfix\Api;
use \OPNsense\Base\ApiControllerBase;
use \OPNsense\Core\Backend;
use \OPNsense\Postfix\General;
/**
* Class ServiceController
* @package OPNsense\Postfix
*/
class ServiceController extends ApiControllerBase
{
/**
* check rspamd
* @return array
*/
public function checkrspamdAction()
{
$backend = new Backend();
$mdlGeneral = new General();
$response = $backend->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");
}
}
}

View file

@ -0,0 +1,34 @@
<?php
/*
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.
*/
namespace OPNsense\Postfix;
class DomainController extends \OPNsense\Base\IndexController
{
public function indexAction()
{
$this->view->title = gettext("Postfix Domains");
$this->view->formDialogEditPostfixDomain = $this->getForm("dialogEditPostfixDomain");
$this->view->pick('OPNsense/Postfix/domain');
}
}

View file

@ -0,0 +1,39 @@
<?php
/*
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.
*/
namespace OPNsense\Postfix;
class GeneralController extends \OPNsense\Base\IndexController
{
public function indexAction()
{
$this->view->title = gettext("Postfix Settings");
$this->view->generalForm = $this->getForm("general");
$this->view->antispamForm = $this->getForm("antispam");
$this->view->pick('OPNsense/Postfix/general');
}
}

View file

@ -0,0 +1,8 @@
<form>
<field>
<id>antispam.enable_rspamd</id>
<label>Enable Rspamd Integration</label>
<type>checkbox</type>
<help>This will allow Postfix to connect to rspamd via milter protocol.</help>
</field>
</form>

View file

@ -0,0 +1,20 @@
<form>
<field>
<id>domain.enabled</id>
<label>Enabled</label>
<type>checkbox</type>
<help>This will enable or disable domain routing for this entry.</help>
</field>
<field>
<id>domain.domainname</id>
<label>Domainname</label>
<type>text</type>
<help>Set the unique domain name to relay for.</help>
</field>
<field>
<id>domain.destination</id>
<label>Destination</label>
<type>text</type>
<help>Set the IP or FQDN to where to send the mails to. Empty means MX will be used.</help>
</field>
</form>

View file

@ -0,0 +1,112 @@
<form>
<field>
<id>general.enabled</id>
<label>Enable</label>
<type>checkbox</type>
<help>This will activate the Postfix daemon.</help>
</field>
<field>
<id>general.myhostname</id>
<label>System Hostname</label>
<type>text</type>
<help>The 'System Hostname' parameter specifies the internet hostname of this mail system. The default is to use the fully-qualified domain name from gethostname(). It is used as a default value for many other configuration parameters.</help>
</field>
<field>
<id>general.mydomain</id>
<label>System Domain</label>
<type>text</type>
<help>The 'System Domain' parameter specifies the local internet domain name. The default is to use 'System Hostname' minus the first component. It is used as a default value for many other configuration parameters.</help>
</field>
<field>
<id>general.myorigin</id>
<label>System Origin</label>
<type>text</type>
<help>The 'System Origin' parameter specifies the domain that locally-posted mail appears to come from. The default is to append 'System Hostname', which is fine for small sites.</help>
</field>
<field>
<id>general.inet_interfaces</id>
<label>Listen IPs</label>
<type>text</type>
<help>The 'Listen IPs' parameter specifies the IP address to listen to. Default is to listen on all interfaces.</help>
</field>
<field>
<id>general.mynetworks</id>
<label>Trusted Networks</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<help>The 'Trusted Networks' parameter specifies the list of trusted SMTP clients. In particular, trusted SMTP clients are allowed to relay mail through Postfix. Please use CIDR notation like 192.168.0.0/24 separated by spaces. IPv6 addresses have to be in square brackets like [::1]/128.</help>
</field>
<field>
<id>general.banner</id>
<label>SMTP Banner</label>
<type>text</type>
<help>The smtpd_banner parameter specifies the text that follows the 220 code in the SMTP server's greeting banner. Default is "'System Hostname' ESMTP Postfix".</help>
</field>
<field>
<id>general.message_size_limit</id>
<label>Message Size Limit</label>
<type>text</type>
<help>Set the max size for messages to accept, default is 501200000 Byte which is 50MB. Values must be entered in Bytes.</help>
</field>
<field>
<id>general.disable_ssl</id>
<label>Allow TLS Only</label>
<type>checkbox</type>
<help>Disable SSLv2 and SSLv3, only TLS allowed.</help>
</field>
<field>
<id>general.disable_weak_ciphers</id>
<label>Disable Weak Ciphers And Algorithms</label>
<type>checkbox</type>
<help>This will disable known weak ciphers like DES, RC4 or MD5.</help>
</field>
<field>
<id>general.reject_unauth_pipelining</id>
<label>Reject Unauthenticated Pipelining</label>
<type>checkbox</type>
</field>
<field>
<id>general.reject_unknown_sender_domain</id>
<label>Reject Unknown Sender Domain</label>
<type>checkbox</type>
<help>This will reject mails from domains which do not exist.</help>
</field>
<field>
<id>general.reject_unknown_recipient_domain</id>
<label>Reject Unknown Recipient Domain</label>
<type>checkbox</type>
</field>
<field>
<id>general.reject_non_fqdn_sender</id>
<label>Reject Non FQDN Sender</label>
<type>checkbox</type>
<help>For example senders without a domain or only a hostname.</help>
</field>
<field>
<id>general.reject_non_fqdn_recipient</id>
<label>Reject Non FQDN Recipient</label>
<type>checkbox</type>
<help>For example recipients without a domain or only a hostname.</help>
</field>
<field>
<id>general.permit_sasl_authenticated</id>
<label>Permit SASL Authenticated</label>
<type>checkbox</type>
</field>
<field>
<id>general.permit_tls_clientcerts</id>
<label>Permit TLS Client Certificate Authenticated Users</label>
<type>checkbox</type>
</field>
<field>
<id>general.permit_mynetworks</id>
<label>Permit Trusted Networks</label>
<type>checkbox</type>
</field>
<field>
<id>general.reject_unauth_destination</id>
<label>Reject Unauthenticated Destination</label>
<type>checkbox</type>
</field>
</form>

View file

@ -0,0 +1,9 @@
<acl>
<page-services-postfix>
<name>Services: Postfix</name>
<patterns>
<pattern>ui/postfix/*</pattern>
<pattern>api/postfix/*</pattern>
</patterns>
</page-services-postfix>
</acl>

View file

@ -0,0 +1,34 @@
<?php
namespace OPNsense\Postfix;
use OPNsense\Base\BaseModel;
/*
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.
*/
class Antispam extends BaseModel
{
}

View file

@ -0,0 +1,11 @@
<model>
<mount>//OPNsense/postfix/antispam</mount>
<description>Postfix Antispam configuration</description>
<version>1.0.0</version>
<items>
<enable_rspamd type="BooleanField">
<default>0</default>
<Required>Y</Required>
</enable_rspamd>
</items>
</model>

View file

@ -0,0 +1,30 @@
<?php
namespace OPNsense\Postfix;
use OPNsense\Base\BaseModel;
/*
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.
*/
class Domain extends BaseModel
{
}

View file

@ -0,0 +1,23 @@
<model>
<mount>//OPNsense/postfix/domain</mount>
<description>Postfix domain configuration</description>
<version>1.0.0</version>
<items>
<domains>
<domain type="ArrayField">
<enabled type="BooleanField">
<default>1</default>
<Required>Y</Required>
</enabled>
<domainname type="TextField">
<default></default>
<Required>Y</Required>
</domainname>
<destination type="NetworkField">
<default></default>
<Required>Y</Required>
</destination>
</domain>
</domains>
</items>
</model>

View file

@ -0,0 +1,34 @@
<?php
namespace OPNsense\Postfix;
use OPNsense\Base\BaseModel;
/*
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.
*/
class General extends BaseModel
{
}

View file

@ -0,0 +1,87 @@
<model>
<mount>//OPNsense/postfix/general</mount>
<description>Postfix configuration</description>
<version>1.0.0</version>
<items>
<enabled type="BooleanField">
<default>0</default>
<Required>Y</Required>
</enabled>
<myhostname type="TextField">
<default></default>
<Required>N</Required>
</myhostname>
<mydomain type="TextField">
<default></default>
<Required>N</Required>
</mydomain>
<myorigin type="TextField">
<default></default>
<Required>N</Required>
</myorigin>
<inet_interfaces type="TextField">
<default>all</default>
<Required>Y</Required>
</inet_interfaces>
<mynetworks type="CSVListField">
<default>127.0.0.0/8,[::ffff:127.0.0.0]/104,[::1]/128</default>
<Required>Y</Required>
</mynetworks>
<banner type="TextField">
<default></default>
<Required>N</Required>
</banner>
<message_size_limit type="IntegerField">
<default>51200000</default>
<Required>Y</Required>
</message_size_limit>
<disable_ssl type="BooleanField">
<default>1</default>
<Required>Y</Required>
</disable_ssl>
<disable_weak_ciphers type="BooleanField">
<default>1</default>
<Required>Y</Required>
</disable_weak_ciphers>
<check_recipient_access type="ArrayField">
</check_recipient_access>
<reject_unauth_pipelining type="BooleanField">
<default>1</default>
<Required>Y</Required>
</reject_unauth_pipelining>
<check_sender_access type="ArrayField">
</check_sender_access>
<reject_unknown_sender_domain type="BooleanField">
<default>1</default>
<Required>Y</Required>
</reject_unknown_sender_domain>
<reject_unknown_recipient_domain type="BooleanField">
<default>1</default>
<Required>Y</Required>
</reject_unknown_recipient_domain>
<reject_non_fqdn_sender type="BooleanField">
<default>1</default>
<Required>Y</Required>
</reject_non_fqdn_sender>
<reject_non_fqdn_recipient type="BooleanField">
<default>1</default>
<Required>Y</Required>
</reject_non_fqdn_recipient>
<permit_sasl_authenticated type="BooleanField">
<default>1</default>
<Required>Y</Required>
</permit_sasl_authenticated>
<permit_tls_clientcerts type="BooleanField">
<default>1</default>
<Required>Y</Required>
</permit_tls_clientcerts>
<permit_mynetworks type="BooleanField">
<default>1</default>
<Required>Y</Required>
</permit_mynetworks>
<reject_unauth_destination type="BooleanField">
<default>1</default>
<Required>Y</Required>
</reject_unauth_destination>
</items>
</model>

View file

@ -0,0 +1,8 @@
<menu>
<Services>
<Postfix cssClass="fa fa-envelope fa-fw">
<General url="/ui/postfix/general/index" order="10"/>
<Domains url="/ui/postfix/domain/index" order="20"/>
</Postfix>
</Services>
</menu>

View file

@ -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.
#}
<script type="text/javascript">
$( document ).ready(function() {
/*************************************************************************************************************
* link grid actions
*************************************************************************************************************/
$("#grid-domains").UIBootgrid(
{ 'search':'/api/postfix/domain/searchDomain',
'get':'/api/postfix/domain/getDomain/',
'set':'/api/postfix/domain/setDomain/',
'add':'/api/postfix/domain/addDomain/',
'del':'/api/postfix/domain/delDomain/',
'toggle':'/api/postfix/domain/toggleDomain/'
}
);
/*************************************************************************************************************
* Commands
*************************************************************************************************************/
/**
* Reconfigure
*/
$("#reconfigureAct").click(function(){
$("#reconfigureAct_progress").addClass("fa fa-spinner fa-pulse");
ajaxCall(url="/api/postfix/service/reconfigure", sendData={}, callback=function(data,status) {
// when done, disable progress animation.
$("#reconfigureAct_progress").removeClass("fa fa-spinner fa-pulse");
if (status != "success" || data['status'] != 'ok') {
BootstrapDialog.show({
type: BootstrapDialog.TYPE_WARNING,
title: "{{ lang._('Error reconfiguring Postfix') }}",
message: data['status'],
draggable: true
});
} else {
ajaxCall(url="/api/postfix/service/reconfigure", sendData={});
}
});
});
});
</script>
<div class="tab-content content-box tab-content">
<div id="domains" class="tab-pane fade in active">
<!-- tab page "domains" -->
<table id="grid-domains" class="table table-condensed table-hover table-striped table-responsive" data-editDialog="dialogEditPostfixDomain">
<thead>
<tr>
<th data-column-id="enabled" data-type="string" data-formatter="rowtoggle">{{ lang._('Enabled') }}</th>
<th data-column-id="domainname" data-type="string" data-visible="true">{{ lang._('Domain') }}</th>
<th data-column-id="destination" data-type="string" data-visible="true">{{ lang._('Destination') }}</th>
<th data-column-id="uuid" data-type="string" data-identifier="true" data-visible="false">{{ lang._('ID') }}</th>
<th data-column-id="commands" data-formatter="commands" data-sortable="false">{{ lang._('Commands') }}</th> </tr>
</thead>
<tbody>
</tbody>
<tfoot>
<tr>
<td></td>
<td>
<button data-action="add" type="button" class="btn btn-xs btn-default"><span class="fa fa-plus"></span></button>
<button data-action="deleteSelected" type="button" class="btn btn-xs btn-default"><span class="fa fa-trash-o"></span></button>
</td>
</tr>
</tfoot>
</table>
</div>
<div class="col-md-12">
<hr/>
<button class="btn btn-primary" id="reconfigureAct" type="button"><b>{{ lang._('Apply') }}</b> <i id="reconfigureAct_progress" class=""></i></button>
<br/><br/>
</div>
</div>
{{ partial("layout_partials/base_dialog",['fields':formDialogEditPostfixDomain,'id':'dialogEditPostfixDomain','label':lang._('Edit Domain')])}}

View file

@ -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.
#}
<ul class="nav nav-tabs" data-tabs="tabs" id="maintabs">
<li class="active"><a data-toggle="tab" href="#general">{{ lang._('General') }}</a></li>
<li><a data-toggle="tab" href="#antispam">{{ lang._('Antispam') }}</a></li>
</ul>
<div class="tab-content content-box tab-content">
<div id="general" class="tab-pane fade in active">
<div class="content-box" style="padding-bottom: 1.5em;">
{{ partial("layout_partials/base_form",['fields':generalForm,'id':'frm_general_settings'])}}
<hr />
<div class="col-md-12">
<button class="btn btn-primary" id="saveAct" type="button"><b>{{ lang._('Save') }}</b><i id="saveAct_progress" class=""></i></button>
</div>
</div>
</div>
<div id="antispam" class="tab-pane fade in">
<div class="content-box" style="padding-bottom: 1.5em;">
<div class="alert alert-warning" role="alert" id="missing_rspamd" style="display:none;min-height:65px;">
<div style="margin-top: 8px;">{{ lang._('No Rspamd plugin found, please install and come back.')}}</div>
</div>
{{ partial("layout_partials/base_form",['fields':antispamForm,'id':'frm_antispam_settings'])}}
<hr />
<div class="col-md-12">
<button class="btn btn-primary" id="saveAct2" type="button"><b>{{ lang._('Save') }}</b><i id="saveAct2_progress" class=""></i></button>
</div>
</div>
</div>
</div>
<script type="text/javascript">
$( document ).ready(function () {
var data_get_map = {'frm_general_settings':"/api/postfix/general/get", 'frm_antivirus_settings':"/api/postfix/antispam/get"};
mapDataToFormUI(data_get_map).done(function (data) {
formatTokenizersUI();
$('.selectpicker').selectpicker('refresh');
});
ajaxCall(url="/api/postfix/service/status", sendData={}, callback=function (data, status) {
updateServiceStatusUI(data['status']);
});
// check if Rspamd plugin is installed
ajaxCall(url="/api/postfix/service/checkrspamd", sendData={}, callback=function(data,status) {
if (data == "0") {
$('#missing_rspamd').show();
}
});
// link save button to API set action
$("#saveAct").click(function () {
saveFormToEndpoint(url="/api/postfix/general/set", formid='frm_general_settings',callback_ok=function () {
$("#saveAct_progress").addClass("fa fa-spinner fa-pulse");
ajaxCall(url="/api/postfix/service/reconfigure", sendData={}, callback=function (data,status) {
ajaxCall(url="/api/postfix/service/status", sendData={}, callback=function (data,status) {
updateServiceStatusUI(data['status']);
});
$("#saveAct_progress").removeClass("fa fa-spinner fa-pulse");
});
});
});
$("#saveAct2").click(function(){
saveFormToEndpoint(url="/api/postfix/antispam/set", formid='frm_antispam_settings',callback_ok=function(){
$("#saveAct2_progress").addClass("fa fa-spinner fa-pulse");
ajaxCall(url="/api/postfix/service/reconfigure", sendData={}, callback=function(data,status) {
ajaxCall(url="/api/postfix/service/status", sendData={}, callback=function(data,status) {
updateServiceStatusUI(data['status']);
});
$("#saveAct2_progress").removeClass("fa fa-spinner fa-pulse");
});
});
});
});
</script>

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

8
mail/rspamd/Makefile Normal file
View file

@ -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"

3
mail/rspamd/pkg-descr Normal file
View file

@ -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.

View file

@ -0,0 +1,62 @@
<?php
/*
Copyright (C) 2017 Fabian Franz
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.
*/
function rspamd_enabled()
{
$model = new \OPNsense\Rspamd\RSpamd();
if ((string)$model->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;
}

View file

@ -0,0 +1,139 @@
<?php
/**
* Copyright (C) 2017 Fabian Franz
*
* 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.
*
*/
namespace OPNsense\Rspamd\Api;
use \OPNsense\Base\ApiControllerBase;
use \OPNsense\Core\Backend;
use \OPNsense\Rspamd\RSpamd;
class ServiceController extends ApiControllerBase
{
/**
* restart rspamd service
* @return array
*/
public function restartAction()
{
if ($this->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());
}
}
}

View file

@ -0,0 +1,38 @@
<?php
/**
* Copyright (C) 2017 Fabian Franz
*
* 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.
*
*/
namespace OPNsense\Rspamd\Api;
use \OPNsense\Base\ApiMutableModelControllerBase;
class SettingsController extends ApiMutableModelControllerBase
{
static protected $internalModelClass = '\OPNsense\Rspamd\RSpamd';
static protected $internalModelName = 'rspamd';
}

View file

@ -0,0 +1,53 @@
<?php
/*
Copyright (C) 2017 Fabian Franz
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.
*/
namespace OPNsense\Rspamd;
use \OPNsense\Core\Backend;
use \OPNsense\Rspamd\RSpamd;
/**
* Class IndexController
* @package OPNsense/Rspamd
*/
class IndexController extends \OPNsense\Base\IndexController
{
public function indexAction()
{
$backend = new Backend();
$this->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');
}
}

View file

@ -0,0 +1,348 @@
<form>
<tab id="rspamd-general" description="General Settings">
<subtab id="rspamd-general-settings" description="General Rspamd Settings">
<field>
<id>rspamd.general.enabled</id>
<label>Enable rspamd</label>
<type>checkbox</type>
<help>Enable or disable the rspamd service.</help>
</field>
<field>
<id>rspamd.general.enable_redis_plugin</id>
<label>Enable Redis Plugin</label>
<type>checkbox</type>
<help>If you check this box, the local Redis server will be available to the modules (some do not work without it).</help>
</field>
</subtab>
</tab>
<tab id="rspamd-anti-spam" description="Spam Protection">
<subtab id="rspamd-anti-spam-graylist" description="Graylisting">
<field>
<id>rspamd.graylist.expire</id>
<label>Expiration</label>
<type>text</type>
<help>Time after which the graylist state expires in days.</help>
</field>
<field>
<id>rspamd.graylist.timeout</id>
<label>Timeout</label>
<type>text</type>
</field>
<field>
<id>rspamd.graylist.max_data_len</id>
<label>Maximum Data Length</label>
<type>text</type>
<help>The limit of the length of data to hash.</help>
</field>
<field>
<id>rspamd.graylist.ipv4mask</id>
<label>IPv4 Mask</label>
<type>text</type>
<help>Mask bits are used to limit the network range from which the message may be resent. This is used to avoid a rejection if another server sends the second mail.</help>
</field>
<field>
<id>rspamd.graylist.ipv6mask</id>
<label>IPv6 Mask</label>
<type>text</type>
<help>Mask bits are used to limit the network range from which the message may be resent. This is used to avoid a rejection if another server sends the second mail.</help>
</field>
</subtab>
<subtab id="rspamd-anti-spam-dkim" description="DKIM">
<!-- dkim -->
<field>
<id>rspamd.dkim.cache_size</id>
<label>Cache Size</label>
<type>text</type>
</field>
<field>
<id>rspamd.dkim.cache_expire</id>
<label>Cache Expire</label>
<type>text</type>
</field>
<field>
<id>rspamd.dkim.time_jitter</id>
<label>Time Jitter</label>
<type>text</type>
</field>
<field>
<id>rspamd.dkim.trusted_only</id>
<label>Trusted Only</label>
<type>checkbox</type>
</field>
<field>
<id>rspamd.dkim.skip_multi</id>
<label>Skip Multi</label>
<type>checkbox</type>
</field>
<!-- dkim signing -->
<field>
<id>rspamd.dkim.allow_envfrom_empty</id>
<label>Allow Environment From Empty</label>
<type>checkbox</type>
</field>
<field>
<id>rspamd.dkim.allow_hdrfrom_mismatch</id>
<label>Allow Header From Mismatch</label>
<type>checkbox</type>
</field>
<field>
<id>rspamd.dkim.allow_hdrfrom_multiple</id>
<label>Allow Header From Multiple</label>
<type>checkbox</type>
</field>
<field>
<id>rspamd.dkim.allow_username_mismatch</id>
<label>Allow Username Mismatch</label>
<type>checkbox</type>
</field>
<field>
<id>rspamd.dkim.auth_only</id>
<label>Auth Only</label>
<type>checkbox</type>
</field>
<field>
<id>rspamd.dkim.sign_local</id>
<label>Sign Local</label>
<type>checkbox</type>
</field>
<field>
<id>rspamd.dkim.try_fallback</id>
<label>Try Fallback</label>
<type>checkbox</type>
</field>
<field>
<id>rspamd.dkim.use_domain</id>
<label>Use Domain</label>
<type>dropdown</type>
</field>
<field>
<id>rspamd.dkim.use_esld</id>
<label>Use eSLD</label>
<type>checkbox</type>
</field>
</subtab>
<subtab id="rspamd-anti-spam-mx-check" description="MX Check">
<field>
<id>rspamd.mx-check.enabled</id>
<label>Enabled</label>
<type>checkbox</type>
</field>
<field>
<id>rspamd.mx_check.expire</id>
<label>Expiration</label>
<type>text</type>
</field>
</subtab>
<subtab id="rspamd-anti-spam-phishing" description="Phishing">
<field>
<id>rspamd.phishing.openphish_enabled</id>
<label>Enable Openphish</label>
<type>checkbox</type>
</field>
<field>
<id>rspamd.phishing.openphish_premium_enabled</id>
<label>Enable Openphish Premium</label>
<type>checkbox</type>
</field>
<field>
<id>rspamd.phishing.phishtank_enabled</id>
<label>Enable Phishtank</label>
<type>checkbox</type>
</field>
</subtab>
<subtab id="rspamd-anti-spam-rate-limit" description="Rate Limit">
<field>
<id>rspamd.rate_limit.per_recipient.count</id>
<label>Per Recipient Limit: Mail Count</label>
<type>text</type>
</field>
<field>
<id>rspamd.rate_limit.per_recipient.time</id>
<label>Per Recipient Limit: Time</label>
<type>text</type>
</field>
<field>
<id>rspamd.rate_limit.per_recipient.time_unit</id>
<label>Per Recipient Limit: Time Unit</label>
<type>dropdown</type>
</field>
<field>
<id>rspamd.rate_limit.per_ip.count</id>
<label>Per IP Limit: Mail Count</label>
<type>text</type>
</field>
<field>
<id>rspamd.rate_limit.per_ip.time</id>
<label>Per IP Limit: Time</label>
<type>text</type>
</field>
<field>
<id>rspamd.rate_limit.per_ip.time_unit</id>
<label>Per IP Limit: Time Unit</label>
<type>dropdown</type>
</field>
<field>
<id>rspamd.rate_limit.per_ip_from.count</id>
<label>Per IP and From Limit: Mail Count</label>
<type>text</type>
</field>
<field>
<id>rspamd.rate_limit.per_ip_from.time</id>
<label>Per IP and From Limit: Time</label>
<type>text</type>
</field>
<field>
<id>rspamd.rate_limit.per_ip_from.time_unit</id>
<label>Per IP and From Limit: Time Unit</label>
<type>dropdown</type>
</field>
<field>
<id>rspamd.rate_limit.bounce.count</id>
<label>Bounce Limit: Mail Count</label>
<type>text</type>
</field>
<field>
<id>rspamd.rate_limit.bounce.time</id>
<label>Bounce Limit: Time</label>
<type>text</type>
</field>
<field>
<id>rspamd.rate_limit.bounce.time_unit</id>
<label>Bounce Limit: Time Unit</label>
<type>dropdown</type>
</field>
<field>
<id>rspamd.rate_limit.bounce_ip.count</id>
<label>Bounce Limit per IP: Mail Count</label>
<type>text</type>
</field>
<field>
<id>rspamd.rate_limit.bounce_ip.time</id>
<label>Bounce Limit per IP: Time</label>
<type>text</type>
</field>
<field>
<id>rspamd.rate_limit.bounce_ip.time_unit</id>
<label>Bounce Limit per IP: Time Unit</label>
<type>dropdown</type>
</field>
<field>
<id>rspamd.rate_limit.user.count</id>
<label>User Limit: Mail Count</label>
<type>text</type>
</field>
<field>
<id>rspamd.rate_limit.user.time</id>
<label>User Limit: Time</label>
<type>text</type>
</field>
<field>
<id>rspamd.rate_limit.user.time_unit</id>
<label>User Limit: Time Unit</label>
<type>dropdown</type>
</field>
<field>
<id>rspamd.rate_limit.whitelisted_rcpts</id>
<label>Whitelist Recipients</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
</field>
<field>
<id>rspamd.rate_limit.max_rcpt</id>
<label>Maximum Recipients</label>
<type>text</type>
</field>
</subtab>
<subtab id="rspamd-anti-spam-spamtrap" description="Spam Trap">
<field>
<id>rspamd.spamtrap.enabled</id>
<label>Enabled</label>
<type>checkbox</type>
<help>Enable this if you want to enable the spam trap.</help>
</field>
<field>
<id>rspamd.spamtrap.fuzzy_learning</id>
<label>Fuzzy Leraning</label>
<type>checkbox</type>
<help>Enable this if you want to enable fuzzy learning.</help>
</field>
<field>
<id>rspamd.spamtrap.spam_learning</id>
<label>Bayes Leraning</label>
<type>checkbox</type>
<help>Enable this if you want to enable bayes learning.</help>
</field>
<field>
<id>rspamd.spamtrap.spam_recipients</id>
<label>Recipients</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<help>Enter regular expressions in the form trap@example\.com into this field. The value is automatically enclosed in slashes and the case insensitive option is added.</help>
</field>
</subtab>
<subtab id="rspamd-anti-spam-spf" description="Sender Policy Framework (SPF)">
<field>
<id>rspamd.spf.spf_cache_size</id>
<label>Cache Size</label>
<type>text</type>
<help>Enter the size of the SPF cache.</help>
</field>
<field>
<id>rspamd.spf.spf_cache_expire</id>
<label>Cache Expiration</label>
<type>text</type>
<help>Enter how long SPF entries are valid.</help>
</field>
</subtab>
</tab>
<tab id="rspamd-anti-malware" description="Anti Malware">
<subtab id="rspamd-anti-malware-general" description="General Anti Malware Settings">
<field>
<id>rspamd.av.force-reject</id>
<label>Force Reject</label>
<type>checkbox</type>
<help>If set, the mail will be rejected.</help>
</field>
<field>
<id>rspamd.av.attachments-only</id>
<label>Only Scan Attachments</label>
<type>checkbox</type>
<help>If checked, only attached files are scanned and images are omitted.</help>
</field>
<field>
<id>rspamd.av.max-size</id>
<label>Maximum Size</label>
<type>text</type>
<help>If set, a message large than this size will not be scanned.</help>
</field>
<field>
<id>rspamd.av.whitelist</id>
<label>Whitelist</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
<help>Mails from IPs entered here will not be scanned.</help>
</field>
</subtab>
<subtab id="rspamd-anti-malware-surbl" description="SURBL">
<field>
<id>rspamd.surbl.whitelist</id>
<label>Whitelist</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
</field>
<field>
<id>rspamd.surbl.exceptions</id>
<label>Exceptions</label>
<type>select_multiple</type>
<style>tokenize</style>
<allownew>true</allownew>
</field>
</subtab>
</tab>
<activetab>rspamd-general-settings</activetab>
</form>

View file

@ -0,0 +1,9 @@
<acl>
<page-Antispam>
<name>antispam</name>
<patterns>
<pattern>ui/rspamd/*</pattern>
<pattern>api/rspamd/*</pattern>
</patterns>
</page-Antispam>
</acl>

View file

@ -0,0 +1,5 @@
<menu>
<Services>
<antispam VisibleName="Rspamd" cssClass="fa fa-envelope fa-fw" url="/ui/rspamd/" />
</Services>
</menu>

View file

@ -0,0 +1,34 @@
<?php
/*
Copyright (C) 2017 Fabian Franz
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.
*/
namespace OPNsense\Rspamd;
use OPNsense\Base\BaseModel;
class RSpamd extends BaseModel
{
}

View file

@ -0,0 +1,337 @@
<model>
<mount>//OPNsense/Rspamd</mount>
<description>rspamd anti spam filter</description>
<items>
<general>
<enabled type="BooleanField">
<default>0</default>
<Required>Y</Required>
</enabled>
<enable_redis_plugin type="BooleanField">
<default>0</default>
<Required>Y</Required>
</enable_redis_plugin>
</general>
<graylist>
<expire type="IntegerField">
<Required>N</Required>
<MinimumValue>1</MinimumValue>
</expire>
<timeout type="IntegerField">
<Required>N</Required>
<MinimumValue>1</MinimumValue>
</timeout>
<max_data_len type="IntegerField">
<Required>N</Required>
</max_data_len>
<ipv4mask type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>N</Required>
<MaximumValue>32</MaximumValue>
<ValidationMessage>A valid IPv4 mask must be between 1 and 32 bits.</ValidationMessage>
<default>19</default>
</ipv4mask>
<ipv6mask type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>N</Required>
<MaximumValue>128</MaximumValue>
<default>64</default>
<ValidationMessage>A valid IPv6 mask must be between 1 and 128 bits. 64 bits are recommended as this is the recommended subnet size in IPv6.</ValidationMessage>
</ipv6mask>
</graylist>
<dkim>
<cache_size type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>N</Required>
<ValidationMessage>A valid cache size must be set.</ValidationMessage>
</cache_size>
<cache_expire type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>N</Required>
<ValidationMessage>A valid cache expiration must be set.</ValidationMessage>
</cache_expire>
<time_jitter type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>N</Required>
<ValidationMessage>A valid time jitter must be set.</ValidationMessage>
</time_jitter>
<trusted_only type="BooleanField">
<default>0</default>
<Required>Y</Required>
</trusted_only>
<skip_multi type="BooleanField">
<default>0</default>
<Required>Y</Required>
</skip_multi>
<!-- dkim signing -->
<allow_envfrom_empty type="BooleanField">
<default>1</default>
<Required>Y</Required>
</allow_envfrom_empty>
<allow_hdrfrom_mismatch type="BooleanField">
<default>0</default>
<Required>Y</Required>
</allow_hdrfrom_mismatch>
<allow_hdrfrom_multiple type="BooleanField">
<default>0</default>
<Required>Y</Required>
</allow_hdrfrom_multiple>
<allow_username_mismatch type="BooleanField">
<default>0</default>
<Required>Y</Required>
</allow_username_mismatch>
<auth_only type="BooleanField">
<default>1</default>
<Required>Y</Required>
</auth_only>
<sign_local type="BooleanField">
<default>1</default>
<Required>Y</Required>
</sign_local>
<try_fallback type="BooleanField">
<default>0</default>
<Required>Y</Required>
</try_fallback>
<use_domain type="OptionField">
<default>header</default>
<Required>Y</Required>
<OptionValues>
<header>Header</header>
<envelope>Envelope</envelope>
</OptionValues>
</use_domain>
<use_esld type="BooleanField">
<default>1</default>
<Required>Y</Required>
</use_esld>
</dkim>
<mx-check>
<enabled type="BooleanField">
<default>0</default>
<Required>Y</Required>
</enabled>
<expire type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>N</Required>
<default>86400</default>
<ValidationMessage>A valid cache expiration must be set.</ValidationMessage>
</expire>
</mx-check>
<phishing>
<openphish_enabled type="BooleanField">
<default>0</default>
<Required>Y</Required>
</openphish_enabled>
<openphish_premium_enabled type="BooleanField">
<default>0</default>
<Required>Y</Required>
</openphish_premium_enabled>
<phishtank_enabled type="BooleanField">
<default>0</default>
<Required>Y</Required>
</phishtank_enabled>
</phishing>
<rate_limit>
<per_recipient>
<count type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>N</Required>
<ValidationMessage>The count value must be a positive number.</ValidationMessage>
</count>
<time type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>N</Required>
<ValidationMessage>The time must be a positive integer.</ValidationMessage>
</time>
<time_unit type="OptionField">
<default>m</default>
<Required>Y</Required>
<OptionValues>
<s>Seconds</s>
<m>Minutes</m>
<h>Hours</h>
</OptionValues>
</time_unit>
</per_recipient>
<per_ip>
<count type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>N</Required>
<ValidationMessage>The count value must be a positive number.</ValidationMessage>
</count>
<time type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>N</Required>
<ValidationMessage>The time must be a positive integer.</ValidationMessage>
</time>
<time_unit type="OptionField">
<default>m</default>
<Required>Y</Required>
<OptionValues>
<s>Seconds</s>
<m>Minutes</m>
<h>Hours</h>
</OptionValues>
</time_unit>
</per_ip>
<per_ip_from>
<count type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>N</Required>
<ValidationMessage>The count value must be a positive number.</ValidationMessage>
</count>
<time type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>N</Required>
<ValidationMessage>The time must be a positive integer.</ValidationMessage>
</time>
<time_unit type="OptionField">
<default>m</default>
<Required>Y</Required>
<OptionValues>
<s>Seconds</s>
<m>Minutes</m>
<h>Hours</h>
</OptionValues>
</time_unit>
</per_ip_from>
<bounce>
<count type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>N</Required>
<ValidationMessage>The count value must be a positive number.</ValidationMessage>
</count>
<time type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>N</Required>
<ValidationMessage>The time must be a positive integer.</ValidationMessage>
</time>
<time_unit type="OptionField">
<default>m</default>
<Required>Y</Required>
<OptionValues>
<s>Seconds</s>
<m>Minutes</m>
<h>Hours</h>
</OptionValues>
</time_unit>
</bounce>
<bounce_ip>
<count type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>N</Required>
<ValidationMessage>The count value must be a positive number.</ValidationMessage>
</count>
<time type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>N</Required>
<ValidationMessage>The time must be a positive integer.</ValidationMessage>
</time>
<time_unit type="OptionField">
<default>m</default>
<Required>Y</Required>
<OptionValues>
<s>Seconds</s>
<m>Minutes</m>
<h>Hours</h>
</OptionValues>
</time_unit>
</bounce_ip>
<user>
<count type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>N</Required>
<ValidationMessage>The count value must be a positive number.</ValidationMessage>
</count>
<time type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>N</Required>
<ValidationMessage>The time must be a positive integer.</ValidationMessage>
</time>
<time_unit type="OptionField">
<default>m</default>
<Required>Y</Required>
<OptionValues>
<s>Seconds</s>
<m>Minutes</m>
<h>Hours</h>
</OptionValues>
</time_unit>
</user>
<whitelisted_rcpts type="CSVListField">
<default>postmaster,mailer-daemon</default>
</whitelisted_rcpts>
<max_rcpt type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>Y</Required>
<default>20</default>
</max_rcpt>
</rate_limit>
<spamtrap>
<enabled type="BooleanField">
<default>0</default>
<Required>Y</Required>
</enabled>
<fuzzy_learning type="BooleanField">
<default>0</default>
<Required>Y</Required>
</fuzzy_learning>
<spam_learning type="BooleanField">
<default>1</default>
<Required>Y</Required>
</spam_learning>
<spam_recipients type="CSVListField">
<Required>N</Required>
</spam_recipients>
</spamtrap>
<spf>
<spf_cache_size type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>N</Required>
<default>2</default>
<ValidationMessage>A valid cache size in kilobytes must be set.</ValidationMessage>
</spf_cache_size>
<spf_cache_expire type="IntegerField">
<MinimumValue>1</MinimumValue>
<Required>N</Required>
<ValidationMessage>A valid expiration time must be set.</ValidationMessage>
</spf_cache_expire>
</spf>
<av>
<force-reject type="BooleanField">
<default>1</default>
<Required>Y</Required>
</force-reject>
<attachments-only type="BooleanField">
<default>1</default>
<Required>Y</Required>
</attachments-only>
<max-size type="IntegerField">
<MinimumValue>1</MinimumValue>
<default>20000000</default>
<Required>N</Required>
<ValidationMessage>A valid maximum size in bytes must be set.</ValidationMessage>
</max-size>
<whitelist type="CSVListField">
<Required>N</Required>
</whitelist>
</av>
<surbl>
<whitelist type="CSVListField">
<Required>N</Required>
</whitelist>
<exceptions type="CSVListField">
<Required>N</Required>
</exceptions>
</surbl>
</items>
</model>

View file

@ -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.
#}
<script type="text/javascript">
window.redis_installed = {{ redis_installed ? 'true' : 'false' }};
$( document ).ready(function() {
var data_get_map = {'frm_rspamd':'/api/rspamd/settings/get'};
// load initial data
mapDataToFormUI(data_get_map).done(function(){
formatTokenizersUI();
$('.selectpicker').selectpicker('refresh');
// request service status on load and update status box
ajaxCall(url="/api/rspamd/service/status", sendData={}, callback=function(data,status) {
updateServiceStatusUI(data['status']);
});
});
// update history on tab state and implement navigation
if(window.location.hash != "") {
$('a[href="' + window.location.hash + '"]').click()
}
$('.nav-tabs a').on('shown.bs.tab', function (e) {
history.pushState(null, null, e.target.hash);
});
$('#rspamd\\.general\\.enable_redis_plugin').change(function (evt) {
$('#missing_redis_plugin').hide();
if (!window.redis_installed && $(this).is(':checked')) {
$('#missing_redis_plugin').show();
}
});
// form save event handlers for all defined forms
$('[id*="save_"]').each(function(){
$(this).click(function() {
var frm_id = $(this).closest("form").attr("id");
var frm_title = $(this).closest("form").attr("data-title");
// save data for General TAB
saveFormToEndpoint(url="/api/rspamd/settings/set", formid=frm_id, callback_ok=function(){
// on correct save, perform reconfigure. set progress animation when reloading
$("#"+frm_id+"_progress").addClass("fa fa-spinner fa-pulse");
ajaxCall(url="/api/rspamd/service/reconfigure", sendData={}, callback=function(data,status){
// when done, disable progress animation.
$("#"+frm_id+"_progress").removeClass("fa fa-spinner fa-pulse");
if (status != "success" || data['status'] != 'ok' ) {
// fix error handling
BootstrapDialog.show({
type:BootstrapDialog.TYPE_WARNING,
title: frm_title,
message: JSON.stringify(data),
draggable: true
});
} else {
// request service status after successful save and update status box (wait a few seconds before update)
setTimeout(function(){
ajaxCall(url="/api/rspamd/service/status", sendData={}, callback=function(data,status) {
updateServiceStatusUI(data['status']);
});
},3000);
}
});
});
});
});
});
</script>
{% if !clamav_installed %}
<div class="alert alert-warning" role="alert" id="missing_clamav" style="min-height:65px;">
<div style="margin-top: 8px;">{{ 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('<a href="/ui/core/firmware/#plugins">','</a>')}}</div>
</div>
{% endif %}
<div class="alert alert-danger" role="alert" id="missing_redis_plugin" style="min-height:65px;{% if !redis_plugin_enabled or redis_installed %} display:none;{% endif %}">
<div style="margin-top: 8px;">{{ lang._('The Redis plugin is configured to use but it is not installed. Please install it via %sSystem > Firmware > Plugins%s.')|format('<a href="/ui/core/firmware/#plugins">','</a>')}}</div>
</div>
<ul class="nav nav-tabs" role="tablist" id="maintabs">
{% 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 %}
<li role="presentation" class="dropdown {% if settings['activetab']|default("") == active_subtab %}active{% endif %}">
<a data-toggle="dropdown" href="#" class="dropdown-toggle pull-right visible-lg-inline-block visible-md-inline-block visible-xs-inline-block visible-sm-inline-block" role="button" style="border-left: 1px dashed lightgray;">
<b><span class="caret"></span></b>
</a>
<a data-toggle="tab" href="#subtab_{{ tab['subtabs'][0][0] }}" class="visible-lg-inline-block visible-md-inline-block visible-xs-inline-block visible-sm-inline-block" style="border-right:0px;"><b>{{ tab[1] }}</b></a>
<ul class="dropdown-menu" role="menu">
{% for subtab in tab['subtabs']|default({}) %}
<li class="{% if settings['activetab']|default("") == subtab[0] %}active{% endif %}"><a data-toggle="tab" href="#subtab_{{subtab[0]}}"><i class="fa fa-check-square"></i> {{ subtab[1] }}</a></li>
{% endfor %}
</ul>
</li>
{% else %}
{# Standard Tab #}
<li {% if settings['activetab']|default("") == tab[0] %} class="active" {% endif %}>
<a data-toggle="tab" href="#tab_{{ tab[0] }}">
<b>{{ tab[1] }}</b>
</a>
</li>
{% endif %}
{% endfor %}
{# add custom content
<li><a data-toggle="tab" href="#remote_acls"><b>{{ lang._('Remote Access Control Lists') }}</b></a></li>
#}
</ul>
<div class="content-box tab-content">
{% for tab in settings['tabs']|default([]) %}
{% if tab['subtabs']|default(false) %}
{# Tab with dropdown #}
{% for subtab in tab['subtabs']|default({})%}
<div id="subtab_{{subtab[0]}}" class="tab-pane fade{% if settings['activetab']|default("") == subtab[0] %} in active {% endif %}">
{{ partial("layout_partials/base_form",['fields':subtab[2],'id':'frm_'~subtab[0],'data_title':subtab[1],'apply_btn_id':'save_'~subtab[0]]) }}
</div>
{% endfor %}
{% endif %}
{% if tab['subtabs']|default(false)==false %}
<div id="tab_{{tab[0]}}" class="tab-pane fade{% if settings['activetab']|default("") == tab[0] %} in active {% endif %}">
{{ partial("layout_partials/base_form",['fields':tab[2],'id':'frm_'~tab[0],'apply_btn_id':'save_'~tab[0]]) }}
</div>
{% endif %}
{% endfor %}
</div>

View file

@ -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

View file

@ -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

View file

@ -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

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}

View file

@ -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 %}