fix(haproxy-config): ACL rules must come before http-request directives

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
This commit is contained in:
Taylan Bakırcıoğlu
2025-11-18 21:50:03 +03:00
committed by taylanbakircioglu
parent 1d17dd8fd5
commit 378710db3f
+26 -24
View File
@@ -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']