From 061309675b5db0092788288b8ed55d8c040741ac Mon Sep 17 00:00:00 2001 From: Josh Knapp Date: Tue, 12 May 2026 17:07:12 -0700 Subject: [PATCH] fix(coraza-spoe): collapse args to one line + ensure trailing LF on spoe cfg MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two HAProxy parse errors caught in staging functional test: 1. coraza-spoe.cfg:39 'args': missing fetch method The args directive had backslash line continuations. HAProxy doesn't support those in SPOE configs — args must be one physical line. Collapsed to a single line. 2. coraza-spoe.cfg:50 Missing LF on last line Same trailing-LF issue we hit on haproxy.cfg one commit ago. The Jinja2 template ends with content rather than a newline, and write() doesn't add one. Belt-and-suspenders: explicitly append '\n' before writing if not already there. After this commit HAProxy validates the generated config cleanly. Will verify on staging now (combined SPOE injection + fail-open + active attack-detection tests). Co-Authored-By: Claude Opus 4.7 (1M context) --- haproxy_manager.py | 6 ++++++ templates/hap_coraza_spoe_engine.tpl | 16 ++++------------ 2 files changed, 10 insertions(+), 12 deletions(-) diff --git a/haproxy_manager.py b/haproxy_manager.py index 4ec5a14..504d43b 100644 --- a/haproxy_manager.py +++ b/haproxy_manager.py @@ -1863,6 +1863,12 @@ backend default-backend coraza_spoe_cfg = template_env.get_template( 'hap_coraza_spoe_engine.tpl' ).render() + # HAProxy also rejects this file without a trailing LF + # ("Missing LF on last line"). Belt-and-suspenders — even if the + # template ends with a newline, Jinja2 can trim it depending on + # how the file was authored. + if not coraza_spoe_cfg.endswith('\n'): + coraza_spoe_cfg += '\n' coraza_spoe_path = '/etc/haproxy/coraza-spoe.cfg' with open(coraza_spoe_path, 'w') as f: f.write(coraza_spoe_cfg) diff --git a/templates/hap_coraza_spoe_engine.tpl b/templates/hap_coraza_spoe_engine.tpl index d8809df..37a64a3 100644 --- a/templates/hap_coraza_spoe_engine.tpl +++ b/templates/hap_coraza_spoe_engine.tpl @@ -36,15 +36,7 @@ spoe-message coraza-check # `app=str(haproxy)` matches the application named "haproxy" in # coraza-spoa's config.yaml — that's how Coraza picks which ruleset # to apply. - args app=str(haproxy) \ - src-ip=src \ - src-port=src_port \ - dest-ip=dst \ - dest-port=dst_port \ - method=method \ - path=path \ - query=query \ - version=req.ver \ - headers=req.hdrs \ - body=req.body - event on-frontend-http-request + # NOTE: args must be on ONE line. HAProxy does not support backslash + # line continuations in spoe configs (verified the hard way 2026-05-12). + args app=str(haproxy) src-ip=src src-port=src_port dest-ip=dst dest-port=dst_port method=method path=path query=query version=req.ver headers=req.hdrs body=req.body + event on-frontend-http-request