feat(haproxy): Add Sync WAF Routes button and fix LuCI backend routing

- Add "Sync WAF Routes" button to HAProxy vhosts page in LuCI
- Add sync_mitmproxy_routes RPC method to HAProxy RPCD backend
- Fix mitmproxyctl and secubox-route to handle LuCI backends (luci, luci_default, luci_control)
- Remove outdated port 8081 skip filter in route sync that prevented LuCI routes
- These changes allow vhosts with original_backend='luci' to be properly
  routed through the WAF

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-03-19 19:15:51 +01:00
parent 64a12a65ad
commit a61b0fcda8
7 changed files with 116 additions and 29 deletions

View File

@ -596,7 +596,11 @@
"Bash(while read f)", "Bash(while read f)",
"Bash(do basename \"$f\")", "Bash(do basename \"$f\")",
"Bash(git rebase:*)", "Bash(git rebase:*)",
"Bash(GIT_EDITOR=true git rebase:*)" "Bash(GIT_EDITOR=true git rebase:*)",
"Bash(gh release:*)",
"Bash(do gh run cancel $run)",
"Bash(while read id)",
"Bash(do echo \"=== $id ===\")"
] ]
} }
} }

View File

@ -288,6 +288,12 @@ var callListExposedServices = rpc.declare({
expect: { services: [] } expect: { services: [] }
}); });
var callSyncMitmproxyRoutes = rpc.declare({
object: 'luci.haproxy',
method: 'sync_mitmproxy_routes',
expect: {}
});
// ============================================ // ============================================
// Helper Functions // Helper Functions
// ============================================ // ============================================
@ -376,6 +382,9 @@ return baseclass.extend({
// Exposed services // Exposed services
listExposedServices: callListExposedServices, listExposedServices: callListExposedServices,
// Mitmproxy
syncMitmproxyRoutes: callSyncMitmproxyRoutes,
// Helpers // Helpers
getDashboardData: getDashboardData getDashboardData: getDashboardData
}); });

View File

@ -36,7 +36,12 @@ return view.extend({
]), ]),
K.E('p', { 'style': 'margin: 4px 0 0; color: var(--kiss-muted, #94a3b8); font-size: 14px;' }, K.E('p', { 'style': 'margin: 4px 0 0; color: var(--kiss-muted, #94a3b8); font-size: 14px;' },
'Configure domain-based routing to backend servers') 'Configure domain-based routing to backend servers')
]) ]),
K.E('button', {
'class': 'kiss-btn',
'style': 'display: flex; align-items: center; gap: 6px;',
'onClick': function() { self.handleSyncRoutes(); }
}, ['🔄 ', 'Sync WAF Routes'])
]), ]),
// Add Virtual Host Card // Add Virtual Host Card
@ -271,6 +276,21 @@ return view.extend({
}); });
}, },
handleSyncRoutes: function() {
var self = this;
self.showToast('Syncing mitmproxy routes...', 'info');
return api.syncMitmproxyRoutes().then(function(res) {
if (res.success) {
self.showToast('WAF routes synchronized successfully', 'success');
} else {
self.showToast('Failed: ' + (res.error || 'Unknown error'), 'error');
}
}).catch(function(err) {
self.showToast('Error: ' + err.message, 'error');
});
},
handleToggleVhost: function(vh) { handleToggleVhost: function(vh) {
var self = this; var self = this;
var newEnabled = vh.enabled ? 0 : 1; var newEnabled = vh.enabled ? 0 : 1;

View File

@ -1640,6 +1640,34 @@ _add_exposed_service() {
json_close_object json_close_object
} }
# Sync mitmproxy routes from HAProxy backends
method_sync_mitmproxy_routes() {
json_init
# Run mitmproxyctl sync-routes
if command -v mitmproxyctl >/dev/null 2>&1; then
local output
output=$(mitmproxyctl sync-routes 2>&1)
local exit_code=$?
if [ $exit_code -eq 0 ]; then
# Restart mitmproxy to apply new routes
/etc/init.d/mitmproxy restart >/dev/null 2>&1
json_add_boolean "success" 1
json_add_string "message" "Routes synchronized and mitmproxy restarted"
else
json_add_boolean "success" 0
json_add_string "error" "$output"
fi
else
json_add_boolean "success" 0
json_add_string "error" "mitmproxyctl not found"
fi
json_dump
}
# Main RPC interface # Main RPC interface
case "$1" in case "$1" in
list) list)
@ -1686,7 +1714,8 @@ case "$1" in
"generate": {}, "generate": {},
"validate": {}, "validate": {},
"get_logs": { "lines": "integer" }, "get_logs": { "lines": "integer" },
"list_exposed_services": {} "list_exposed_services": {},
"sync_mitmproxy_routes": {}
} }
EOF EOF
;; ;;
@ -1734,6 +1763,7 @@ EOF
validate) method_validate ;; validate) method_validate ;;
get_logs) method_get_logs ;; get_logs) method_get_logs ;;
list_exposed_services) method_list_exposed_services ;; list_exposed_services) method_list_exposed_services ;;
sync_mitmproxy_routes) method_sync_mitmproxy_routes ;;
esac esac
;; ;;
esac esac

