Files
claudetools/clients/internal-infrastructure/scripts/cloudflared-tunnel-setup/pfsense_diag2.py
Mike Swanson a78fb96f95 Session log: Cloudflare Tunnel for azcomputerguru + Cox BGP diagnosis
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>
2026-04-13 10:30:51 -07:00

66 lines
2.9 KiB
Python

"""pfSense deeper diag — read filter log + check inbound 443 to 172.16.3.10."""
import paramiko, socket
socket.setdefaulttimeout(60)
HOST, PORT, USER = "172.16.0.1", 2248, "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 = [
('clog binary locations',
'which clog 2>/dev/null; ls /usr/local/sbin/clog* /usr/sbin/clog* /sbin/clog* 2>/dev/null; pkg info clog 2>/dev/null | head -3'),
('filter log type + size',
'file /var/log/filter.log 2>/dev/null; ls -la /var/log/filter.log'),
('Try to read filter.log as text',
'tail -50 /var/log/filter.log | grep -v "^$" | tail -30'),
('Inbound :443 -> 172.16.3.10 states (right now)',
'pfctl -s states | grep "172.16.3.10:443\\|-> 172.16.3.10" | grep "443" | head -30'),
('Inbound :443 states total count',
'pfctl -s states | grep "172.16.3.10:443" | wc -l; pfctl -s states | grep ":443.*172\\.16\\.3\\.10" | wc -l'),
('State count broken out by direction',
'pfctl -s states | awk \'/172\\.16\\.3\\.10/ {print $0}\' | head -20'),
('Cloudflare PHX IPs sample (CF publishes these)',
'curl -s -m 10 https://www.cloudflare.com/ips-v4 2>/dev/null | head -5; echo "---"; curl -s -m 10 https://www.cloudflare.com/ips-v4 2>/dev/null | wc -l'),
('Test-send a SYN from pfSense to known CF edge IP (simulate return path)',
'nc -z -v -w 3 162.158.0.1 443 2>&1; echo "---"; nc -z -v -w 3 104.26.8.237 443 2>&1'),
('Check WAN interface health',
'ifconfig igc0 | grep -E "inet |status"; echo "---"; netstat -rn | grep default'),
('Recently-logged DROP/BLOCK (pf log format 5)',
'tcpdump -n -e -ttt -r /var/log/filter.log 2>&1 | head -30 || echo "(tcpdump cant read binary)"'),
('Try pfSsh.php for log',
'echo "exec;tail -30 /var/log/filter.log" | pfSsh.php 2>&1 | tail -40'),
('PF filter log read alt (pfctl loginterface / pflog0 dump)',
'tcpdump -n -e -ttt -i pflog0 -c 20 2>&1 | head -40 || echo "(no pflog0)"'),
]
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()