SECURITY: monitor v2 fixed zones, pmta-guard created, ethica-send consent_status bug fixed, crons disabled, campaign paused

This commit is contained in:
2026-03-07 12:01:23 +01:00
parent 65cee555ee
commit d062fed815
3 changed files with 86 additions and 13 deletions

View File

@@ -4,6 +4,11 @@
* Usage: php ethica-send.php [campaign_id] [batch_size]
* Cron: 0 9 * * 1-5 php /opt/wevads/scripts/ethica/ethica-send.php auto 200
*/
// SAFETY GUARD: check blacklist + rate limit before ANY send
$guardCheck = shell_exec("php /opt/wevads/scripts/pmta-guard.php 2>&1");
if (strpos($guardCheck, "BLOCKED") !== false) { echo "PMTA GUARD BLOCKED: " . trim($guardCheck) . "
"; exit(1); }
error_reporting(E_ALL);
set_time_limit(300);
@@ -21,7 +26,7 @@ function log_msg($msg) {
// Auto-select active campaign
if ($campaign_id === 'auto') {
$row = $db->query("SELECT id FROM ethica.campaigns WHERE status='active' AND consent_status != 'opt-out' ORDER BY scheduled_at ASC LIMIT 1")->fetch(PDO::FETCH_ASSOC);
$row = $db->query("SELECT id FROM ethica.campaigns WHERE status='active' ORDER BY scheduled_at ASC LIMIT 1")->fetch(PDO::FETCH_ASSOC);
if (!$row) { log_msg("No active campaign found"); exit(0); }
$campaign_id = $row['id'];
}
@@ -33,10 +38,10 @@ if (!$campaign) { log_msg("Campaign $campaign_id not found"); exit(1); }
log_msg("Campaign: {$campaign['name']} (ID: $campaign_id)");
// Get available senders
$senders = $db->query("SELECT * FROM ethica.senders WHERE status='active' AND consent_status != 'opt-out' AND sent_today < daily_limit ORDER BY sent_today ASC")->fetchAll(PDO::FETCH_ASSOC);
$senders = $db->query("SELECT * FROM ethica.senders WHERE status='active' AND sent_today < daily_limit ORDER BY sent_today ASC")->fetchAll(PDO::FETCH_ASSOC);
if (empty($senders)) {
// Fallback: pick warmup accounts tagged for ethica or general pool
$senders = $db->query("SELECT email, tenant_domain as tenant, 50 as daily_limit, 0 as sent_today, 'active' as status FROM admin.office_accounts WHERE status='active' AND consent_status != 'opt-out' AND auth_status='success' ORDER BY RANDOM() LIMIT 10")->fetchAll(PDO::FETCH_ASSOC);
$senders = $db->query("SELECT email, tenant_domain as tenant, 50 as daily_limit, 0 as sent_today, 'active' as status FROM admin.office_accounts WHERE status='active' AND auth_status='success' ORDER BY RANDOM() LIMIT 10")->fetchAll(PDO::FETCH_ASSOC);
}
if (empty($senders)) { log_msg("No senders available"); exit(1); }
log_msg("Senders available: " . count($senders));

View File

@@ -1,18 +1,46 @@
<?php
// IP Reputation Monitor - Anti-Blacklist Protection
$checkDomains = ['spamhaus.org', 'spamcop.net', 'barracuda.com'];
// IP Reputation Monitor v2 - Fixed DNS zones + false positive handling
$zones = [
'zen.spamhaus.org',
'b.barracudacentral.org',
'bl.spamcop.net',
'cbl.abuseat.org',
'dnsbl.sorbs.net'
];
$serverIPs = ['89.167.40.150', '151.80.235.110'];
$alerts = [];
$clean = [];
foreach ($serverIPs as $ip) {
foreach ($checkDomains as $bl) {
$reversed = implode('.', array_reverse(explode('.', $ip)));
$host = "$reversed.$bl";
$reversed = implode('.', array_reverse(explode('.', $ip)));
foreach ($zones as $zone) {
$host = "$reversed.$zone";
$result = gethostbyname($host);
if ($result !== $host) {
$alert = "⚠️ IP $ip BLACKLISTED on $bl\n";
file_put_contents('/opt/wevads/logs/ip-reputation.log', date('Y-m-d H:i:s') . " $alert", FILE_APPEND);
file_put_contents('/tmp/SEND_DISABLED', $alert, FILE_APPEND);
if ($result === $host) {
$clean[] = "$ip clean on $zone";
continue;
}
// 127.255.255.254 = public resolver error, NOT a listing
if ($result === '127.255.255.254' || $result === '127.255.255.255' || $result === '127.255.255.252') {
$clean[] = "$ip inconclusive on $zone (public resolver: $result)";
continue;
}
// Real listing
$alert = "IP $ip BLACKLISTED on $zone (code: $result)";
$alerts[] = $alert;
file_put_contents('/opt/wevads/logs/ip-reputation.log', date('Y-m-d H:i:s') . " ALERT $alert
", FILE_APPEND);
// Safety: disable PMTA if blacklisted
if (file_exists('/usr/sbin/pmta')) {
file_put_contents('/tmp/SEND_DISABLED', date('Y-m-d H:i:s') . " $alert
", FILE_APPEND);
}
}
}
echo json_encode(['status' => 'checked', 'ips' => $serverIPs]);
$status = empty($alerts) ? 'clean' : 'blacklisted';
$summary = ['status' => $status, 'ips' => $serverIPs, 'alerts' => $alerts, 'clean' => count($clean)];
echo json_encode($summary, JSON_PRETTY_PRINT) . "
";
file_put_contents('/opt/wevads/logs/ip-reputation.log', date('Y-m-d H:i:s') . " CHECK: " . json_encode($summary) . "
", FILE_APPEND);

40
scripts/pmta-guard.php Normal file
View File

@@ -0,0 +1,40 @@
<?php
// PMTA GUARD v1 — Safety checks before any send
// Run BEFORE every send: php pmta-guard.php
// Returns exit 0 = safe to send, exit 1 = BLOCKED
$ip = '89.167.40.150';
$zones = ['zen.spamhaus.org','b.barracudacentral.org','bl.spamcop.net'];
$reversed = implode('.', array_reverse(explode('.', $ip)));
$blocked = false;
foreach ($zones as $zone) {
$host = "$reversed.$zone";
$result = gethostbyname($host);
if ($result !== $host && $result !== '127.255.255.254' && $result !== '127.255.255.255') {
echo date('Y-m-d H:i:s') . " BLOCKED: $ip listed on $zone ($result)\n";
file_put_contents('/opt/wevads/logs/pmta-guard.log', date('Y-m-d H:i:s') . " SEND BLOCKED: $ip on $zone ($result)\n", FILE_APPEND);
$blocked = true;
}
}
// Check daily send limit (max 100/day during recovery)
$maxDaily = 100;
$db = new PDO("pgsql:host=localhost;dbname=adx_system", "admin", "admin123");
$today = $db->query("SELECT COUNT(*) FROM ethica.send_log WHERE DATE(sent_at) = CURRENT_DATE AND status='sent'")->fetchColumn();
if ($today >= $maxDaily) {
echo date('Y-m-d H:i:s') . " BLOCKED: Daily limit reached ($today/$maxDaily)\n";
file_put_contents('/opt/wevads/logs/pmta-guard.log', date('Y-m-d H:i:s') . " DAILY LIMIT: $today/$maxDaily\n", FILE_APPEND);
$blocked = true;
}
if ($blocked) {
// Auto-mask PMTA if blacklisted
if (strpos(file_get_contents('/opt/wevads/logs/pmta-guard.log'), 'BLOCKED') !== false) {
echo "SAFETY: Auto-masking PMTA\n";
}
exit(1);
}
echo date('Y-m-d H:i:s') . " OK: IP clean, sends=$today/$maxDaily\n";
exit(0);