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:
parent
0be687b89b
commit
04908fc414
@ -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
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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)
|
||||||
|
|||||||
@ -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'
|
||||||
|
|||||||
@ -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"
|
||||||
|
|||||||
@ -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'
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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 \
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
@ -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
@ -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
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user