python/Caddy/addEntry.py
2025-03-10 14:24:56 +01:00

191 lines
6.5 KiB
Python
Executable file
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.

#!/usr/bin/env python3
import os
import re
def get_user_input(prompt, default=None):
"""Interactive prompt with default values"""
value = input(f"{prompt} [{default}]: ") or default
return value
def find_caddyfile():
"""Check if the Caddyfile exists, otherwise ask for the path"""
default_path = "./conf/Caddyfile"
if os.path.exists(default_path):
return default_path
print("⚠ No Caddyfile found!")
while True:
custom_path = get_user_input("Enter the path to your Caddyfile")
if os.path.exists(custom_path):
return custom_path
print("❌ File not found, please try again.")
def parse_existing_entries(caddyfile_path):
"""Parse the existing Caddyfile to extract all configured domains"""
existing_entries = {}
try:
with open(caddyfile_path, "r") as file:
content = file.read()
# Regex to find domain blocks and associated reverse proxy targets
pattern = re.compile(r"(?P<domains>[^\s{]+(?:,\s*[^\s{]+)*)\s*{.*?reverse_proxy\s+(?P<target>https?:\/\/[\d\.]+:\d+|[\d\.]+:\d+).*?}", re.DOTALL)
matches = pattern.findall(content)
for domains, target in matches:
for domain in domains.split(", "):
existing_entries[domain] = target.strip()
except Exception as e:
print(f"❌ Error reading Caddyfile: {e}")
return existing_entries
def format_caddy_entry(domains, target_ip, target_port, proxy_type):
"""Generate a properly formatted Caddy entry based on proxy type"""
domain_list = ", ".join(domains) # Multiple domains in a single line
if proxy_type == "standard":
return f"""
{domain_list} {{
tls {{
dns cloudflare {{env.CLOUDFLARE_API_TOKEN}}
}}
reverse_proxy {target_ip}:{target_port}
}}
"""
elif proxy_type == "https_skip_verify":
return f"""
{domain_list} {{
tls {{
dns cloudflare {{env.CLOUDFLARE_API_TOKEN}}
}}
reverse_proxy https://{target_ip}:{target_port} {{
transport http {{
tls
tls_insecure_skip_verify
}}
}}
}}
"""
elif proxy_type == "opnsense":
return f"""
{domain_list} {{
tls {{
dns cloudflare {{env.CLOUDFLARE_API_TOKEN}}
}}
reverse_proxy https://{target_ip}:{target_port} {{
transport http {{
tls
tls_insecure_skip_verify
versions h1.1 # Enforce HTTP/1.1
}}
header_up Host {{host}}
header_up X-Real-IP {{remote_host}}
header_up X-Forwarded-Proto {{scheme}}
header_up X-Forwarded-For {{remote}}
header_down Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
# Remove problematic headers
header_up -Connection
header_up -Upgrade
}}
}}
"""
def update_existing_entry(caddyfile_path, domain, new_entry):
"""Replace an existing entry for the given domain"""
try:
with open(caddyfile_path, "r") as file:
content = file.read()
# Find and replace the domain entry
pattern = re.compile(rf"({domain}\s*\{{.*?reverse_proxy\s+[^\n]+\n.*?\}})", re.DOTALL)
content = pattern.sub(new_entry.strip(), content)
with open(caddyfile_path, "w") as file:
file.write(content)
print(f"✅ Updated entry for {domain}")
except Exception as e:
print(f"❌ Error updating Caddyfile: {e}")
def add_caddy_entry(caddyfile_path):
"""Add new Caddy reverse proxy entries, showing existing entries first"""
existing_entries = parse_existing_entries(caddyfile_path)
print("\n📌 Existing Caddy Entries:")
if existing_entries:
for domain, target in existing_entries.items():
print(f" 🔹 {domain}{target}")
else:
print(" ⚠ No entries found.")
while True:
domain = get_user_input("\nEnter the domain you want to configure", "")
if not domain:
print("❌ No domain provided. Skipping entry.")
continue
# If domain exists, extract its current values
if domain in existing_entries:
print(f"⚠ The domain {domain} already exists.")
edit_existing = get_user_input("Do you want to edit this entry? (y/n)", "y").lower() == "y"
if not edit_existing:
continue
existing_target = existing_entries[domain]
existing_ip, existing_port = existing_target.replace("https://", "").replace("http://", "").split(":")
else:
existing_ip, existing_port = "192.168.1.100", "8080"
target_ip = get_user_input("Enter the target IP", existing_ip)
target_port = get_user_input("Enter the target port", existing_port)
print("\nChoose the proxy mode:")
print("1⃣ Standard (No HTTPS changes)")
print("2⃣ Internal HTTPS (skip verify)")
print("3⃣ OPNsense Mode (skip verify + enforce HTTP/1.1)")
# Pre-fill proxy type if editing
mode_choice_default = "1"
if "https://" in existing_entries.get(domain, "") and "tls_insecure_skip_verify" in existing_entries.get(domain, ""):
mode_choice_default = "3" if "versions h1.1" in existing_entries.get(domain, "") else "2"
mode_choice = get_user_input("Enter option (1/2/3)", mode_choice_default)
proxy_type = "standard"
if mode_choice == "2":
proxy_type = "https_skip_verify"
elif mode_choice == "3":
proxy_type = "opnsense"
new_entry = format_caddy_entry([domain], target_ip, target_port, proxy_type)
if domain in existing_entries:
update_existing_entry(caddyfile_path, domain, new_entry)
else:
try:
with open(caddyfile_path, "a") as file:
file.write(new_entry)
print(f"\n✅ New entry added: {domain}{target_ip}:{target_port}")
except Exception as e:
print(f"\n❌ Error writing to Caddyfile: {e}")
return
# Ask if another entry should be added
more_entries = get_user_input("\nDo you want to add or edit another entry? (y/n)", "n").lower() == "y"
if not more_entries:
break
# Restart Caddy container
restart_caddy = get_user_input("\nDo you want to restart the Caddy container? (y/n)", "y").lower() == "y"
if restart_caddy:
os.system("docker compose restart caddy")
print("🔄 Caddy container restarted!")
if __name__ == "__main__":
caddyfile_path = find_caddyfile()
add_caddy_entry(caddyfile_path)