"""pfSense diagnostic for azcomputerguru.com 521 — suspected CF IP blocks.

Runs a single SSH session with batched diagnostics targeted at identifying
why Cloudflare PHX PoP can't reach 72.194.62.5:443.
"""
import paramiko, socket
socket.setdefaulttimeout(60)

HOST = '172.16.0.1'
PORT = 2248
USER = 'admin'
import subprocess as _sp, yaml as _y
PWD = _y.safe_load(_sp.run(['sops','-d','D:/vault/infrastructure/pfsense-firewall.sops.yaml'],capture_output=True,text=True,timeout=30,check=True).stdout)['credentials']['password']

CMDS = [
    ('installed packages (IDS/IPS/blocker)',
     'pkg info 2>/dev/null | egrep -i "suricata|snort|pfblocker|crowdsec" || echo "(none)"'),

    ('NAT rules for 72.194.62.5 / port 443',
     'pfctl -s nat 2>/dev/null | grep -E "72\\.194\\.62\\.5|443" | head -30 || echo "(pfctl nat empty)"'),

    ('Rules in PF referencing .62.5',
     'pfctl -sr 2>/dev/null | grep "72\\.194\\.62\\.5" | head -20 || echo "(none)"'),

    ('PF aliases referencing Cloudflare (case-insensitive)',
     'pfctl -T show -a cloudflare 2>/dev/null | head -30 ; pfctl -sT 2>/dev/null | grep -i "cloudflare\\|cf_\\|_cf"'),

    ('Recent filter.log entries mentioning 72.194.62.5 (last 200 binary-decoded)',
     'clog /var/log/filter.log | tail -2000 | grep "72\\.194\\.62\\.5" | tail -40 || echo "(no recent entries)"'),

    ('Recent BLOCK actions from filter.log (last 500 lines)',
     'clog /var/log/filter.log | tail -500 | grep -E "block|reject" | head -40 || echo "(no blocks)"'),

    ('Current states for :443 dst (limit 15)',
     'pfctl -s states 2>/dev/null | awk \'$6 ~ /:443$/\' | head -15 || echo "(no :443 states)"'),

    ('State table total count',
     'pfctl -s info 2>/dev/null | grep -i "states\\|limit\\|current" | head -10'),

    ('Suricata status + alert log if installed',
     'service suricata status 2>/dev/null ; ls -la /var/log/suricata/ 2>/dev/null | head'),

    ('pfBlockerNG log if installed',
     'ls -la /var/log/pfblockerng/ 2>/dev/null | head ; cat /var/log/pfblockerng/block.log 2>/dev/null | tail -30'),

    ('IP reputation / GeoIP blocks on WAN',
     'pfctl -sr 2>/dev/null | grep -iE "geoip|pfblocker|block in" | head -20'),

    ('Last 30 dropped packets to :443 (any dst)',
     'clog /var/log/filter.log | tail -2000 | grep -E "port 443" | grep -E "block|reject" | tail -30 || echo "(none)"'),
]

def main():
    c = paramiko.SSHClient()
    c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
    c.connect(HOST, port=PORT, username=USER, password=PWD,
              timeout=30, banner_timeout=30, look_for_keys=False, allow_agent=False)
    try:
        for label, cmd in CMDS:
            print(f'\n===== {label} =====', flush=True)
            stdin, stdout, stderr = c.exec_command(cmd, timeout=60)
            out = stdout.read().decode('utf-8','replace')
            err = stderr.read().decode('utf-8','replace')
            if out.strip(): print(out.rstrip())
            if err.strip() and 'stty' not in err and 'terminal' not in err.lower():
                print(f'  [stderr] {err.rstrip()[:300]}')
    finally:
        c.close()

if __name__ == '__main__':
    main()
