From 4769f67fe99c8f98d41a88c39beba9b2781ebb92 Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Tue, 12 May 2026 17:03:56 -0700 Subject: [PATCH] fix(coraza): ensure haproxy.cfg ends with LF when SPOE backend appended The SPOE backend block from hap_coraza_spoa_backend.tpl was being appended last to config_parts. The template's render output doesn't end with a newline (and config_parts is joined with '\n' BETWEEN elements, not after the last one), so the resulting haproxy.cfg ended on `server coraza-spoa ...` with no trailing LF. HAProxy refuses to parse such files: [ALERT] config: parsing [/etc/haproxy/haproxy.cfg:288]: Missing LF on last line, file might have been truncated at position 70. Match the existing pattern at the previous-last config_parts.append (line 1850 uses `'\n'.join(config_backends) + '\n'`) and add an explicit '\n' on the coraza block append. Caught immediately on staging: HTTP 000 to localhost:80 because HAProxy never started; gunicorn/management API kept serving on :8000 fine. Co-Authored-By: Claude Opus 4.7 (1M context) --- haproxy_manager.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/haproxy_manager.py b/haproxy_manager.py index 183cfca..4ec5a14 100644 --- a/haproxy_manager.py +++ b/haproxy_manager.py @@ -1852,11 +1852,13 @@ backend default-backend # Coraza WAF backend + SPOE engine config file (only when env var set). # Writing /etc/haproxy/coraza-spoe.cfg here keeps it in sync with the # filter line that hap_listener.tpl just rendered into the frontend. + # Explicit trailing '\n' because this is now the LAST config_part — + # HAProxy fails parse with "Missing LF on last line" otherwise. if coraza_spoe_backend: coraza_backend_block = template_env.get_template( 'hap_coraza_spoa_backend.tpl' ).render(agent_target=coraza_spoe_backend) - config_parts.append(coraza_backend_block) + config_parts.append(coraza_backend_block + '\n') coraza_spoe_cfg = template_env.get_template( 'hap_coraza_spoe_engine.tpl'