rewrite
This commit is contained in:
222
utils/config_generator.py
Normal file
222
utils/config_generator.py
Normal file
@@ -0,0 +1,222 @@
|
||||
"""HAProxy Config Generator - Build config from database"""
|
||||
|
||||
import os
|
||||
import subprocess
|
||||
import shutil
|
||||
from datetime import datetime
|
||||
from database import db
|
||||
from database.models import VirtualHost, Certificate, HAPROXY_STATS_PORT
|
||||
from config.settings import HAPROXY_CONFIG_PATH, HAPROXY_BACKUP_DIR
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def generate_haproxy_config():
|
||||
"""Generate HAProxy config from database"""
|
||||
config_lines = []
|
||||
|
||||
# ===== GLOBAL SECTION =====
|
||||
config_lines.extend([
|
||||
"# HAProxy Configuration - Auto-generated by HAProxy Manager",
|
||||
f"# Generated: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}",
|
||||
"",
|
||||
"global",
|
||||
" log stdout local0",
|
||||
" log stdout local1 notice",
|
||||
" chroot /var/lib/haproxy",
|
||||
" stats socket /run/haproxy/admin.sock mode 660 level admin",
|
||||
" stats timeout 30s",
|
||||
" user haproxy",
|
||||
" group haproxy",
|
||||
" daemon",
|
||||
" maxconn 4096",
|
||||
" tune.ssl.default-dh-param 2048",
|
||||
"",
|
||||
])
|
||||
|
||||
# ===== DEFAULTS SECTION =====
|
||||
config_lines.extend([
|
||||
"defaults",
|
||||
" log global",
|
||||
" mode http",
|
||||
" option httplog",
|
||||
" option dontlognull",
|
||||
" option http-server-close",
|
||||
" option forwardfor except 127.0.0.0/8",
|
||||
" option redispatch",
|
||||
" retries 3",
|
||||
" timeout connect 5000",
|
||||
" timeout client 50000",
|
||||
" timeout server 50000",
|
||||
" errorfile 400 /etc/haproxy/errors/400.http",
|
||||
" errorfile 403 /etc/haproxy/errors/403.http",
|
||||
" errorfile 408 /etc/haproxy/errors/408.http",
|
||||
" errorfile 500 /etc/haproxy/errors/500.http",
|
||||
" errorfile 502 /etc/haproxy/errors/502.http",
|
||||
" errorfile 503 /etc/haproxy/errors/503.http",
|
||||
" errorfile 504 /etc/haproxy/errors/504.http",
|
||||
"",
|
||||
])
|
||||
|
||||
# ===== STATS FRONTEND (HARDCODED :8404) =====
|
||||
config_lines.extend([
|
||||
"# HAProxy Statistics - Hardcoded on port 8404",
|
||||
"frontend stats",
|
||||
f" bind *:{HAPROXY_STATS_PORT}",
|
||||
" stats enable",
|
||||
" stats admin if TRUE",
|
||||
" stats uri /stats",
|
||||
" stats refresh 30s",
|
||||
"",
|
||||
])
|
||||
|
||||
# ===== FRONTENDS & BACKENDS FROM DATABASE =====
|
||||
vhosts = VirtualHost.query.filter_by(enabled=True).all()
|
||||
|
||||
for vhost in vhosts:
|
||||
# Skip if no backend servers
|
||||
if not vhost.backend_servers:
|
||||
logger.warning(f"[CONFIG] VHost '{vhost.name}' has no backend servers, skipping")
|
||||
continue
|
||||
|
||||
# ===== FRONTEND =====
|
||||
config_lines.append(f"# VHost: {vhost.name}")
|
||||
config_lines.append(f"frontend {vhost.name}")
|
||||
config_lines.append(f" description {vhost.hostname}")
|
||||
config_lines.append(f" bind {vhost.frontend_ip}:{vhost.frontend_port}")
|
||||
|
||||
# SSL config
|
||||
if vhost.use_ssl and vhost.certificate_id:
|
||||
cert = Certificate.query.get(vhost.certificate_id)
|
||||
if cert:
|
||||
# Certificate file path
|
||||
cert_path = f"/app/uploads/certificates/{cert.name}.pem"
|
||||
config_lines.append(f" ssl crt {cert_path}")
|
||||
|
||||
config_lines.append(f" mode {vhost.protocol}")
|
||||
config_lines.append(f" option httplog")
|
||||
|
||||
# Custom headers
|
||||
if vhost.del_server_header:
|
||||
config_lines.append(" http-response del-header Server")
|
||||
|
||||
if vhost.add_custom_header and vhost.custom_header_name:
|
||||
config_lines.append(f" http-response add-header {vhost.custom_header_name} {vhost.custom_header_value}")
|
||||
|
||||
if vhost.forward_for:
|
||||
config_lines.append(" option forwardfor except 127.0.0.0/8")
|
||||
|
||||
# Security rules
|
||||
if vhost.dos_protection:
|
||||
config_lines.extend([
|
||||
f" stick-table type ip size 100k expire {vhost.dos_ban_duration} store http_req_rate(10s)",
|
||||
f" http-request track-sc0 src",
|
||||
f" http-request deny if {{ sc_http_req_rate(0) gt {vhost.dos_limit_requests} }}",
|
||||
])
|
||||
|
||||
if vhost.sql_injection_check:
|
||||
config_lines.append(" # SQL Injection protection enabled")
|
||||
|
||||
if vhost.xss_check:
|
||||
config_lines.append(" # XSS protection enabled")
|
||||
|
||||
if vhost.webshell_check:
|
||||
config_lines.append(" # WebShell protection enabled")
|
||||
|
||||
config_lines.append(f" default_backend {vhost.name}_backend")
|
||||
config_lines.append("")
|
||||
|
||||
# ===== BACKEND =====
|
||||
config_lines.append(f"backend {vhost.name}_backend")
|
||||
config_lines.append(f" balance {vhost.lb_method}")
|
||||
config_lines.append(f" option httpchk GET / HTTP/1.1\\r\\nHost:\\ www")
|
||||
|
||||
# Backend servers
|
||||
for i, server in enumerate(vhost.backend_servers, 1):
|
||||
if not server.enabled:
|
||||
continue
|
||||
|
||||
server_line = f" server {server.name} {server.ip_address}:{server.port}"
|
||||
|
||||
if server.weight != 1:
|
||||
server_line += f" weight {server.weight}"
|
||||
|
||||
if server.maxconn:
|
||||
server_line += f" maxconn {server.maxconn}"
|
||||
|
||||
if server.health_check:
|
||||
server_line += f" check inter 5000 rise 2 fall 3"
|
||||
|
||||
config_lines.append(server_line)
|
||||
|
||||
config_lines.append("")
|
||||
|
||||
return "\n".join(config_lines)
|
||||
|
||||
|
||||
def save_haproxy_config(config_content):
|
||||
"""Save config to file"""
|
||||
try:
|
||||
# Create backup
|
||||
if os.path.exists(HAPROXY_CONFIG_PATH):
|
||||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||
backup_path = os.path.join(HAPROXY_BACKUP_DIR, f'haproxy_backup_{timestamp}.cfg')
|
||||
shutil.copy2(HAPROXY_CONFIG_PATH, backup_path)
|
||||
logger.info(f"[CONFIG] Backup created: {backup_path}", flush=True)
|
||||
|
||||
# Write new config
|
||||
with open(HAPROXY_CONFIG_PATH, 'w') as f:
|
||||
f.write(config_content)
|
||||
|
||||
logger.info(f"[CONFIG] Config saved to {HAPROXY_CONFIG_PATH}", flush=True)
|
||||
return True
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"[CONFIG] Error saving config: {e}", flush=True)
|
||||
return False
|
||||
|
||||
|
||||
def reload_haproxy():
|
||||
"""Reload HAProxy service"""
|
||||
try:
|
||||
# Generate config
|
||||
config_content = generate_haproxy_config()
|
||||
|
||||
# Test config syntax
|
||||
result = subprocess.run(
|
||||
['haproxy', '-c', '-f', HAPROXY_CONFIG_PATH],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=5
|
||||
)
|
||||
|
||||
if result.returncode != 0:
|
||||
logger.error(f"[CONFIG] HAProxy syntax error: {result.stderr}", flush=True)
|
||||
return False
|
||||
|
||||
# Save config
|
||||
if not save_haproxy_config(config_content):
|
||||
return False
|
||||
|
||||
# Reload HAProxy
|
||||
result = subprocess.run(
|
||||
['systemctl', 'reload', 'haproxy'],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=10
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
logger.info("[CONFIG] HAProxy reloaded successfully", flush=True)
|
||||
return True
|
||||
else:
|
||||
logger.error(f"[CONFIG] HAProxy reload failed: {result.stderr}", flush=True)
|
||||
return False
|
||||
|
||||
except subprocess.TimeoutExpired:
|
||||
logger.error("[CONFIG] HAProxy reload timeout", flush=True)
|
||||
return False
|
||||
except Exception as e:
|
||||
logger.error(f"[CONFIG] Error reloading HAProxy: {e}", flush=True)
|
||||
return False
|
||||
Reference in New Issue
Block a user