diff --git a/package/secubox/secubox-app-haproxy/files/usr/sbin/haproxyctl b/package/secubox/secubox-app-haproxy/files/usr/sbin/haproxyctl index 7ab05107..93c3aea0 100644 --- a/package/secubox/secubox-app-haproxy/files/usr/sbin/haproxyctl +++ b/package/secubox/secubox-app-haproxy/files/usr/sbin/haproxyctl @@ -951,12 +951,56 @@ backend acme_challenge EOF + # PRE-COLLECT ALL SERVER SECTIONS TO AVOID NESTED config_foreach + # This prevents UCI state corruption during backend iteration + # Bug fix: nested config_foreach calls were interleaving output + _servers_data="/tmp/haproxy_servers.$$" + rm -f "$_servers_data" + + _precollect_server() { + local section="$1" + local backend server_name address port weight check enabled + config_get backend "$section" backend + config_get enabled "$section" enabled "0" + [ "$enabled" = "1" ] || return + [ -n "$backend" ] || return + + config_get server_name "$section" name "$section" + config_get address "$section" address + config_get port "$section" port "80" + config_get weight "$section" weight "100" + config_get check "$section" check "1" + + [ -n "$address" ] || return + + # Resolve hostname to IP if needed + if ! echo "$address" | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$'; then + local resolved_ip="" + resolved_ip=$(nslookup "$address" 2>/dev/null | awk '/^Address: / { print $2; exit }') + if [ -z "$resolved_ip" ]; then + resolved_ip=$(getent hosts "$address" 2>/dev/null | awk '{print $1; exit}') + fi + if [ -z "$resolved_ip" ]; then + log_warn "Cannot resolve hostname '$address' for server $server_name in backend $backend - skipping" + return + fi + address="$resolved_ip" + fi + + # Store in temp file: backend|server_name|address|port|weight|check + echo "${backend}|${server_name}|${address}|${port}|${weight}|${check}" >> "$_servers_data" + } + config_foreach _precollect_server server + # Track which backends are generated _generated_backends="" - # Generate each backend from UCI + # Generate each backend from UCI (now safe - no nested config_foreach needed) config_foreach _generate_backend backend + # Cleanup server data temp file + rm -f "$_servers_data" + # Collect all backends referenced by vhosts _referenced_backends="" _collect_vhost_backend() { @@ -1039,17 +1083,12 @@ _generate_backend() { fi # Check if there are separate server sections for this backend + # NOW USES PRE-COLLECTED DATA instead of nested config_foreach + # This avoids UCI state corruption that caused server lines in wrong sections local has_server_sections=0 - _check_server_sections() { - local srv_section="$1" - local srv_backend - config_get srv_backend "$srv_section" backend - config_get srv_enabled "$srv_section" enabled "0" - if [ "$srv_backend" = "$name" ] && [ "$srv_enabled" = "1" ]; then - has_server_sections=1 - fi - } - config_foreach _check_server_sections server + if [ -f "$_servers_data" ] && grep -q "^${name}|" "$_servers_data"; then + has_server_sections=1 + fi # Add inline server ONLY if no separate server sections exist # This prevents duplicate server names @@ -1059,11 +1098,20 @@ _generate_backend() { [ -n "$server_line" ] && echo " server $server_line" fi - # Add servers from separate server UCI sections - config_foreach _add_server_to_backend server "$name" + # Add servers from pre-collected data (avoiding nested config_foreach) + if [ -f "$_servers_data" ]; then + grep "^${name}|" "$_servers_data" 2>/dev/null | while IFS='|' read -r _backend srv_name srv_addr srv_port srv_weight srv_check; do + local check_opt="" + [ "$srv_check" = "1" ] && check_opt="check" + echo " server $srv_name $srv_addr:$srv_port weight $srv_weight $check_opt" + done + fi } -_add_server_to_backend() { +# DEPRECATED: This function is no longer used - server data is now pre-collected +# in _generate_backends() to avoid nested config_foreach bugs. +# Kept for reference only. +_add_server_to_backend_DEPRECATED() { local section="$1" local target_backend="$2" local backend server_name address port weight check enabled