Merge pull request #14 from gkerma/release/v0.15.0

Release/v0.15.0
This commit is contained in:
CyberMind 2026-01-27 08:40:58 +01:00 committed by GitHub
commit aeda39b516
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
49 changed files with 2300 additions and 1016 deletions

View File

@ -122,7 +122,24 @@
"Bash(gh run:*)",
"Bash(dig:*)",
"Bash(nslookup:*)",
"Bash(host:*)"
"Bash(host:*)",
"Bash(git fetch:*)",
"Bash(/home/reepost/CyberMindStudio/secubox-openwrt/secubox-tools/deploy-remote.sh:*)",
"Bash(for i in 1 2 3 4 5)",
"Bash(do echo \"Attempt $i...\")",
"Bash(if ssh -o ConnectTimeout=10 root@192.168.255.1 'echo \"\"Connected\"\"; df -h /')",
"Bash(then break)",
"Bash(fi)",
"Bash(done)",
"Bash(for i in 1 2 3 4 5 6)",
"Bash(do echo \"Checking... \\($i\\)\")",
"Bash(if ssh -o ConnectTimeout=10 root@192.168.255.1 'df -h / /overlay 2>/dev/null')",
"Bash(./secubox-tools/deploy-remote.sh:*)",
"Bash(do)",
"Bash(if ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no root@192.168.255.1 \"echo ''Router is back!''\")",
"Bash(then)",
"Bash(exit 0)",
"Bash(if ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no root@192.168.255.1 \"echo ''Router online!''\")"
]
}
}

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

View File

View File

View File

@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-hexojs
PKG_VERSION:=1.0.0
PKG_RELEASE:=2
PKG_RELEASE:=3
PKG_ARCH:=all
PKG_LICENSE:=MIT

View File

