From 378710db3f0047af33cc414937ec620b0ecb62e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Taylan=20Bak=C4=B1rc=C4=B1o=C4=9Flu?= Date: Tue, 18 Nov 2025 21:50:03 +0300 Subject: [PATCH] fix(haproxy-config): ACL rules must come before http-request directives MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CRITICAL HAProxy Validation Error Fix Problem: Generated HAProxy config failed validation with error: [ALERT] error detected while parsing an 'http-request deny' condition: no such ACL: 'waf_demo-rule1_path'. Root Cause: Config generator was writing http-request directives BEFORE ACL definitions. HAProxy requires ACLs to be defined before they are referenced. Generated Config (WRONG ORDER): http-request deny if waf_demo-rule1_path ❌ ACL not defined yet! acl waf_demo-rule1_path path_reg ^/admin ❌ Too late! Fix: Reordered frontend config generation: 1. ACL Rules (Line 339-362) - Define ACLs FIRST 2. HTTP Request Headers (Line 364-371) - Use ACLs AFTER Generated Config (CORRECT ORDER): acl waf_demo-rule1_path path_reg ^/admin ✅ Define first acl waf_demo-rule1_method method POST ✅ Define first http-request deny if waf_demo-rule1_path waf_demo-rule1_method ✅ Use after Impact: - Parsing logic: UNCHANGED (no breaking changes) - Database storage: UNCHANGED (no schema changes) - Config generation: FIXED (correct HAProxy syntax) - Bulk import: Works correctly now - Agent config apply: Validation passes Testing: 1. Bulk import config with ACLs and http-request rules 2. Agent applies config successfully 3. HAProxy validation passes Files Changed: - backend/services/haproxy_config.py: Reordered ACL and request_headers generation --- backend/services/haproxy_config.py | 50 ++++++++++++++++-------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/backend/services/haproxy_config.py b/backend/services/haproxy_config.py index d5b33b5..b31485c 100644 --- a/backend/services/haproxy_config.py +++ b/backend/services/haproxy_config.py @@ -336,7 +336,32 @@ async def generate_haproxy_config_for_cluster(cluster_id: int, conn: Optional[An if monitor_uri and monitor_uri not in ('[]', '{}', 'null', 'None'): config_lines.append(f" monitor-uri {monitor_uri}") - # HTTP Request Headers + # CRITICAL FIX: ACL Rules MUST come BEFORE http-request directives + # HAProxy requires ACL definitions before they are referenced + # ACL Rules + if frontend.get('acl_rules'): + acl_rules = frontend['acl_rules'] + # Parse JSON string if needed + if isinstance(acl_rules, str): + try: + acl_rules = json.loads(acl_rules) + except: + acl_rules = [] + + if isinstance(acl_rules, list): + for acl in acl_rules: + if acl and acl.strip(): + acl_text = acl.strip() + # Skip empty strings, "[]", or invalid ACL rules + if acl_text and acl_text not in ('[]', '{}', 'null', 'None'): + # CRITICAL FIX: ACL rules from parser already include "acl" keyword + # Don't add it again! Parser stores: "acl name condition value" + # If ACL doesn't start with "acl ", add it (for manual entries) + if not acl_text.startswith('acl '): + acl_text = f"acl {acl_text}" + config_lines.append(f" {acl_text}") + + # HTTP Request Headers (must come AFTER ACL definitions) if frontend.get('request_headers'): for line in frontend['request_headers'].split('\n'): line_stripped = line.strip() @@ -363,29 +388,6 @@ async def generate_haproxy_config_for_cluster(cluster_id: int, conn: Optional[An # Lines are already complete directives (e.g., "tcp-request inspect-delay 5s") config_lines.append(f" {line_stripped}") - # ACL Rules - if frontend.get('acl_rules'): - acl_rules = frontend['acl_rules'] - # Parse JSON string if needed - if isinstance(acl_rules, str): - try: - acl_rules = json.loads(acl_rules) - except: - acl_rules = [] - - if isinstance(acl_rules, list): - for acl in acl_rules: - if acl and acl.strip(): - acl_text = acl.strip() - # Skip empty strings, "[]", or invalid ACL rules - if acl_text and acl_text not in ('[]', '{}', 'null', 'None'): - # CRITICAL FIX: ACL rules from parser already include "acl" keyword - # Don't add it again! Parser stores: "acl name condition value" - # If ACL doesn't start with "acl ", add it (for manual entries) - if not acl_text.startswith('acl '): - acl_text = f"acl {acl_text}" - config_lines.append(f" {acl_text}") - # Redirect Rules if frontend.get('redirect_rules'): redirect_rules = frontend['redirect_rules']