""" Wire auto-publisher sites to Cloudflare home tunnel. Adds ingress rules to the existing home tunnel for each site subdomain. """ import json import subprocess import time # Home tunnel ID (the existing tunnel that routes ~38 services) HOME_TUNNEL_ID = "d2871458-1737-4844-b114-9a44b9d71e25" ACCT_ID = "895479ab3540daa6d61ae702a5164475" ZONE_ID = "93634d23ff8138fbd795763b681158ac" DOMAIN = "thetempleofdoom.com" # Site IPs SITES = { "ai": "10.30.20.240", "tech": "10.30.20.241", "science": "10.30.20.242", "crypto": "10.30.20.243", "linux": "10.30.20.244", "gaming": "10.30.20.246", "diy": "10.30.20.247", "guides": "10.30.20.248", } def get_home_tunnel_config(): """Get current home tunnel ingress configuration.""" result = subprocess.run( ["cloudflared", "tunnel", "info", "--output", "json", HOME_TUNNEL_ID], capture_output=True, text=True, timeout=10 ) if result.returncode == 0 and result.stdout.strip(): return json.loads(result.stdout) return None def add_ingress_rules(): """Add ingress rules for all 8 publisher sites to the home tunnel.""" print("Adding Cloudflare tunnel ingress rules...") for name, ip in SITES.items(): hostname = f"{name}.{DOMAIN}" # Check if DNS record already exists print(f"\n {hostname}:") # Use cloudflared CLI to add route cmd = [ "cloudflared", "tunnel", "route", "dns", "--overwrite-dns", HOME_TUNNEL_ID, hostname ] result = subprocess.run(cmd, capture_output=True, text=True, timeout=30) if result.returncode == 0: print(f" ✅ DNS route created: {hostname} → tunnel") else: error = result.stderr.strip() if "already exists" in error.lower() or "conflict" in error.lower(): print(f" ⚠️ DNS already exists: {hostname}") else: print(f" ❌ Failed: {error[:200]}") # Fallback: manual DNS via API _add_dns_manual(name, hostname) # Add local ingress mapping using config file approach _add_config_ingress(name, hostname, ip) def _add_dns_manual(name, hostname): """Manual DNS CNAME creation via API.""" # This requires a working CF token CF_TOKEN = "cfat_M62Ke6eLXPZnb26qW4FCRL4T4h8l4KrRVLFIRWad9455c3e0" result = subprocess.run([ "curl", "-s", "-X", "POST", f"https://api.cloudflare.com/client/v4/zones/{ZONE_ID}/dns_records", "-H", f"Authorization: Bearer {CF_TOKEN}", "-H", "Content-Type: application/json", "-d", json.dumps({ "type": "CNAME", "name": name, "content": f"{HOME_TUNNEL_ID}.cfargotunnel.com", "proxied": True, "ttl": 1, }), ], capture_output=True, text=True, timeout=15) try: resp = json.loads(result.stdout) if resp.get("success"): print(f" ✅ Manual DNS created: {hostname}") else: errors = resp.get("errors", []) for e in errors: if "already exists" in str(e).lower() or e.get("code") == 81053: print(f" ⚠️ DNS already exists: {hostname}") return print(f" ❌ Manual DNS failed: {errors}") except Exception: print(f" ❌ API error: {result.stdout[:200]}") def _add_config_ingress(name, hostname, ip): """Add ingress rule to cloudflared config file.""" config_path = "/usr/local/etc/cloudflared/config.yml" # Check if cloudflared is running locally result = subprocess.run( ["pgrep", "-f", "cloudflared"], capture_output=True, text=True ) if result.returncode != 0: print(f" ℹ️ cloudflared not running locally — tunnel is on Proxmox CT680") print(f" ℹ️ Add manually: cloudflared tunnel route dns {HOME_TUNNEL_ID} {hostname}") return # Read existing config try: with open(config_path) as f: config = f.read() except FileNotFoundError: print(f" ℹ️ No local cloudflared config — tunnel managed elsewhere") return # Check if entry exists if f"hostname: {hostname}" in config: print(f" ⚠️ Ingress already in config for {hostname}") return # Add ingress rule before the catch-all new_rule = f""" - hostname: {hostname} service: http://{ip}:80""" if "service: http_status:404" in config: config = config.replace( "service: http_status:404", f"{new_rule}\n - service: http_status:404" ) with open(config_path, "w") as f: f.write(config) print(f" ✅ Ingress added locally for {hostname}") # Restart cloudflared subprocess.run(["brew", "services", "restart", "cloudflared"], capture_output=True, timeout=10) print(f" ✅ cloudflared restarted") def verify_all(): """Verify all sites are accessible via Cloudflare tunnel.""" print("\n\nVerifying all sites...") time.sleep(5) for name in SITES: url = f"https://{name}.{DOMAIN}" result = subprocess.run( ["curl", "-s", "-o", "/dev/null", "-w", "%{http_code}", url], capture_output=True, text=True, timeout=10 ) code = result.stdout.strip() status = "✅" if code in ("200", "301", "302") else "❌" print(f" {status} {url} → HTTP {code}") if __name__ == "__main__": print("=" * 60) print("CLOUDFLARE TUNNEL SETUP — Auto Publisher Sites") print("=" * 60) print(f"\nHome Tunnel: {HOME_TUNNEL_ID}") print(f"Domain: {DOMAIN}") print() add_ingress_rules() verify_all() print(f"\n{'='*60}") print("TUNNEL SETUP COMPLETE") print(f"{'='*60}")