feat(haproxy): Add exposed services integration and fix cert key naming
- Fix HAProxy certificate key naming (.key -> .crt.key) for directory loading - Add auto-fix in container startup script for existing certificates - Add list_exposed_services RPC method to fetch services from secubox-exposure - Add dynamic port scanning for running services discovery - Add "Quick Select" dropdown in Add Server modal for service auto-fill - Bump luci-app-haproxy to 1.0.0-r8 - Bump secubox-app-haproxy to 1.0.0-r15 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
62cf871eeb
commit
c9075bc190
@ -11,7 +11,7 @@ LUCI_PKGARCH:=all
|
|||||||
|
|
||||||
PKG_NAME:=luci-app-haproxy
|
PKG_NAME:=luci-app-haproxy
|
||||||
PKG_VERSION:=1.0.0
|
PKG_VERSION:=1.0.0
|
||||||
PKG_RELEASE:=7
|
PKG_RELEASE:=8
|
||||||
|
|
||||||
PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>
|
PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>
|
||||||
PKG_LICENSE:=MIT
|
PKG_LICENSE:=MIT
|
||||||
|
|||||||
@ -282,6 +282,12 @@ var callGetLogs = rpc.declare({
|
|||||||
expect: { logs: '' }
|
expect: { logs: '' }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
var callListExposedServices = rpc.declare({
|
||||||
|
object: 'luci.haproxy',
|
||||||
|
method: 'list_exposed_services',
|
||||||
|
expect: { services: [] }
|
||||||
|
});
|
||||||
|
|
||||||
// ============================================
|
// ============================================
|
||||||
// Helper Functions
|
// Helper Functions
|
||||||
// ============================================
|
// ============================================
|
||||||
@ -367,6 +373,9 @@ return baseclass.extend({
|
|||||||
validate: callValidate,
|
validate: callValidate,
|
||||||
getLogs: callGetLogs,
|
getLogs: callGetLogs,
|
||||||
|
|
||||||
|
// Exposed services
|
||||||
|
listExposedServices: callListExposedServices,
|
||||||
|
|
||||||
// Helpers
|
// Helpers
|
||||||
getDashboardData: getDashboardData
|
getDashboardData: getDashboardData
|
||||||
});
|
});
|
||||||
|
|||||||
@ -23,7 +23,8 @@ return view.extend({
|
|||||||
var backends = (result && result.backends) || result || [];
|
var backends = (result && result.backends) || result || [];
|
||||||
return Promise.all([
|
return Promise.all([
|
||||||
Promise.resolve(backends),
|
Promise.resolve(backends),
|
||||||
api.listServers('')
|
api.listServers(''),
|
||||||
|
api.listExposedServices()
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
@ -33,6 +34,8 @@ return view.extend({
|
|||||||
var backends = data[0] || [];
|
var backends = data[0] || [];
|
||||||
var serversResult = data[1] || {};
|
var serversResult = data[1] || {};
|
||||||
var servers = (serversResult && serversResult.servers) || serversResult || [];
|
var servers = (serversResult && serversResult.servers) || serversResult || [];
|
||||||
|
var exposedResult = data[2] || {};
|
||||||
|
self.exposedServices = (exposedResult && exposedResult.services) || exposedResult || [];
|
||||||
|
|
||||||
// Group servers by backend
|
// Group servers by backend
|
||||||
var serversByBackend = {};
|
var serversByBackend = {};
|
||||||
@ -405,9 +408,45 @@ return view.extend({
|
|||||||
|
|
||||||
showAddServerModal: function(backend) {
|
showAddServerModal: function(backend) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var exposedServices = self.exposedServices || [];
|
||||||
|
|
||||||
|
// Build service selector options
|
||||||
|
var serviceOptions = [E('option', { 'value': '' }, '-- Select a service --')];
|
||||||
|
exposedServices.forEach(function(svc) {
|
||||||
|
var label = svc.name + ' (' + svc.address + ':' + svc.port + ')';
|
||||||
|
if (svc.category) label += ' [' + svc.category + ']';
|
||||||
|
serviceOptions.push(E('option', {
|
||||||
|
'value': JSON.stringify(svc),
|
||||||
|
'data-name': svc.name,
|
||||||
|
'data-address': svc.address,
|
||||||
|
'data-port': svc.port
|
||||||
|
}, label));
|
||||||
|
});
|
||||||
|
|
||||||
ui.showModal('Add Server to ' + backend.name, [
|
ui.showModal('Add Server to ' + backend.name, [
|
||||||
E('div', { 'style': 'max-width: 500px;' }, [
|
E('div', { 'style': 'max-width: 500px;' }, [
|
||||||
|
// Quick service selector
|
||||||
|
exposedServices.length > 0 ? E('div', { 'class': 'cbi-value' }, [
|
||||||
|
E('label', { 'class': 'cbi-value-title' }, 'Quick Select'),
|
||||||
|
E('div', { 'class': 'cbi-value-field' }, [
|
||||||
|
E('select', {
|
||||||
|
'id': 'modal-service-select',
|
||||||
|
'class': 'cbi-input-select',
|
||||||
|
'style': 'width: 100%;',
|
||||||
|
'change': function(ev) {
|
||||||
|
var val = ev.target.value;
|
||||||
|
if (val) {
|
||||||
|
var svc = JSON.parse(val);
|
||||||
|
document.getElementById('modal-server-name').value = svc.name;
|
||||||
|
document.getElementById('modal-server-address').value = svc.address;
|
||||||
|
document.getElementById('modal-server-port').value = svc.port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, serviceOptions),
|
||||||
|
E('small', { 'style': 'color: var(--hp-text-muted); display: block; margin-top: 4px;' },
|
||||||
|
'Select a known service to auto-fill details, or enter manually below')
|
||||||
|
])
|
||||||
|
]) : null,
|
||||||
E('div', { 'class': 'cbi-value' }, [
|
E('div', { 'class': 'cbi-value' }, [
|
||||||
E('label', { 'class': 'cbi-value-title' }, 'Server Name'),
|
E('label', { 'class': 'cbi-value-title' }, 'Server Name'),
|
||||||
E('div', { 'class': 'cbi-value-field' }, [
|
E('div', { 'class': 'cbi-value-field' }, [
|
||||||
|
|||||||
@ -1285,6 +1285,74 @@ method_get_logs() {
|
|||||||
json_dump
|
json_dump
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# List exposed services (from secubox-exposure config)
|
||||||
|
method_list_exposed_services() {
|
||||||
|
json_init
|
||||||
|
json_add_array "services"
|
||||||
|
|
||||||
|
# Load known services from exposure config
|
||||||
|
if uci -q show secubox-exposure >/dev/null 2>&1; then
|
||||||
|
config_load "secubox-exposure"
|
||||||
|
config_foreach _add_exposed_service known
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Also scan listening ports for dynamic discovery
|
||||||
|
if command -v netstat >/dev/null 2>&1; then
|
||||||
|
netstat -tlnp 2>/dev/null | grep LISTEN | while read line; do
|
||||||
|
local addr_port=$(echo "$line" | awk '{print $4}')
|
||||||
|
local port=$(echo "$addr_port" | awk -F: '{print $NF}')
|
||||||
|
local proc=$(echo "$line" | awk '{print $7}' | cut -d'/' -f2)
|
||||||
|
|
||||||
|
# Skip if already added from known services or common system ports
|
||||||
|
case "$port" in
|
||||||
|
22|53|80|443|8404) continue ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Only add if process name is useful
|
||||||
|
if [ -n "$proc" ] && [ "$proc" != "-" ] && [ "$proc" != "unknown" ]; then
|
||||||
|
json_add_object
|
||||||
|
json_add_string "id" "dynamic_${proc}_${port}"
|
||||||
|
json_add_string "name" "$proc"
|
||||||
|
json_add_int "port" "$port"
|
||||||
|
json_add_string "address" "127.0.0.1"
|
||||||
|
json_add_string "category" "detected"
|
||||||
|
json_add_boolean "dynamic" 1
|
||||||
|
json_close_object
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
json_close_array
|
||||||
|
json_dump
|
||||||
|
}
|
||||||
|
|
||||||
|
_add_exposed_service() {
|
||||||
|
local section="$1"
|
||||||
|
local default_port config_path category actual_port
|
||||||
|
|
||||||
|
config_get default_port "$section" default_port ""
|
||||||
|
config_get config_path "$section" config_path ""
|
||||||
|
config_get category "$section" category "app"
|
||||||
|
|
||||||
|
[ -z "$default_port" ] && return
|
||||||
|
|
||||||
|
# Try to get actual port from UCI config if available
|
||||||
|
actual_port="$default_port"
|
||||||
|
if [ -n "$config_path" ]; then
|
||||||
|
local configured_port=$(uci -q get "$config_path" 2>/dev/null)
|
||||||
|
[ -n "$configured_port" ] && actual_port="$configured_port"
|
||||||
|
fi
|
||||||
|
|
||||||
|
json_add_object
|
||||||
|
json_add_string "id" "$section"
|
||||||
|
json_add_string "name" "$section"
|
||||||
|
json_add_int "port" "$actual_port"
|
||||||
|
json_add_string "address" "127.0.0.1"
|
||||||
|
json_add_string "category" "$category"
|
||||||
|
json_add_boolean "dynamic" 0
|
||||||
|
json_close_object
|
||||||
|
}
|
||||||
|
|
||||||
# Main RPC interface
|
# Main RPC interface
|
||||||
case "$1" in
|
case "$1" in
|
||||||
list)
|
list)
|
||||||
@ -1326,7 +1394,8 @@ case "$1" in
|
|||||||
"reload": {},
|
"reload": {},
|
||||||
"generate": {},
|
"generate": {},
|
||||||
"validate": {},
|
"validate": {},
|
||||||
"get_logs": { "lines": "integer" }
|
"get_logs": { "lines": "integer" },
|
||||||
|
"list_exposed_services": {}
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
;;
|
;;
|
||||||
@ -1369,6 +1438,7 @@ EOF
|
|||||||
generate) method_generate ;;
|
generate) method_generate ;;
|
||||||
validate) method_validate ;;
|
validate) method_validate ;;
|
||||||
get_logs) method_get_logs ;;
|
get_logs) method_get_logs ;;
|
||||||
|
list_exposed_services) method_list_exposed_services ;;
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|||||||
@ -270,13 +270,24 @@ EOF
|
|||||||
echo -e " ${CYAN}Port:${NC} $onion_port -> 127.0.0.1:$local_port"
|
echo -e " ${CYAN}Port:${NC} $onion_port -> 127.0.0.1:$local_port"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# Save to UCI
|
# Save to exposure UCI
|
||||||
uci set "${CONFIG_NAME}.${service}=service"
|
uci set "${CONFIG_NAME}.${service}=service"
|
||||||
uci set "${CONFIG_NAME}.${service}.port=$local_port"
|
uci set "${CONFIG_NAME}.${service}.port=$local_port"
|
||||||
uci set "${CONFIG_NAME}.${service}.tor=1"
|
uci set "${CONFIG_NAME}.${service}.tor=1"
|
||||||
uci set "${CONFIG_NAME}.${service}.tor_onion=$onion"
|
uci set "${CONFIG_NAME}.${service}.tor_onion=$onion"
|
||||||
uci set "${CONFIG_NAME}.${service}.tor_port=$onion_port"
|
uci set "${CONFIG_NAME}.${service}.tor_port=$onion_port"
|
||||||
uci commit "$CONFIG_NAME"
|
uci commit "$CONFIG_NAME"
|
||||||
|
|
||||||
|
# Sync to Tor Shield UCI
|
||||||
|
local hs_name="hs_${service}"
|
||||||
|
uci set "tor-shield.${hs_name}=hidden_service"
|
||||||
|
uci set "tor-shield.${hs_name}.name=${service}"
|
||||||
|
uci set "tor-shield.${hs_name}.enabled=1"
|
||||||
|
uci set "tor-shield.${hs_name}.local_port=${local_port}"
|
||||||
|
uci set "tor-shield.${hs_name}.onion_port=${onion_port}"
|
||||||
|
uci set "tor-shield.${hs_name}.onion_address=${onion}"
|
||||||
|
uci commit tor-shield
|
||||||
|
log_ok "Synced to Tor Shield"
|
||||||
else
|
else
|
||||||
log_err "Failed to generate onion address"
|
log_err "Failed to generate onion address"
|
||||||
return 1
|
return 1
|
||||||
@ -335,22 +346,77 @@ cmd_tor_remove() {
|
|||||||
# Remove directory
|
# Remove directory
|
||||||
rm -rf "$hidden_dir"
|
rm -rf "$hidden_dir"
|
||||||
|
|
||||||
# Update UCI
|
# Update exposure UCI
|
||||||
uci delete "${CONFIG_NAME}.${service}.tor" 2>/dev/null
|
uci delete "${CONFIG_NAME}.${service}.tor" 2>/dev/null
|
||||||
uci delete "${CONFIG_NAME}.${service}.tor_onion" 2>/dev/null
|
uci delete "${CONFIG_NAME}.${service}.tor_onion" 2>/dev/null
|
||||||
uci delete "${CONFIG_NAME}.${service}.tor_port" 2>/dev/null
|
uci delete "${CONFIG_NAME}.${service}.tor_port" 2>/dev/null
|
||||||
uci commit "$CONFIG_NAME"
|
uci commit "$CONFIG_NAME"
|
||||||
|
|
||||||
|
# Remove from Tor Shield UCI
|
||||||
|
local hs_name="hs_${service}"
|
||||||
|
if uci -q get "tor-shield.${hs_name}" >/dev/null 2>&1; then
|
||||||
|
uci delete "tor-shield.${hs_name}"
|
||||||
|
uci commit tor-shield
|
||||||
|
log_ok "Removed from Tor Shield"
|
||||||
|
fi
|
||||||
|
|
||||||
# Restart Tor
|
# Restart Tor
|
||||||
/etc/init.d/tor restart 2>/dev/null || systemctl restart tor 2>/dev/null
|
/etc/init.d/tor restart 2>/dev/null || systemctl restart tor 2>/dev/null
|
||||||
|
|
||||||
log_ok "Hidden service removed"
|
log_ok "Hidden service removed"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cmd_tor_sync() {
|
||||||
|
load_config
|
||||||
|
|
||||||
|
log_info "Syncing hidden services to Tor Shield..."
|
||||||
|
local synced=0
|
||||||
|
|
||||||
|
# List from filesystem and sync to Tor Shield
|
||||||
|
if [ -d "$TOR_HIDDEN_DIR" ]; then
|
||||||
|
for dir in "$TOR_HIDDEN_DIR"/*/; do
|
||||||
|
[ -d "$dir" ] || continue
|
||||||
|
local svc=$(basename "$dir")
|
||||||
|
local onion=""
|
||||||
|
[ -f "$dir/hostname" ] && onion=$(cat "$dir/hostname")
|
||||||
|
|
||||||
|
# Get port from torrc
|
||||||
|
local port=$(grep -A1 "HiddenServiceDir $dir" "$TOR_CONFIG" 2>/dev/null | grep HiddenServicePort | awk '{print $2}')
|
||||||
|
local local_port=$(grep -A1 "HiddenServiceDir $dir" "$TOR_CONFIG" 2>/dev/null | grep HiddenServicePort | awk '{split($3,a,":"); print a[2]}')
|
||||||
|
|
||||||
|
if [ -n "$onion" ]; then
|
||||||
|
local hs_name="hs_${svc}"
|
||||||
|
if ! uci -q get "tor-shield.${hs_name}" >/dev/null 2>&1; then
|
||||||
|
log_info "Adding $svc to Tor Shield"
|
||||||
|
uci set "tor-shield.${hs_name}=hidden_service"
|
||||||
|
uci set "tor-shield.${hs_name}.name=${svc}"
|
||||||
|
uci set "tor-shield.${hs_name}.enabled=1"
|
||||||
|
uci set "tor-shield.${hs_name}.local_port=${local_port:-80}"
|
||||||
|
uci set "tor-shield.${hs_name}.onion_port=${port:-80}"
|
||||||
|
uci set "tor-shield.${hs_name}.onion_address=${onion}"
|
||||||
|
synced=$((synced + 1))
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$synced" -gt 0 ]; then
|
||||||
|
uci commit tor-shield
|
||||||
|
log_ok "Synced $synced hidden service(s) to Tor Shield"
|
||||||
|
else
|
||||||
|
log_info "All hidden services already synced"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# HAPROXY SSL BACKENDS
|
# HAPROXY SSL BACKENDS (UCI-based integration with haproxyctl)
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
|
|
||||||
|
# Sanitize name for UCI section (replace dots/hyphens with underscores)
|
||||||
|
sanitize_uci_name() {
|
||||||
|
echo "$1" | sed 's/[.-]/_/g'
|
||||||
|
}
|
||||||
|
|
||||||
cmd_ssl_add() {
|
cmd_ssl_add() {
|
||||||
local service="$1"
|
local service="$1"
|
||||||
local domain="$2"
|
local domain="$2"
|
||||||
@ -379,80 +445,66 @@ cmd_ssl_add() {
|
|||||||
fi
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if HAProxy config exists
|
# Check if haproxyctl exists
|
||||||
if [ ! -f "$HAPROXY_CONFIG" ]; then
|
if [ ! -x "/usr/sbin/haproxyctl" ]; then
|
||||||
log_err "HAProxy config not found: $HAPROXY_CONFIG"
|
log_err "haproxyctl not found. Is secubox-app-haproxy installed?"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Check if already configured
|
# Sanitize names for UCI
|
||||||
if grep -q "backend ${service}_backend" "$HAPROXY_CONFIG"; then
|
local backend_name="$service"
|
||||||
log_warn "Backend for $service already exists in HAProxy config"
|
local vhost_name=$(sanitize_uci_name "$domain")
|
||||||
return 0
|
|
||||||
fi
|
|
||||||
|
|
||||||
log_info "Adding SSL backend for $service ($domain -> 127.0.0.1:$local_port)"
|
# Check if backend already exists in UCI
|
||||||
|
if uci -q get "haproxy.${backend_name}" >/dev/null 2>&1; then
|
||||||
# Create backend config
|
log_warn "Backend '$backend_name' already exists in HAProxy UCI config"
|
||||||
local backend_config="
|
|
||||||
# Backend for $service (added by secubox-exposure)
|
|
||||||
backend ${service}_backend
|
|
||||||
mode http
|
|
||||||
option httpchk GET /
|
|
||||||
http-request set-header X-Forwarded-Proto https
|
|
||||||
server ${service} 127.0.0.1:$local_port check
|
|
||||||
"
|
|
||||||
|
|
||||||
# Add ACL to https frontend
|
|
||||||
local acl_line=" acl host_${service} hdr(host) -i $domain"
|
|
||||||
local use_line=" use_backend ${service}_backend if host_${service}"
|
|
||||||
|
|
||||||
# Check if https-in frontend exists
|
|
||||||
if grep -q "frontend https-in" "$HAPROXY_CONFIG"; then
|
|
||||||
# Add ACL and use_backend before the default_backend line
|
|
||||||
sed -i "/frontend https-in/,/default_backend/ {
|
|
||||||
/default_backend/ i\\
|
|
||||||
$acl_line\\
|
|
||||||
$use_line
|
|
||||||
}" "$HAPROXY_CONFIG"
|
|
||||||
else
|
else
|
||||||
log_warn "No https-in frontend found. Adding basic HTTPS frontend."
|
# Create backend in HAProxy UCI config
|
||||||
cat >> "$HAPROXY_CONFIG" << EOF
|
log_info "Adding backend '$backend_name' (127.0.0.1:$local_port)"
|
||||||
|
uci set "haproxy.${backend_name}=backend"
|
||||||
frontend https-in
|
uci set "haproxy.${backend_name}.name=${backend_name}"
|
||||||
bind *:443 ssl crt $HAPROXY_CERTS/
|
uci set "haproxy.${backend_name}.mode=http"
|
||||||
mode http
|
uci set "haproxy.${backend_name}.balance=roundrobin"
|
||||||
option httplog
|
uci set "haproxy.${backend_name}.enabled=1"
|
||||||
$acl_line
|
uci add_list "haproxy.${backend_name}.server=${service} 127.0.0.1:${local_port} check"
|
||||||
$use_line
|
|
||||||
default_backend default_backend
|
|
||||||
EOF
|
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Add backend at end of file
|
# Check if vhost already exists
|
||||||
echo "$backend_config" >> "$HAPROXY_CONFIG"
|
if uci -q get "haproxy.${vhost_name}" >/dev/null 2>&1; then
|
||||||
|
log_warn "Vhost for '$domain' already exists"
|
||||||
|
else
|
||||||
|
# Create vhost in HAProxy UCI config
|
||||||
|
log_info "Adding vhost '$domain' -> backend '$backend_name'"
|
||||||
|
uci set "haproxy.${vhost_name}=vhost"
|
||||||
|
uci set "haproxy.${vhost_name}.domain=${domain}"
|
||||||
|
uci set "haproxy.${vhost_name}.backend=${backend_name}"
|
||||||
|
uci set "haproxy.${vhost_name}.ssl=1"
|
||||||
|
uci set "haproxy.${vhost_name}.ssl_redirect=1"
|
||||||
|
uci set "haproxy.${vhost_name}.enabled=1"
|
||||||
|
fi
|
||||||
|
|
||||||
# Save to UCI
|
# Commit HAProxy UCI changes
|
||||||
|
uci commit haproxy
|
||||||
|
|
||||||
|
# Also save to exposure UCI for tracking
|
||||||
uci set "${CONFIG_NAME}.${service}=service"
|
uci set "${CONFIG_NAME}.${service}=service"
|
||||||
uci set "${CONFIG_NAME}.${service}.port=$local_port"
|
uci set "${CONFIG_NAME}.${service}.port=$local_port"
|
||||||
uci set "${CONFIG_NAME}.${service}.ssl=1"
|
uci set "${CONFIG_NAME}.${service}.ssl=1"
|
||||||
uci set "${CONFIG_NAME}.${service}.ssl_domain=$domain"
|
uci set "${CONFIG_NAME}.${service}.ssl_domain=$domain"
|
||||||
uci commit "$CONFIG_NAME"
|
uci commit "$CONFIG_NAME"
|
||||||
|
|
||||||
log_ok "HAProxy backend added for $service"
|
log_ok "HAProxy UCI config updated"
|
||||||
log_info "Domain: $domain -> 127.0.0.1:$local_port"
|
log_info "Domain: $domain -> 127.0.0.1:$local_port"
|
||||||
log_warn "Note: You need to add SSL certificate for $domain to $HAPROXY_CERTS/"
|
|
||||||
log_info "Reloading HAProxy..."
|
|
||||||
|
|
||||||
# Reload HAProxy (in LXC container)
|
# Regenerate and reload HAProxy
|
||||||
if [ -x "/usr/sbin/haproxyctl" ]; then
|
log_info "Regenerating HAProxy config..."
|
||||||
/usr/sbin/haproxyctl reload
|
/usr/sbin/haproxyctl generate
|
||||||
else
|
|
||||||
lxc-attach -n haproxy -- /etc/init.d/haproxy reload 2>/dev/null || \
|
log_info "Reloading HAProxy..."
|
||||||
/etc/init.d/haproxy reload 2>/dev/null
|
/usr/sbin/haproxyctl reload
|
||||||
fi
|
|
||||||
|
|
||||||
log_ok "SSL backend configured"
|
log_ok "SSL backend configured"
|
||||||
|
log_warn "Note: Ensure SSL certificate exists for $domain"
|
||||||
}
|
}
|
||||||
|
|
||||||
cmd_ssl_list() {
|
cmd_ssl_list() {
|
||||||
@ -463,21 +515,27 @@ cmd_ssl_list() {
|
|||||||
printf "%-15s %-30s %-20s\n" "SERVICE" "DOMAIN" "BACKEND"
|
printf "%-15s %-30s %-20s\n" "SERVICE" "DOMAIN" "BACKEND"
|
||||||
printf "%-15s %-30s %-20s\n" "---------------" "------------------------------" "--------------------"
|
printf "%-15s %-30s %-20s\n" "---------------" "------------------------------" "--------------------"
|
||||||
|
|
||||||
# Parse from HAProxy config
|
# Read from HAProxy UCI config (vhosts with their backends)
|
||||||
if [ -f "$HAPROXY_CONFIG" ]; then
|
local found=0
|
||||||
grep -E "^backend .+_backend$" "$HAPROXY_CONFIG" | while read line; do
|
for vhost in $(uci show haproxy 2>/dev/null | grep "=vhost$" | cut -d'.' -f2 | cut -d'=' -f1); do
|
||||||
local backend=$(echo "$line" | awk '{print $2}')
|
local domain=$(uci -q get "haproxy.${vhost}.domain")
|
||||||
local service=$(echo "$backend" | sed 's/_backend$//')
|
local backend=$(uci -q get "haproxy.${vhost}.backend")
|
||||||
|
local enabled=$(uci -q get "haproxy.${vhost}.enabled")
|
||||||
|
|
||||||
# Get domain from ACL
|
[ "$enabled" != "1" ] && continue
|
||||||
local domain=$(grep "acl host_${service} " "$HAPROXY_CONFIG" | awk '{print $NF}')
|
[ -z "$domain" ] && continue
|
||||||
|
|
||||||
# Get server line
|
# Get server from backend
|
||||||
local server=$(grep -A5 "backend $backend" "$HAPROXY_CONFIG" | grep "server " | awk '{print $3}')
|
local server=""
|
||||||
|
if [ -n "$backend" ]; then
|
||||||
|
server=$(uci -q get "haproxy.${backend}.server" | head -1 | awk '{print $2}')
|
||||||
|
fi
|
||||||
|
|
||||||
printf "%-15s %-30s %-20s\n" "$service" "${domain:-N/A}" "${server:-N/A}"
|
printf "%-15s %-30s %-20s\n" "${backend:-N/A}" "$domain" "${server:-N/A}"
|
||||||
done
|
found=1
|
||||||
fi
|
done
|
||||||
|
|
||||||
|
[ "$found" = "0" ] && echo " No SSL backends configured"
|
||||||
echo ""
|
echo ""
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -491,38 +549,51 @@ cmd_ssl_remove() {
|
|||||||
|
|
||||||
load_config
|
load_config
|
||||||
|
|
||||||
if [ ! -f "$HAPROXY_CONFIG" ]; then
|
# Check if haproxyctl exists
|
||||||
log_err "HAProxy config not found"
|
if [ ! -x "/usr/sbin/haproxyctl" ]; then
|
||||||
|
log_err "haproxyctl not found"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! grep -q "backend ${service}_backend" "$HAPROXY_CONFIG"; then
|
local backend_name="$service"
|
||||||
log_err "No backend found for $service"
|
local removed=0
|
||||||
|
|
||||||
|
# Find and remove vhosts pointing to this backend
|
||||||
|
for vhost in $(uci show haproxy 2>/dev/null | grep "=vhost$" | cut -d'.' -f2 | cut -d'=' -f1); do
|
||||||
|
local vhost_backend=$(uci -q get "haproxy.${vhost}.backend")
|
||||||
|
if [ "$vhost_backend" = "$backend_name" ]; then
|
||||||
|
log_info "Removing vhost '$vhost'"
|
||||||
|
uci delete "haproxy.${vhost}"
|
||||||
|
removed=1
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Remove backend if it exists
|
||||||
|
if uci -q get "haproxy.${backend_name}" >/dev/null 2>&1; then
|
||||||
|
log_info "Removing backend '$backend_name'"
|
||||||
|
uci delete "haproxy.${backend_name}"
|
||||||
|
removed=1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$removed" = "0" ]; then
|
||||||
|
log_err "No backend or vhost found for '$service'"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
log_info "Removing SSL backend for $service"
|
# Commit HAProxy UCI changes
|
||||||
|
uci commit haproxy
|
||||||
|
|
||||||
# Remove ACL and use_backend lines
|
# Update exposure UCI
|
||||||
sed -i "/acl host_${service} /d" "$HAPROXY_CONFIG"
|
|
||||||
sed -i "/use_backend ${service}_backend/d" "$HAPROXY_CONFIG"
|
|
||||||
|
|
||||||
# Remove backend block
|
|
||||||
sed -i "/# Backend for $service/,/^$/d" "$HAPROXY_CONFIG"
|
|
||||||
sed -i "/^backend ${service}_backend$/,/^$/d" "$HAPROXY_CONFIG"
|
|
||||||
|
|
||||||
# Update UCI
|
|
||||||
uci delete "${CONFIG_NAME}.${service}.ssl" 2>/dev/null
|
uci delete "${CONFIG_NAME}.${service}.ssl" 2>/dev/null
|
||||||
uci delete "${CONFIG_NAME}.${service}.ssl_domain" 2>/dev/null
|
uci delete "${CONFIG_NAME}.${service}.ssl_domain" 2>/dev/null
|
||||||
uci commit "$CONFIG_NAME"
|
uci commit "$CONFIG_NAME"
|
||||||
|
|
||||||
# Reload HAProxy
|
# Regenerate and reload HAProxy
|
||||||
if [ -x "/usr/sbin/haproxyctl" ]; then
|
log_info "Regenerating HAProxy config..."
|
||||||
/usr/sbin/haproxyctl reload
|
/usr/sbin/haproxyctl generate
|
||||||
else
|
|
||||||
lxc-attach -n haproxy -- /etc/init.d/haproxy reload 2>/dev/null || \
|
log_info "Reloading HAProxy..."
|
||||||
/etc/init.d/haproxy reload 2>/dev/null
|
/usr/sbin/haproxyctl reload
|
||||||
fi
|
|
||||||
|
|
||||||
log_ok "SSL backend removed"
|
log_ok "SSL backend removed"
|
||||||
}
|
}
|
||||||
@ -563,16 +634,19 @@ cmd_status() {
|
|||||||
fi
|
fi
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
# HAProxy backends
|
# HAProxy backends (from UCI)
|
||||||
local ssl_backends=0
|
local ssl_backends=0
|
||||||
[ -f "$HAPROXY_CONFIG" ] && ssl_backends=$(grep -c "^backend.*_backend$" "$HAPROXY_CONFIG" 2>/dev/null || echo 0)
|
echo -e "${BLUE}HAProxy SSL Backends:${NC}"
|
||||||
echo -e "${BLUE}HAProxy SSL Backends:${NC} $ssl_backends"
|
for vhost in $(uci show haproxy 2>/dev/null | grep "=vhost$" | cut -d'.' -f2 | cut -d'=' -f1); do
|
||||||
if [ "$ssl_backends" -gt 0 ] && [ -f "$HAPROXY_CONFIG" ]; then
|
local domain=$(uci -q get "haproxy.${vhost}.domain")
|
||||||
grep -E "^backend .+_backend$" "$HAPROXY_CONFIG" | while read line; do
|
local backend=$(uci -q get "haproxy.${vhost}.backend")
|
||||||
local backend=$(echo "$line" | awk '{print $2}' | sed 's/_backend$//')
|
local enabled=$(uci -q get "haproxy.${vhost}.enabled")
|
||||||
echo " - $backend"
|
[ "$enabled" != "1" ] && continue
|
||||||
done
|
[ -z "$domain" ] && continue
|
||||||
fi
|
echo " - ${backend}: ${domain}"
|
||||||
|
ssl_backends=$((ssl_backends + 1))
|
||||||
|
done
|
||||||
|
[ "$ssl_backends" = "0" ] && echo " (none configured)"
|
||||||
echo ""
|
echo ""
|
||||||
echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
|
echo -e "${CYAN}═══════════════════════════════════════════════════════════════${NC}"
|
||||||
}
|
}
|
||||||
@ -592,6 +666,7 @@ COMMANDS:
|
|||||||
tor add <svc> [port] Create Tor hidden service
|
tor add <svc> [port] Create Tor hidden service
|
||||||
tor list List hidden services
|
tor list List hidden services
|
||||||
tor remove <svc> Remove hidden service
|
tor remove <svc> Remove hidden service
|
||||||
|
tor sync Sync hidden services to Tor Shield
|
||||||
|
|
||||||
ssl add <svc> <domain> Add HAProxy SSL backend
|
ssl add <svc> <domain> Add HAProxy SSL backend
|
||||||
ssl list List SSL backends
|
ssl list List SSL backends
|
||||||
@ -636,7 +711,8 @@ case "$1" in
|
|||||||
add) cmd_tor_add "$3" "$4" "$5" ;;
|
add) cmd_tor_add "$3" "$4" "$5" ;;
|
||||||
list) cmd_tor_list ;;
|
list) cmd_tor_list ;;
|
||||||
remove) cmd_tor_remove "$3" ;;
|
remove) cmd_tor_remove "$3" ;;
|
||||||
*) log_err "Usage: secubox-exposure tor {add|list|remove}"; exit 1 ;;
|
sync) cmd_tor_sync ;;
|
||||||
|
*) log_err "Usage: secubox-exposure tor {add|list|remove|sync}"; exit 1 ;;
|
||||||
esac
|
esac
|
||||||
;;
|
;;
|
||||||
ssl)
|
ssl)
|
||||||
|
|||||||
@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
|
|||||||
|
|
||||||
PKG_NAME:=secubox-app-haproxy
|
PKG_NAME:=secubox-app-haproxy
|
||||||
PKG_VERSION:=1.0.0
|
PKG_VERSION:=1.0.0
|
||||||
PKG_RELEASE:=14
|
PKG_RELEASE:=15
|
||||||
|
|
||||||
PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>
|
PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>
|
||||||
PKG_LICENSE:=MIT
|
PKG_LICENSE:=MIT
|
||||||
|
|||||||
@ -246,6 +246,20 @@ echo "Config: $CONFIG_FILE"
|
|||||||
ls -la /opt/haproxy/
|
ls -la /opt/haproxy/
|
||||||
ls -la /opt/haproxy/certs/ 2>/dev/null || echo "No certs dir"
|
ls -la /opt/haproxy/certs/ 2>/dev/null || echo "No certs dir"
|
||||||
|
|
||||||
|
# Fix certificate key naming for HAProxy compatibility
|
||||||
|
# HAProxy expects .crt.key when it finds a .crt file in the directory
|
||||||
|
if [ -d "/opt/haproxy/certs" ]; then
|
||||||
|
for crt in /opt/haproxy/certs/*.crt; do
|
||||||
|
[ -f "$crt" ] || continue
|
||||||
|
base="${crt%.crt}"
|
||||||
|
# If .key exists but .crt.key doesn't, rename it
|
||||||
|
if [ -f "${base}.key" ] && [ ! -f "${crt}.key" ]; then
|
||||||
|
echo "[haproxy] Renaming ${base}.key -> ${crt}.key"
|
||||||
|
mv "${base}.key" "${crt}.key"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
|
||||||
# Wait for config
|
# Wait for config
|
||||||
if [ ! -f "$CONFIG_FILE" ]; then
|
if [ ! -f "$CONFIG_FILE" ]; then
|
||||||
echo "[haproxy] Config not found, generating default..."
|
echo "[haproxy] Config not found, generating default..."
|
||||||
@ -637,6 +651,13 @@ cmd_cert_add() {
|
|||||||
log_info "Creating combined PEM for HAProxy..."
|
log_info "Creating combined PEM for HAProxy..."
|
||||||
cat "$CERTS_PATH/$domain.fullchain.pem" "$CERTS_PATH/$domain.key" > "$CERTS_PATH/$domain.pem"
|
cat "$CERTS_PATH/$domain.fullchain.pem" "$CERTS_PATH/$domain.key" > "$CERTS_PATH/$domain.pem"
|
||||||
chmod 600 "$CERTS_PATH/$domain.pem"
|
chmod 600 "$CERTS_PATH/$domain.pem"
|
||||||
|
|
||||||
|
# HAProxy expects key files named <cert>.key when loading .crt files from directory
|
||||||
|
# Rename the key file to match the .crt file naming convention
|
||||||
|
if [ -f "$CERTS_PATH/$domain.crt" ] && [ -f "$CERTS_PATH/$domain.key" ]; then
|
||||||
|
mv "$CERTS_PATH/$domain.key" "$CERTS_PATH/$domain.crt.key"
|
||||||
|
chmod 600 "$CERTS_PATH/$domain.crt.key"
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Restart HAProxy if it was running
|
# Restart HAProxy if it was running
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user