Audited all 25 proxied zone records and expanded tunnel ingress to cover 9 hostnames total (azcomputerguru + analytics + community + radio + git + plexrequest + rmm + rmm-api + sync). All verified HTTP 200. Reverted 3 hostnames to original A records after discovering they require backend work, not tunnel changes: - plex/rustdesk: NPM on Jupiter has no vhost for these (returned 'tls: unrecognized name' when tunneled) - secure: Jupiter can't route to its backend subnet 172.16.1.0/24 Reverted ix.azcomputerguru.com to DNS-only A record after user reported :2087 WHM access broken. Cloudflare Tunnel is hostname-bound, not port-bound, so non-standard admin ports can't pass through. Direct NAT to 72.194.62.5 restored WHM/cPanel access. Adds four new helper scripts under clients/internal-infrastructure/ scripts/cloudflared-tunnel-setup/ (audit_proxied, discover_backends, expand_tunnel, revert_broken). All use SOPS vault / env var for creds. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
69 lines
2.8 KiB
Python
69 lines
2.8 KiB
Python
"""Discover internal backends for each proxied hostname by tracing NAT rules.
|
|
|
|
For each public IP in the 72.194.62.x block, pull pfSense port forwards on 443
|
|
(and other ports if visible) and map them to internal LAN IPs:ports.
|
|
Also pull NPM hosts from Jupiter to map hostnames -> backend services.
|
|
"""
|
|
import json, os, re, subprocess
|
|
import paramiko, yaml
|
|
|
|
def _pwd(vault_path):
|
|
r = subprocess.run(['sops','-d',vault_path], capture_output=True, text=True, timeout=30, check=True)
|
|
return yaml.safe_load(r.stdout)['credentials']['password']
|
|
|
|
def ssh(host, user, pwd, port=22):
|
|
c = paramiko.SSHClient(); c.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
|
c.connect(host, port=port, username=user, password=pwd, timeout=30, look_for_keys=False, allow_agent=False)
|
|
return c
|
|
|
|
def run(c, cmd, to=60):
|
|
_, o, _ = c.exec_command(cmd, timeout=to)
|
|
return o.read().decode('utf-8','replace')
|
|
|
|
# -----------------------------------------------------------------
|
|
print('=== [1] pfSense NAT rules: public 72.194.62.x -> internal ===')
|
|
pf_pwd = _pwd('D:/vault/infrastructure/pfsense-firewall.sops.yaml')
|
|
pf = ssh('172.16.0.1', 'admin', pf_pwd, port=2248)
|
|
# Pull rdr rules referencing each public IP on :443
|
|
out = run(pf, r'pfctl -s nat 2>/dev/null | grep -E "rdr on igc0 .*tcp.*72\.194\.62\.[0-9]+ port = (https|2083|2087|3389|3000|8000)" | sort -u | head -40')
|
|
print(out.strip())
|
|
print()
|
|
|
|
# -----------------------------------------------------------------
|
|
print('=== [2] Jupiter docker ps + NPM inspection for :4 traffic ===')
|
|
j_pwd = _pwd('D:/vault/infrastructure/jupiter-unraid-primary.sops.yaml')
|
|
j = ssh('172.16.3.20', 'root', j_pwd)
|
|
|
|
# NPM container: find its config file
|
|
out = run(j, 'docker ps --format "{{.Names}}\\t{{.Image}}\\t{{.Ports}}" | grep -iE "npm|nginx-proxy|proxy"')
|
|
print('-- NPM container --')
|
|
print(out.strip())
|
|
print()
|
|
|
|
# Find NPM hosts config (usually /data/nginx/proxy_host or in database)
|
|
out = run(j, 'ls /mnt/user/appdata/NginxProxyManager*/data/nginx/proxy_host/ 2>/dev/null | head')
|
|
print('-- NPM proxy_host configs --')
|
|
print(out.strip())
|
|
print()
|
|
|
|
# Show the first few proxy_host configs to extract hostname -> upstream mappings
|
|
out = run(j, r'''
|
|
for f in /mnt/user/appdata/NginxProxyManager-v3/data/nginx/proxy_host/*.conf /mnt/user/appdata/NginxProxyManager/data/nginx/proxy_host/*.conf 2>/dev/null; do
|
|
if [ -f "$f" ]; then
|
|
srv=$(grep -oP "server_name \K[^;]+" "$f" | head -1)
|
|
ups=$(grep -oP "(proxy_pass|set \$server) \K[^;\"]+" "$f" | head -2 | tr '\n' '|')
|
|
echo "$(basename $f): server=$srv upstream=$ups"
|
|
fi
|
|
done 2>/dev/null
|
|
''', to=60)
|
|
print('-- server_name -> upstream --')
|
|
print(out.strip())
|
|
print()
|
|
|
|
# Also dump docker ps for the services themselves
|
|
out = run(j, 'docker ps --format "{{.Names}}\\t{{.Ports}}" | head -30')
|
|
print('-- all docker containers + ports --')
|
|
print(out.strip())
|
|
|
|
pf.close(); j.close()
|