feat(multi): CrowdSec LAPI port fix, Streamlit/HexoJS multi-instance

CrowdSec:
- Change LAPI default port from 8080 to 8180 (avoid Docker conflict)
- Update bouncer config, init script, and RPCD dashboard
- Fix port detection hex value (1FF4 for 8180)

Streamlit:
- Complete rewrite with folder-based app structure
- Multi-instance support (multiple apps on different ports)
- Gitea integration (clone, pull, setup commands)
- Auto-install requirements.txt with hash-based caching

HexoJS:
- Multi-instance support with folder structure
- Multiple blog instances on different ports

HAProxy:
- Auto-generate fallback backends (luci, apps, default_luci)
- Add --server letsencrypt to ACME commands

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-01-27 06:37:19 +01:00
parent 0be687b89b
commit 04908fc414
13 changed files with 1384 additions and 1012 deletions

View File

@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-crowdsec-dashboard
PKG_VERSION:=0.7.0
PKG_RELEASE:=28
PKG_RELEASE:=29
PKG_ARCH:=all
PKG_LICENSE:=Apache-2.0

View File

@ -181,14 +181,14 @@ get_status() {
elif ! grep -q "password:" "$creds_file" 2>/dev/null; then
lapi_reason="credentials incomplete"
else
# Check if LAPI port is listening (8080 hex = 1F90)
# Check if LAPI port is listening (8180 hex = 1FF4)
local port_up=0
if grep -qi ":1F90 " /proc/net/tcp 2>/dev/null; then
if grep -qi ":1FF4 " /proc/net/tcp 2>/dev/null; then
port_up=1
fi
if [ "$port_up" = "0" ]; then
lapi_reason="port 8080 not listening"
lapi_reason="port 8180 not listening"
else
# Try actual LAPI status check
if run_cscli lapi status >/dev/null 2>&1; then
@ -746,7 +746,7 @@ get_firewall_bouncer_config() {
val=$(uci -q get crowdsec.bouncer.ipv6 || echo "1")
json_add_string "ipv6" "$val"
val=$(uci -q get crowdsec.bouncer.api_url || echo "http://127.0.0.1:8080/")
val=$(uci -q get crowdsec.bouncer.api_url || echo "http://127.0.0.1:8180/")
json_add_string "api_url" "$val"
val=$(uci -q get crowdsec.bouncer.update_frequency || echo "10s")
@ -1822,7 +1822,7 @@ get_health_check() {
# LAPI status
local lapi_status="unavailable"
local lapi_url="http://127.0.0.1:8080"
local lapi_url="http://127.0.0.1:8180"
if [ -x "$CSCLI" ]; then
if run_with_timeout 5 "$CSCLI" lapi status >/dev/null 2>&1; then
lapi_status="available"

View File

@ -10,7 +10,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=secubox-app-cs-firewall-bouncer
PKG_VERSION:=0.0.31
PKG_RELEASE:=3
PKG_RELEASE:=4
# Source from upstream CrowdSec
# Note: v0.0.31 is the last version compatible with Go 1.23 (OpenWrt 24.10 SDK)

View File

@ -88,7 +88,7 @@ merge_config() {
uci set crowdsec.bouncer.enabled='0'
uci set crowdsec.bouncer.ipv4='1'
uci set crowdsec.bouncer.ipv6='1'
uci set crowdsec.bouncer.api_url='http://127.0.0.1:8080/'
uci set crowdsec.bouncer.api_url='http://127.0.0.1:8180/'
uci set crowdsec.bouncer.update_frequency='10s'
uci set crowdsec.bouncer.deny_action='drop'
uci set crowdsec.bouncer.deny_log='1'

View File

@ -50,7 +50,7 @@ init_yaml() {
config_get hook_priority $section priority "4"
config_get update_frequency $section update_frequency '10s'
config_get log_level $section log_level 'info'
config_get api_url $section api_url "http://127.0.0.1:8080"
config_get api_url $section api_url "http://127.0.0.1:8180"
config_get api_key $section api_key "API_KEY"
config_get_bool ipv6 $section ipv6 '1'
config_get deny_action $section deny_action "drop"

View File

@ -8,7 +8,7 @@ config bouncer
option enabled '0'
option ipv4 '1'
option ipv6 '1'
option api_url 'http://127.0.0.1:8080/'
option api_url 'http://127.0.0.1:8180/'
option api_key ''
option update_frequency '10s'
option priority '4'

View File

@ -6,7 +6,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=secubox-app-haproxy
PKG_VERSION:=1.0.0
PKG_RELEASE:=19
PKG_RELEASE:=21
PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>
PKG_LICENSE:=MIT

View File

@ -19,6 +19,7 @@ CONFIG_PATH="$DATA_PATH/config"
# Logging
log_info() { echo "[INFO] $*"; logger -t haproxy "$*"; }
log_warn() { echo "[WARN] $*" >&2; logger -t haproxy -p warning "$*"; }
log_error() { echo "[ERROR] $*" >&2; logger -t haproxy -p err "$*"; }
log_debug() { [ "$DEBUG" = "1" ] && echo "[DEBUG] $*"; }
@ -44,6 +45,7 @@ load_config() {
memory_limit="$(uci_get main.memory_limit)" || memory_limit="256M"
maxconn="$(uci_get main.maxconn)" || maxconn="4096"
log_level="$(uci_get main.log_level)" || log_level="warning"
default_backend="$(uci_get main.default_backend)" || default_backend="default_luci"
CERTS_PATH="$data_path/certs"
CONFIG_PATH="$data_path/config"
@ -288,11 +290,12 @@ frontend stats
frontend http-in
bind *:80
mode http
default_backend fallback
default_backend default_luci
backend fallback
backend default_luci
mode http
server local 127.0.0.1:8080 check
balance roundrobin
server luci 192.168.255.1:8081 check
CFGEOF
fi
@ -415,7 +418,7 @@ EOF
# Add vhost ACLs for HTTP
config_foreach _add_vhost_acl vhost "http"
echo " default_backend fallback"
echo " default_backend $default_backend"
echo ""
# HTTPS Frontend (if certificates exist)
@ -432,7 +435,7 @@ EOF
# Add vhost ACLs for HTTPS
config_foreach _add_vhost_acl vhost "https"
echo " default_backend fallback"
echo " default_backend $default_backend"
echo ""
fi
}
@ -481,18 +484,43 @@ _add_vhost_acl() {
_generate_backends() {
config_load haproxy
# Track which backends are generated
_generated_backends=""
# Generate each backend from UCI
config_foreach _generate_backend backend
# Only add default fallback if no "fallback" backend exists in UCI
if ! uci -q get haproxy.fallback >/dev/null 2>&1; then
# Collect all backends referenced by vhosts
_referenced_backends=""
_collect_vhost_backend() {
local section="$1"
local enabled backend
config_get enabled "$section" enabled "0"
[ "$enabled" = "1" ] || return
config_get backend "$section" backend
[ -n "$backend" ] && _referenced_backends="$_referenced_backends $backend"
}
config_foreach _collect_vhost_backend vhost
# Add default_backend to referenced list
_referenced_backends="$_referenced_backends $default_backend"
# Generate fallback backends for any referenced but not generated
# These common backends route to uhttpd on the host
for backend_name in default_luci luci apps; do
# Check if this backend is referenced
echo "$_referenced_backends" | grep -qw "$backend_name" || continue
# Check if already generated
echo "$_generated_backends" | grep -qw "$backend_name" && continue
# Generate fallback
cat << EOF
backend fallback
backend $backend_name
mode http
http-request deny deny_status 503
balance roundrobin
server $backend_name 192.168.255.1:8081 check
EOF
fi
done
}
_generate_backend() {
@ -507,6 +535,9 @@ _generate_backend() {
config_get balance "$section" balance "roundrobin"
config_get health_check "$section" health_check ""
# Track generated backend
_generated_backends="$_generated_backends $name"
echo ""
echo "backend $name"
echo " mode $mode"
@ -759,7 +790,7 @@ cmd_cert_add() {
# Register account if needed
if [ ! -f "$LE_WORKING_DIR/account.conf" ]; then
log_info "Registering ACME account..."
"$ACME_SH" --register-account -m "$email" $staging_flag --home "$LE_WORKING_DIR" || true
"$ACME_SH" --register-account -m "$email" --server letsencrypt $staging_flag --home "$LE_WORKING_DIR" || true
fi
# Check if HAProxy is using the port
@ -775,6 +806,7 @@ cmd_cert_add() {
log_info "Issuing certificate (standalone mode on port $http_port)..."
local acme_result=0
"$ACME_SH" --issue -d "$domain" \
--server letsencrypt \
--standalone --httpport "$http_port" \
--keylength "$key_type" \
$staging_flag \

View File

@ -45,7 +45,7 @@ frontend http-in
# Default: redirect to HTTPS
http-request redirect scheme https code 301 unless is_acme
default_backend fallback
default_backend default_luci
# HTTPS frontend - SSL termination
frontend https-in
@ -62,14 +62,15 @@ frontend https-in
http-request set-header X-Real-IP %[src]
http-request set-header X-Forwarded-For %[src]
default_backend fallback
default_backend default_luci
# ACME challenge backend
backend acme
mode http
server acme 127.0.0.1:8080 check
# Fallback backend
backend fallback
# Default LuCI backend - routes to uhttpd
backend default_luci
mode http
http-request deny deny_status 503
balance roundrobin
server luci 192.168.255.1:8081 check

View File

@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=secubox-app-hexojs
PKG_VERSION:=1.0.0
PKG_RELEASE:=6
PKG_RELEASE:=8
PKG_ARCH:=all
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr>

File diff suppressed because it is too large Load Diff

View File

@ -8,7 +8,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=secubox-app-streamlit
PKG_VERSION:=1.0.0
PKG_RELEASE:=4
PKG_RELEASE:=5
PKG_ARCH:=all
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr>
@ -22,22 +22,25 @@ define Package/secubox-app-streamlit
PKGARCH:=all
SUBMENU:=SecuBox Apps
TITLE:=SecuBox Streamlit Platform
DEPENDS:=+uci +libuci +jsonfilter +wget-ssl +tar +lxc +lxc-common
DEPENDS:=+uci +libuci +jsonfilter +wget-ssl +tar +lxc +lxc-common +git
endef
define Package/secubox-app-streamlit/description
Streamlit App Platform - Self-hosted Python data app platform
Features:
- Run Streamlit apps in LXC container
- Folder-based app structure (app.py, requirements.txt, .streamlit/)
- Multi-instance support (multiple apps on different ports)
- Python 3.12 with Streamlit 1.53.x
- Auto-install requirements.txt dependencies
- Gitea integration for app deployment and updates
- Python 3.12 with Streamlit in LXC container
- Auto-install requirements.txt with hash-based caching
- HAProxy publish wizard for vhost routing
- Web dashboard integration
- Configurable port and memory limits
Runs in LXC container with Alpine Linux.
App folder structure:
/srv/streamlit/apps/<appname>/
app.py, requirements.txt, .streamlit/
Configure in /etc/config/streamlit.
endef
@ -74,7 +77,9 @@ define Package/secubox-app-streamlit/postinst
echo ""
echo "Web interface: http://<router-ip>:8501"
echo ""
echo "Deploy your apps with: streamlitctl app add <name> /path/to/app.py"
echo "Create apps: streamlitctl app create myapp"
echo "Add instance: streamlitctl instance add myapp 8502"
echo "Gitea clone: streamlitctl gitea clone myapp user/repo"
echo ""
}
exit 0