feat(portal): Add Threat Monitor to security cards and stats

- Add threat-monitor app to security section in portal.js
- Add security stats RPC call (get_security_stats)
- Display packets blocked and alerts on dashboard
- Add Threat Monitor to featured quick access apps
- Show WAN dropped + firewall rejects in events section
- Link to Threat Monitor dashboard from events

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-01-25 15:24:37 +01:00
parent 283f2567be
commit 37b88e47b9
2 changed files with 52 additions and 13 deletions

View File

@ -61,6 +61,18 @@ return baseclass.extend({
service: 'mitmproxy', service: 'mitmproxy',
version: '8.1.1' version: '8.1.1'
}, },
'threat-monitor': {
id: 'threat-monitor',
name: 'Threat Monitor',
desc: 'Real-time threat detection combining netifyd DPI with CrowdSec intelligence',
icon: '\ud83d\udc41\ufe0f',
iconBg: 'rgba(239, 68, 68, 0.15)',
iconColor: '#ef4444',
section: 'security',
path: 'admin/secubox/security/threats/dashboard',
service: null,
version: '1.0.0'
},
// Network Apps // Network Apps
'bandwidth-manager': { 'bandwidth-manager': {

View File

@ -23,6 +23,11 @@ var callCrowdSecStats = rpc.declare({
method: 'nftables_stats' method: 'nftables_stats'
}); });
var callSecurityStats = rpc.declare({
object: 'luci.secubox-security-threats',
method: 'get_security_stats'
});
var callGetServices = rpc.declare({ var callGetServices = rpc.declare({
object: 'luci.secubox', object: 'luci.secubox',
method: 'get_services', method: 'get_services',
@ -42,13 +47,16 @@ return view.extend({
this.loadAppStatuses(), this.loadAppStatuses(),
callCrowdSecStats().catch(function() { return null; }), callCrowdSecStats().catch(function() { return null; }),
portal.checkInstalledApps(), portal.checkInstalledApps(),
callGetServices().catch(function() { return []; }) callGetServices().catch(function() { return []; }),
callSecurityStats().catch(function() { return null; })
]).then(function(results) { ]).then(function(results) {
// Store installed apps info from the last promise // Store installed apps info from the last promise
self.installedApps = results[4] || {}; self.installedApps = results[4] || {};
// RPC expect unwraps the services array directly // RPC expect unwraps the services array directly
var svcResult = results[5] || []; var svcResult = results[5] || [];
self.detectedServices = Array.isArray(svcResult) ? svcResult : (svcResult.services || []); self.detectedServices = Array.isArray(svcResult) ? svcResult : (svcResult.services || []);
// Security stats
self.securityStats = results[6] || {};
return results; return results;
}); });
}, },
@ -104,6 +112,7 @@ return view.extend({
var boardInfo = data[0] || {}; var boardInfo = data[0] || {};
var sysInfo = data[1] || {}; var sysInfo = data[1] || {};
var crowdSecStats = data[3] || {}; var crowdSecStats = data[3] || {};
var securityStats = this.securityStats || {};
var self = this; var self = this;
// Set portal app context and hide LuCI navigation // Set portal app context and hide LuCI navigation
@ -142,7 +151,7 @@ return view.extend({
this.renderHeader(), this.renderHeader(),
// Content // Content
E('div', { 'class': 'sb-portal-content' }, [ E('div', { 'class': 'sb-portal-content' }, [
this.renderDashboardSection(boardInfo, sysInfo, crowdSecStats), this.renderDashboardSection(boardInfo, sysInfo, crowdSecStats, securityStats),
this.renderSecuritySection(), this.renderSecuritySection(),
this.renderNetworkSection(), this.renderNetworkSection(),
this.renderMonitoringSection(), this.renderMonitoringSection(),
@ -228,7 +237,7 @@ return view.extend({
}); });
}, },
renderDashboardSection: function(boardInfo, sysInfo, crowdSecStats) { renderDashboardSection: function(boardInfo, sysInfo, crowdSecStats, securityStats) {
var self = this; var self = this;
var securityApps = portal.getAppsBySection('security'); var securityApps = portal.getAppsBySection('security');
var networkApps = portal.getAppsBySection('network'); var networkApps = portal.getAppsBySection('network');
@ -245,6 +254,12 @@ return view.extend({
var crowdSecHealth = crowdSecStats.firewall_health || {}; var crowdSecHealth = crowdSecStats.firewall_health || {};
var crowdSecActive = crowdSecHealth.bouncer_running && crowdSecHealth.decisions_synced; var crowdSecActive = crowdSecHealth.bouncer_running && crowdSecHealth.decisions_synced;
// Security stats
var wanDropped = securityStats.wan_dropped || 0;
var fwRejects = securityStats.firewall_rejects || 0;
var csBans = securityStats.crowdsec_bans || 0;
var csAlerts = securityStats.crowdsec_alerts_24h || 0;
return E('div', { 'class': 'sb-portal-section active', 'data-section': 'dashboard' }, [ return E('div', { 'class': 'sb-portal-section active', 'data-section': 'dashboard' }, [
E('div', { 'class': 'sb-section-header' }, [ E('div', { 'class': 'sb-section-header' }, [
E('h2', { 'class': 'sb-section-title' }, 'SecuBox Dashboard'), E('h2', { 'class': 'sb-section-title' }, 'SecuBox Dashboard'),
@ -275,32 +290,33 @@ return view.extend({
E('div', { 'class': 'sb-quick-stat-label' }, 'Services Running') E('div', { 'class': 'sb-quick-stat-label' }, 'Services Running')
]), ]),
// CrowdSec Blocked IPs // Firewall Blocked
E('div', { 'class': 'sb-quick-stat' }, [ E('div', { 'class': 'sb-quick-stat' }, [
E('div', { 'class': 'sb-quick-stat-header' }, [ E('div', { 'class': 'sb-quick-stat-header' }, [
E('div', { 'class': 'sb-quick-stat-icon security' }, '\ud83d\udeab'), E('div', { 'class': 'sb-quick-stat-icon security' }, '\ud83d\udeab'),
E('span', { 'class': 'sb-quick-stat-status ' + (crowdSecActive ? 'running' : 'warning') }, E('span', { 'class': 'sb-quick-stat-status ' + (crowdSecActive ? 'running' : 'warning') },
crowdSecActive ? 'Active' : 'Inactive') crowdSecActive ? 'Protected' : 'Monitoring')
]), ]),
E('div', { 'class': 'sb-quick-stat-value' }, totalBlocked.toLocaleString()), E('div', { 'class': 'sb-quick-stat-value' }, (wanDropped + fwRejects).toLocaleString()),
E('div', { 'class': 'sb-quick-stat-label' }, 'IPs Blocked') E('div', { 'class': 'sb-quick-stat-label' }, 'Packets Blocked')
]), ]),
// Network Apps // Threat Alerts
E('div', { 'class': 'sb-quick-stat' }, [ E('div', { 'class': 'sb-quick-stat' }, [
E('div', { 'class': 'sb-quick-stat-header' }, [ E('div', { 'class': 'sb-quick-stat-header' }, [
E('div', { 'class': 'sb-quick-stat-icon network' }, '\ud83c\udf10'), E('div', { 'class': 'sb-quick-stat-icon security' }, '\ud83d\udc41\ufe0f'),
E('span', { 'class': 'sb-quick-stat-status running' }, 'Configured') E('span', { 'class': 'sb-quick-stat-status ' + (csAlerts > 0 ? 'warning' : 'running') },
csAlerts > 0 ? 'Alerts' : 'Clear')
]), ]),
E('div', { 'class': 'sb-quick-stat-value' }, networkApps.length), E('div', { 'class': 'sb-quick-stat-value' }, csBans + '/' + csAlerts),
E('div', { 'class': 'sb-quick-stat-label' }, 'Network Tools') E('div', { 'class': 'sb-quick-stat-label' }, 'Bans / Alerts 24h')
]) ])
]), ]),
// Featured Apps // Featured Apps
E('h3', { 'style': 'margin: 1.5rem 0 1rem; color: var(--cyber-text-primary);' }, 'Quick Access'), E('h3', { 'style': 'margin: 1.5rem 0 1rem; color: var(--cyber-text-primary);' }, 'Quick Access'),
E('div', { 'class': 'sb-app-grid' }, E('div', { 'class': 'sb-app-grid' },
this.renderFeaturedApps(['crowdsec', 'bandwidth-manager', 'media-flow', 'ndpid']) this.renderFeaturedApps(['crowdsec', 'threat-monitor', 'bandwidth-manager', 'media-flow'])
), ),
// Recent Events placeholder // Recent Events placeholder
@ -342,6 +358,17 @@ return view.extend({
(crowdSecStats.ipv4_cscli_count || 0) + ' local) | IPv6: ' + blockedIPv6.toLocaleString()), (crowdSecStats.ipv4_cscli_count || 0) + ' local) | IPv6: ' + blockedIPv6.toLocaleString()),
E('span', { 'class': 'sb-events-meta' }, 'CrowdSec Firewall Protection') E('span', { 'class': 'sb-events-meta' }, 'CrowdSec Firewall Protection')
]) ])
]) : null,
wanDropped > 0 ? E('div', { 'class': 'sb-events-item' }, [
E('div', { 'class': 'sb-events-icon warning' }, '\ud83d\udc41\ufe0f'),
E('div', { 'class': 'sb-events-content' }, [
E('p', { 'class': 'sb-events-message' },
'WAN Dropped: ' + wanDropped.toLocaleString() + ' | Firewall Rejects: ' + fwRejects),
E('span', { 'class': 'sb-events-meta' }, [
'Threat Monitor - ',
E('a', { 'href': L.url('admin/secubox/security/threats/dashboard') }, 'View Details')
])
])
]) : null ]) : null
].filter(Boolean)) ].filter(Boolean))
]); ]);