145 lines
4.6 KiB
Python
145 lines
4.6 KiB
Python
|
|
import requests
|
||
|
|
import json
|
||
|
|
import os
|
||
|
|
|
||
|
|
# --- CONFIGURATION ---
|
||
|
|
# Set these via environment variables or edit directly before use
|
||
|
|
ZONE_ID = os.getenv("CF_ZONE_ID", "YOUR_ZONE_ID_HERE")
|
||
|
|
API_TOKEN = os.getenv("CF_API_TOKEN", "YOUR_API_TOKEN_HERE")
|
||
|
|
# ---------------------
|
||
|
|
|
||
|
|
BASE_URL = "https://api.cloudflare.com/client/v4"
|
||
|
|
HEADERS = {
|
||
|
|
"Authorization": f"Bearer {API_TOKEN}",
|
||
|
|
"Content-Type": "application/json"
|
||
|
|
}
|
||
|
|
|
||
|
|
def validate_config():
|
||
|
|
"""Validates that credentials are configured"""
|
||
|
|
if ZONE_ID == "YOUR_ZONE_ID_HERE" or not ZONE_ID:
|
||
|
|
print("ERROR: Cloudflare Zone ID not configured!")
|
||
|
|
print("Set CF_ZONE_ID environment variable or edit ZONE_ID in this script")
|
||
|
|
print("Example: export CF_ZONE_ID='your_zone_id_here'")
|
||
|
|
return False
|
||
|
|
|
||
|
|
if API_TOKEN == "YOUR_API_TOKEN_HERE" or not API_TOKEN:
|
||
|
|
print("ERROR: Cloudflare API Token not configured!")
|
||
|
|
print("Set CF_API_TOKEN environment variable or edit API_TOKEN in this script")
|
||
|
|
print("Example: export CF_API_TOKEN='your_token_here'")
|
||
|
|
return False
|
||
|
|
|
||
|
|
return True
|
||
|
|
|
||
|
|
def get_paged_data(endpoint):
|
||
|
|
"""Generic function to handle pagination for lists (like DNS records)"""
|
||
|
|
url = f"{BASE_URL}/{endpoint}"
|
||
|
|
items = []
|
||
|
|
page = 1
|
||
|
|
|
||
|
|
while True:
|
||
|
|
params = {'page': page, 'per_page': 100}
|
||
|
|
try:
|
||
|
|
response = requests.get(url, headers=HEADERS, params=params)
|
||
|
|
response.raise_for_status()
|
||
|
|
data = response.json()
|
||
|
|
|
||
|
|
if not data['success']:
|
||
|
|
print(f"Error fetching {endpoint}: {data['errors']}")
|
||
|
|
break
|
||
|
|
|
||
|
|
current_batch = data['result']
|
||
|
|
if not current_batch:
|
||
|
|
break
|
||
|
|
|
||
|
|
items.extend(current_batch)
|
||
|
|
|
||
|
|
# Check pagination info if it exists
|
||
|
|
if 'result_info' in data and page < data['result_info']['total_pages']:
|
||
|
|
page += 1
|
||
|
|
else:
|
||
|
|
break
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
print(f"Request failed for {endpoint}: {e}")
|
||
|
|
break
|
||
|
|
|
||
|
|
return items
|
||
|
|
|
||
|
|
def get_zone_settings(zone_id):
|
||
|
|
"""Fetches key SSL and Network settings"""
|
||
|
|
print(f"Fetching Zone Settings for {zone_id}...")
|
||
|
|
url = f"{BASE_URL}/zones/{zone_id}/settings"
|
||
|
|
|
||
|
|
try:
|
||
|
|
response = requests.get(url, headers=HEADERS)
|
||
|
|
response.raise_for_status()
|
||
|
|
data = response.json()
|
||
|
|
|
||
|
|
if not data['success']:
|
||
|
|
print(f"Error fetching settings: {data['errors']}")
|
||
|
|
return {}
|
||
|
|
|
||
|
|
# We convert the list of settings into a dictionary for easier reading
|
||
|
|
# The API returns a list like [{"id": "ssl", "value": "strict"}, ...]
|
||
|
|
settings_map = {item['id']: item['value'] for item in data['result']}
|
||
|
|
|
||
|
|
# Filter for the ones that actually matter for Homelabs
|
||
|
|
relevant_keys = [
|
||
|
|
"ssl", # The encryption mode (off, flexible, full, strict)
|
||
|
|
"always_use_https", # Force HTTPS redirect
|
||
|
|
"min_tls_version", # Can break old hardware/OS
|
||
|
|
"security_level", # "I'm Under Attack" mode breaks APIs
|
||
|
|
"pseudo_ipv4", # Header modification
|
||
|
|
"websockets", # Critical for some apps
|
||
|
|
"cname_flattening" # DNS behavior
|
||
|
|
]
|
||
|
|
|
||
|
|
return {k: settings_map.get(k, "UNKNOWN") for k in relevant_keys}
|
||
|
|
|
||
|
|
except Exception as e:
|
||
|
|
print(f"Failed to fetch settings: {e}")
|
||
|
|
return {}
|
||
|
|
|
||
|
|
def main():
|
||
|
|
# Validate configuration first
|
||
|
|
if not validate_config():
|
||
|
|
return
|
||
|
|
|
||
|
|
# 1. Fetch DNS Records
|
||
|
|
print("Fetching DNS Records...")
|
||
|
|
raw_dns = get_paged_data(f"zones/{ZONE_ID}/dns_records")
|
||
|
|
|
||
|
|
clean_dns = []
|
||
|
|
for r in raw_dns:
|
||
|
|
clean_dns.append({
|
||
|
|
"name": r.get("name"),
|
||
|
|
"type": r.get("type"),
|
||
|
|
"content": r.get("content"),
|
||
|
|
"proxied": r.get("proxied"),
|
||
|
|
"ttl": r.get("ttl")
|
||
|
|
})
|
||
|
|
|
||
|
|
# 2. Fetch Zone Settings (SSL, etc.)
|
||
|
|
zone_settings = get_zone_settings(ZONE_ID)
|
||
|
|
|
||
|
|
# 3. Combine into one object
|
||
|
|
full_export = {
|
||
|
|
"metadata": {
|
||
|
|
"zone_id": ZONE_ID,
|
||
|
|
"export_date": "Now"
|
||
|
|
},
|
||
|
|
"zone_settings": zone_settings,
|
||
|
|
"dns_records": clean_dns
|
||
|
|
}
|
||
|
|
|
||
|
|
filename = "cloudflare_full_config.json"
|
||
|
|
with open(filename, 'w') as f:
|
||
|
|
json.dump(full_export, f, indent=2)
|
||
|
|
|
||
|
|
print(f"\nSuccess! Configuration saved to {filename}")
|
||
|
|
print(f"Found {len(clean_dns)} DNS records.")
|
||
|
|
print(f"SSL Mode detected: {zone_settings.get('ssl', 'unknown')}")
|
||
|
|
|
||
|
|
if __name__ == "__main__":
|
||
|
|
main()
|