diff --git a/scripts/dmarc_report_parser.py b/scripts/dmarc_report_parser.py index 376c994..5149ed4 100644 --- a/scripts/dmarc_report_parser.py +++ b/scripts/dmarc_report_parser.py @@ -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