Diagnosed azcomputerguru.com 521 errors: Cox's BGP route to specific Cloudflare origin-pull prefixes (162.158.0.0/16, 172.64.0.0/13, 173.245.48.0/20, 141.101.64.0/18) is broken from 72.194.62.0/29. Confirmed by TCP probe matrix from pfSense WAN, traceroute latency comparison, and state-table showing 0 inbound CF connections while direct-internet traffic still reached origin. Deployed Cloudflare Tunnel 'acg-origin' on Jupiter Unraid as a Docker container. Routes 4 proxied hostnames (azcomputerguru.com, analytics., community., radio.) through the tunnel with HTTPS backend to IX 172.16.3.10:443 with per-ingress SNI matching. All 4 hostnames return 200 OK through CF edge after the cutover. Repo hygiene: - Merged clients/ix-server/ into clients/internal-infrastructure/ (IX is internal infra, not a paying-client account). Git detected the session-log files as renames so history is preserved. Updated 4 stale path references in 2 files. - Moved cox-bgp ticket draft out of projects/dataforth-dos/ (wrong project) to clients/internal-infrastructure/vendor-tickets/. - Relocated tunnel-setup helper scripts from projects/dataforth-dos/datasheet-pipeline/implementation/ to clients/internal-infrastructure/scripts/cloudflared-tunnel-setup/. Deleted superseded/abandoned login attempts. Sanitized hardcoded Jupiter/pfSense SSH passwords to pull from SOPS vault at runtime; Cloudflare token reads from env var (tokens still in 1Password, vault entry is metadata-only). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
72 lines
3.1 KiB
Python
72 lines
3.1 KiB
Python
"""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()
|