public
nobgit
read
NobMail
Based on mailcow: dockerized
Languages
Repository composition by tracked source files.
PHP
49%
JavaScript
35%
HTML
9%
CSS
4%
Shell
2%
Python
1%
Lua
0%
Perl
0%
Ruby
0%
SCSS
0%
Create file
Wiki Documentation
Clone
https://nobgit.com/orgs/nobgit/nobmail.git
ssh://[email protected]:2222/orgs/nobgit/nobmail.git
Commit
[Rspamd] Create BCC plugin
c8955284
data/conf/postfix/main.cf | 5 +-
data/conf/rspamd/dynmaps/bcc.php | 88 ++++++++++++++++++++++++++++
data/conf/rspamd/lua/rspamd.local.lua | 106 ++++++++++++++++++++++++++++++++++
3 files changed, 197 insertions(+), 2 deletions(-)
create mode 100644 data/conf/rspamd/dynmaps/bcc.php
Diff
diff --git a/data/conf/postfix/main.cf b/data/conf/postfix/main.cf
index a1bcf578..c4704d56 100644
--- a/data/conf/postfix/main.cf
+++ b/data/conf/postfix/main.cf
@@ -161,8 +161,9 @@ virtual_alias_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_alias_maps.
virtual_gid_maps = static:5000
virtual_mailbox_base = /var/vmail/
virtual_mailbox_domains = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_domains_maps.cf
-recipient_bcc_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_recipient_bcc_maps.cf
-sender_bcc_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_sender_bcc_maps.cf
+# -- moved to rspamd on 2021-06-01
+#recipient_bcc_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_recipient_bcc_maps.cf
+#sender_bcc_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_sender_bcc_maps.cf
recipient_canonical_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_recipient_canonical_maps.cf
recipient_canonical_classes = envelope_recipient
virtual_mailbox_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_virtual_mailbox_maps.cf
diff --git a/data/conf/rspamd/dynmaps/bcc.php b/data/conf/rspamd/dynmaps/bcc.php
new file mode 100644
index 00000000..3145feeb
--- /dev/null
+++ b/data/conf/rspamd/dynmaps/bcc.php
@@ -0,0 +1,88 @@
+<?php
+// File size is limited by Nginx site to 10M
+// To speed things up, we do not include prerequisites
+header('Content-Type: text/plain');
+require_once "vars.inc.php";
+// Do not show errors, we log to using error_log
+ini_set('error_reporting', 0);
+// Init database
+//$dsn = $database_type . ':host=' . $database_host . ';dbname=' . $database_name;
+$dsn = $database_type . ":unix_socket=" . $database_sock . ";dbname=" . $database_name;
+$opt = [
+ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
+ PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
+ PDO::ATTR_EMULATE_PREPARES => false,
+];
+try {
+ $pdo = new PDO($dsn, $database_user, $database_pass, $opt);
+}
+catch (PDOException $e) {
+ error_log("BCC MAP SQL ERROR: " . $e . PHP_EOL);
+ http_response_code(501);
+ exit;
+}
+
+function parse_email($email) {
+ if(!filter_var($email, FILTER_VALIDATE_EMAIL)) return false;
+ $a = strrpos($email, '@');
+ return array('local' => substr($email, 0, $a), 'domain' => substr(substr($email, $a), 1));
+}
+if (!function_exists('getallheaders')) {
+ function getallheaders() {
+ if (!is_array($_SERVER)) {
+ return array();
+ }
+ $headers = array();
+ foreach ($_SERVER as $name => $value) {
+ if (substr($name, 0, 5) == 'HTTP_') {
+ $headers[str_replace(' ', '-', ucwords(strtolower(str_replace('_', ' ', substr($name, 5)))))] = $value;
+ }
+ }
+ return $headers;
+ }
+}
+
+// Read headers
+$headers = getallheaders();
+// Get rcpt
+$rcpt = $headers['Rcpt'];
+// Get from
+$from = $headers['From'];
+// Remove tags
+$rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt);
+$from = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $from);
+
+try {
+ if (!empty($rcpt)) {
+ $stmt = $pdo->prepare("SELECT `bcc_dest` FROM `bcc_maps` WHERE `type` = 'rcpt' AND `local_dest` = :local_dest AND `active` = '1'");
+ $stmt->execute(array(
+ ':local_dest' => $rcpt
+ ));
+ $bcc_dest = $stmt->fetch(PDO::FETCH_ASSOC)['bcc_dest'];
+ if (!empty($bcc_dest) && filter_var($bcc_dest, FILTER_VALIDATE_EMAIL)) {
+ error_log("BCC MAP: returning ". $bcc_dest . " for " . $rcpt . PHP_EOL);
+ http_response_code(201);
+ echo trim($bcc_dest);
+ exit;
+ }
+ }
+ if (!empty($from)) {
+ $stmt = $pdo->prepare("SELECT `bcc_dest` FROM `bcc_maps` WHERE `type` = 'sender' AND `local_dest` = :local_dest AND `active` = '1'");
+ $stmt->execute(array(
+ ':local_dest' => $from
+ ));
+ $bcc_dest = $stmt->fetch(PDO::FETCH_ASSOC)['bcc_dest'];
+ if (!empty($bcc_dest) && filter_var($bcc_dest, FILTER_VALIDATE_EMAIL)) {
+ error_log("BCC MAP: returning ". $bcc_dest . " for " . $from . PHP_EOL);
+ http_response_code(201);
+ echo trim($bcc_dest);
+ exit;
+ }
+ }
+}
+catch (PDOException $e) {
+ error_log("BCC MAP SQL ERROR: " . $e->getMessage() . PHP_EOL);
+ http_response_code(502);
+ exit;
+}
+
diff --git a/data/conf/rspamd/lua/rspamd.local.lua b/data/conf/rspamd/lua/rspamd.local.lua
index 3f4c326d..3c2aa7b2 100644
--- a/data/conf/rspamd/lua/rspamd.local.lua
+++ b/data/conf/rspamd/lua/rspamd.local.lua
@@ -320,6 +320,112 @@ rspamd_config:register_symbol({
priority = 19
})
+rspamd_config:register_symbol({
+ name = 'BCC',
+ type = 'postfilter',
+ callback = function(task)
+ local util = require("rspamd_util")
+ local rspamd_http = require "rspamd_http"
+ local rspamd_logger = require "rspamd_logger"
+
+ local from_table = {}
+ local rcpt_table = {}
+
+ local send_mail = function(task, bcc_dest)
+ local lua_smtp = require "lua_smtp"
+ local function sendmail_cb(ret, err)
+ if not ret then
+ rspamd_logger.errx(task, 'BCC SMTP ERROR: %s', err)
+ else
+ rspamd_logger.infox(rspamd_config, "BCC SMTP SUCCESS TO %s", bcc_dest)
+ end
+ end
+ if not bcc_dest then
+ return -- stop
+ end
+ lua_smtp.sendmail({
+ task = task,
+ host = 'postfix',
+ port = 591,
+ from = task:get_from(stp)[1].addr,
+ recipients = bcc_dest,
+ helo = 'bcc',
+ timeout = 10,
+ }, task:get_content(), sendmail_cb)
+ end
+
+ -- determine from
+ local from = task:get_from('smtp')
+ if from then
+ for _, a in ipairs(from) do
+ table.insert(from_table, a['addr']) -- add this rcpt to table
+ table.insert(from_table, '@' .. a['domain']) -- add this rcpts domain to table
+ end
+ else
+ return -- stop
+ end
+
+ -- determine rcpts
+ local rcpts = task:get_recipients('smtp')
+ if rcpts then
+ for _, a in ipairs(rcpts) do
+ table.insert(rcpt_table, a['addr']) -- add this rcpt to table
+ table.insert(rcpt_table, '@' .. a['domain']) -- add this rcpts domain to table
+ end
+ else
+ return -- stop
+ end
+
+ local action = task:get_metric_action('default')
+ rspamd_logger.infox("metric action now: %s", action)
+
+ local function rcpt_callback(err_message, code, body, headers)
+ if err_message == nil and code == 201 and body ~= nil then
+ if action == 'no action' or action == 'add header' or action == 'rewrite subject' then
+ send_mail(task, body)
+ end
+ end
+ end
+
+ local function from_callback(err_message, code, body, headers)
+ if err_message == nil and code == 201 and body ~= nil then
+ if action == 'no action' or action == 'add header' or action == 'rewrite subject' then
+ send_mail(task, body)
+ end
+ end
+ end
+
+ if rcpt_table then
+ for _,e in ipairs(rcpt_table) do
+ rspamd_logger.infox(rspamd_config, "checking bcc for rcpt address %s", e)
+ rspamd_http.request({
+ task=task,
+ url='http://nginx:8081/bcc.php',
+ body='',
+ callback=rcpt_callback,
+ headers={Rcpt=e}
+ })
+ end
+ end
+
+ if from_table then
+ for _,e in ipairs(from_table) do
+ rspamd_logger.infox(rspamd_config, "checking bcc for from address %s", e)
+ rspamd_http.request({
+ task=task,
+ url='http://nginx:8081/bcc.php',
+ body='',
+ callback=from_callback,
+ headers={From=e}
+ })
+ end
+ end
+
+ return true
+ end,
+ priority = 20
+})
+
rspamd_config:register_symbol({
name = 'DYN_RL_CHECK',
type = 'prefilter',