"""Revert the 3 hostnames that have no functional backend: - plex (NPM has no vhost) - rustdesk (NPM has no vhost) - secure (Jupiter can't route to 172.16.1.16) Removes them from tunnel ingress and restores their original A records. """ import json, os, subprocess, urllib.error, urllib.request, time import paramiko, yaml ZONE = '1beb9917c22b54be32e5215df2c227ce' CF_TOKEN = os.environ.get('CF_API_TOKEN_FULL_DNS','') if not CF_TOKEN: raise SystemExit('set CF_API_TOKEN_FULL_DNS') REVERT = { # hostname: original A content 'plex.azcomputerguru.com': '72.194.62.4', 'rustdesk.azcomputerguru.com': '72.194.62.10', 'secure.azcomputerguru.com': '72.194.62.2', } def cfapi(method, path, body=None): req = urllib.request.Request( f'https://api.cloudflare.com/client/v4{path}', data=json.dumps(body).encode() if body else None, method=method, headers={'Authorization': f'Bearer {CF_TOKEN}','Content-Type':'application/json'}, ) try: with urllib.request.urlopen(req, timeout=30) as r: return json.loads(r.read()) except urllib.error.HTTPError as e: try: return json.loads(e.read()) except: return {'success':False,'errors':[{'message':str(e)}]} def _pwd(v): return yaml.safe_load(subprocess.run(['sops','-d',v],capture_output=True,text=True,timeout=30,check=True).stdout)['credentials']['password'] j = paramiko.SSHClient(); j.set_missing_host_key_policy(paramiko.AutoAddPolicy()) j.connect('172.16.3.20', username='root', password=_pwd('D:/vault/infrastructure/jupiter-unraid-primary.sops.yaml'), timeout=30, look_for_keys=False, allow_agent=False) def jrun(cmd, to=60): _, o, _ = j.exec_command(cmd, timeout=to) return o.read().decode() try: print('=== [1] rewrite config.yml without the 3 broken hosts ===') APPDATA = '/mnt/cache/appdata/cloudflared' # Read UUID UUID = jrun(f'grep "^tunnel:" {APPDATA}/config.yml').split(':',1)[1].strip() IX = 'https://172.16.3.10:443' JNPM = 'https://172.16.3.20:18443' KEEP = [ ('azcomputerguru.com', IX), ('analytics.azcomputerguru.com', IX), ('community.azcomputerguru.com', IX), ('radio.azcomputerguru.com', IX), ('ix.azcomputerguru.com', IX), ('git.azcomputerguru.com', JNPM), ('plexrequest.azcomputerguru.com', JNPM), ('rmm.azcomputerguru.com', JNPM), ('rmm-api.azcomputerguru.com', JNPM), ('sync.azcomputerguru.com', JNPM), ] config = f'tunnel: {UUID}\ncredentials-file: /home/nonroot/.cloudflared/{UUID}.json\ningress:\n' for h, svc in KEEP: config += f' - hostname: {h}\n service: {svc}\n originRequest:\n originServerName: {h}\n noTLSVerify: true\n' config += ' - service: http_status:404\n' jrun(f'cp {APPDATA}/config.yml {APPDATA}/config.yml.bak-$(date +%Y%m%d-%H%M%S)') HD = "'EOF_CFG'" jrun(f"cat > {APPDATA}/config.yml <<{HD}\n{config}\nEOF_CFG") jrun(f'chown 65532:65532 {APPDATA}/config.yml') print(f' 10 ingress hostnames kept (plex/rustdesk/secure removed)') print('\n=== [2] revert DNS for 3 hosts ===') for host, orig_ip in REVERT.items(): r = cfapi('GET', f'/zones/{ZONE}/dns_records?name={host}') if not r.get('success') or not r['result']: print(f' [{host}] no record, skipping'); continue rec = r['result'][0] print(f' [{host}] current: type={rec["type"]} content={rec["content"]}') d = cfapi('DELETE', f'/zones/{ZONE}/dns_records/{rec["id"]}') if not d.get('success'): print(f' [FAIL delete] {d.get("errors")}'); continue body = {'type':'A','name':host,'content':orig_ip,'proxied':True,'ttl':1} cr = cfapi('POST', f'/zones/{ZONE}/dns_records', body) if cr.get('success'): print(f' [OK] restored A {orig_ip} proxied') else: print(f' [FAIL create] {cr.get("errors")}') print('\n=== [3] restart cloudflared ===') print(jrun('docker restart cloudflared').rstrip()) print('\n=== [4] wait for reconnect ===') for i in range(20): time.sleep(3) logs = jrun('docker logs cloudflared 2>&1 | tail -30') conns = logs.count('Registered tunnel connection') if conns >= 4: print(f' [try {i+1}] {conns} connections') break finally: j.close() print('\n=== [5] external probe all 10 tunneled hostnames ===') import urllib.request for h in [k[0] for k in [ ('azcomputerguru.com',),('analytics.azcomputerguru.com',),('community.azcomputerguru.com',), ('radio.azcomputerguru.com',),('ix.azcomputerguru.com',),('git.azcomputerguru.com',), ('plexrequest.azcomputerguru.com',),('rmm.azcomputerguru.com',),('rmm-api.azcomputerguru.com',), ('sync.azcomputerguru.com',), ]]: try: req = urllib.request.Request(f'https://{h}/', method='HEAD', headers={'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/120.0'}) with urllib.request.urlopen(req, timeout=15) as r: print(f' {h:42} HTTP {r.status} {r.headers.get("Server","-")}') except urllib.error.HTTPError as e: print(f' {h:42} HTTP {e.code}') except Exception as e: print(f' {h:42} ERR {str(e)[:40]}')