View File

@ -54,7 +54,8 @@
"restart", "restart",
"reload", "reload",
"generate", "generate",
"validate" "validate",
"sync_mitmproxy_routes"
] ]
}, },
"uci": ["haproxy"] "uci": ["haproxy"]

View File

@ -1665,16 +1665,30 @@ cmd_sync_routes_legacy() {
backend=$(uci -q get haproxy.$vhost.original_backend) backend=$(uci -q get haproxy.$vhost.original_backend)
fi fi
# Skip fallback, luci, and mitmproxy backends # Skip fallback and mitmproxy backends (no valid destination)
case "$backend" in case "$backend" in
fallback|luci|luci_default|mitmproxy_inspector|"") continue ;; fallback|mitmproxy_inspector|"") continue ;;
esac esac
if [ -n "$domain" ] && [ -n "$backend" ]; then if [ -n "$domain" ] && [ -n "$backend" ]; then
local ip="" local ip=""
local port="" local port=""
# Handle well-known backends with fixed addresses
case "$backend" in
luci|luci_default)
ip="127.0.0.1"
port="8081"
;;
luci_control)
ip="192.168.255.1"
port="8515"
;;
esac
# Method 1: Check for inline server field (old style) # Method 1: Check for inline server field (old style)
# Only lookup if not already set by well-known backend
if [ -z "$ip" ]; then
local server=$(uci -q get haproxy.$backend.server) local server=$(uci -q get haproxy.$backend.server)
if [ -n "$server" ]; then if [ -n "$server" ]; then
# Parse server spec: "name ip:port check [options]" # Parse server spec: "name ip:port check [options]"
@ -1684,6 +1698,7 @@ cmd_sync_routes_legacy() {
# Handle backends without explicit port # Handle backends without explicit port
[ "$ip" = "$port" ] && port="80" [ "$ip" = "$port" ] && port="80"
fi fi
fi
# Method 2: Check for separate server section (new style) # Method 2: Check for separate server section (new style)
if [ -z "$ip" ]; then if [ -z "$ip" ]; then

View File

@ -196,12 +196,6 @@ cmd_sync() {
[ -z "$domain" ] || [ -z "$host" ] || [ -z "$port" ] && continue [ -z "$domain" ] || [ -z "$host" ] || [ -z "$port" ] && continue
# Skip LuCI routes (port 8081)
if [ "$port" = "8081" ]; then
log_warn "Skipping LuCI route: $domain -> $host:8081"
continue
fi
if [ $first -eq 1 ]; then if [ $first -eq 1 ]; then
printf ' "%s": ["%s", %s]' "$domain" "$host" "$port" >> "$tmp_file" printf ' "%s": ["%s", %s]' "$domain" "$host" "$port" >> "$tmp_file"
first=0 first=0
@ -290,15 +284,28 @@ cmd_import_haproxy() {
fi fi
fi fi
# Skip special backends # Skip backends without valid destinations
case "$backend" in case "$backend" in
fallback|luci|luci_default|mitmproxy_inspector|"") continue ;; fallback|mitmproxy_inspector|"") continue ;;
esac esac
# Find backend server info # Find backend server info
local ip="" port="" local ip="" port=""
# Method 1: Check inline server field # Handle well-known backends with fixed addresses
case "$backend" in
luci|luci_default)
ip="127.0.0.1"
port="8081"
;;
luci_control)
ip="192.168.255.1"
port="8515"
;;
esac
# Method 1: Check inline server field (only if not already set)
if [ -z "$ip" ]; then
local server=$(uci -q get haproxy.$backend.server) local server=$(uci -q get haproxy.$backend.server)
if [ -n "$server" ]; then if [ -n "$server" ]; then
local addr=$(echo "$server" | awk '{print $2}') local addr=$(echo "$server" | awk '{print $2}')
@ -306,6 +313,7 @@ cmd_import_haproxy() {
port=$(echo "$addr" | cut -d':' -f2) port=$(echo "$addr" | cut -d':' -f2)
[ "$ip" = "$port" ] && port="80" [ "$ip" = "$port" ] && port="80"
fi fi
fi
# Method 2: Check separate server section # Method 2: Check separate server section
if [ -z "$ip" ]; then if [ -z "$ip" ]; then