@ -154,6 +154,13 @@ var callDeployStatus = rpc.declare({
expect: {}
});
var callPublishToWww = rpc.declare({
object: 'luci.hexojs',
method: 'publish_to_www',
params: ['path'],
expect: {}
});
// ============================================
// Preview
// ============================================
@ -478,6 +485,7 @@ return baseclass.extend({
clean: callClean,
deploy: callDeploy,
getDeployStatus: callDeployStatus,
publishToWww: callPublishToWww,
// Preview
previewStart: callPreviewStart,

View File

@ -28,6 +28,19 @@ var callGiteaSaveConfig = rpc.declare({
params: ['enabled', 'gitea_url', 'gitea_user', 'gitea_token', 'content_repo', 'content_branch', 'auto_sync']
});
var callBuild = rpc.declare({
object: 'luci.hexojs',
method: 'generate',
expect: {}
});
var callPublishToWww = rpc.declare({
object: 'luci.hexojs',
method: 'publish_to_www',
params: ['path'],
expect: {}
});
return view.extend({
title: _('Content Sync'),
wizardStep: 0,
@ -210,6 +223,50 @@ return view.extend({
});
},
// ============================================
// Build & Publish Actions
// ============================================
handleBuild: function() {
ui.showModal(_('Building Site'), [
E('p', { 'class': 'spinning' }, _('Generating static files...'))
]);
callBuild().then(function(result) {
ui.hideModal();
if (result.success) {
ui.addNotification(null, E('p', _('Site built successfully!')), 'info');
} else {
ui.addNotification(null, E('p', result.error || _('Build failed')), 'error');
}
}).catch(function(err) {
ui.hideModal();
ui.addNotification(null, E('p', _('Error: %s').format(err.message || err)), 'error');
});
},
handlePublish: function() {
var self = this;
var path = document.querySelector('#publish-path');
var publishPath = path ? path.value : '/www/blog';
ui.showModal(_('Publishing Site'), [
E('p', { 'class': 'spinning' }, _('Building and publishing to %s...').format(publishPath))
]);
callPublishToWww(publishPath).then(function(result) {
ui.hideModal();
if (result.success) {
ui.addNotification(null, E('p', _('Published to %s!').format(result.path || publishPath)), 'info');
} else {
ui.addNotification(null, E('p', result.error || _('Publish failed')), 'error');
}
}).catch(function(err) {
ui.hideModal();
ui.addNotification(null, E('p', _('Error: %s').format(err.message || err)), 'error');
});
},
// ============================================
// Gitea Actions
// ============================================
@ -297,6 +354,49 @@ return view.extend({
]) : ''
]),
// Build & Publish Card
E('div', { 'class': 'hexo-card' }, [
E('div', { 'class': 'hexo-card-header' }, [
E('div', { 'class': 'hexo-card-title' }, [
E('span', { 'style': 'margin-right: 8px;' }, '\uD83D\uDE80'),
_('Build & Publish')
])
]),
E('p', { 'style': 'margin-bottom: 16px; color: var(--hexo-text-muted);' },
_('Build static files and publish to web server.')),
E('div', { 'style': 'display: grid; grid-template-columns: 1fr 1fr; gap: 16px;' }, [
// Build Section
E('div', { 'class': 'hexo-card', 'style': 'background: var(--hexo-bg-input);' }, [
E('h4', { 'style': 'margin-bottom: 8px;' }, ['\uD83D\uDD28 ', _('Build')]),
E('p', { 'style': 'font-size: 13px; color: var(--hexo-text-muted); margin-bottom: 12px;' },
_('Generate static HTML files from your content.')),
E('button', {
'class': 'hexo-btn hexo-btn-primary',
'click': function() { self.handleBuild(); }
}, _('Build Site'))
]),
// Publish Section
E('div', { 'class': 'hexo-card', 'style': 'background: var(--hexo-bg-input);' }, [
E('h4', { 'style': 'margin-bottom: 8px;' }, ['\uD83C\uDF10 ', _('Publish')]),
E('p', { 'style': 'font-size: 13px; color: var(--hexo-text-muted); margin-bottom: 12px;' },
_('Copy built files to web server directory.')),
E('div', { 'class': 'hexo-form-group', 'style': 'margin-bottom: 8px;' }, [
E('input', {
'type': 'text',
'id': 'publish-path',
'class': 'hexo-input',
'value': '/www/blog',
'placeholder': '/www/blog'
})
]),
E('button', {
'class': 'hexo-btn hexo-btn-success',
'click': function() { self.handlePublish(); }
}, _('Publish to Web'))
])
])
]),
// Gitea Integration Card
E('div', { 'class': 'hexo-card' }, [
E('div', { 'class': 'hexo-card-header' }, [

View File

@ -1544,6 +1544,45 @@ gitea_save_config() {
json_dump
}
# Publish to /www (portal)
publish_to_www() {
read input
json_load "$input"
json_get_var path path
json_init
if ! is_running; then
json_add_boolean "success" 0
json_add_string "error" "Container not running"
json_dump
return
fi
# Default path is /www/blog
[ -z "$path" ] && path="/www/blog"
# Set portal path in UCI for hexoctl
uci set hexojs.portal=hexojs
uci set hexojs.portal.path="$path"
uci commit hexojs
# Run publish command
local output=$("$HEXOCTL" publish 2>&1)
local result=$?
if [ "$result" -eq 0 ]; then
json_add_boolean "success" 1
json_add_string "message" "Published to $path"
json_add_string "path" "$path"
else
json_add_boolean "success" 0
json_add_string "error" "$output"
fi
json_dump
}
# ============================================
# Service Control
# ============================================
@ -1634,7 +1673,8 @@ case "$1" in
"gitea_setup": {},
"gitea_clone": {},
"gitea_sync": {},
"gitea_save_config": {"enabled": "bool", "gitea_url": "str", "gitea_user": "str", "gitea_token": "str", "content_repo": "str", "content_branch": "str", "auto_sync": "bool"}
"gitea_save_config": {"enabled": "bool", "gitea_url": "str", "gitea_user": "str", "gitea_token": "str", "content_repo": "str", "content_branch": "str", "auto_sync": "bool"},
"publish_to_www": {"path": "str"}
}
EOF
;;
@ -1686,6 +1726,7 @@ EOF
gitea_clone) gitea_clone ;;
gitea_sync) gitea_sync ;;
gitea_save_config) gitea_save_config ;;
publish_to_www) publish_to_www ;;
*) echo '{"error": "Unknown method"}' ;;
esac
;;

View File

@ -56,7 +56,8 @@
"gitea_setup",
"gitea_clone",
"gitea_sync",
"gitea_save_config"
"gitea_save_config",
"publish_to_www"
]
},
"uci": ["hexojs"]

View File

View File

View File

View File

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

View File

View File

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

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>

View File

File diff suppressed because it is too large Load Diff

View File

View File

View File

0
package/secubox/secubox-app-mmpm/files/etc/init.d/mmpm Normal file → Executable file
View File

View File

View File

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

View File

View File

View File

@ -0,0 +1,235 @@
#!/bin/sh /etc/rc.common
# SecuBox Extroot Auto-Setup
# Automatically configures overlay on mmcblk0p3 after fresh install/upgrade
# Copyright (C) 2025 CyberMind.fr
START=10
STOP=90
EXTRA_COMMANDS="status setup"
EXTRA_HELP=" status Show extroot status
setup Configure extroot on mmcblk0p3"
OVERLAY_DEV="/dev/mmcblk0p3"
OVERLAY_LABEL="rootfs_data"
LOG_TAG="secubox-extroot"
log() { logger -t "$LOG_TAG" "$*"; echo "$*"; }
# Check if extroot is already active
is_extroot_active() {
mount | grep -q "on /overlay type" && [ -d /overlay/upper ]
}
# Check if overlay partition exists
has_overlay_partition() {
[ -b "$OVERLAY_DEV" ] || return 1
blkid "$OVERLAY_DEV" >/dev/null 2>&1
}
# Get filesystem type of overlay partition
get_overlay_fstype() {
blkid "$OVERLAY_DEV" -s TYPE -o value 2>/dev/null
}
# Get UUID of overlay partition
get_overlay_uuid() {
blkid "$OVERLAY_DEV" -s UUID -o value 2>/dev/null
}
# Install required filesystem support
install_fs_support() {
local fstype="$1"
case "$fstype" in
f2fs)
if ! opkg list-installed 2>/dev/null | grep -q "^kmod-fs-f2fs"; then
log "Installing f2fs support..."
opkg update >/dev/null 2>&1
opkg install kmod-fs-f2fs f2fs-tools 2>/dev/null || true
fi
;;
ext4)
if ! opkg list-installed 2>/dev/null | grep -q "^kmod-fs-ext4"; then
log "Installing ext4 support..."
opkg update >/dev/null 2>&1
opkg install kmod-fs-ext4 e2fsprogs 2>/dev/null || true
fi
;;
esac
# Always ensure block-mount is available
if ! opkg list-installed 2>/dev/null | grep -q "^block-mount"; then
log "Installing block-mount..."
opkg install block-mount 2>/dev/null || true
fi
}
# Prepare overlay partition structure
prepare_overlay() {
local fstype="$1"
local mount_point="/tmp/extroot_setup"
mkdir -p "$mount_point"
# Mount the partition
if ! mount -t "$fstype" "$OVERLAY_DEV" "$mount_point" 2>/dev/null; then
log "Failed to mount $OVERLAY_DEV"
return 1
fi
# Create overlay directories
mkdir -p "$mount_point/upper"
mkdir -p "$mount_point/work"
# Copy existing data if overlay was previously active
if [ -d /overlay/upper ] && [ "$(ls -A /overlay/upper 2>/dev/null)" ]; then
log "Migrating existing overlay data..."
cp -a /overlay/upper/* "$mount_point/upper/" 2>/dev/null || true
fi
sync
umount "$mount_point"
rmdir "$mount_point"
return 0
}
# Configure fstab for extroot
configure_fstab() {
local uuid="$1"
local fstype="$2"
# Check if already configured
if uci -q get fstab.extroot >/dev/null 2>&1; then
local current_uuid=$(uci -q get fstab.extroot.uuid)
if [ "$current_uuid" = "$uuid" ]; then
log "Extroot already configured"
return 0
fi
fi
log "Configuring fstab for extroot..."
# Remove old extroot config if exists
uci -q delete fstab.extroot 2>/dev/null || true
# Add new extroot configuration
uci set fstab.extroot=mount
uci set fstab.extroot.target="/overlay"
uci set fstab.extroot.uuid="$uuid"
uci set fstab.extroot.enabled="1"
if [ "$fstype" = "f2fs" ]; then
uci set fstab.extroot.fstype="f2fs"
uci set fstab.extroot.options="rw,noatime"
fi
uci commit fstab
log "Fstab configured - reboot required to activate"
return 0
}
# Main setup function
setup_extroot() {
log "SecuBox Extroot Setup starting..."
# Check if already active
if is_extroot_active; then
log "Extroot already active on /overlay"
df -h /overlay | tail -1
return 0
fi
# Check for overlay partition
if ! has_overlay_partition; then
log "No overlay partition found at $OVERLAY_DEV"
return 1
fi
# Get filesystem info
local fstype=$(get_overlay_fstype)
local uuid=$(get_overlay_uuid)
if [ -z "$fstype" ] || [ -z "$uuid" ]; then
log "Cannot determine filesystem type or UUID"
return 1
fi
log "Found overlay partition: $OVERLAY_DEV ($fstype, UUID=$uuid)"
# Install filesystem support
install_fs_support "$fstype"
# Prepare the partition
if ! prepare_overlay "$fstype"; then
log "Failed to prepare overlay partition"
return 1
fi
# Configure fstab
if ! configure_fstab "$uuid" "$fstype"; then
log "Failed to configure fstab"
return 1
fi
log "Extroot setup complete - REBOOT REQUIRED"
# Create flag file to indicate reboot needed
touch /tmp/extroot-reboot-required
return 0
}
start() {
# Only run setup if not already configured or after upgrade
if is_extroot_active; then
log "Extroot active"
return 0
fi
if has_overlay_partition; then
setup_extroot
fi
}
stop() {
return 0
}
boot() {
start
}
# Manual commands
status() {
echo "=== SecuBox Extroot Status ==="
echo ""
if is_extroot_active; then
echo "Status: ACTIVE"
echo ""
echo "Overlay mount:"
mount | grep overlay
echo ""
echo "Storage:"
df -h /overlay
else
echo "Status: NOT ACTIVE"
echo ""
if has_overlay_partition; then
echo "Overlay partition available: $OVERLAY_DEV"
echo "Filesystem: $(get_overlay_fstype)"
echo "UUID: $(get_overlay_uuid)"
echo ""
echo "Run '/etc/init.d/secubox-extroot setup' to configure"
else
echo "No overlay partition found"
fi
fi
}
setup() {
setup_extroot
}

View File

@ -0,0 +1,23 @@
#!/bin/bash
# Deploy SecuBox Extroot init.d script to router
# Usage: ./deploy-extroot-init.sh [ROUTER_IP]
ROUTER_IP="${1:-192.168.255.1}"
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
echo "Deploying secubox-extroot to $ROUTER_IP..."
# Deploy the init script
cat "$SCRIPT_DIR/../package/secubox/secubox-core/files/etc/init.d/secubox-extroot" | \
ssh root@$ROUTER_IP 'cat > /etc/init.d/secubox-extroot && chmod 755 /etc/init.d/secubox-extroot'
# Enable and run setup
ssh root@$ROUTER_IP '
/etc/init.d/secubox-extroot enable
/etc/init.d/secubox-extroot setup
/etc/init.d/secubox-extroot status
'
echo ""
echo "Done! If setup was successful, reboot to activate extroot:"
echo " ssh root@$ROUTER_IP reboot"

226
secubox-tools/deploy-remote.sh Executable file
View File

@ -0,0 +1,226 @@
#!/bin/bash
# SecuBox Remote Deployment Script
# Deploys all SecuBox packages to a remote OpenWrt router
set -e
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
BUILD_DIR="$SCRIPT_DIR/sdk/bin/packages/aarch64_cortex-a72/secubox"
ROUTER_IP="${1:-192.168.255.1}"
ROUTER_USER="${2:-root}"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
info() { echo -e "${BLUE} $*${NC}"; }
success() { echo -e "${GREEN}$*${NC}"; }
warn() { echo -e "${YELLOW}⚠️ $*${NC}"; }
error() { echo -e "${RED}$*${NC}"; exit 1; }
# Check SSH connectivity
check_ssh() {
info "Checking SSH connectivity to $ROUTER_IP..."
if ! ssh -o ConnectTimeout=10 "$ROUTER_USER@$ROUTER_IP" "echo ok" 2>/dev/null; then
error "Cannot connect to $ROUTER_IP via SSH"
fi
success "SSH connection OK"
}
# Setup local feed on router
setup_feed() {
info "Setting up local package feed on router..."
ssh "$ROUTER_USER@$ROUTER_IP" 'mkdir -p /www/secubox-feed /tmp/secubox-install'
# Copy packages
info "Copying packages to router (this may take a while)..."
scp -q "$BUILD_DIR"/*.ipk "$ROUTER_USER@$ROUTER_IP:/www/secubox-feed/" 2>/dev/null || true
# Generate Packages index
info "Generating package index..."
ssh "$ROUTER_USER@$ROUTER_IP" 'cd /www/secubox-feed && {
rm -f Packages Packages.gz
for ipk in *.ipk; do
[ -f "$ipk" ] || continue
tar -xOzf "$ipk" ./control.tar.gz 2>/dev/null | tar -xOz ./control 2>/dev/null >> Packages
echo "Filename: $ipk" >> Packages
echo "" >> Packages
done
gzip -k Packages
}'
# Configure opkg
ssh "$ROUTER_USER@$ROUTER_IP" 'grep -q "secubox-feed" /etc/opkg/customfeeds.conf 2>/dev/null || {
echo "src/gz secubox file:///www/secubox-feed" >> /etc/opkg/customfeeds.conf
}'
success "Local feed configured"
}
# Install core packages first
install_core() {
info "Installing core SecuBox packages..."
ssh "$ROUTER_USER@$ROUTER_IP" 'opkg update 2>/dev/null
# Core packages (order matters)
for pkg in secubox-core; do
if [ -f "/www/secubox-feed/${pkg}_"*.ipk ]; then
echo "Installing $pkg..."
opkg install --force-reinstall /www/secubox-feed/${pkg}_*.ipk 2>&1 || true
fi
done'
success "Core packages installed"
}
# Install LuCI apps
install_luci_apps() {
info "Installing LuCI applications..."
ssh "$ROUTER_USER@$ROUTER_IP" '
cd /www/secubox-feed
# Skip large bonus package and packages with missing deps
SKIP="secubox-bonus|auth-guardian|ksm-manager|vhost-manager|zigbee"
for ipk in luci-app-*.ipk; do
[ -f "$ipk" ] || continue
# Skip excluded packages
if echo "$ipk" | grep -qE "$SKIP"; then
echo "Skipping: $ipk (excluded)"
continue
fi
pkg_name=$(echo "$ipk" | sed "s/_[0-9].*//")
echo "Installing: $pkg_name"
opkg install --force-reinstall "/www/secubox-feed/$ipk" 2>&1 | grep -v "^Collected" || true
done'
success "LuCI apps installed"
}
# Install service packages
install_services() {
info "Installing service packages..."
ssh "$ROUTER_USER@$ROUTER_IP" '
cd /www/secubox-feed
for ipk in secubox-app-*.ipk; do
[ -f "$ipk" ] || continue
pkg_name=$(echo "$ipk" | sed "s/_[0-9].*//")
echo "Installing: $pkg_name"
opkg install --force-reinstall "/www/secubox-feed/$ipk" 2>&1 | grep -v "^Collected" || true
done'
success "Service packages installed"
}
# Fix permissions and restart services
finalize() {
info "Finalizing installation..."
ssh "$ROUTER_USER@$ROUTER_IP" '
# Fix RPCD script permissions
chmod 755 /usr/libexec/rpcd/luci.* 2>/dev/null || true
# Restart rpcd
/etc/init.d/rpcd restart
# Clear LuCI cache
rm -rf /tmp/luci-modulecache /tmp/luci-indexcache 2>/dev/null
# Show installed packages
echo ""
echo "=== Installed SecuBox packages ==="
opkg list-installed | grep -E "^(luci-app-|secubox-)" | wc -l
echo "packages installed"
'
success "Installation complete!"
}
# Generate apps-local.json for store UI
generate_apps_json() {
info "Generating apps manifest..."
ssh "$ROUTER_USER@$ROUTER_IP" 'cd /www/secubox-feed && {
cat > apps-local.json << "HEADER"
{
"feed_url": "/secubox-feed",
"generated": "TIMESTAMP",
"packages": [
HEADER
sed -i "s/TIMESTAMP/$(date -Iseconds)/" apps-local.json
first=true
for pkg in *.ipk; do
[ -f "$pkg" ] || continue
filename="$pkg"
name=$(echo "$filename" | sed "s/_[0-9].*$//")
version=$(echo "$filename" | sed "s/^[^_]*_//; s/_[^_]*$//")
size=$(stat -c%s "$pkg" 2>/dev/null || ls -l "$pkg" | awk "{print \$5}")
# Category based on name
category="utility"
case "$name" in
*crowdsec*|*mitmproxy*|*tor*) category="security";;
*bandwidth*|*traffic*|*network*|*wireguard*) category="network";;
*hexojs*|*gitea*|*streamlit*) category="apps";;
*secubox*) category="system";;
esac
[ "$first" = "true" ] || echo "," >> apps-local.json
first=false
cat >> apps-local.json << ENTRY
{
"name": "$name",
"version": "$version",
"filename": "$filename",
"size": $size,
"category": "$category"
}
ENTRY
done
echo " ]" >> apps-local.json
echo "}" >> apps-local.json
}'
success "Apps manifest generated"
}
# Main
main() {
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " SecuBox Remote Deployment"
echo " Target: $ROUTER_USER@$ROUTER_IP"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
check_ssh
setup_feed
install_core
install_services
install_luci_apps
generate_apps_json
finalize
echo ""
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
success "Deployment complete!"
echo " Access LuCI at: http://$ROUTER_IP"
echo " Local feed at: http://$ROUTER_IP/secubox-feed/"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
}
main "$@"

85
secubox-tools/fix-extroot.sh Executable file
View File

@ -0,0 +1,85 @@
#!/bin/sh
# Fix extroot by formatting mmcblk0p3 fresh
# Run this from console or SSH after failsafe boot
OVERLAY_DEV="/dev/mmcblk0p3"
echo "=== SecuBox Extroot Fix ==="
echo ""
# Stop services using overlay
echo "Stopping services..."
for svc in /etc/init.d/*; do
[ -x "$svc" ] && "$svc" stop 2>/dev/null &
done
sleep 3
# Kill processes using overlay
echo "Killing processes using overlay..."
fuser -km /overlay 2>/dev/null || true
sleep 2
# Unmount everything
echo "Unmounting filesystems..."
umount -l /overlay 2>/dev/null || true
umount -l /mnt/extroot 2>/dev/null || true
umount -l "$OVERLAY_DEV" 2>/dev/null || true
sync
sleep 2
# Verify unmounted
if mount | grep -q "$OVERLAY_DEV"; then
echo "ERROR: Cannot unmount $OVERLAY_DEV"
echo "Try: reboot into failsafe mode and run this script"
exit 1
fi
echo ""
echo "Formatting $OVERLAY_DEV as f2fs..."
mkfs.f2fs -f -l rootfs_data "$OVERLAY_DEV" || {
echo "Format failed! Try: mkfs.ext4 -L rootfs_data $OVERLAY_DEV"
exit 1
}
echo ""
echo "Preparing overlay structure..."
mkdir -p /mnt/extroot
mount -t f2fs "$OVERLAY_DEV" /mnt/extroot
mkdir -p /mnt/extroot/upper /mnt/extroot/work
sync
umount /mnt/extroot
echo ""
echo "Configuring fstab..."
UUID=$(blkid "$OVERLAY_DEV" -s UUID -o value)
echo "UUID: $UUID"
# Write fstab config
cat > /etc/config/fstab << EOF
config global
option anon_swap '0'
option anon_mount '0'
option auto_swap '0'
option auto_mount '1'
option delay_root '5'
option check_fs '1'
config mount 'extroot'
option target '/overlay'
option uuid '$UUID'
option enabled '1'
option fstype 'f2fs'
option options 'rw,noatime'
config mount 'srv'
option target '/srv'
option uuid '443e8304-4d3b-4fd0-9f26-74c50ba64113'
option enabled '1'
EOF
echo ""
echo "=== Setup complete ==="
echo ""
cat /etc/config/fstab
echo ""
echo "Now reboot: reboot"

176
secubox-tools/setup-extroot.sh Executable file
View File

@ -0,0 +1,176 @@
#!/bin/bash
# SecuBox Extroot Setup Script
# Configures overlay on mmcblk0p3 for expanded storage
# Run after fresh install or upgrade
ROUTER_IP="${1:-192.168.255.1}"
ROUTER_USER="${2:-root}"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m'
info() { echo -e "${BLUE} $*${NC}"; }
success() { echo -e "${GREEN}$*${NC}"; }
warn() { echo -e "${YELLOW}⚠️ $*${NC}"; }
error() { echo -e "${RED}$*${NC}"; }
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo " SecuBox Extroot Setup"
echo " Target: $ROUTER_USER@$ROUTER_IP"
echo "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
echo ""
info "Connecting to router..."
ssh "$ROUTER_USER@$ROUTER_IP" '
#!/bin/sh
# Extroot setup script - runs on router
set -e
OVERLAY_DEV="/dev/mmcblk0p3"
OVERLAY_MOUNT="/mnt/extroot"
echo "=== Checking current setup ==="
echo "Root filesystem:"
df -h /
echo ""
# Check if overlay already mounted
if mount | grep -q "overlay on / "; then
echo "Overlay already active!"
df -h /overlay
exit 0
fi
# Check if partition exists
if [ ! -b "$OVERLAY_DEV" ]; then
echo "ERROR: Partition $OVERLAY_DEV not found"
exit 1
fi
# Get partition info
FSTYPE=$(blkid "$OVERLAY_DEV" -s TYPE -o value 2>/dev/null || echo "unknown")
echo "Partition: $OVERLAY_DEV"
echo "Filesystem: $FSTYPE"
echo ""
# Install required packages if not present
echo "=== Checking required packages ==="
opkg update 2>/dev/null || true
if [ "$FSTYPE" = "f2fs" ]; then
if ! opkg list-installed | grep -q "^kmod-fs-f2fs"; then
echo "Installing f2fs support..."
opkg install kmod-fs-f2fs f2fs-tools 2>/dev/null || true
fi
elif [ "$FSTYPE" = "ext4" ]; then
if ! opkg list-installed | grep -q "^kmod-fs-ext4"; then
echo "Installing ext4 support..."
opkg install kmod-fs-ext4 e2fsprogs 2>/dev/null || true
fi
fi
# Ensure block-mount is installed
if ! opkg list-installed | grep -q "^block-mount"; then
echo "Installing block-mount..."
opkg install block-mount 2>/dev/null || true
fi
echo ""
echo "=== Preparing overlay partition ==="
# Mount partition temporarily
mkdir -p "$OVERLAY_MOUNT"
mount -t "$FSTYPE" "$OVERLAY_DEV" "$OVERLAY_MOUNT" || {
echo "ERROR: Cannot mount $OVERLAY_DEV"
exit 1
}
# Create overlay directories
mkdir -p "$OVERLAY_MOUNT/upper"
mkdir -p "$OVERLAY_MOUNT/work"
# Copy existing overlay data if any
if [ -d /overlay/upper ]; then
echo "Copying existing overlay data..."
cp -a /overlay/upper/* "$OVERLAY_MOUNT/upper/" 2>/dev/null || true
fi
# Sync
sync
# Unmount
umount "$OVERLAY_MOUNT"
echo ""
echo "=== Configuring fstab ==="
# Get block info
eval $(block info "$OVERLAY_DEV" | grep -o "UUID=\S*")
echo "UUID: $UUID"
# Configure fstab using UCI
uci -q delete fstab.extroot 2>/dev/null || true
uci set fstab.extroot=mount
uci set fstab.extroot.target="/overlay"
uci set fstab.extroot.uuid="$UUID"
uci set fstab.extroot.enabled="1"
if [ "$FSTYPE" = "f2fs" ]; then
uci set fstab.extroot.fstype="f2fs"
uci set fstab.extroot.options="rw,noatime"
fi
uci commit fstab
echo ""
echo "=== Updated fstab ==="
cat /etc/config/fstab
echo ""
echo "=== Setup complete! ==="
echo ""
echo "IMPORTANT: Reboot to activate the overlay"
echo "After reboot, /overlay will be mounted on the 14GB partition"
echo ""
echo "Run: reboot"
'
if [ $? -eq 0 ]; then
success "Extroot configuration complete!"
echo ""
warn "REBOOT REQUIRED to activate overlay"
echo ""
read -p "Reboot router now? [y/N] " -n 1 -r
echo
if [[ $REPLY =~ ^[Yy]$ ]]; then
info "Rebooting router..."
ssh "$ROUTER_USER@$ROUTER_IP" "reboot" 2>/dev/null || true
echo ""
info "Waiting for router to come back online..."
sleep 60
# Wait for router to come back
for i in {1..30}; do
if ssh -o ConnectTimeout=5 "$ROUTER_USER@$ROUTER_IP" "echo ok" 2>/dev/null; then
success "Router is back online!"
echo ""
ssh "$ROUTER_USER@$ROUTER_IP" '
echo "=== Overlay status ==="
mount | grep overlay || echo "No overlay (yet)"
echo ""
echo "=== Disk usage ==="
df -h / /overlay 2>/dev/null
'
break
fi
echo "Waiting... ($i/30)"
sleep 5
done
fi
else
error "Setup failed"
fi