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] Migrate metadata_exporter to multipart formatter
24cc369d
data/conf/rspamd/local.d/metadata_exporter.conf | 6 +--
data/conf/rspamd/meta_exporter/pipe.php | 59 +++++++++++--------------
data/conf/rspamd/meta_exporter/pushover.php | 48 +++++++++-----------
docker-compose.yml | 2 +-
4 files changed, 52 insertions(+), 63 deletions(-)
Diff
diff --git a/data/conf/rspamd/local.d/metadata_exporter.conf b/data/conf/rspamd/local.d/metadata_exporter.conf
index daaa79b4..cc85faac 100644
--- a/data/conf/rspamd/local.d/metadata_exporter.conf
+++ b/data/conf/rspamd/local.d/metadata_exporter.conf
@@ -3,8 +3,7 @@ rules {
backend = "http";
url = "http://nginx:9081/pipe.php";
selector = "reject_no_global_bl";
- formatter = "default";
- meta_headers = true;
+ formatter = "multipart";
}
RLINFO {
backend = "http";
@@ -16,8 +15,7 @@ rules {
backend = "http";
url = "http://nginx:9081/pushover.php";
selector = "mailcow_rcpt";
- formatter = "json";
- meta_headers = true;
+ formatter = "multipart";
}
}
diff --git a/data/conf/rspamd/meta_exporter/pipe.php b/data/conf/rspamd/meta_exporter/pipe.php
index 003639b0..0a9ed59e 100644
--- a/data/conf/rspamd/meta_exporter/pipe.php
+++ b/data/conf/rspamd/meta_exporter/pipe.php
@@ -32,47 +32,42 @@ function parse_email($email) {
$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;
- }
+// rspamd metadata_exporter (multipart formatter):
+// - $_POST['metadata'] JSON with the rspamd metadata
+// - $_FILES['message'] raw RFC822 message
+if (empty($_POST['metadata']) || !isset($_FILES['message']) || $_FILES['message']['error'] !== UPLOAD_ERR_OK) {
+ error_log("QUARANTINE: missing multipart parts from rspamd" . PHP_EOL);
+ http_response_code(400);
+ exit;
}
-$raw_data_content = file_get_contents('php://input');
-$raw_data = mb_convert_encoding($raw_data_content, 'HTML-ENTITIES', "UTF-8");
-$headers = getallheaders();
+$meta = json_decode($_POST['metadata'], true);
+if (!is_array($meta)) {
+ error_log("QUARANTINE: cannot decode metadata JSON" . PHP_EOL);
+ http_response_code(400);
+ exit;
+}
-$qid = $headers['X-Rspamd-Qid'];
-$fuzzy = $headers['X-Rspamd-Fuzzy'];
-$subject = iconv_mime_decode($headers['X-Rspamd-Subject']);
-$score = $headers['X-Rspamd-Score'];
-$rcpts = $headers['X-Rspamd-Rcpt'];
-$user = $headers['X-Rspamd-User'];
-$ip = $headers['X-Rspamd-Ip'];
-$action = $headers['X-Rspamd-Action'];
-$sender = $headers['X-Rspamd-From'];
-$symbols = $headers['X-Rspamd-Symbols'];
+$raw_data_content = file_get_contents($_FILES['message']['tmp_name']);
+$raw_data = mb_convert_encoding($raw_data_content, 'HTML-ENTITIES', "UTF-8");
+$raw_size = (int)$_FILES['message']['size'];
-$raw_size = (int)$_SERVER['CONTENT_LENGTH'];
+$qid = $meta['qid'] ?? 'unknown';
+$subject = iconv_mime_decode($meta['subject'] ?? '');
+$score = $meta['score'] ?? 0;
+$rcpts = $meta['rcpt'] ?? array();
+$user = $meta['user'] ?? 'unknown';
+$ip = $meta['ip'] ?? 'unknown';
+$action = $meta['action'] ?? 'no action';
+$sender = $meta['from'] ?? '';
+$symbols = json_encode($meta['symbols'] ?? array());
+$fuzzy = json_encode(is_array($meta['fuzzy'] ?? null) ? $meta['fuzzy'] : array());
if (empty($sender)) {
error_log("QUARANTINE: Unknown sender, assuming empty-env-from@localhost" . PHP_EOL);
$sender = 'empty-env-from@localhost';
}
-if ($fuzzy == 'unknown') {
- $fuzzy = '[]';
-}
-
try {
$max_size = (int)$redis->Get('Q_MAX_SIZE');
if (($max_size * 1048576) < $raw_size) {
@@ -94,7 +89,7 @@ catch (RedisException $e) {
$rcpt_final_mailboxes = array();
// Loop through all rcpts
-foreach (json_decode($rcpts, true) as $rcpt) {
+foreach ($rcpts as $rcpt) {
// Remove tag
$rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt);
diff --git a/data/conf/rspamd/meta_exporter/pushover.php b/data/conf/rspamd/meta_exporter/pushover.php
index efe5fdd5..a2cd0995 100644
--- a/data/conf/rspamd/meta_exporter/pushover.php
+++ b/data/conf/rspamd/meta_exporter/pushover.php
@@ -32,50 +32,46 @@ function parse_email($email) {
$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;
- }
+// rspamd metadata_exporter (multipart formatter): metadata JSON arrives as $_POST['metadata'].
+if (empty($_POST['metadata'])) {
+ error_log("NOTIFY: missing metadata part from rspamd" . PHP_EOL);
+ http_response_code(400);
+ exit;
}
-$headers = getallheaders();
-$json_body = json_decode(file_get_contents('php://input'));
+$meta = json_decode($_POST['metadata'], true);
+if (!is_array($meta)) {
+ error_log("NOTIFY: cannot decode metadata JSON" . PHP_EOL);
+ http_response_code(400);
+ exit;
+}
-$qid = $headers['X-Rspamd-Qid'];
-$rcpts = $headers['X-Rspamd-Rcpt'];
-$sender = $headers['X-Rspamd-From'];
-$ip = $headers['X-Rspamd-Ip'];
-$subject = iconv_mime_decode($headers['X-Rspamd-Subject']);
-$messageid= $json_body->message_id;
+$qid = $meta['qid'] ?? 'unknown';
+$rcpts = $meta['rcpt'] ?? array();
+$sender = $meta['from'] ?? '';
+$ip = $meta['ip'] ?? 'unknown';
+$subject = iconv_mime_decode($meta['subject'] ?? '');
+$messageid= $meta['message_id'] ?? '';
$priority = 0;
-$symbols_array = json_decode($headers['X-Rspamd-Symbols'], true);
+$symbols_array = $meta['symbols'] ?? array();
if (is_array($symbols_array)) {
foreach ($symbols_array as $symbol) {
- if ($symbol['name'] == 'HAS_X_PRIO_ONE') {
+ if (($symbol['name'] ?? null) == 'HAS_X_PRIO_ONE') {
$priority = 1;
break;
}
}
}
-$sender_address = $json_body->header_from[0];
+$sender_address = $meta['header_from'][0] ?? '';
$sender_name = '-';
if (preg_match('/(?<name>.*?)<(?<address>.*?)>/i', $sender_address, $matches)) {
$sender_address = $matches['address'];
$sender_name = trim($matches['name'], '"\' ');
}
-$to_address = $json_body->header_to[0];
+$to_address = $meta['header_to'][0] ?? '';
$to_name = '-';
if (preg_match('/(?<name>.*?)<(?<address>.*?)>/i', $to_address, $matches)) {
$to_address = $matches['address'];
@@ -85,7 +81,7 @@ if (preg_match('/(?<name>.*?)<(?<address>.*?)>/i', $to_address, $matches)) {
$rcpt_final_mailboxes = array();
// Loop through all rcpts
-foreach (json_decode($rcpts, true) as $rcpt) {
+foreach ($rcpts as $rcpt) {
// Remove tag
$rcpt = preg_replace('/^(.*?)\+.*(@.*)$/', '$1$2', $rcpt);
diff --git a/docker-compose.yml b/docker-compose.yml
index 615e19f5..16370a7b 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -84,7 +84,7 @@ services:
- clamd
rspamd-mailcow:
- image: ghcr.io/mailcow/rspamd:3.14.3-1
+ image: ghcr.io/mailcow/rspamd:4.1.0-1
stop_grace_period: 30s
depends_on:
- dovecot-mailcow