From 2d53843f342737b408220426248bad5fd833978f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Mon, 3 Nov 2025 12:17:32 +0100 Subject: [PATCH] new options --- routes/main_routes.py | 21 ++-- templates/index.html | 51 ++++------ utils/haproxy_config.py | 209 ++++++++++++++++++---------------------- 3 files changed, 120 insertions(+), 161 deletions(-) diff --git a/routes/main_routes.py b/routes/main_routes.py index 4b5b0ec..7e88104 100644 --- a/routes/main_routes.py +++ b/routes/main_routes.py @@ -1,12 +1,10 @@ -from flask import Blueprint, render_template, request, flash +from flask import Blueprint, render_template, request import subprocess from auth.auth_middleware import requires_auth -from utils.haproxy_config import update_haproxy_config, is_frontend_exist, count_frontends_and_backends +from utils.haproxy_config import update_haproxy_config, count_frontends_and_backends main_bp = Blueprint('main', __name__) -import subprocess - def reload_haproxy(): """Reload HAProxy by killing it - supervisord restarts automatically""" try: @@ -37,20 +35,19 @@ def reload_haproxy(): else: print(f"[HAPROXY] pkill failed: {result.stdout}", flush=True) return False, f"pkill failed: {result.stdout}" - except Exception as e: print(f"[HAPROXY] Error: {e}", flush=True) return False, f"Error: {str(e)}" - @main_bp.route('/', methods=['GET', 'POST']) @requires_auth def index(): if request.method == 'POST': - frontend_name = request.form['frontend_name'] + # Frontend IP i port (używane do generowania nazwy frontendu) frontend_ip = request.form['frontend_ip'] frontend_port = request.form['frontend_port'] frontend_hostname = request.form.get('frontend_hostname', '').strip() + lb_method = request.form['lb_method'] protocol = request.form['protocol'] backend_name = request.form['backend_name'] @@ -130,12 +127,6 @@ def index(): if ip and port: backend_servers.append((name, ip, port, maxconn)) - # Validate frontend existence - if is_frontend_exist(frontend_name, frontend_ip, frontend_port): - return render_template('index.html', - message="Frontend or Port already exists. Cannot add duplicate.", - message_type="danger") - # Health checks health_check = False health_check_link = "" @@ -161,6 +152,10 @@ def index(): acl_action = '' acl_backend_name = '' + # ===== GENERUJ FRONTEND NAME ZAMIAST PRZYJMOWAĆ OD USERA ===== + # frontend_name będzie generowany w haproxy_config.py + frontend_name = None # Będzie wygenerowany automatycznie + # Call update_haproxy_config message = update_haproxy_config( frontend_name=frontend_name, diff --git a/templates/index.html b/templates/index.html index b696289..452f664 100644 --- a/templates/index.html +++ b/templates/index.html @@ -13,7 +13,6 @@ {% endblock %} - {% block content %}
@@ -67,32 +66,24 @@
-
Frontend
+
Frontend Configuration
- - -
-
- +
- +
-
- -
-
- +
+ -
Domain name for the ACL rule - traffic will be matched by Host header
+ Frontend name will be generated automatically
@@ -114,7 +105,6 @@ -
Choose load balancing algorithm or simple single host
@@ -135,7 +125,7 @@ -
Full path to .pem file, upload certs in /ssl/
+ Full path to .pem file
@@ -157,24 +147,23 @@ -
Creates additional frontend on port 80 to redirect HTTP to HTTPS backend
+ Creates additional frontend on port 80
- + -
Name for the redirect backend that will push traffic to HTTPS
+ name="ssl_redirect_backend_name" placeholder="e.g. redirect">

