Files
auto-publisher/core/cloudflare_setup.py

185 lines
5.9 KiB
Python
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
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}")