Merge branch 'feat/mail-policy' into nightly

This commit is contained in:
FreddleSpl0it committed 2026-11-09 03:16:30 +01:00
commit 2afb03dd6a
38 files changed
+3161 -45

No files matched your search

+4 -1
View File
@@ -199,6 +199,9 @@ plugin {
sieve_plugins = sieve_imapsieve sieve_extprograms
sieve_vacation_send_from_recipient = yes
sieve_redirect_envelope_from = recipient
# Notifications are sent with the address of the mailbox (not :from or postmaster), so mail policies apply;
# the old notify extension always sends as postmaster and is on by default, so sieve_extensions disables it
sieve_notify_mailto_envelope_from = recipient
# From elsewhere to Spam folder
imapsieve_mailbox1_name = Junk
imapsieve_mailbox1_causes = COPY
@@ -215,7 +218,7 @@ plugin {
quota_warning2 = storage=80%% quota-warning 80 %u
sieve_pipe_bin_dir = /usr/lib/dovecot/sieve
sieve_global_extensions = +vnd.dovecot.pipe +vnd.dovecot.execute
sieve_extensions = +notify +imapflags +vacation-seconds +editheader
sieve_extensions = -notify +imapflags +vacation-seconds +editheader
sieve_max_script_size = 1M
sieve_max_redirects = 100
sieve_max_actions = 101
+8
View File
@@ -53,6 +53,7 @@ proxy_read_maps = proxy:mysql:/opt/postfix/conf/sql/mysql_sasl_passwd_maps_trans
proxy:mysql:/opt/postfix/conf/sql/mysql_archive_sender_filter_maps.cf,
proxy:mysql:/opt/postfix/conf/sql/mysql_archive_sasl_passwd_maps.cf,
proxy:mysql:/opt/postfix/conf/sql/mysql_archive_tls_policy_maps.cf,
proxy:mysql:/opt/postfix/conf/sql/mysql_mail_policy_maps.cf,
proxy:mysql:/opt/postfix/conf/sql/mysql_mbr_access_maps.cf,
proxy:mysql:/opt/postfix/conf/sql/mysql_tls_enforce_in_policy.cf,
$sender_dependent_default_transport_maps,
@@ -93,6 +94,7 @@ smtpd_hard_error_limit = ${stress?1}${stress:5}
smtpd_helo_required = yes
smtpd_proxy_timeout = 600s
smtpd_recipient_restrictions = check_recipient_mx_access proxy:mysql:/opt/postfix/conf/sql/mysql_mbr_access_maps.cf,
check_recipient_access proxy:mysql:/opt/postfix/conf/sql/mysql_mail_policy_maps.cf,
permit_sasl_authenticated,
permit_mynetworks,
check_recipient_access proxy:mysql:/opt/postfix/conf/sql/mysql_tls_enforce_in_policy.cf,
@@ -173,5 +175,11 @@ parent_domain_matches_subdomains = debug_peer_list,fast_flush_domains,mynetworks
# This Option is added to correctly set the X-Original-To Header when mails are send to lmtp (dovecot)
lmtp_destination_recipient_limit=1
# Mail policies: while policies or internal aliases exist, mysql_mail_policy_maps.cf returns this class and smtpd
# asks the policy service for every recipient (mailcow-policy in master.cf); no answer defers the recipient
smtpd_restriction_classes = mailcow_mail_policy
mailcow_mail_policy = check_policy_service { unix:private/mailcow-policy, timeout=10s, try_limit=1, default_action=DEFER_IF_PERMIT }
mailcow-policy_time_limit = 3600s
# DO NOT EDIT ANYTHING BELOW #
# Overrides #
+5 -1
View File
@@ -140,10 +140,14 @@ smtp_sender_cleanup unix n - y - 0 cleanup
127.0.0.1:10027 inet n n n - 0 spawn user=nobody argv=/usr/local/bin/whitelist_forwardinghosts.sh
# end whitelist_fwd
# mail policies: policy service of smtpd, asks the mailcow UI (mailcow_mail_policy in main.cf)
mailcow-policy unix - n n - 0 spawn user=nobody argv=/usr/local/bin/mailcow-policy.py
# start watchdog-specific
# logs to local7 (hidden)
# logs to local7 (hidden), no mail policy check
589 inet n - n - - smtpd
-o smtpd_client_restrictions=permit_mynetworks,reject
-o smtpd_recipient_restrictions=permit_mynetworks,reject
-o syslog_name=watchdog
-o syslog_facility=local7
-o smtpd_milters=
+39
View File
@@ -0,0 +1,39 @@
<?php
// Mail policy check for one recipient, asked by the Postfix policy service (mailcow-policy in master.cf)
// Request: POST with the attributes of the Postfix policy request; answer: the action (DUNNO or the rejection)
header('Content-Type: text/plain');
require_once "vars.inc.php";
require_once __DIR__ . '/../web/inc/lib/vendor/autoload.php';
// Do not show errors, we log to using error_log
ini_set('error_reporting', 0);
// Init database
$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("MAILPOLICY: " . $e . PHP_EOL);
http_response_code(501);
exit;
}
if (!isset($_POST['request']) || $_POST['request'] !== 'smtpd_access_policy') {
http_response_code(400);
exit;
}
$value = fn(string $key): string => is_string($_POST[$key] ?? null) ? $_POST[$key] : '';
try {
$policy = \Mailcow\MailPolicy\MailPolicyModule::create($pdo, \Mailcow\Core\Context::system());
echo $policy->api->check($value('sender'), $value('recipient'), $value('sasl_username'), (int)$value('server_port'), $value('helo_name'));
}
catch (Throwable $e) {
// the policy service answers with a temporary error
error_log("MAILPOLICY: " . $e->getMessage() . PHP_EOL);
http_response_code(500);
}
-30
View File
@@ -469,35 +469,5 @@ while ($row = array_shift($rows)) {
}
?>
<?php
// Start internal aliases
$stmt = $pdo->query("SELECT `id`, `address`, `domain` FROM `alias` WHERE `active` = '1' AND `internal` = '1'");
$aliases = $stmt->fetchAll(PDO::FETCH_ASSOC);
while ($alias = array_shift($aliases)) {
// build allowed_domains regex and add target domain and alias domains
$stmt = $pdo->prepare("SELECT `alias_domain` FROM `alias_domain` WHERE `active` = '1' AND `target_domain` = :target_domain");
$stmt->execute(array(':target_domain' => $alias['domain']));
$allowed_domains = $stmt->fetchAll(PDO::FETCH_ASSOC);
$allowed_domains = array_map(function($item) {
return str_replace('.', '\.', $item['alias_domain']);
}, $allowed_domains);
$allowed_domains[] = str_replace('.', '\.', $alias['domain']);
$allowed_domains = implode('|', $allowed_domains);
?>
internal_alias_<?=$alias['id'];?> {
priority = 10;
rcpt = "<?=$alias['address'];?>";
from = "/^((?!.*@(<?=$allowed_domains;?>)).)*$/";
apply "default" {
MAILCOW_INTERNAL_ALIAS = 9999.0;
}
symbols [
"MAILCOW_INTERNAL_ALIAS"
]
}
<?php
}
?>
}