-
Backend
+
Backend Configuration
@@ -297,9 +286,7 @@ -
- Adds: http-response del-header Server (security) -
+ Adds: http-response del-header Server
@@ -336,7 +323,7 @@ -
e.g. 30m, 1h, 24h
+ e.g. 30m, 1h, 24h
@@ -398,7 +385,7 @@
- +
Custom ACL Rules (Advanced)
@@ -408,18 +395,17 @@ -
Create additional routing or blocking rules beyond hostname matching
+ Create additional routing or blocking rules
- +
-
Unique identifier for this rule
@@ -437,8 +423,7 @@
-
Value to match (path, header name, IP, method)
+ placeholder="e.g. /admin, api, 192.168.1.0/24">
@@ -454,14 +439,12 @@ -
Backend to send matching traffic to
-
URL to redirect matching requests to
diff --git a/utils/haproxy_config.py b/utils/haproxy_config.py index df18e6a..9b85215 100644 --- a/utils/haproxy_config.py +++ b/utils/haproxy_config.py @@ -2,29 +2,84 @@ import os HAPROXY_CFG = '/etc/haproxy/haproxy.cfg' -def is_frontend_exist(frontend_name, frontend_ip, frontend_port): +def sanitize_name(name): + """Convert hostname/name to valid ACL name""" + return name.replace('.', '_').replace('-', '_').replace('/', '_').replace(':', '_') + +def frontend_exists_at_port(frontend_ip, frontend_port): + """Check if frontend already exists at specific port""" + if not os.path.exists(HAPROXY_CFG): + return None + + try: + with open(HAPROXY_CFG, 'r') as f: + content = f.read() + lines = content.split('\n') + + for i, line in enumerate(lines): + if line.strip().startswith('frontend'): + # Szukaj bind line + for j in range(i+1, min(i+10, len(lines))): + if lines[j].strip().startswith('bind'): + bind_info = lines[j].strip().split(' ', 1)[1] + if f"{frontend_ip}:{frontend_port}" in bind_info: + return line.strip().split(' ', 1)[1] # Zwróć nazwę frontendu + elif lines[j].strip().startswith('frontend') or lines[j].strip().startswith('backend'): + break + except Exception as e: + print(f"[HAPROXY_CONFIG] Error: {e}", flush=True) + + return None + +def add_acl_to_frontend(frontend_name, acl_name, hostname, backend_name): + """Dodaj ACL i use_backend do istniejącego frontendu""" if not os.path.exists(HAPROXY_CFG): return False try: - with open(HAPROXY_CFG, 'r') as haproxy_cfg: - frontend_found = False - for line in haproxy_cfg: - if line.strip().startswith('frontend'): - _, existing_frontend_name = line.strip().split(' ', 1) - if existing_frontend_name.strip() == frontend_name: - frontend_found = True - else: - frontend_found = False - elif frontend_found and line.strip().startswith('bind'): - _, bind_info = line.strip().split(' ', 1) - existing_ip, existing_port = bind_info.split(':', 1) - if existing_ip.strip() == frontend_ip and existing_port.strip() == frontend_port: - return True + with open(HAPROXY_CFG, 'r') as f: + lines = f.readlines() + + # Znajdź frontend + frontend_idx = -1 + for i, line in enumerate(lines): + if 'frontend' in line and frontend_name in line: + frontend_idx = i + break + + if frontend_idx == -1: + return False + + # Sprawdź czy ACL już istnieje + for line in lines[frontend_idx:]: + if acl_name in line and 'acl' in line: + return True # Już istnieje + if line.strip().startswith('backend'): + break + + # Znajdź ostatnią linię ACL/use_backend w tym frontendzie + insert_idx = frontend_idx + 1 + for i in range(frontend_idx + 1, len(lines)): + if lines[i].strip().startswith('backend'): + insert_idx = i + break + if 'use_backend' in lines[i] or 'default_backend' in lines[i]: + insert_idx = i + 1 + + # Wstaw ACL i use_backend + acl_line = f" acl {acl_name} hdr(host) -i {hostname}\n" + use_backend_line = f" use_backend {backend_name} if {acl_name}\n" + + lines.insert(insert_idx, use_backend_line) + lines.insert(insert_idx, acl_line) + + with open(HAPROXY_CFG, 'w') as f: + f.writelines(lines) + + return True except Exception as e: - print(f"[HAPROXY_CONFIG] Error checking frontend: {e}", flush=True) - - return False + print(f"[HAPROXY_CONFIG] Error adding ACL: {e}", flush=True) + return False def is_backend_exist(backend_name): if not os.path.exists(HAPROXY_CFG): @@ -75,85 +130,6 @@ def count_frontends_and_backends(): return frontend_count, backend_count, acl_count, layer7_count, layer4_count -def sanitize_name(name): - """Convert hostname/name to valid ACL name""" - return name.replace('.', '_').replace('-', '_').replace('/', '_').replace(':', '_') - -def frontend_exists_at_port(frontend_ip, frontend_port): - """Check if frontend already exists at specific port""" - if not os.path.exists(HAPROXY_CFG): - return None - - try: - with open(HAPROXY_CFG, 'r') as f: - content = f.read() - lines = content.split('\n') - - for i, line in enumerate(lines): - if line.strip().startswith('frontend'): - frontend_name = line.strip().split(' ', 1)[1] - for j in range(i+1, min(i+10, len(lines))): - if lines[j].strip().startswith('bind'): - bind_info = lines[j].strip().split(' ', 1)[1] - if f"{frontend_ip}:{frontend_port}" in bind_info: - return frontend_name - elif lines[j].strip().startswith('frontend') or lines[j].strip().startswith('backend'): - break - except Exception as e: - print(f"[HAPROXY_CONFIG] Error: {e}", flush=True) - - return None - -def add_acl_to_frontend(frontend_name, acl_name, hostname, backend_name): - """Dodaj ACL i use_backend do istniejącego frontendu""" - if not os.path.exists(HAPROXY_CFG): - return False - - try: - with open(HAPROXY_CFG, 'r') as f: - content = f.read() - - lines = content.split('\n') - - frontend_idx = -1 - for i, line in enumerate(lines): - if line.strip().startswith('frontend') and frontend_name in line: - frontend_idx = i - break - - if frontend_idx == -1: - return False - - insert_idx = -1 - for i in range(frontend_idx + 1, len(lines)): - if lines[i].strip().startswith('backend'): - insert_idx = i - break - if 'use_backend' in lines[i] or 'default_backend' in lines[i]: - insert_idx = i - break - - if insert_idx == -1: - insert_idx = len(lines) - 1 - - acl_line = f" acl {acl_name} hdr(host) -i {hostname}\n" - use_backend_line = f" use_backend {backend_name} if {acl_name}\n" - - for line in lines[frontend_idx:insert_idx]: - if acl_name in line and 'acl' in line: - return True - - lines.insert(insert_idx, use_backend_line) - lines.insert(insert_idx, acl_line) - - with open(HAPROXY_CFG, 'w') as f: - f.write('\n'.join(lines)) - - return True - except Exception as e: - print(f"[HAPROXY_CONFIG] Error adding ACL: {e}", flush=True) - return False - def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method, protocol, backend_name, backend_servers, health_check, health_check_tcp, health_check_link, sticky_session, add_header, header_name, header_value, sticky_session_type, is_acl, acl_name, @@ -182,11 +158,11 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method, existing_frontend = frontend_exists_at_port(frontend_ip, frontend_port) if existing_frontend: - # Frontend już istnieje - dodaj tylko backend i ACL - unique_backend_name = f"{backend_name}_{sanitize_name(frontend_hostname)}" if frontend_hostname else backend_name + # Frontend już istnieje - dodaj tylko backend + ACL + print(f"[HAPROXY] Found existing frontend '{existing_frontend}' at {frontend_ip}:{frontend_port}", flush=True) - # Utwórz backend with open(HAPROXY_CFG, 'a') as haproxy_cfg: + # ===== BACKEND ===== haproxy_cfg.write(f"\nbackend {unique_backend_name}\n") if not is_no_lb: @@ -213,6 +189,7 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method, if forward_for: haproxy_cfg.write(f" option forwardfor\n") + # Add servers for server_name, server_ip, server_port, maxconn in backend_servers: maxconn_str = f" maxconn {maxconn}" if maxconn else "" @@ -221,18 +198,22 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method, else: haproxy_cfg.write(f" server {server_name} {server_ip}:{server_port}{maxconn_str}\n") + # Dodaj ACL do istniejącego frontendu acl_name_sanitized = f"is_{sanitize_name(frontend_hostname)}" if frontend_hostname else f"is_{unique_backend_name}" add_acl_to_frontend(existing_frontend, acl_name_sanitized, frontend_hostname or 'localhost', unique_backend_name) - return f"Backend added to existing frontend {existing_frontend}" + return f"Backend added to existing frontend" + + # ===== TWORZENIE NOWEGO FRONTENDU (GENERYCZNE NAZWY) ===== + # Generuj generyczną nazwę frontendu + generic_frontend_name = f"https_frontend" if use_ssl else f"http_frontend" + generic_http_redirect_name = f"http_redirect_frontend" + + print(f"[HAPROXY] Creating new frontend '{generic_frontend_name}' at {frontend_ip}:{frontend_port}", flush=True) - # ===== TWORZENIE NOWEGO FRONTENDU ===== with open(HAPROXY_CFG, 'a') as haproxy_cfg: - haproxy_cfg.write(f"\nfrontend {frontend_name}\n") - - if is_frontend_exist(frontend_name, frontend_ip, frontend_port): - return "Frontend or Port already exists. Cannot add duplicate." - + # ===== PRIMARY FRONTEND (GENERIC NAME) ===== + haproxy_cfg.write(f"\nfrontend {generic_frontend_name}\n") haproxy_cfg.write(f" bind {frontend_ip}:{frontend_port}") if use_ssl: @@ -240,7 +221,7 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method, haproxy_cfg.write("\n") - # Headers ZARAZ PO BIND/CERT + # Headers zaraz po BIND/CERT haproxy_cfg.write(f" http-request set-header X-Forwarded-For %[src]\n") if use_ssl: haproxy_cfg.write(f" http-request set-header X-Forwarded-Proto https\n") @@ -249,7 +230,7 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method, haproxy_cfg.write(f" mode {protocol}\n") - # ACL + # ACL dla pierwszego vhost acl_name_sanitized = None if frontend_hostname: acl_name_sanitized = f"is_{sanitize_name(frontend_hostname)}" @@ -331,17 +312,16 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method, else: haproxy_cfg.write(f" server {server_name} {server_ip}:{server_port}{maxconn_str}\n") - # ===== REDIRECT HTTP -> HTTPS ===== + # ===== REDIRECT HTTP -> HTTPS (GENERIC NAME) ===== if backend_ssl_redirect and ssl_redirect_backend_name: unique_redirect_backend_name = f"{ssl_redirect_backend_name}_redirect_{sanitize_name(frontend_hostname)}" if frontend_hostname else ssl_redirect_backend_name - # Check if HTTP frontend exists + # Check if HTTP redirect frontend exists existing_http_frontend = frontend_exists_at_port(frontend_ip, ssl_redirect_port) if not existing_http_frontend: - redirect_frontend_name = f"redirect_https_{sanitize_name(frontend_hostname)}" if frontend_hostname else f"redirect_https_{frontend_name}" - - haproxy_cfg.write(f"\nfrontend {redirect_frontend_name}\n") + # Utwórz nowy HTTP redirect frontend (generic name) + haproxy_cfg.write(f"\nfrontend {generic_http_redirect_name}\n") haproxy_cfg.write(f" bind {frontend_ip}:{ssl_redirect_port}\n") haproxy_cfg.write(f" mode http\n") @@ -352,6 +332,7 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method, else: haproxy_cfg.write(f" default_backend {unique_redirect_backend_name}\n") else: + # Dodaj ACL do istniejącego HTTP frontendu if frontend_hostname: acl_name_redirect = f"is_{sanitize_name(frontend_hostname)}_redirect" add_acl_to_frontend(existing_http_frontend, acl_name_redirect, frontend_hostname, unique_redirect_backend_name)