diff --git a/.claude/settings.local.json b/.claude/settings.local.json index 68369e5d..93824476 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -4,7 +4,9 @@ "Bash(done)", "Bash(ls:*)", "Bash(find:*)", - "Bash(xargs:*)" + "Bash(xargs:*)", + "Bash(mkdir:*)", + "Bash(shellcheck:*)" ] } } diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000..20b81770 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "anthropic.claude-code" + ] +} \ No newline at end of file diff --git a/luci-app-secubox/README.md b/luci-app-secubox/README.md index a34c4acf..a1baba73 100644 --- a/luci-app-secubox/README.md +++ b/luci-app-secubox/README.md @@ -1,22 +1,75 @@ -# SecuBox - Security Suite for OpenWrt +# SecuBox Central Hub -Unified security dashboard providing central management for: +Central management dashboard for the SecuBox security and network management suite for OpenWrt. -- **CrowdSec** - Collaborative intrusion prevention +## Features + +### Dashboard Overview +- Real-time system health monitoring (CPU, Memory, Disk, Network) +- Visual gauges with color-coded status indicators +- Module status grid with quick access links +- Aggregated alerts from all modules +- Quick action buttons for common tasks + +### System Health Monitoring +- **CPU**: Load average and percentage with multi-core support +- **Memory**: RAM usage with total/used/available metrics +- **Disk**: Root filesystem usage and available space +- **Network**: Real-time RX/TX bandwidth statistics + +### Quick Actions +- Restart RPCD service +- Restart uHTTPd web server +- Clear system cache +- Create configuration backup +- Restart network services +- Restart firewall + +### Module Management +Auto-detection and status monitoring for all SecuBox modules: + +**Security & Monitoring** +- **CrowdSec** - Collaborative threat intelligence - **Netdata** - Real-time system monitoring -- **Netifyd** - Deep packet inspection and traffic analysis -- **WireGuard** - Modern VPN -- **Network Modes** - Simplified network configuration -- **Client Guardian** - Access control and captive portal -- **System Hub** - System control center -- **CDN Cache** - Bandwidth optimization proxy -- **Traffic Shaper** - QoS, priorities, and quotas +- **Netifyd** - Deep packet inspection +- **Client Guardian** - Network access control and captive portal +- **Auth Guardian** - Advanced authentication system + +**Network Management** +- **WireGuard** - Modern VPN with QR codes +- **Network Modes** - Network topology configuration +- **Bandwidth Manager** - QoS and bandwidth quotas +- **Media Flow** - Media traffic detection and optimization +- **Traffic Shaper** - Advanced traffic shaping + +**System & Performance** +- **System Hub** - Unified control center +- **CDN Cache** - Local caching proxy +- **Virtual Host Manager** - Virtual host configuration + +## RPCD API Methods + +The hub provides a comprehensive RPC API via ubus: + +- `status` - Get hub status and basic system info +- `modules` - List all SecuBox modules with status +- `modules_by_category` - Filter modules by category +- `module_info` - Get detailed info for a specific module +- `get_system_health` - Detailed system health metrics +- `get_alerts` - Aggregated alerts from all modules +- `get_dashboard_data` - All dashboard data in one call +- `quick_action` - Execute quick actions +- `start_module` / `stop_module` / `restart_module` - Module control +- `health` - System health checks +- `diagnostics` - Generate diagnostics bundle ## Installation ```bash opkg update opkg install luci-app-secubox +/etc/init.d/rpcd restart +/etc/init.d/uhttpd restart ``` ## Building @@ -27,6 +80,33 @@ git clone https://github.com/youruser/luci-app-secubox.git package/luci-app-secu make package/luci-app-secubox/compile V=s ``` +## Configuration + +Edit `/etc/config/secubox` to customize module definitions and settings. + +## File Structure + +``` +luci-app-secubox/ +├── Makefile +├── README.md +├── htdocs/luci-static/resources/ +│ ├── view/secubox/ +│ │ ├── dashboard.js # Main dashboard view +│ │ ├── modules.js # Modules management view +│ │ └── settings.js # Settings view +│ └── secubox/ +│ ├── api.js # RPC API client +│ └── secubox.css # Dashboard styles +└── root/ + ├── etc/config/secubox # UCI configuration + └── usr/ + ├── libexec/rpcd/secubox # RPCD backend + └── share/ + ├── luci/menu.d/luci-app-secubox.json + └── rpcd/acl.d/luci-app-secubox.json +``` + ## License -MIT License - CyberMind Security +Apache-2.0 - Copyright (C) 2025 CyberMind.fr diff --git a/luci-app-secubox/htdocs/luci-static/resources/secubox/api.js b/luci-app-secubox/htdocs/luci-static/resources/secubox/api.js index 2c996e6f..8b1e4d27 100644 --- a/luci-app-secubox/htdocs/luci-static/resources/secubox/api.js +++ b/luci-app-secubox/htdocs/luci-static/resources/secubox/api.js @@ -64,6 +64,31 @@ var callDiagnostics = rpc.declare({ expect: { } }); +var callSystemHealth = rpc.declare({ + object: 'luci.secubox', + method: 'get_system_health', + expect: { } +}); + +var callAlerts = rpc.declare({ + object: 'luci.secubox', + method: 'get_alerts', + expect: { alerts: [] } +}); + +var callQuickAction = rpc.declare({ + object: 'luci.secubox', + method: 'quick_action', + params: ['action'], + expect: { } +}); + +var callDashboardData = rpc.declare({ + object: 'luci.secubox', + method: 'get_dashboard_data', + expect: { } +}); + function formatUptime(seconds) { if (!seconds) return '0s'; var d = Math.floor(seconds / 86400); @@ -74,6 +99,14 @@ function formatUptime(seconds) { return m + 'm'; } +function formatBytes(bytes) { + if (!bytes) return '0 B'; + var k = 1024; + var sizes = ['B', 'KB', 'MB', 'GB', 'TB']; + var i = Math.floor(Math.log(bytes) / Math.log(k)); + return (bytes / Math.pow(k, i)).toFixed(2) + ' ' + sizes[i]; +} + return baseclass.extend({ getStatus: callStatus, getModules: callModules, @@ -84,5 +117,10 @@ return baseclass.extend({ restartModule: callRestartModule, getHealth: callHealth, getDiagnostics: callDiagnostics, - formatUptime: formatUptime + getSystemHealth: callSystemHealth, + getAlerts: callAlerts, + quickAction: callQuickAction, + getDashboardData: callDashboardData, + formatUptime: formatUptime, + formatBytes: formatBytes }); diff --git a/luci-app-secubox/htdocs/luci-static/resources/secubox/secubox.css b/luci-app-secubox/htdocs/luci-static/resources/secubox/secubox.css index ca93c662..ecd26c0e 100644 --- a/luci-app-secubox/htdocs/luci-static/resources/secubox/secubox.css +++ b/luci-app-secubox/htdocs/luci-static/resources/secubox/secubox.css @@ -30,3 +30,262 @@ .sb-color-client-guardian { --module-color: #ef4444; } .sb-color-system-hub { --module-color: #6366f1; } .sb-color-cdn-cache { --module-color: #06b6d4; } + +/* System Health Section */ +.secubox-health-section { + margin: 20px 0; + padding: 20px; + background: var(--sb-bg-card); + border-radius: 8px; + border: 1px solid var(--sb-border); +} + +.secubox-health-section h3 { + margin: 0 0 16px 0; + color: var(--sb-text); + font-size: 18px; + font-weight: 600; +} + +.secubox-health-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); + gap: 20px; +} + +.secubox-gauge { + display: flex; + flex-direction: column; + align-items: center; + padding: 16px; + background: rgba(15, 23, 42, 0.5); + border-radius: 8px; +} + +.secubox-gauge-label { + font-size: 14px; + font-weight: 600; + color: var(--sb-text); + margin-bottom: 8px; +} + +.secubox-gauge-chart { + margin: 8px 0; +} + +.secubox-gauge-details { + font-size: 12px; + color: var(--sb-text-muted); + text-align: center; + margin-top: 8px; +} + +/* Quick Actions Section */ +.secubox-quick-actions { + margin: 20px 0; + padding: 20px; + background: var(--sb-bg-card); + border-radius: 8px; + border: 1px solid var(--sb-border); +} + +.secubox-quick-actions h3 { + margin: 0 0 16px 0; + color: var(--sb-text); + font-size: 18px; + font-weight: 600; +} + +.secubox-actions-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); + gap: 12px; +} + +.secubox-quick-actions .cbi-button-action { + padding: 12px 16px; + font-size: 14px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +/* Alerts Section */ +.secubox-alerts-section { + margin: 20px 0; + padding: 20px; + background: var(--sb-bg-card); + border-radius: 8px; + border: 1px solid var(--sb-border); +} + +.secubox-alerts-section h3 { + margin: 0 0 16px 0; + color: var(--sb-text); + font-size: 18px; + font-weight: 600; +} + +.secubox-alerts-list { + display: flex; + flex-direction: column; + gap: 8px; +} + +.secubox-alert { + padding: 12px 16px; + border-radius: 6px; + border-left: 4px solid; + font-size: 14px; +} + +.secubox-alert-warning { + background: rgba(245, 158, 11, 0.1); + border-left-color: var(--sb-warning); + color: #fbbf24; +} + +.secubox-alert-error { + background: rgba(239, 68, 68, 0.1); + border-left-color: var(--sb-danger); + color: #fca5a5; +} + +.secubox-alert-info { + background: rgba(59, 130, 246, 0.1); + border-left-color: var(--sb-primary); + color: #93c5fd; +} + +/* Modules Section */ +.secubox-modules-section { + margin: 20px 0; +} + +.secubox-modules-section h3 { + margin: 0 0 16px 0; + color: var(--sb-text); + font-size: 18px; + font-weight: 600; +} + +.secubox-modules-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); + gap: 16px; +} + +.secubox-module-card { + background: var(--sb-bg-card); + border: 1px solid var(--sb-border); + border-radius: 8px; + padding: 16px; + transition: all 0.2s ease; + display: flex; + flex-direction: column; +} + +.secubox-module-card:hover { + transform: translateY(-2px); + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + border-color: var(--sb-primary); +} + +.secubox-module-card[data-status="running"] { + border-left: 4px solid var(--sb-success); +} + +.secubox-module-card[data-status="stopped"] { + border-left: 4px solid var(--sb-danger); +} + +.secubox-module-card[data-status="not-installed"] { + opacity: 0.6; +} + +.secubox-module-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 12px; +} + +.secubox-module-icon { + width: 48px; + height: 48px; + border-radius: 8px; + display: flex; + align-items: center; + justify-content: center; + font-size: 24px; +} + +.secubox-module-status { + font-size: 20px; + font-weight: bold; +} + +.secubox-module-body { + flex: 1; + margin-bottom: 12px; +} + +.secubox-module-name { + font-size: 16px; + font-weight: 600; + color: var(--sb-text); + margin-bottom: 4px; +} + +.secubox-module-description { + font-size: 13px; + color: var(--sb-text-muted); + margin-bottom: 8px; + line-height: 1.4; +} + +.secubox-module-category { + display: inline-block; + padding: 4px 8px; + background: rgba(59, 130, 246, 0.2); + color: var(--sb-primary); + border-radius: 4px; + font-size: 11px; + text-transform: uppercase; + font-weight: 600; + letter-spacing: 0.5px; +} + +.secubox-module-footer { + padding-top: 12px; + border-top: 1px solid var(--sb-border); +} + +.secubox-module-footer .cbi-button-link { + width: 100%; + text-align: center; + padding: 8px; +} + +.secubox-not-installed { + display: block; + text-align: center; + color: var(--sb-text-muted); + font-size: 13px; + font-style: italic; +} + +/* Responsive Design */ +@media (max-width: 768px) { + .secubox-health-grid { + grid-template-columns: repeat(2, 1fr); + } + + .secubox-actions-grid { + grid-template-columns: 1fr; + } + + .secubox-modules-grid { + grid-template-columns: 1fr; + } +} diff --git a/luci-app-secubox/htdocs/luci-static/resources/view/secubox/dashboard.js b/luci-app-secubox/htdocs/luci-static/resources/view/secubox/dashboard.js index f7828b34..db9b3c47 100644 --- a/luci-app-secubox/htdocs/luci-static/resources/view/secubox/dashboard.js +++ b/luci-app-secubox/htdocs/luci-static/resources/view/secubox/dashboard.js @@ -1,38 +1,216 @@ 'use strict'; 'require view'; -'require rpc'; +'require ui'; +'require secubox/api as API'; +'require dom'; -var callStatus = rpc.declare({object:'luci.secubox',method:'status',expect:{}}); -var callModules = rpc.declare({object:'luci.secubox',method:'modules',expect:{modules:[]}}); +// Load CSS +document.head.appendChild(E('link', { + 'rel': 'stylesheet', + 'type': 'text/css', + 'href': L.resource('secubox/secubox.css') +})); return view.extend({ - load: function() { return Promise.all([callStatus(), callModules()]); }, - render: function(data) { - var status = data[0] || {}, modules = data[1].modules || []; - var cats = {security:[], monitoring:[], network:[], system:[]}; - modules.forEach(function(m) { if(cats[m.category]) cats[m.category].push(m); }); - - return E('div', {class:'cbi-map'}, [ - E('h2', {}, '🛡️ SecuBox Dashboard'), - E('div', {style:'display:grid;grid-template-columns:repeat(4,1fr);gap:16px;margin:20px 0'}, [ - E('div', {style:'background:#1e293b;padding:16px;border-radius:8px;text-align:center'}, [ - E('div', {style:'font-size:24px;font-weight:bold;color:#22d3ee'}, status.modules_total || 0), - E('div', {style:'color:#94a3b8'}, 'Total Modules') - ]), - E('div', {style:'background:#1e293b;padding:16px;border-radius:8px;text-align:center'}, [ - E('div', {style:'font-size:24px;font-weight:bold;color:#3b82f6'}, status.modules_installed || 0), - E('div', {style:'color:#94a3b8'}, 'Installed') - ]), - E('div', {style:'background:#1e293b;padding:16px;border-radius:8px;text-align:center'}, [ - E('div', {style:'font-size:24px;font-weight:bold;color:#22c55e'}, status.modules_running || 0), - E('div', {style:'color:#94a3b8'}, 'Running') - ]), - E('div', {style:'background:#1e293b;padding:16px;border-radius:8px;text-align:center'}, [ - E('div', {style:'font-size:24px;font-weight:bold;color:#f59e0b'}, (status.memory_percent || 0) + '%'), - E('div', {style:'color:#94a3b8'}, 'Memory') - ]) - ]), - E('p', {}, 'Hostname: ' + (status.hostname || 'unknown') + ' | Load: ' + (status.load || '0')) - ]); - } + load: function() { + return Promise.all([ + API.getDashboardData(), + API.getSystemHealth(), + API.getAlerts() + ]); + }, + + render: function(data) { + var dashboard = data[0] || {}; + var health = data[1] || {}; + var alertsData = data[2] || {}; + + var status = dashboard.status || {}; + var modules = dashboard.modules || []; + var counts = dashboard.counts || {}; + var alerts = alertsData.alerts || []; + + var container = E('div', { 'class': 'cbi-map' }); + + // Header + container.appendChild(E('h2', {}, 'SecuBox Central Hub')); + container.appendChild(E('p', {}, + 'Hostname: ' + (status.hostname || 'Unknown') + ' | ' + + 'Uptime: ' + API.formatUptime(status.uptime) + ' | ' + + 'Load: ' + (status.load || '0.00') + )); + + // System Health Section + container.appendChild(this.renderSystemHealth(health)); + + // Quick Actions Section + container.appendChild(this.renderQuickActions()); + + // Alerts Section + if (alerts.length > 0) { + container.appendChild(this.renderAlerts(alerts)); + } + + // Modules Grid + container.appendChild(this.renderModulesGrid(modules)); + + return container; + }, + + renderSystemHealth: function(health) { + var cpu = health.cpu || {}; + var memory = health.memory || {}; + var disk = health.disk || {}; + var network = health.network || {}; + + var section = E('div', { 'class': 'secubox-health-section' }, [ + E('h3', {}, 'System Health'), + E('div', { 'class': 'secubox-health-grid' }, [ + this.renderGauge('CPU', cpu.percent || 0, '%', cpu.load_1min), + this.renderGauge('Memory', memory.percent || 0, '%', + API.formatBytes(memory.used_kb * 1024) + ' / ' + API.formatBytes(memory.total_kb * 1024)), + this.renderGauge('Disk', disk.percent || 0, '%', + API.formatBytes(disk.used_kb * 1024) + ' / ' + API.formatBytes(disk.total_kb * 1024)), + this.renderGauge('Network', 0, '', + 'RX: ' + API.formatBytes(network.rx_bytes) + ' | TX: ' + API.formatBytes(network.tx_bytes)) + ]) + ]); + + return section; + }, + + renderGauge: function(label, percent, unit, details) { + var color = percent < 70 ? '#22c55e' : percent < 85 ? '#f59e0b' : '#ef4444'; + + return E('div', { 'class': 'secubox-gauge' }, [ + E('div', { 'class': 'secubox-gauge-label' }, label), + E('div', { 'class': 'secubox-gauge-chart' }, [ + E('svg', { 'width': '120', 'height': '120', 'viewBox': '0 0 120 120' }, [ + E('circle', { + 'cx': '60', 'cy': '60', 'r': '54', + 'fill': 'none', 'stroke': '#1e293b', 'stroke-width': '12' + }), + E('circle', { + 'cx': '60', 'cy': '60', 'r': '54', + 'fill': 'none', 'stroke': color, 'stroke-width': '12', + 'stroke-dasharray': (339.292 * percent / 100) + ' 339.292', + 'stroke-linecap': 'round', + 'transform': 'rotate(-90 60 60)' + }), + E('text', { + 'x': '60', 'y': '65', 'text-anchor': 'middle', + 'font-size': '24', 'font-weight': 'bold', 'fill': color + }, percent + unit) + ]) + ]), + E('div', { 'class': 'secubox-gauge-details' }, details || '') + ]); + }, + + renderQuickActions: function() { + var self = this; + var actions = [ + { name: 'restart_rpcd', label: 'Restart RPCD', icon: '🔄' }, + { name: 'restart_uhttpd', label: 'Restart uHTTPd', icon: '🌐' }, + { name: 'clear_cache', label: 'Clear Cache', icon: '🧹' }, + { name: 'backup_config', label: 'Backup Config', icon: '💾' }, + { name: 'restart_network', label: 'Restart Network', icon: '📡' }, + { name: 'restart_firewall', label: 'Restart Firewall', icon: '🛡️' } + ]; + + var buttons = actions.map(function(action) { + return E('button', { + 'class': 'cbi-button cbi-button-action', + 'click': function() { + self.executeQuickAction(action.name, action.label); + } + }, action.icon + ' ' + action.label); + }); + + return E('div', { 'class': 'secubox-quick-actions' }, [ + E('h3', {}, 'Quick Actions'), + E('div', { 'class': 'secubox-actions-grid' }, buttons) + ]); + }, + + executeQuickAction: function(action, label) { + ui.showModal(_('Quick Action'), [ + E('p', { 'class': 'spinning' }, _('Executing: ') + label + '...') + ]); + + API.quickAction(action).then(function(result) { + ui.hideModal(); + if (result && result.success) { + ui.addNotification(null, E('p', result.message || 'Action completed'), 'info'); + } else { + ui.addNotification(null, E('p', result.message || 'Action failed'), 'error'); + } + }).catch(function(err) { + ui.hideModal(); + ui.addNotification(null, E('p', 'Error: ' + err.message), 'error'); + }); + }, + + renderAlerts: function(alerts) { + var alertItems = alerts.map(function(alert) { + var severityClass = 'secubox-alert-' + (alert.severity || 'info'); + return E('div', { 'class': 'secubox-alert ' + severityClass }, [ + E('strong', {}, alert.module + ': '), + E('span', {}, alert.message) + ]); + }); + + return E('div', { 'class': 'secubox-alerts-section' }, [ + E('h3', {}, 'Recent Alerts (' + alerts.length + ')'), + E('div', { 'class': 'secubox-alerts-list' }, alertItems) + ]); + }, + + renderModulesGrid: function(modules) { + var moduleCards = modules.map(function(module) { + var statusClass = module.installed ? (module.running ? 'running' : 'stopped') : 'not-installed'; + var statusIcon = module.installed ? (module.running ? '✓' : '✗') : '○'; + var statusColor = module.installed ? (module.running ? '#22c55e' : '#ef4444') : '#64748b'; + + return E('div', { + 'class': 'secubox-module-card', + 'data-status': statusClass + }, [ + E('div', { 'class': 'secubox-module-header' }, [ + E('div', { + 'class': 'secubox-module-icon', + 'style': 'background-color: ' + (module.color || '#64748b') + }, module.icon || '📦'), + E('div', { + 'class': 'secubox-module-status', + 'style': 'color: ' + statusColor + }, statusIcon) + ]), + E('div', { 'class': 'secubox-module-body' }, [ + E('div', { 'class': 'secubox-module-name' }, module.name || module.id), + E('div', { 'class': 'secubox-module-description' }, module.description || ''), + E('div', { 'class': 'secubox-module-category' }, module.category || 'other') + ]), + E('div', { 'class': 'secubox-module-footer' }, [ + module.installed ? E('a', { + 'href': '#', + 'class': 'cbi-button cbi-button-link', + 'click': function(ev) { + ev.preventDefault(); + window.location.hash = '#admin/secubox/' + module.id; + } + }, 'Open Dashboard') : E('span', { 'class': 'secubox-not-installed' }, 'Not Installed') + ]) + ]); + }); + + return E('div', { 'class': 'secubox-modules-section' }, [ + E('h3', {}, 'Installed Modules'), + E('div', { 'class': 'secubox-modules-grid' }, moduleCards) + ]); + }, + + handleSaveApply: null, + handleSave: null, + handleReset: null }); diff --git a/luci-app-secubox/root/etc/config/secubox b/luci-app-secubox/root/etc/config/secubox index e74c4f43..f44e8312 100644 --- a/luci-app-secubox/root/etc/config/secubox +++ b/luci-app-secubox/root/etc/config/secubox @@ -95,3 +95,58 @@ config module 'cdn_cache' option config 'cdn-cache' option installed '0' option enabled '0' + +config module 'bandwidth_manager' + option name 'Bandwidth Manager' + option description 'QoS and bandwidth quotas' + option category 'network' + option icon 'activity' + option color '#3b82f6' + option package 'luci-app-bandwidth-manager' + option config 'bandwidth_manager' + option installed '0' + option enabled '0' + +config module 'auth_guardian' + option name 'Auth Guardian' + option description 'Advanced authentication system' + option category 'security' + option icon 'key' + option color '#f59e0b' + option package 'luci-app-auth-guardian' + option config 'auth_guardian' + option installed '0' + option enabled '0' + +config module 'media_flow' + option name 'Media Flow' + option description 'Media traffic detection and optimization' + option category 'network' + option icon 'play-circle' + option color '#ec4899' + option package 'luci-app-media-flow' + option config 'media_flow' + option installed '0' + option enabled '0' + +config module 'vhost_manager' + option name 'Virtual Host Manager' + option description 'Virtual host configuration' + option category 'system' + option icon 'server' + option color '#8b5cf6' + option package 'luci-app-vhost-manager' + option config 'vhost_manager' + option installed '0' + option enabled '0' + +config module 'traffic_shaper' + option name 'Traffic Shaper' + option description 'Advanced traffic shaping' + option category 'network' + option icon 'trending-up' + option color '#10b981' + option package 'luci-app-traffic-shaper' + option config 'traffic_shaper' + option installed '0' + option enabled '0' diff --git a/luci-app-secubox/root/usr/libexec/rpcd/secubox b/luci-app-secubox/root/usr/libexec/rpcd/secubox index dadfc072..6a700bbc 100755 --- a/luci-app-secubox/root/usr/libexec/rpcd/secubox +++ b/luci-app-secubox/root/usr/libexec/rpcd/secubox @@ -7,8 +7,23 @@ . /lib/functions.sh . /usr/share/libubox/jshn.sh -# Module registry -MODULES="crowdsec netdata netifyd wireguard network_modes client_guardian system_hub cdn_cache" +# Module registry - auto-detected from /usr/libexec/rpcd/ +detect_modules() { + local modules="" + local scripts="crowdsec-dashboard netdata-dashboard netifyd-dashboard wireguard-dashboard network-modes client-guardian system-hub bandwidth-manager auth-guardian media-flow vhost-manager cdn-cache traffic-shaper" + + for script in $scripts; do + if [ -x "/usr/libexec/rpcd/$script" ]; then + # Convert script name to module ID (remove -dashboard suffix) + local module_id=$(echo "$script" | sed 's/-dashboard$//') + modules="$modules $module_id" + fi + done + + echo "$modules" +} + +MODULES=$(detect_modules) # Check if a module is installed check_module_installed() { @@ -338,7 +353,7 @@ get_health() { # Generate diagnostics bundle get_diagnostics() { json_init - + # System info json_add_object "system" json_add_string "hostname" "$(uci -q get system.@system[0].hostname)" @@ -347,13 +362,13 @@ get_diagnostics() { json_add_string "kernel" "$(uname -r)" json_add_int "uptime" "$(cat /proc/uptime | cut -d. -f1)" json_close_object - + # Modules status json_add_array "modules" for module in $MODULES; do local is_installed=$(check_module_installed "$module") local is_running=$(check_module_running "$module") - + json_add_object "" json_add_string "id" "$module" json_add_boolean "installed" "$is_installed" @@ -361,13 +376,13 @@ get_diagnostics() { json_close_object done json_close_array - + # Network interfaces json_add_array "interfaces" for iface in $(ls /sys/class/net/); do local mac=$(cat /sys/class/net/$iface/address 2>/dev/null) local state=$(cat /sys/class/net/$iface/operstate 2>/dev/null) - + json_add_object "" json_add_string "name" "$iface" json_add_string "mac" "$mac" @@ -375,9 +390,287 @@ get_diagnostics() { json_close_object done json_close_array - + json_add_int "generated" "$(date +%s)" - + + json_dump +} + +# Get system health metrics +get_system_health() { + json_init + + # CPU usage (based on load average) + local load=$(cat /proc/loadavg | cut -d' ' -f1) + local cpu_cores=$(grep -c processor /proc/cpuinfo) + local cpu_pct=$(awk "BEGIN {printf \"%.0f\", ($load / $cpu_cores) * 100}") + + # Memory + local mem_total=$(grep MemTotal /proc/meminfo | awk '{print $2}') + local mem_avail=$(grep MemAvailable /proc/meminfo | awk '{print $2}') + local mem_used=$((mem_total - mem_avail)) + local mem_pct=$((mem_used * 100 / mem_total)) + + # Disk usage (root filesystem) + local disk_info=$(df / | tail -1) + local disk_total=$(echo "$disk_info" | awk '{print $2}') + local disk_used=$(echo "$disk_info" | awk '{print $3}') + local disk_pct=$(echo "$disk_info" | awk '{print $5}' | tr -d '%') + + # Network stats (sum all interfaces) + local net_rx=0 + local net_tx=0 + for iface in $(ls /sys/class/net/ | grep -v lo); do + if [ -f "/sys/class/net/$iface/statistics/rx_bytes" ]; then + local rx=$(cat /sys/class/net/$iface/statistics/rx_bytes 2>/dev/null || echo 0) + local tx=$(cat /sys/class/net/$iface/statistics/tx_bytes 2>/dev/null || echo 0) + net_rx=$((net_rx + rx)) + net_tx=$((net_tx + tx)) + fi + done + + # Uptime + local uptime=$(cat /proc/uptime | cut -d. -f1) + + # Load averages + local load_1min=$(cat /proc/loadavg | cut -d' ' -f1) + local load_5min=$(cat /proc/loadavg | cut -d' ' -f2) + local load_15min=$(cat /proc/loadavg | cut -d' ' -f3) + + json_add_object "cpu" + json_add_int "percent" "$cpu_pct" + json_add_int "cores" "$cpu_cores" + json_add_string "load_1min" "$load_1min" + json_add_string "load_5min" "$load_5min" + json_add_string "load_15min" "$load_15min" + json_close_object + + json_add_object "memory" + json_add_int "percent" "$mem_pct" + json_add_int "total_kb" "$mem_total" + json_add_int "used_kb" "$mem_used" + json_add_int "available_kb" "$mem_avail" + json_close_object + + json_add_object "disk" + json_add_int "percent" "$disk_pct" + json_add_int "total_kb" "$disk_total" + json_add_int "used_kb" "$disk_used" + json_close_object + + json_add_object "network" + json_add_int "rx_bytes" "$net_rx" + json_add_int "tx_bytes" "$net_tx" + json_close_object + + json_add_int "uptime" "$uptime" + json_add_int "timestamp" "$(date +%s)" + + json_dump +} + +# Get aggregated alerts from all modules +get_alerts() { + json_init + json_add_array "alerts" + + # Check each installed module for alerts via ubus + for module in $MODULES; do + local is_installed=$(check_module_installed "$module") + + if [ "$is_installed" = "1" ]; then + # Try to call module's status method to get alerts + local module_script=$(echo "$module" | sed 's/_/-/g') + if [ -x "/usr/libexec/rpcd/${module_script}" ] || [ -x "/usr/libexec/rpcd/${module_script}-dashboard" ]; then + # Call via ubus if available + local alerts=$(ubus call "luci.${module_script//-/_}" status 2>/dev/null | jsonfilter -e '@.alerts[@]' 2>/dev/null) + + if [ -n "$alerts" ]; then + # Module has alerts, add them + json_add_object "" + json_add_string "module" "$module" + json_add_string "message" "$alerts" + json_add_string "severity" "warning" + json_add_int "timestamp" "$(date +%s)" + json_close_object + fi + fi + + # Check if module service is not running + local is_running=$(check_module_running "$module") + if [ "$is_running" != "1" ]; then + config_load secubox + local name + config_get name "$module" name "$module" + + json_add_object "" + json_add_string "module" "$module" + json_add_string "message" "$name service is not running" + json_add_string "severity" "warning" + json_add_int "timestamp" "$(date +%s)" + json_close_object + fi + fi + done + + json_close_array + json_add_int "count" "$(ubus call luci.secubox get_alerts 2>/dev/null | jsonfilter -e '@.alerts[*]' | wc -l || echo 0)" + json_add_int "timestamp" "$(date +%s)" + + json_dump +} + +# Execute quick actions +quick_action() { + local action="$1" + + json_init + + case "$action" in + restart_rpcd) + /etc/init.d/rpcd restart >/dev/null 2>&1 + json_add_boolean "success" 1 + json_add_string "message" "RPCD service restarted" + ;; + restart_uhttpd) + /etc/init.d/uhttpd restart >/dev/null 2>&1 + json_add_boolean "success" 1 + json_add_string "message" "uHTTPd service restarted" + ;; + clear_cache) + sync + echo 3 > /proc/sys/vm/drop_caches 2>/dev/null + json_add_boolean "success" 1 + json_add_string "message" "System cache cleared" + ;; + backup_config) + local backup_file="/tmp/backup-$(date +%Y%m%d-%H%M%S).tar.gz" + sysupgrade -b "$backup_file" >/dev/null 2>&1 + if [ -f "$backup_file" ]; then + json_add_boolean "success" 1 + json_add_string "message" "Configuration backup created" + json_add_string "file" "$backup_file" + else + json_add_boolean "success" 0 + json_add_string "message" "Backup failed" + fi + ;; + restart_network) + /etc/init.d/network restart >/dev/null 2>&1 + json_add_boolean "success" 1 + json_add_string "message" "Network services restarted" + ;; + restart_firewall) + /etc/init.d/firewall restart >/dev/null 2>&1 + json_add_boolean "success" 1 + json_add_string "message" "Firewall restarted" + ;; + *) + json_add_boolean "success" 0 + json_add_string "message" "Unknown action: $action" + ;; + esac + + json_add_int "timestamp" "$(date +%s)" + json_dump +} + +# Get all dashboard data in one call +get_dashboard_data() { + json_init + + # Get status info + local uptime=$(cat /proc/uptime | cut -d. -f1) + local load=$(cat /proc/loadavg | cut -d' ' -f1-3) + local mem_total=$(grep MemTotal /proc/meminfo | awk '{print $2}') + local mem_free=$(grep MemAvailable /proc/meminfo | awk '{print $2}') + local mem_used=$((mem_total - mem_free)) + local mem_pct=$((mem_used * 100 / mem_total)) + + json_add_object "status" + json_add_string "version" "1.0.0" + json_add_string "hostname" "$(uci -q get system.@system[0].hostname || echo 'SecuBox')" + json_add_int "uptime" "$uptime" + json_add_string "load" "$load" + json_add_int "memory_total" "$mem_total" + json_add_int "memory_used" "$mem_used" + json_add_int "memory_percent" "$mem_pct" + json_close_object + + # Get modules list + json_add_array "modules" + config_load secubox + for module in $MODULES; do + local name desc category icon color + config_get name "$module" name "$module" + config_get desc "$module" description "" + config_get category "$module" category "other" + config_get icon "$module" icon "box" + config_get color "$module" color "#64748b" + + local is_installed=$(check_module_installed "$module") + local is_running=$(check_module_running "$module") + + json_add_object "" + json_add_string "id" "$module" + json_add_string "name" "$name" + json_add_string "description" "$desc" + json_add_string "category" "$category" + json_add_string "icon" "$icon" + json_add_string "color" "$color" + json_add_boolean "installed" "$is_installed" + json_add_boolean "running" "$is_running" + json_close_object + done + json_close_array + + # Count installed and running modules + local total=0 installed=0 running=0 + for module in $MODULES; do + total=$((total + 1)) + [ "$(check_module_installed "$module")" = "1" ] && installed=$((installed + 1)) + [ "$(check_module_running "$module")" = "1" ] && running=$((running + 1)) + done + + json_add_object "counts" + json_add_int "total" "$total" + json_add_int "installed" "$installed" + json_add_int "running" "$running" + json_close_object + + # Get system health + local cpu_cores=$(grep -c processor /proc/cpuinfo) + local load_val=$(cat /proc/loadavg | cut -d' ' -f1) + local cpu_pct=$(awk "BEGIN {printf \"%.0f\", ($load_val / $cpu_cores) * 100}") + local disk_pct=$(df / | tail -1 | awk '{print $5}' | tr -d '%') + + json_add_object "health" + json_add_int "cpu_percent" "$cpu_pct" + json_add_int "memory_percent" "$mem_pct" + json_add_int "disk_percent" "$disk_pct" + json_close_object + + # Get recent alerts (simplified) + json_add_array "alerts" + for module in $MODULES; do + local is_installed=$(check_module_installed "$module") + local is_running=$(check_module_running "$module") + + if [ "$is_installed" = "1" ] && [ "$is_running" != "1" ]; then + config_load secubox + local name + config_get name "$module" name "$module" + + json_add_object "" + json_add_string "module" "$module" + json_add_string "message" "$name is not running" + json_add_string "severity" "warning" + json_close_object + fi + done + json_close_array + + json_add_int "timestamp" "$(date +%s)" json_dump } @@ -408,6 +701,15 @@ case "$1" in json_close_object json_add_object "diagnostics" json_close_object + json_add_object "get_system_health" + json_close_object + json_add_object "get_alerts" + json_close_object + json_add_object "quick_action" + json_add_string "action" "string" + json_close_object + json_add_object "get_dashboard_data" + json_close_object json_dump ;; call) @@ -454,6 +756,21 @@ case "$1" in diagnostics) get_diagnostics ;; + get_system_health) + get_system_health + ;; + get_alerts) + get_alerts + ;; + quick_action) + read -r input + json_load "$input" + json_get_var action action "" + quick_action "$action" + ;; + get_dashboard_data) + get_dashboard_data + ;; *) echo '{"error":"Unknown method"}' ;; diff --git a/luci-app-secubox/root/usr/share/rpcd/acl.d/luci-app-secubox.json b/luci-app-secubox/root/usr/share/rpcd/acl.d/luci-app-secubox.json index c715bc8b..efe9926e 100644 --- a/luci-app-secubox/root/usr/share/rpcd/acl.d/luci-app-secubox.json +++ b/luci-app-secubox/root/usr/share/rpcd/acl.d/luci-app-secubox.json @@ -1,7 +1,30 @@ { "luci-app-secubox": { "description": "SecuBox Dashboard", - "read": {"ubus": {"luci.secubox": ["status", "modules"]}}, - "write": {} + "read": { + "ubus": { + "luci.secubox": [ + "status", + "modules", + "modules_by_category", + "module_info", + "health", + "diagnostics", + "get_system_health", + "get_alerts", + "get_dashboard_data" + ] + } + }, + "write": { + "ubus": { + "luci.secubox": [ + "start_module", + "stop_module", + "restart_module", + "quick_action" + ] + } + } } }