fix: host
This commit is contained in:
parent
1ded54588d
commit
c8ae3809da
1 changed files with 166 additions and 26 deletions
|
@ -1,6 +1,19 @@
|
||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
import socket
|
||||||
|
|
||||||
|
def get_host_ip():
|
||||||
|
"""Get the host IP address"""
|
||||||
|
try:
|
||||||
|
# Create a socket to determine the outgoing IP address
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
s.connect(("8.8.8.8", 80)) # Connect to Google DNS (doesn't send data)
|
||||||
|
host_ip = s.getsockname()[0]
|
||||||
|
s.close()
|
||||||
|
return host_ip
|
||||||
|
except Exception:
|
||||||
|
return "127.0.0.1" # Fallback to localhost
|
||||||
|
|
||||||
def get_user_input(prompt, default=None):
|
def get_user_input(prompt, default=None):
|
||||||
"""Interactive prompt with default values"""
|
"""Interactive prompt with default values"""
|
||||||
|
@ -27,16 +40,113 @@ def parse_existing_entries(caddyfile_path):
|
||||||
with open(caddyfile_path, "r") as file:
|
with open(caddyfile_path, "r") as file:
|
||||||
content = file.read()
|
content = file.read()
|
||||||
|
|
||||||
# Regex to find domain blocks and associated reverse proxy targets
|
# First, normalize the content to make parsing more reliable
|
||||||
pattern = re.compile(r"(?P<domains>[^\s{]+(?:,\s*[^\s{]+)*)\s*{.*?reverse_proxy\s+(?P<target>https?:\/\/[\d\.]+:\d+|[\d\.]+:\d+).*?}", re.DOTALL)
|
# This removes comments and normalizes whitespace
|
||||||
matches = pattern.findall(content)
|
lines = []
|
||||||
|
in_comment = False
|
||||||
for domains, target in matches:
|
for line in content.splitlines():
|
||||||
for domain in domains.split(", "):
|
line = line.strip()
|
||||||
existing_entries[domain] = target.strip()
|
if not line or line.startswith('#'):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Handle inline comments
|
||||||
|
if '#' in line and not in_comment:
|
||||||
|
line = line[:line.index('#')].strip()
|
||||||
|
|
||||||
|
lines.append(line)
|
||||||
|
|
||||||
|
normalized_content = '\n'.join(lines)
|
||||||
|
|
||||||
|
# Use brace matching to properly extract domain blocks
|
||||||
|
blocks = []
|
||||||
|
current_position = 0
|
||||||
|
|
||||||
|
while current_position < len(normalized_content):
|
||||||
|
# Find the next domain block start
|
||||||
|
block_start = normalized_content.find('{', current_position)
|
||||||
|
if block_start == -1:
|
||||||
|
break
|
||||||
|
|
||||||
|
# Find corresponding domain definition
|
||||||
|
domain_start = normalized_content.rfind('\n', 0, block_start)
|
||||||
|
if domain_start == -1:
|
||||||
|
domain_start = 0
|
||||||
|
else:
|
||||||
|
domain_start += 1 # Skip the newline
|
||||||
|
|
||||||
|
domain_def = normalized_content[domain_start:block_start].strip()
|
||||||
|
|
||||||
|
# Find end of this block (accounting for nested braces)
|
||||||
|
brace_count = 1
|
||||||
|
block_end = block_start + 1
|
||||||
|
|
||||||
|
while brace_count > 0 and block_end < len(normalized_content):
|
||||||
|
if normalized_content[block_end] == '{':
|
||||||
|
brace_count += 1
|
||||||
|
elif normalized_content[block_end] == '}':
|
||||||
|
brace_count -= 1
|
||||||
|
block_end += 1
|
||||||
|
|
||||||
|
if brace_count == 0:
|
||||||
|
# We found a complete block
|
||||||
|
block_content = normalized_content[domain_start:block_end]
|
||||||
|
|
||||||
|
# Only process blocks with reverse_proxy directives
|
||||||
|
if 'reverse_proxy' in block_content:
|
||||||
|
blocks.append((domain_def, block_content))
|
||||||
|
|
||||||
|
current_position = block_end
|
||||||
|
|
||||||
|
# Process the extracted blocks
|
||||||
|
for domain_def, block_content in blocks:
|
||||||
|
# Extract target from reverse_proxy directive
|
||||||
|
proxy_match = re.search(r'reverse_proxy\s+(https?:\/\/[\d\.]+:\d+|[\d\.]+:\d+)', block_content)
|
||||||
|
if not proxy_match:
|
||||||
|
continue
|
||||||
|
|
||||||
|
target = proxy_match.group(1).strip()
|
||||||
|
|
||||||
|
# Process domains (handle comma-separated lists correctly)
|
||||||
|
domains = [d.strip() for d in domain_def.split(',')]
|
||||||
|
|
||||||
|
# Process each domain
|
||||||
|
for domain in domains:
|
||||||
|
# Skip if it looks like a directive rather than a domain
|
||||||
|
if '{' in domain or '}' in domain or not domain:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Skip literal "Host" that are likely from host header directives rather than domains
|
||||||
|
if domain == "Host" or domain == "{host}":
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Verify domain format (basic check)
|
||||||
|
if not re.match(r'^[a-zA-Z0-9][-a-zA-Z0-9.]*[a-zA-Z0-9]$', domain) and not domain.startswith('*.'):
|
||||||
|
print(f"⚠️ Skipping invalid domain format: '{domain}'")
|
||||||
|
continue
|
||||||
|
|
||||||
|
# Determine proxy type
|
||||||
|
proxy_type = 'standard'
|
||||||
|
if "https://" in target and "tls_insecure_skip_verify" in block_content:
|
||||||
|
if "versions h1.1" in block_content:
|
||||||
|
proxy_type = 'opnsense'
|
||||||
|
else:
|
||||||
|
proxy_type = 'https_skip_verify'
|
||||||
|
|
||||||
|
# Store the entry
|
||||||
|
existing_entries[domain] = {
|
||||||
|
'target': target,
|
||||||
|
'content': block_content,
|
||||||
|
'proxy_type': proxy_type
|
||||||
|
}
|
||||||
|
|
||||||
|
# Debug output for special cases
|
||||||
|
if domain.lower() == "host":
|
||||||
|
print(f"⚠️ Warning: Found domain named 'host': {domain}")
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ Error reading Caddyfile: {e}")
|
print(f"❌ Error reading Caddyfile: {e}")
|
||||||
|
import traceback
|
||||||
|
print(traceback.format_exc())
|
||||||
|
|
||||||
return existing_entries
|
return existing_entries
|
||||||
|
|
||||||
|
@ -98,26 +208,39 @@ def update_existing_entry(caddyfile_path, domain, new_entry):
|
||||||
with open(caddyfile_path, "r") as file:
|
with open(caddyfile_path, "r") as file:
|
||||||
content = file.read()
|
content = file.read()
|
||||||
|
|
||||||
# Find and replace the domain entry
|
# New improved pattern to correctly match complete domain blocks
|
||||||
pattern = re.compile(rf"({domain}\s*\{{.*?reverse_proxy\s+[^\n]+\n.*?\}})", re.DOTALL)
|
# This regex matches the domain block from start to finish, including all braces
|
||||||
content = pattern.sub(new_entry.strip(), content)
|
domain_pattern = fr'(?m)^(?:(?:{re.escape(domain)}|[^{{,\s]+(?:,\s*{re.escape(domain)})(?:,\s*[^{{,\s]+)*|{re.escape(domain)}(?:,\s*[^{{,\s]+)+))\s*{{(?:[^{{}}]|{{(?:[^{{}}]|{{[^{{}}]*}})*}})*}}'
|
||||||
|
|
||||||
with open(caddyfile_path, "w") as file:
|
pattern = re.compile(domain_pattern, re.DOTALL)
|
||||||
file.write(content)
|
match = pattern.search(content)
|
||||||
|
|
||||||
print(f"✅ Updated entry for {domain}")
|
if match:
|
||||||
|
# Replace the block containing this domain with the new entry
|
||||||
|
new_content = content[:match.start()] + new_entry.strip() + content[match.end():]
|
||||||
|
|
||||||
|
with open(caddyfile_path, "w") as file:
|
||||||
|
file.write(new_content)
|
||||||
|
|
||||||
|
print(f"✅ Updated entry for {domain}")
|
||||||
|
else:
|
||||||
|
print(f"⚠ Could not find exact entry for {domain}. Adding as new entry.")
|
||||||
|
with open(caddyfile_path, "a") as file:
|
||||||
|
file.write(new_entry)
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"❌ Error updating Caddyfile: {e}")
|
print(f"❌ Error updating Caddyfile: {e}")
|
||||||
|
print(f"Error details: {str(e)}")
|
||||||
|
|
||||||
def add_caddy_entry(caddyfile_path):
|
def add_caddy_entry(caddyfile_path):
|
||||||
"""Add new Caddy reverse proxy entries, showing existing entries first"""
|
"""Add new Caddy reverse proxy entries, showing existing entries first"""
|
||||||
|
host_ip = get_host_ip()
|
||||||
existing_entries = parse_existing_entries(caddyfile_path)
|
existing_entries = parse_existing_entries(caddyfile_path)
|
||||||
|
|
||||||
print("\n📌 Existing Caddy Entries:")
|
print("\n📌 Existing Caddy Entries:")
|
||||||
if existing_entries:
|
if existing_entries:
|
||||||
for domain, target in existing_entries.items():
|
for domain, data in existing_entries.items():
|
||||||
print(f" 🔹 {domain} → {target}")
|
print(f" 🔹 {domain} → {data['target']}")
|
||||||
else:
|
else:
|
||||||
print(" ⚠ No entries found.")
|
print(" ⚠ No entries found.")
|
||||||
|
|
||||||
|
@ -129,19 +252,34 @@ def add_caddy_entry(caddyfile_path):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
# If domain exists, extract its current values
|
# If domain exists, extract its current values
|
||||||
|
existing_ip = host_ip
|
||||||
|
existing_port = "8080"
|
||||||
|
proxy_type = "standard"
|
||||||
|
|
||||||
if domain in existing_entries:
|
if domain in existing_entries:
|
||||||
print(f"⚠ The domain {domain} already exists.")
|
print(f"⚠ The domain {domain} already exists.")
|
||||||
edit_existing = get_user_input("Do you want to edit this entry? (y/n)", "y").lower() == "y"
|
edit_existing = get_user_input("Do you want to edit this entry? (y/n)", "y").lower() == "y"
|
||||||
if not edit_existing:
|
if not edit_existing:
|
||||||
continue
|
continue
|
||||||
|
|
||||||
existing_target = existing_entries[domain]
|
existing_target = existing_entries[domain]['target']
|
||||||
existing_ip, existing_port = existing_target.replace("https://", "").replace("http://", "").split(":")
|
proxy_type = existing_entries[domain]['proxy_type']
|
||||||
|
target_without_protocol = existing_target.replace("https://", "").replace("http://", "")
|
||||||
|
|
||||||
|
if ":" in target_without_protocol:
|
||||||
|
existing_ip, existing_port = target_without_protocol.split(":")
|
||||||
|
else:
|
||||||
|
existing_ip = target_without_protocol
|
||||||
|
existing_port = "80"
|
||||||
|
|
||||||
else:
|
# Show host IP as an option
|
||||||
existing_ip, existing_port = "192.168.1.100", "8080"
|
target_ip_prompt = f"Enter the target IP (type 'host' for {host_ip})"
|
||||||
|
target_ip = get_user_input(target_ip_prompt, existing_ip)
|
||||||
target_ip = get_user_input("Enter the target IP", existing_ip)
|
|
||||||
|
# Replace 'host' with actual host IP
|
||||||
|
if target_ip.lower() == 'host':
|
||||||
|
target_ip = host_ip
|
||||||
|
|
||||||
target_port = get_user_input("Enter the target port", existing_port)
|
target_port = get_user_input("Enter the target port", existing_port)
|
||||||
|
|
||||||
print("\nChoose the proxy mode:")
|
print("\nChoose the proxy mode:")
|
||||||
|
@ -149,10 +287,12 @@ def add_caddy_entry(caddyfile_path):
|
||||||
print("2️⃣ Internal HTTPS (skip verify)")
|
print("2️⃣ Internal HTTPS (skip verify)")
|
||||||
print("3️⃣ OPNsense Mode (skip verify + enforce HTTP/1.1)")
|
print("3️⃣ OPNsense Mode (skip verify + enforce HTTP/1.1)")
|
||||||
|
|
||||||
# Pre-fill proxy type if editing
|
# Pre-fill proxy type based on detected configuration
|
||||||
mode_choice_default = "1"
|
mode_choice_default = "1"
|
||||||
if "https://" in existing_entries.get(domain, "") and "tls_insecure_skip_verify" in existing_entries.get(domain, ""):
|
if proxy_type == "https_skip_verify":
|
||||||
mode_choice_default = "3" if "versions h1.1" in existing_entries.get(domain, "") else "2"
|
mode_choice_default = "2"
|
||||||
|
elif proxy_type == "opnsense":
|
||||||
|
mode_choice_default = "3"
|
||||||
|
|
||||||
mode_choice = get_user_input("Enter option (1/2/3)", mode_choice_default)
|
mode_choice = get_user_input("Enter option (1/2/3)", mode_choice_default)
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue