mail: DMARC parser — classify whole 207.174.124.0/24 as ours (warmup pool)
First live ingest (28 reports) showed our warmup rotation pool (.91-.109, out0x) mislabeled EXTERNAL because OUR_IPS only listed 4 specific IPs -- every one was 100% DMARC-passing, clearly ours, and would have generated false spoofing alerts. Replace the literal-IP set with an ipaddress subnet check on 207.174.124.0/24 (our whole block). The only genuinely-external failing sender is 35.174.145.124 (AWS, 32 msgs spoofing us, SPF-fail/no-DKIM, all correctly rejected by p=reject) -- exactly the signal the --alert path is meant to surface.
This commit is contained in:
parent
8e5590b492
commit
707d538847
1 changed files with 20 additions and 11 deletions
|
|
@ -48,6 +48,7 @@ import argparse
|
|||
import email
|
||||
import gzip
|
||||
import io
|
||||
import ipaddress
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
|
|
@ -69,13 +70,20 @@ IMAP_USER = os.getenv("DMARC_IMAP_USER", "dmarc@performancewest.net")
|
|||
IMAP_PASS = os.getenv("DMARC_IMAP_PASS", "")
|
||||
IMAP_FOLDER = os.getenv("DMARC_IMAP_FOLDER", "INBOX")
|
||||
|
||||
# Our own egress IPs (so the summary can flag UNKNOWN senders failing alignment).
|
||||
OUR_IPS = {
|
||||
"207.174.124.71", # transactional (app server)
|
||||
"207.174.124.94", # bulk / trucking (out05)
|
||||
"207.174.124.107", # healthcare (hcout1)
|
||||
"207.174.124.15", # co.carrierone.com relay (Carbonio)
|
||||
}
|
||||
# Our own egress space. The whole 207.174.124.0/24 is Performance West's block:
|
||||
# the warmup rotation pool sends from .91-.109 (out0x), plus .71 transactional,
|
||||
# .94 bulk, .107 hcout, .15 relay. Anything OUTSIDE this is a third party sending
|
||||
# as us -- either a legit forwarder we authorized, or (if it fails) a spoofer that
|
||||
# our p=reject policy is correctly rejecting.
|
||||
OUR_NETS = [ipaddress.ip_network("207.174.124.0/24")]
|
||||
|
||||
|
||||
def is_ours(ip: str) -> bool:
|
||||
try:
|
||||
addr = ipaddress.ip_address(ip)
|
||||
except ValueError:
|
||||
return False
|
||||
return any(addr in net for net in OUR_NETS)
|
||||
|
||||
|
||||
# ── attachment extraction ─────────────────────────────────────────────────────
|
||||
|
|
@ -271,12 +279,13 @@ def summarize(conn, days: int = 7) -> tuple[str, list[str]]:
|
|||
if total == 0:
|
||||
continue
|
||||
pass_pct = round(100 * passed / total)
|
||||
ours = "ours" if ip in OUR_IPS else "EXTERNAL"
|
||||
lines.append(f" {ip:<16} [{ours:<8}] total={total:<6} pass={pass_pct}% fail={failed}")
|
||||
ours = is_ours(ip)
|
||||
tag = "ours" if ours else "EXTERNAL"
|
||||
lines.append(f" {ip:<16} [{tag:<8}] total={total:<6} pass={pass_pct}% fail={failed}")
|
||||
# Alerts: our IP failing alignment, OR an external IP sending as us at volume.
|
||||
if ip in OUR_IPS and pass_pct < 95 and total >= 20:
|
||||
if ours and pass_pct < 95 and total >= 20:
|
||||
problems.append(f"{ip} (ours): only {pass_pct}% DMARC pass ({failed}/{total} fail) -- alignment broken")
|
||||
if ip not in OUR_IPS and failed >= 20:
|
||||
if not ours and failed >= 20:
|
||||
problems.append(f"{ip} (EXTERNAL): {failed} failing msgs sending as us -- possible spoofing")
|
||||
return "\n".join(lines), problems
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue