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_NAME:=luci-app-crowdsec-dashboard
PKG_VERSION:=0.7.0 PKG_VERSION:=0.7.0
PKG_RELEASE:=28 PKG_RELEASE:=29
PKG_ARCH:=all PKG_ARCH:=all
PKG_LICENSE:=Apache-2.0 PKG_LICENSE:=Apache-2.0

View File

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

View File

@ -10,7 +10,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=secubox-app-cs-firewall-bouncer PKG_NAME:=secubox-app-cs-firewall-bouncer
PKG_VERSION:=0.0.31 PKG_VERSION:=0.0.31
PKG_RELEASE:=3 PKG_RELEASE:=4
# Source from upstream CrowdSec # Source from upstream CrowdSec
# Note: v0.0.31 is the last version compatible with Go 1.23 (OpenWrt 24.10 SDK) # 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.enabled='0'
uci set crowdsec.bouncer.ipv4='1' uci set crowdsec.bouncer.ipv4='1'
uci set crowdsec.bouncer.ipv6='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.update_frequency='10s'
uci set crowdsec.bouncer.deny_action='drop' uci set crowdsec.bouncer.deny_action='drop'
uci set crowdsec.bouncer.deny_log='1' uci set crowdsec.bouncer.deny_log='1'

View File

@ -50,7 +50,7 @@ init_yaml() {
config_get hook_priority $section priority "4" config_get hook_priority $section priority "4"
config_get update_frequency $section update_frequency '10s' config_get update_frequency $section update_frequency '10s'
config_get log_level $section log_level 'info' 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 api_key $section api_key "API_KEY"
config_get_bool ipv6 $section ipv6 '1' config_get_bool ipv6 $section ipv6 '1'
config_get deny_action $section deny_action "drop" config_get deny_action $section deny_action "drop"

View File

@ -8,7 +8,7 @@ config bouncer
option enabled '0' option enabled '0'
option ipv4 '1' option ipv4 '1'
option ipv6 '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 api_key ''
option update_frequency '10s' option update_frequency '10s'
option priority '4' option priority '4'

View File

@ -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:=19 PKG_RELEASE:=21
PKG_MAINTAINER:=CyberMind <contact@cybermind.fr> PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>
PKG_LICENSE:=MIT PKG_LICENSE:=MIT

View File

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

View File

@ -45,7 +45,7 @@ frontend http-in
# Default: redirect to HTTPS # Default: redirect to HTTPS
http-request redirect scheme https code 301 unless is_acme http-request redirect scheme https code 301 unless is_acme
default_backend fallback default_backend default_luci
# HTTPS frontend - SSL termination # HTTPS frontend - SSL termination
frontend https-in frontend https-in
@ -62,14 +62,15 @@ frontend https-in
http-request set-header X-Real-IP %[src] http-request set-header X-Real-IP %[src]
http-request set-header X-Forwarded-For %[src] http-request set-header X-Forwarded-For %[src]
default_backend fallback default_backend default_luci
# ACME challenge backend # ACME challenge backend
backend acme backend acme
mode http mode http
server acme 127.0.0.1:8080 check server acme 127.0.0.1:8080 check
# Fallback backend # Default LuCI backend - routes to uhttpd
backend fallback backend default_luci
mode http 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_NAME:=secubox-app-hexojs
PKG_VERSION:=1.0.0 PKG_VERSION:=1.0.0
PKG_RELEASE:=6 PKG_RELEASE:=8
PKG_ARCH:=all PKG_ARCH:=all
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr> 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_NAME:=secubox-app-streamlit
PKG_VERSION:=1.0.0 PKG_VERSION:=1.0.0
PKG_RELEASE:=4 PKG_RELEASE:=5
PKG_ARCH:=all PKG_ARCH:=all
PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr> PKG_MAINTAINER:=CyberMind Studio <contact@cybermind.fr>
@ -22,22 +22,25 @@ define Package/secubox-app-streamlit
PKGARCH:=all PKGARCH:=all
SUBMENU:=SecuBox Apps SUBMENU:=SecuBox Apps
TITLE:=SecuBox Streamlit Platform 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 endef
define Package/secubox-app-streamlit/description define Package/secubox-app-streamlit/description
Streamlit App Platform - Self-hosted Python data app platform Streamlit App Platform - Self-hosted Python data app platform
Features: Features:
- Run Streamlit apps in LXC container - Folder-based app structure (app.py, requirements.txt, .streamlit/)
- Multi-instance support (multiple apps on different ports) - Multi-instance support (multiple apps on different ports)
- Python 3.12 with Streamlit 1.53.x - Gitea integration for app deployment and updates
- Auto-install requirements.txt dependencies - Python 3.12 with Streamlit in LXC container
- Auto-install requirements.txt with hash-based caching
- HAProxy publish wizard for vhost routing - HAProxy publish wizard for vhost routing
- Web dashboard integration - 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. Configure in /etc/config/streamlit.
endef endef
@ -74,7 +77,9 @@ define Package/secubox-app-streamlit/postinst
echo "" echo ""
echo "Web interface: http://<router-ip>:8501" echo "Web interface: http://<router-ip>:8501"
echo "" 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 "" echo ""
} }
exit 0 exit 0