fix(haproxy): Sort path ACLs by length for correct matching order

Path-based ACLs are now sorted by pattern length (longest first) before
being emitted to haproxy.cfg. This ensures specific paths like /gk2/evolution
match before general paths like /gk2.

Two-phase approach:
- _collect_path_acl() stores ACL data with pattern length prefix
- _emit_sorted_path_acls() sorts by length descending and emits rules

Enables apex domain path routing: secubox.in/gk2/** instead of *.gk2.secubox.in

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-02-09 06:37:25 +01:00
parent e91c6519eb
commit 5ccba836fa

View File

@ -567,7 +567,10 @@ EOF
config_foreach _add_ssl_redirect vhost config_foreach _add_ssl_redirect vhost
# Add path-based ACLs BEFORE vhost ACLs (path rules take precedence) # Add path-based ACLs BEFORE vhost ACLs (path rules take precedence)
config_foreach _add_path_acl acl "http" # Two-phase: collect then emit sorted by pattern length (longest first)
rm -f "$PATH_ACL_TMPFILE"
config_foreach _collect_path_acl acl
_emit_sorted_path_acls
# Add vhost ACLs for HTTP # Add vhost ACLs for HTTP
config_foreach _add_vhost_acl vhost "http" config_foreach _add_vhost_acl vhost "http"
@ -601,7 +604,10 @@ frontend https-in
EOF EOF
fi fi
# Add path-based ACLs BEFORE vhost ACLs (path rules take precedence) # Add path-based ACLs BEFORE vhost ACLs (path rules take precedence)
config_foreach _add_path_acl acl "https" # Two-phase: collect then emit sorted by pattern length (longest first)
rm -f "$PATH_ACL_TMPFILE"
config_foreach _collect_path_acl acl
_emit_sorted_path_acls
# Add vhost ACLs for HTTPS # Add vhost ACLs for HTTPS
config_foreach _add_vhost_acl vhost "https" config_foreach _add_vhost_acl vhost "https"
@ -629,11 +635,13 @@ _add_ssl_redirect() {
echo " http-request redirect scheme https code 301 if host_${acl_name} !{ ssl_fc } !is_acme_challenge" echo " http-request redirect scheme https code 301 if host_${acl_name} !{ ssl_fc } !is_acme_challenge"
} }
# Generate path-based ACLs from UCI 'acl' sections # Path ACL collection temp file
# These are processed BEFORE vhost ACLs so path rules take precedence PATH_ACL_TMPFILE="/tmp/haproxy_path_acls.$$"
_add_path_acl() {
# Collect path-based ACLs from UCI 'acl' sections into temp file for sorting
# Format: pattern_length|section|type|pattern|backend|host
_collect_path_acl() {
local section="$1" local section="$1"
local proto="$2"
local enabled type pattern backend host priority local enabled type pattern backend host priority
config_get enabled "$section" enabled "1" config_get enabled "$section" enabled "1"
@ -650,45 +658,66 @@ _add_path_acl() {
[ -n "$pattern" ] || return [ -n "$pattern" ] || return
[ -n "$backend" ] || return [ -n "$backend" ] || return
# Generate ACL name from section name # Calculate pattern length for sorting (longer patterns first)
local acl_name=$(echo "$section" | tr '.' '_' | tr '-' '_') local pattern_len=${#pattern}
local host_acl_name=""
# If host is specified, we need a host ACL too # Write to temp file: length|section|type|pattern|backend|host
if [ -n "$host" ]; then echo "${pattern_len}|${section}|${type}|${pattern}|${backend}|${host}" >> "$PATH_ACL_TMPFILE"
host_acl_name=$(echo "$host" | tr '.' '_' | tr '-' '_') }
echo " acl host_${host_acl_name} hdr(host) -i $host"
fi
# Generate path ACL based on type # Emit sorted path ACLs (longest patterns first for correct matching)
case "$type" in # This ensures /gk2/evolution matches before /gk2
path_beg) _emit_sorted_path_acls() {
echo " acl ${acl_name} path_beg $pattern" [ -f "$PATH_ACL_TMPFILE" ] || return
;;
path_end)
echo " acl ${acl_name} path_end $pattern"
;;
path)
echo " acl ${acl_name} path $pattern"
;;
path_reg)
echo " acl ${acl_name} path_reg $pattern"
;;
path_dir)
echo " acl ${acl_name} path_dir $pattern"
;;
*)
log_warn "Unknown ACL type: $type for $section"
return
;;
esac
# Generate use_backend rule local seen_hosts=""
if [ -n "$host_acl_name" ]; then
echo " use_backend $backend if host_${host_acl_name} ${acl_name}" # Sort by pattern length (descending, numeric) and emit
else sort -t'|' -k1 -rn "$PATH_ACL_TMPFILE" | while IFS='|' read -r len section type pattern backend host; do
echo " use_backend $backend if ${acl_name}" local acl_name=$(echo "$section" | tr '.' '_' | tr '-' '_')
fi local host_acl_name=""
# If host is specified, emit host ACL only once
if [ -n "$host" ]; then
host_acl_name=$(echo "$host" | tr '.' '_' | tr '-' '_')
# Check if we've already emitted this host ACL
case "$seen_hosts" in
*"|$host_acl_name|"*) ;;
*)
echo " acl host_${host_acl_name} hdr(host) -i $host"
seen_hosts="${seen_hosts}|${host_acl_name}|"
;;
esac
fi
# Generate path ACL based on type
case "$type" in
path_beg)
echo " acl ${acl_name} path_beg $pattern"
;;
path_end)
echo " acl ${acl_name} path_end $pattern"
;;
path)
echo " acl ${acl_name} path $pattern"
;;
path_reg)
echo " acl ${acl_name} path_reg $pattern"
;;
path_dir)
echo " acl ${acl_name} path_dir $pattern"
;;
esac
# Generate use_backend rule
if [ -n "$host_acl_name" ]; then
echo " use_backend $backend if host_${host_acl_name} ${acl_name}"
else
echo " use_backend $backend if ${acl_name}"
fi
done
rm -f "$PATH_ACL_TMPFILE"
} }
_add_vhost_acl() { _add_vhost_acl() {