#!/usr/bin/env python3 """ Delegate exposedgays.com from GoDaddy to Cloudflare. Requires exposedgays.com zone to exist in Cloudflare (dashboard add if API create fails). """ import json import re import sys import time import urllib.error import urllib.request import paramiko PASS = "Bbt9115xty9176!" DOMAIN = "exposedgays.com" ACCOUNT = "2599c23bbb1255dbb73e8d34b4115fda" TUNNEL_CNAME = "03079a26-f14c-4622-b463-ba54a24f7472.cfargotunnel.com" CF = "https://api.cloudflare.com/client/v4" GD = "https://api.godaddy.com/v1" GODADDY_KEY = "2wYVpc3VT4_Gi9AsNqzJ6BtSfNz6nLDuo" GODADDY_SECRET = "NvC3qexGAU7Rd5QExUTH6z" def load_cf_token(): c = paramiko.SSHClient() c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) c.connect("10.10.0.1", username="admin", password=PASS, timeout=15) _, o, e = c.exec_command("cat /usr/local/etc/wan-dns-failover.env", timeout=30) o.channel.recv_exit_status() text = (o.read() + e.read()).decode() c.close() return re.search(r"CLOUDFLARE_API_TOKEN=([^\s\"']+)", text).group(1) def cf(tok, path, method="GET", body=None): req = urllib.request.Request( CF + path, data=json.dumps(body).encode() if body is not None else None, method=method, headers={"Authorization": f"Bearer {tok}", "Content-Type": "application/json"}, ) try: with urllib.request.urlopen(req, timeout=45) as r: return json.load(r) except urllib.error.HTTPError as e: return json.loads(e.read().decode()) def gd(path, method="GET", body=None): req = urllib.request.Request( GD + path, data=json.dumps(body).encode() if body is not None else None, method=method, headers={ "Authorization": f"sso-key {GODADDY_KEY}:{GODADDY_SECRET}", "Content-Type": "application/json", "Accept": "application/json", }, ) try: with urllib.request.urlopen(req, timeout=45) as r: raw = r.read().decode() return r.status, json.loads(raw) if raw else {} except urllib.error.HTTPError as e: raw = e.read().decode() try: return e.code, json.loads(raw) except Exception: return e.code, {"raw": raw[:800]} def find_zone(tok): for status in ["active", "pending", ""]: q = f"name={DOMAIN}" + (f"&status={status}" if status else "") res = cf(tok, f"/zones?{q}") if res.get("result"): return res["result"][0] page = 1 while page <= 20: res = cf(tok, f"/zones?per_page=50&page={page}") for z in res.get("result", []): if z["name"] == DOMAIN: return z if len(res.get("result", [])) < 50: break page += 1 return None def upsert_tunnel_cnames(tok, zid): records = cf(tok, f"/zones/{zid}/dns_records?per_page=100").get("result", []) for name in [DOMAIN, f"www.{DOMAIN}"]: body = {"type": "CNAME", "name": name, "content": TUNNEL_CNAME, "ttl": 1, "proxied": True} existing = next( (r for r in records if r["name"] == name and r["type"] in ("A", "CNAME", "AAAA")), None, ) if existing: res = cf(tok, f"/zones/{zid}/dns_records/{existing['id']}", "PATCH", body) action = "PATCH" else: res = cf(tok, f"/zones/{zid}/dns_records", "POST", body) action = "POST" print(f" CF {action} {name}: success={res.get('success')} errors={res.get('errors')}") def verify(): c = paramiko.SSHClient() c.set_missing_host_key_policy(paramiko.AutoAddPolicy()) c.connect("10.10.0.10", username="localadministrator", password=PASS, timeout=30) cmds = [ f"dig @1.1.1.1 {DOMAIN} NS +short", f"dig @1.1.1.1 www.{DOMAIN} A +short", f"curl -sSI https://www.{DOMAIN}/ | head -10", f"curl -sSL https://www.{DOMAIN}/ | head -c 200", ] for cmd in cmds: print(f"\n$ {cmd}") _, o, e = c.exec_command(cmd, timeout=30) o.channel.recv_exit_status() print((o.read() + e.read()).decode(errors="replace")) c.close() def main(): tok = load_cf_token() zone = find_zone(tok) if not zone: created = cf( tok, "/zones", "POST", {"name": DOMAIN, "account": {"id": ACCOUNT}, "type": "full", "jump_start": False}, ) if created.get("success"): zone = created["result"] print("Created CF zone via API") else: print("CF zone not found and API create failed:") print(" ", created.get("errors")) print("\nAdd exposedgays.com at https://dash.cloudflare.com then re-run:") print(" python exposedgays/deploy/delegate-godaddy-to-cf.py") return 1 zid = zone["id"] nameservers = zone.get("name_servers", []) status = zone.get("status") print(f"CF zone {zid} status={status}") print(f"CF nameservers: {nameservers}") print("\n=== GoDaddy before ===") code, before = gd(f"/domains/{DOMAIN}") print(f"HTTP {code} NS={before.get('nameServers') if code == 200 else before}") print("\n=== Cloudflare tunnel DNS ===") upsert_tunnel_cnames(tok, zid) if not nameservers: print("No nameservers on zone — cannot delegate GoDaddy") return 1 print("\n=== GoDaddy PATCH nameservers -> Cloudflare ===") code, res = gd(f"/domains/{DOMAIN}", "PATCH", {"nameServers": nameservers}) print(f"HTTP {code}: {json.dumps(res, indent=2) if res else '{}'}") code, after = gd(f"/domains/{DOMAIN}") if code == 200: print(f"\nGoDaddy after NS={after.get('nameServers')}") print("\nWaiting 15s for propagation...") time.sleep(15) verify() return 0 if __name__ == "__main__": raise SystemExit(main())