From e4a3671f9083fb0b096367959a6805fff73e68f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mateusz=20Gruszczy=C5=84ski?= Date: Mon, 3 Nov 2025 09:32:23 +0100 Subject: [PATCH] new options --- routes/main_routes.py | 4 ++- templates/index.html | 13 ++++++++- utils/haproxy_config.py | 58 +++++++++++++++++++++++++++-------------- 3 files changed, 54 insertions(+), 21 deletions(-) diff --git a/routes/main_routes.py b/routes/main_routes.py index 6c88105..cf5ff35 100644 --- a/routes/main_routes.py +++ b/routes/main_routes.py @@ -11,6 +11,7 @@ def index(): frontend_name = request.form['frontend_name'] 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'] @@ -156,7 +157,8 @@ def index(): del_server_header=del_server_header, backend_ssl_redirect=backend_ssl_redirect, ssl_redirect_backend_name=ssl_redirect_backend_name, - ssl_redirect_port=ssl_redirect_port + ssl_redirect_port=ssl_redirect_port, + frontend_hostname=frontend_hostname ) # Determine message type diff --git a/templates/index.html b/templates/index.html index 96b0ade..4da003d 100644 --- a/templates/index.html +++ b/templates/index.html @@ -101,6 +101,16 @@ +
+
+ + +
Domain name for the ACL rule - traffic will be matched by Host header
+
+
+ +
@@ -116,8 +126,9 @@
+ Upload certs in /ssl/ + value="/app/ssl/haproxy-configurator.pem">
Full path to .pem file
diff --git a/utils/haproxy_config.py b/utils/haproxy_config.py index 3624593..0609269 100644 --- a/utils/haproxy_config.py +++ b/utils/haproxy_config.py @@ -75,6 +75,10 @@ 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 (replace special chars with underscores)""" + return name.replace('.', '_').replace('-', '_').replace('/', '_').replace(':', '_') + 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, @@ -83,7 +87,7 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method, allowed_ip, forbidden_path, sql_injection_check, is_xss, is_remote_upload, add_path_based, redirect_domain_name, root_redirect, redirect_to, is_webshells, del_server_header=False, backend_ssl_redirect=False, ssl_redirect_backend_name='', - ssl_redirect_port='80'): + ssl_redirect_port='80', frontend_hostname=''): os.makedirs(os.path.dirname(HAPROXY_CFG), exist_ok=True) @@ -93,10 +97,11 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method, # Tryb no-lb - prosty frontend→backend z pojedynczym serwerem is_no_lb = lb_method == 'no-lb' if is_no_lb and len(backend_servers) > 1: - backend_servers = backend_servers[:1] # Tylko pierwszy serwer + backend_servers = backend_servers[:1] try: with open(HAPROXY_CFG, 'a') as haproxy_cfg: + # ===== PRIMARY FRONTEND ===== haproxy_cfg.write(f"\nfrontend {frontend_name}\n") if is_frontend_exist(frontend_name, frontend_ip, frontend_port): @@ -110,13 +115,19 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method, haproxy_cfg.write("\n") - # HTTPS redirect + # HTTPS redirect (global) if https_redirect: haproxy_cfg.write(f" redirect scheme https code 301 if !{{ ssl_fc }}\n") - # Ustaw mode - zawsze dla no-lb (nie ma balance) + # Mode haproxy_cfg.write(f" mode {protocol}\n") + # ===== HOSTNAME ACL ===== + acl_name_sanitized = None + if frontend_hostname: + acl_name_sanitized = f"is_{sanitize_name(frontend_hostname)}" + haproxy_cfg.write(f" acl {acl_name_sanitized} hdr(host) -i {frontend_hostname}\n") + # W trybie no-lb używamy prostych nagłówków HTTP-request if is_no_lb: haproxy_cfg.write(f" http-request set-header X-Forwarded-For %[src]\n") @@ -125,11 +136,9 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method, else: haproxy_cfg.write(f" http-request set-header X-Forwarded-Proto http\n") - # Opcja ukrycia nagłówka Server if del_server_header: haproxy_cfg.write(f" http-response del-header Server\n") else: - # Standardowy tryb z option forwardfor i balance haproxy_cfg.write(f" balance {lb_method}\n") if forward_for: @@ -138,14 +147,14 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method, if del_server_header: haproxy_cfg.write(f" http-response del-header Server\n") - # DOS protection - działa w obu trybach + # DOS protection if is_dos: haproxy_cfg.write(f" stick-table type ip size 1m expire {ban_duration} store http_req_rate(1m)\n") haproxy_cfg.write(f" http-request track-sc0 src\n") haproxy_cfg.write(f" acl abuse sc_http_req_rate(0) gt {limit_requests}\n") haproxy_cfg.write(f" http-request silent-drop if abuse\n") - # SQL Injection protection - działa w obu trybach + # SQL Injection protection if sql_injection_check: haproxy_cfg.write(" acl is_sql_injection urlp_reg -i (union|select|insert|update|delete|drop|@@|1=1|`1)\n") haproxy_cfg.write(" acl is_long_uri path_len gt 400\n") @@ -153,29 +162,34 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method, haproxy_cfg.write(" acl is_sql_injection2 urlp_reg -i (;|substring|extract|union\\s+all|order\\s+by)\\s+(\\d+|--\\+)\n") haproxy_cfg.write(f" http-request deny if is_sql_injection or is_long_uri or semicolon_path or is_sql_injection2\n") - # XSS protection - działa w obu trybach + # XSS protection if is_xss: haproxy_cfg.write(" acl is_xss_attack urlp_reg -i (<|>|script|alert|onerror|onload|javascript)\n") haproxy_cfg.write(" acl is_xss_attack_2 urlp_reg -i (<\\s*script\\s*|javascript:|<\\s*img\\s*src\\s*=|<\\s*a\\s*href\\s*=|<\\s*iframe\\s*src\\s*=|\\bon\\w+\\s*=|<\\s*input\\s*[^>]*\\s*value\\s*=|<\\s*form\\s*action\\s*=|<\\s*svg\\s*on\\w+\\s*=)\n") haproxy_cfg.write(" acl is_xss_attack_hdr hdr_reg(Cookie|Referer|User-Agent) -i (<|>|script|alert|onerror|onload|javascript)\n") haproxy_cfg.write(f" http-request deny if is_xss_attack or is_xss_attack_2 or is_xss_attack_hdr\n") - # Webshells protection - działa w obu trybach + # Webshells protection if is_webshells: haproxy_cfg.write(" acl blocked_webshell path_reg -i /(cmd|shell|backdoor|webshell|phpspy|c99|kacak|b374k|log4j|log4shell|wsos|madspot|malicious|evil).*\\.php.*\n") haproxy_cfg.write(f" http-request deny if blocked_webshell\n") - # Default backend - haproxy_cfg.write(f" default_backend {backend_name}\n") + # ===== BACKEND ROUTING ===== + if acl_name_sanitized: + # Jeśli jest hostname, routuj z ACL + haproxy_cfg.write(f" use_backend {backend_name} if {acl_name_sanitized}\n") + else: + # Default backend + haproxy_cfg.write(f" default_backend {backend_name}\n") - # Backend section + # ===== PRIMARY BACKEND ===== haproxy_cfg.write(f"\nbackend {backend_name}\n") # Balance tylko dla standardowego trybu if not is_no_lb: haproxy_cfg.write(f" balance {lb_method}\n") - # Sticky sessions - tylko dla standardowego trybu + # Sticky sessions if sticky_session and not is_no_lb: if sticky_session_type == "cookie": haproxy_cfg.write(f" cookie SERVERID insert indirect nocache\n") @@ -183,13 +197,13 @@ def update_haproxy_config(frontend_name, frontend_ip, frontend_port, lb_method, haproxy_cfg.write(f" stick-table type ip size 200k expire 30m\n") haproxy_cfg.write(f" stick on src\n") - # Health checks - działa w obu trybach + # Health checks if health_check and protocol == 'http': haproxy_cfg.write(f" option httpchk GET {health_check_link}\n") elif health_check_tcp and protocol == 'tcp': haproxy_cfg.write(f" option tcp-check\n") - # Custom headers - działa w obu trybach + # Custom headers if add_header: haproxy_cfg.write(f" http-response add-header {header_name} {header_value}\n") @@ -202,16 +216,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") - # ========== REDIRECT FRONTEND (HTTP -> HTTPS) ========== + # ===== REDIRECT FRONTEND (HTTP -> HTTPS) ===== if backend_ssl_redirect and ssl_redirect_backend_name: - # Sprawdź czy taki backend już istnieje if is_backend_exist(ssl_redirect_backend_name): return f"Redirect backend {ssl_redirect_backend_name} already exists. Cannot add duplicate." haproxy_cfg.write(f"\nfrontend redirect_https\n") haproxy_cfg.write(f" bind {frontend_ip}:{ssl_redirect_port}\n") haproxy_cfg.write(f" mode http\n") - haproxy_cfg.write(f" default_backend {ssl_redirect_backend_name}\n") + + # ===== HOSTNAME ACL FOR REDIRECT FRONTEND ===== + if frontend_hostname: + acl_name_redirect = f"is_{sanitize_name(frontend_hostname)}_redirect" + haproxy_cfg.write(f" acl {acl_name_redirect} hdr(host) -i {frontend_hostname}\n") + haproxy_cfg.write(f" use_backend {ssl_redirect_backend_name} if {acl_name_redirect}\n") + else: + haproxy_cfg.write(f" default_backend {ssl_redirect_backend_name}\n") # Redirect backend haproxy_cfg.write(f"\nbackend {ssl_redirect_backend_name}\n")