diff --git a/package/secubox/luci-app-secubox-crowdsec/Makefile b/package/secubox/luci-app-secubox-crowdsec/Makefile deleted file mode 100644 index f6c707f8..00000000 --- a/package/secubox/luci-app-secubox-crowdsec/Makefile +++ /dev/null @@ -1,70 +0,0 @@ -# SPDX-License-Identifier: MIT -# -# LuCI SecuBox CrowdSec Dashboard -# Copyright (C) 2025 CyberMind.fr - Gandalf -# - -include $(TOPDIR)/rules.mk - -PKG_NAME:=luci-app-secubox-crowdsec -PKG_VERSION:=1.0.0 -PKG_RELEASE:=1 - -LUCI_TITLE:=LuCI SecuBox CrowdSec Dashboard -LUCI_DEPENDS:=+luci-base +crowdsec +secubox-app-cs-firewall-bouncer -LUCI_PKGARCH:=all - -PKG_MAINTAINER:=Gerald Kerma -PKG_LICENSE:=MIT - -include $(TOPDIR)/feeds/luci/luci.mk - -define Package/luci-app-secubox-crowdsec/conffiles -/etc/config/crowdsec -endef - -define Package/luci-app-secubox-crowdsec/install - # RPCD backend - $(INSTALL_DIR) $(1)/usr/libexec/rpcd - $(INSTALL_BIN) ./root/usr/libexec/rpcd/luci.secubox-crowdsec $(1)/usr/libexec/rpcd/ - - # ACL permissions - $(INSTALL_DIR) $(1)/usr/share/rpcd/acl.d - $(INSTALL_DATA) ./root/usr/share/rpcd/acl.d/luci-app-secubox-crowdsec.json $(1)/usr/share/rpcd/acl.d/ - - # LuCI menu - $(INSTALL_DIR) $(1)/usr/share/luci/menu.d - $(INSTALL_DATA) ./root/usr/share/luci/menu.d/luci-app-secubox-crowdsec.json $(1)/usr/share/luci/menu.d/ - - # JavaScript API module - $(INSTALL_DIR) $(1)/www/luci-static/resources/secubox-crowdsec - $(INSTALL_DATA) ./htdocs/luci-static/resources/secubox-crowdsec/api.js $(1)/www/luci-static/resources/secubox-crowdsec/ - - # JavaScript views - $(INSTALL_DIR) $(1)/www/luci-static/resources/view/secubox-crowdsec - $(INSTALL_DATA) ./htdocs/luci-static/resources/view/secubox-crowdsec/dashboard.js $(1)/www/luci-static/resources/view/secubox-crowdsec/ - $(INSTALL_DATA) ./htdocs/luci-static/resources/view/secubox-crowdsec/decisions.js $(1)/www/luci-static/resources/view/secubox-crowdsec/ - $(INSTALL_DATA) ./htdocs/luci-static/resources/view/secubox-crowdsec/alerts.js $(1)/www/luci-static/resources/view/secubox-crowdsec/ - $(INSTALL_DATA) ./htdocs/luci-static/resources/view/secubox-crowdsec/collections.js $(1)/www/luci-static/resources/view/secubox-crowdsec/ - $(INSTALL_DATA) ./htdocs/luci-static/resources/view/secubox-crowdsec/settings.js $(1)/www/luci-static/resources/view/secubox-crowdsec/ - - # UCI default config - $(INSTALL_DIR) $(1)/etc/config - $(INSTALL_CONF) ./root/etc/config/crowdsec $(1)/etc/config/crowdsec -endef - -define Package/luci-app-secubox-crowdsec/postinst -#!/bin/sh -[ -n "$${IPKG_INSTROOT}" ] || { - # Restart rpcd to load the new backend - /etc/init.d/rpcd restart - # Clear LuCI cache - rm -rf /tmp/luci-modulecache /tmp/luci-indexcache 2>/dev/null - echo "SecuBox CrowdSec Dashboard installed." - echo "Access via Services -> CrowdSec in LuCI." -} -exit 0 -endef - -# call BuildPackage - OpenWrt buildroot -$(eval $(call BuildPackage,luci-app-secubox-crowdsec)) diff --git a/package/secubox/luci-app-secubox-crowdsec/htdocs/luci-static/resources/secubox-crowdsec/api.js b/package/secubox/luci-app-secubox-crowdsec/htdocs/luci-static/resources/secubox-crowdsec/api.js deleted file mode 100644 index 18de65e7..00000000 --- a/package/secubox/luci-app-secubox-crowdsec/htdocs/luci-static/resources/secubox-crowdsec/api.js +++ /dev/null @@ -1,191 +0,0 @@ -'use strict'; -'require rpc'; - -var callStatus = rpc.declare({ - object: 'luci.secubox-crowdsec', - method: 'status', - expect: { } -}); - -var callDecisions = rpc.declare({ - object: 'luci.secubox-crowdsec', - method: 'decisions', - expect: { decisions: [] } -}); - -var callAddDecision = rpc.declare({ - object: 'luci.secubox-crowdsec', - method: 'add_decision', - params: ['ip', 'duration', 'reason', 'type'], - expect: { } -}); - -var callDeleteDecision = rpc.declare({ - object: 'luci.secubox-crowdsec', - method: 'delete_decision', - params: ['ip', 'decision_id'], - expect: { } -}); - -var callAlerts = rpc.declare({ - object: 'luci.secubox-crowdsec', - method: 'alerts', - params: ['limit', 'since'], - expect: { alerts: [] } -}); - -var callMetrics = rpc.declare({ - object: 'luci.secubox-crowdsec', - method: 'metrics', - expect: { } -}); - -var callStats = rpc.declare({ - object: 'luci.secubox-crowdsec', - method: 'stats', - expect: { } -}); - -var callCollections = rpc.declare({ - object: 'luci.secubox-crowdsec', - method: 'collections', - expect: { collections: [] } -}); - -var callInstallCollection = rpc.declare({ - object: 'luci.secubox-crowdsec', - method: 'install_collection', - params: ['collection'], - expect: { } -}); - -var callRemoveCollection = rpc.declare({ - object: 'luci.secubox-crowdsec', - method: 'remove_collection', - params: ['collection'], - expect: { } -}); - -var callUpdateHub = rpc.declare({ - object: 'luci.secubox-crowdsec', - method: 'update_hub', - expect: { } -}); - -var callUpgradeHub = rpc.declare({ - object: 'luci.secubox-crowdsec', - method: 'upgrade_hub', - expect: { } -}); - -var callBouncers = rpc.declare({ - object: 'luci.secubox-crowdsec', - method: 'bouncers', - expect: { bouncers: [] } -}); - -var callControlService = rpc.declare({ - object: 'luci.secubox-crowdsec', - method: 'control_service', - params: ['service', 'action'], - expect: { } -}); - -var callNftablesStats = rpc.declare({ - object: 'luci.secubox-crowdsec', - method: 'nftables_stats', - expect: { } -}); - -var callBlockedIps = rpc.declare({ - object: 'luci.secubox-crowdsec', - method: 'blocked_ips', - expect: { ipv4: [], ipv6: [] } -}); - -var callConfig = rpc.declare({ - object: 'luci.secubox-crowdsec', - method: 'config', - expect: { } -}); - -var callSaveConfig = rpc.declare({ - object: 'luci.secubox-crowdsec', - method: 'save_config', - params: ['key', 'value'], - expect: { } -}); - -return L.Class.extend({ - getStatus: function() { - return callStatus(); - }, - - getDecisions: function() { - return callDecisions(); - }, - - addDecision: function(ip, duration, reason, type) { - return callAddDecision(ip, duration || '24h', reason || 'Manual ban via LuCI', type || 'ban'); - }, - - deleteDecision: function(ip, decisionId) { - return callDeleteDecision(ip, decisionId); - }, - - getAlerts: function(limit, since) { - return callAlerts(limit || 50, since || '24h'); - }, - - getMetrics: function() { - return callMetrics(); - }, - - getStats: function() { - return callStats(); - }, - - getCollections: function() { - return callCollections(); - }, - - installCollection: function(collection) { - return callInstallCollection(collection); - }, - - removeCollection: function(collection) { - return callRemoveCollection(collection); - }, - - updateHub: function() { - return callUpdateHub(); - }, - - upgradeHub: function() { - return callUpgradeHub(); - }, - - getBouncers: function() { - return callBouncers(); - }, - - controlService: function(service, action) { - return callControlService(service, action); - }, - - getNftablesStats: function() { - return callNftablesStats(); - }, - - getBlockedIps: function() { - return callBlockedIps(); - }, - - getConfig: function() { - return callConfig(); - }, - - saveConfig: function(key, value) { - return callSaveConfig(key, value); - } -}); diff --git a/package/secubox/luci-app-secubox-crowdsec/htdocs/luci-static/resources/view/secubox-crowdsec/alerts.js b/package/secubox/luci-app-secubox-crowdsec/htdocs/luci-static/resources/view/secubox-crowdsec/alerts.js deleted file mode 100644 index 49867012..00000000 --- a/package/secubox/luci-app-secubox-crowdsec/htdocs/luci-static/resources/view/secubox-crowdsec/alerts.js +++ /dev/null @@ -1,117 +0,0 @@ -'use strict'; -'require view'; -'require dom'; -'require ui'; -'require secubox-crowdsec/api as api'; - -return view.extend({ - api: null, - - load: function() { - this.api = new api(); - return this.api.getAlerts(100, '7d'); - }, - - formatDate: function(timestamp) { - if (!timestamp) return '-'; - try { - var date = new Date(timestamp); - return date.toLocaleString(); - } catch(e) { - return timestamp; - } - }, - - renderAlertsTable: function(alerts) { - var self = this; - - if (!alerts || alerts.length === 0) { - return E('p', { 'class': 'alert-message' }, 'No alerts in the selected period'); - } - - var rows = alerts.map(function(a) { - var sourceIp = '-'; - if (a.source && a.source.ip) { - sourceIp = a.source.ip; - } else if (a.source_ip) { - sourceIp = a.source_ip; - } - - return E('tr', { 'class': 'tr' }, [ - E('td', { 'class': 'td' }, self.formatDate(a.created_at || a.timestamp)), - E('td', { 'class': 'td' }, sourceIp), - E('td', { 'class': 'td' }, a.scenario || '-'), - E('td', { 'class': 'td' }, String(a.events_count || a.events || 0)), - E('td', { 'class': 'td' }, a.message || '-') - ]); - }); - - return E('table', { 'class': 'table cbi-section-table' }, [ - E('tr', { 'class': 'tr table-titles' }, [ - E('th', { 'class': 'th' }, 'Time'), - E('th', { 'class': 'th' }, 'Source IP'), - E('th', { 'class': 'th' }, 'Scenario'), - E('th', { 'class': 'th' }, 'Events'), - E('th', { 'class': 'th' }, 'Message') - ]) - ].concat(rows)); - }, - - render: function(data) { - var alerts = data.alerts || []; - var self = this; - - var view = E('div', { 'class': 'cbi-map' }, [ - E('h2', { 'class': 'cbi-map-title' }, 'CrowdSec Alerts'), - E('div', { 'class': 'cbi-map-descr' }, 'Security alerts detected by CrowdSec'), - - // Filter Controls - E('div', { 'class': 'cbi-section' }, [ - E('h3', { 'class': 'cbi-section-title' }, 'Filter'), - E('div', { 'style': 'margin-bottom: 15px;' }, [ - E('label', {}, 'Time Period: '), - E('select', { 'id': 'alert-period', 'class': 'cbi-input-select', 'style': 'margin-right: 15px;' }, [ - E('option', { 'value': '1h' }, 'Last hour'), - E('option', { 'value': '24h' }, 'Last 24 hours'), - E('option', { 'value': '7d', 'selected': 'selected' }, 'Last 7 days'), - E('option', { 'value': '30d' }, 'Last 30 days') - ]), - E('label', {}, 'Limit: '), - E('select', { 'id': 'alert-limit', 'class': 'cbi-input-select', 'style': 'margin-right: 15px;' }, [ - E('option', { 'value': '25' }, '25'), - E('option', { 'value': '50' }, '50'), - E('option', { 'value': '100', 'selected': 'selected' }, '100'), - E('option', { 'value': '200' }, '200') - ]), - E('button', { - 'class': 'btn cbi-button cbi-button-action', - 'click': ui.createHandlerFn(this, function() { - var period = document.getElementById('alert-period').value; - var limit = parseInt(document.getElementById('alert-limit').value); - - return this.api.getAlerts(limit, period).then(function(res) { - var table = self.renderAlertsTable(res.alerts || []); - var container = document.getElementById('alerts-table'); - dom.content(container, table); - var title = document.getElementById('alerts-count'); - if (title) title.textContent = 'Alerts (' + (res.alerts ? res.alerts.length : 0) + ')'; - }); - }) - }, 'Refresh') - ]) - ]), - - // Alerts Table - E('div', { 'class': 'cbi-section' }, [ - E('h3', { 'class': 'cbi-section-title', 'id': 'alerts-count' }, 'Alerts (' + alerts.length + ')'), - E('div', { 'id': 'alerts-table' }, this.renderAlertsTable(alerts)) - ]) - ]); - - return view; - }, - - handleSaveApply: null, - handleSave: null, - handleReset: null -}); diff --git a/package/secubox/luci-app-secubox-crowdsec/htdocs/luci-static/resources/view/secubox-crowdsec/collections.js b/package/secubox/luci-app-secubox-crowdsec/htdocs/luci-static/resources/view/secubox-crowdsec/collections.js deleted file mode 100644 index 1986e307..00000000 --- a/package/secubox/luci-app-secubox-crowdsec/htdocs/luci-static/resources/view/secubox-crowdsec/collections.js +++ /dev/null @@ -1,209 +0,0 @@ -'use strict'; -'require view'; -'require dom'; -'require ui'; -'require secubox-crowdsec/api as api'; - -return view.extend({ - api: null, - - availableCollections: [ - { name: 'crowdsecurity/linux', desc: 'Linux system security' }, - { name: 'crowdsecurity/sshd', desc: 'SSH brute-force protection' }, - { name: 'crowdsecurity/http-cve', desc: 'HTTP CVE exploits' }, - { name: 'crowdsecurity/iptables', desc: 'IPTables/NFTables logs' }, - { name: 'crowdsecurity/nginx', desc: 'Nginx web server' }, - { name: 'crowdsecurity/apache2', desc: 'Apache2 web server' }, - { name: 'crowdsecurity/postfix', desc: 'Postfix mail server' }, - { name: 'crowdsecurity/dovecot', desc: 'Dovecot mail server' }, - { name: 'crowdsecurity/smb', desc: 'SMB/Samba' }, - { name: 'crowdsecurity/wordpress', desc: 'WordPress security' }, - { name: 'crowdsecurity/nextcloud', desc: 'Nextcloud security' } - ], - - load: function() { - this.api = new api(); - return Promise.all([ - this.api.getCollections(), - this.api.getBouncers() - ]); - }, - - isInstalled: function(collections, name) { - if (!collections || !Array.isArray(collections)) return false; - return collections.some(function(c) { - return c.name === name && c.status === 'enabled'; - }); - }, - - renderCollectionsTable: function(collections) { - var self = this; - - if (!collections || collections.length === 0) { - return E('p', { 'class': 'alert-message' }, 'No collections installed'); - } - - var rows = collections.map(function(c) { - var statusBadge = E('span', { - 'style': 'background-color: ' + (c.status === 'enabled' ? 'green' : 'gray') + '; color: white; padding: 2px 6px; border-radius: 3px; font-size: 11px;' - }, c.status || 'unknown'); - - return E('tr', { 'class': 'tr' }, [ - E('td', { 'class': 'td' }, c.name || '-'), - E('td', { 'class': 'td' }, c.local_version || c.version || '-'), - E('td', { 'class': 'td' }, statusBadge), - E('td', { 'class': 'td' }, c.description || '-'), - E('td', { 'class': 'td' }, [ - E('button', { - 'class': 'btn cbi-button cbi-button-remove', - 'click': ui.createHandlerFn(self, function(ev) { - if (!confirm('Remove collection ' + c.name + '?')) return; - - return self.api.removeCollection(c.name).then(function(res) { - if (res.success) { - ui.addNotification(null, E('p', res.message), 'info'); - window.location.reload(); - } else { - ui.addNotification(null, E('p', res.error), 'warning'); - } - }); - }) - }, 'Remove') - ]) - ]); - }); - - return E('table', { 'class': 'table cbi-section-table' }, [ - E('tr', { 'class': 'tr table-titles' }, [ - E('th', { 'class': 'th' }, 'Name'), - E('th', { 'class': 'th' }, 'Version'), - E('th', { 'class': 'th' }, 'Status'), - E('th', { 'class': 'th' }, 'Description'), - E('th', { 'class': 'th' }, 'Actions') - ]) - ].concat(rows)); - }, - - renderBouncersTable: function(bouncers) { - if (!bouncers || bouncers.length === 0) { - return E('p', { 'class': 'alert-message' }, 'No bouncers registered'); - } - - var rows = bouncers.map(function(b) { - var validBadge = E('span', { - 'style': 'background-color: ' + (b.is_valid ? 'green' : 'red') + '; color: white; padding: 2px 6px; border-radius: 3px; font-size: 11px;' - }, b.is_valid ? 'Valid' : 'Invalid'); - - return E('tr', { 'class': 'tr' }, [ - E('td', { 'class': 'td' }, b.name || '-'), - E('td', { 'class': 'td' }, b.ip_address || b.ip || '-'), - E('td', { 'class': 'td' }, b.type || '-'), - E('td', { 'class': 'td' }, b.last_pull || '-'), - E('td', { 'class': 'td' }, validBadge) - ]); - }); - - return E('table', { 'class': 'table cbi-section-table' }, [ - E('tr', { 'class': 'tr table-titles' }, [ - E('th', { 'class': 'th' }, 'Name'), - E('th', { 'class': 'th' }, 'IP'), - E('th', { 'class': 'th' }, 'Type'), - E('th', { 'class': 'th' }, 'Last Pull'), - E('th', { 'class': 'th' }, 'Status') - ]) - ].concat(rows)); - }, - - render: function(data) { - var collections = (data[0] && data[0].collections) ? data[0].collections : []; - var bouncers = (data[1] && data[1].bouncers) ? data[1].bouncers : []; - var self = this; - - // Build available collections dropdown (filter out installed ones) - var availableOptions = this.availableCollections - .filter(function(c) { return !self.isInstalled(collections, c.name); }) - .map(function(c) { - return E('option', { 'value': c.name }, c.name + ' - ' + c.desc); - }); - - var view = E('div', { 'class': 'cbi-map' }, [ - E('h2', { 'class': 'cbi-map-title' }, 'CrowdSec Collections'), - E('div', { 'class': 'cbi-map-descr' }, 'Manage security collections and bouncers'), - - // Hub Actions - E('div', { 'class': 'cbi-section' }, [ - E('h3', { 'class': 'cbi-section-title' }, 'Hub Management'), - E('div', { 'style': 'margin-bottom: 15px;' }, [ - E('button', { - 'class': 'btn cbi-button cbi-button-action', - 'click': ui.createHandlerFn(this, function() { - return this.api.updateHub().then(function(res) { - if (res.success) { - ui.addNotification(null, E('p', res.message), 'info'); - } else { - ui.addNotification(null, E('p', res.error), 'warning'); - } - }); - }) - }, 'Update Hub'), - ' ', - E('button', { - 'class': 'btn cbi-button cbi-button-apply', - 'click': ui.createHandlerFn(this, function() { - return this.api.upgradeHub().then(function(res) { - if (res.success) { - ui.addNotification(null, E('p', res.message), 'info'); - window.location.reload(); - } else { - ui.addNotification(null, E('p', res.error), 'warning'); - } - }); - }) - }, 'Upgrade All') - ]) - ]), - - // Install Collection - E('div', { 'class': 'cbi-section' }, [ - E('h3', { 'class': 'cbi-section-title' }, 'Install Collection'), - E('div', { 'style': 'margin-bottom: 15px;' }, [ - availableOptions.length > 0 ? E('select', { 'id': 'new-collection', 'class': 'cbi-input-select', 'style': 'margin-right: 10px; min-width: 300px;' }, availableOptions) : E('span', {}, 'All recommended collections installed'), - availableOptions.length > 0 ? E('button', { - 'class': 'btn cbi-button cbi-button-add', - 'click': ui.createHandlerFn(this, function() { - var collection = document.getElementById('new-collection').value; - if (!collection) return; - - return this.api.installCollection(collection).then(function(res) { - if (res.success) { - ui.addNotification(null, E('p', res.message), 'info'); - window.location.reload(); - } else { - ui.addNotification(null, E('p', res.error), 'warning'); - } - }); - }) - }, 'Install') : null - ]) - ]), - - // Installed Collections - E('div', { 'class': 'cbi-section' }, [ - E('h3', { 'class': 'cbi-section-title' }, 'Installed Collections (' + collections.length + ')'), - E('div', { 'id': 'collections-table' }, this.renderCollectionsTable(collections)) - ]), - - // Bouncers - E('div', { 'class': 'cbi-section' }, [ - E('h3', { 'class': 'cbi-section-title' }, 'Registered Bouncers (' + bouncers.length + ')'), - E('div', { 'id': 'bouncers-table' }, this.renderBouncersTable(bouncers)) - ]) - ]); - - return view; - }, - - handleSaveApply: null, - handleSave: null, - handleReset: null -}); diff --git a/package/secubox/luci-app-secubox-crowdsec/htdocs/luci-static/resources/view/secubox-crowdsec/dashboard.js b/package/secubox/luci-app-secubox-crowdsec/htdocs/luci-static/resources/view/secubox-crowdsec/dashboard.js deleted file mode 100644 index e4f3eaca..00000000 --- a/package/secubox/luci-app-secubox-crowdsec/htdocs/luci-static/resources/view/secubox-crowdsec/dashboard.js +++ /dev/null @@ -1,162 +0,0 @@ -'use strict'; -'require view'; -'require dom'; -'require poll'; -'require ui'; -'require secubox-crowdsec/api as api'; - -return view.extend({ - api: null, - - load: function() { - this.api = new api(); - return Promise.all([ - this.api.getStatus(), - this.api.getStats(), - this.api.getNftablesStats() - ]); - }, - - renderStatusBadge: function(status, running, stopped) { - var color = (status === running) ? 'green' : 'red'; - var text = (status === running) ? 'Running' : 'Stopped'; - return E('span', { - 'class': 'badge', - 'style': 'background-color: ' + color + '; color: white; padding: 2px 8px; border-radius: 3px;' - }, text); - }, - - renderServiceCard: function(name, status) { - return E('div', { 'class': 'cbi-section', 'style': 'display: inline-block; width: 200px; margin: 10px; text-align: center; padding: 15px; border: 1px solid #ddd; border-radius: 5px;' }, [ - E('h4', { 'style': 'margin: 0 0 10px 0;' }, name), - this.renderStatusBadge(status, 'running', 'stopped') - ]); - }, - - renderStatCard: function(label, value, icon) { - return E('div', { 'class': 'cbi-section', 'style': 'display: inline-block; width: 150px; margin: 10px; text-align: center; padding: 15px; border: 1px solid #ddd; border-radius: 5px;' }, [ - E('div', { 'style': 'font-size: 24px; font-weight: bold; color: #0066cc;' }, String(value || 0)), - E('div', { 'style': 'color: #666; font-size: 12px;' }, label) - ]); - }, - - render: function(data) { - var status = data[0] || {}; - var stats = data[1] || {}; - var nftStats = data[2] || {}; - var self = this; - - var view = E('div', { 'class': 'cbi-map' }, [ - E('h2', { 'class': 'cbi-map-title' }, 'CrowdSec Dashboard'), - E('div', { 'class': 'cbi-map-descr' }, 'Security engine status and statistics'), - - // Services Status - E('div', { 'class': 'cbi-section' }, [ - E('h3', { 'class': 'cbi-section-title' }, 'Services Status'), - E('div', { 'id': 'service-status', 'style': 'text-align: center;' }, [ - this.renderServiceCard('CrowdSec', status.crowdsec), - this.renderServiceCard('Bouncer', status.bouncer), - this.renderServiceCard('Syslog-ng', status.syslog) - ]) - ]), - - // API Status - E('div', { 'class': 'cbi-section' }, [ - E('h3', { 'class': 'cbi-section-title' }, 'API Status'), - E('table', { 'class': 'table cbi-section-table' }, [ - E('tr', { 'class': 'tr' }, [ - E('td', { 'class': 'td' }, 'CrowdSec Version'), - E('td', { 'class': 'td' }, status.version || 'Unknown') - ]), - E('tr', { 'class': 'tr' }, [ - E('td', { 'class': 'td' }, 'Local API (LAPI)'), - E('td', { 'class': 'td' }, this.renderStatusBadge(status.lapi_status, 'available', 'unavailable')) - ]), - E('tr', { 'class': 'tr' }, [ - E('td', { 'class': 'td' }, 'Central API (CAPI)'), - E('td', { 'class': 'td' }, this.renderStatusBadge(status.capi_status, 'connected', 'disconnected')) - ]) - ]) - ]), - - // Statistics - E('div', { 'class': 'cbi-section' }, [ - E('h3', { 'class': 'cbi-section-title' }, 'Statistics'), - E('div', { 'id': 'stats-cards', 'style': 'text-align: center;' }, [ - this.renderStatCard('Active Decisions', stats.total_decisions), - this.renderStatCard('Alerts (24h)', stats.alerts_24h), - this.renderStatCard('Bouncers', stats.bouncers), - this.renderStatCard('Parsers', stats.parsers), - this.renderStatCard('Scenarios', stats.scenarios) - ]) - ]), - - // NFTables Status - E('div', { 'class': 'cbi-section' }, [ - E('h3', { 'class': 'cbi-section-title' }, 'Firewall (nftables)'), - E('table', { 'class': 'table cbi-section-table' }, [ - E('tr', { 'class': 'tr' }, [ - E('td', { 'class': 'td' }, 'IPv4 Table'), - E('td', { 'class': 'td' }, nftStats.ipv4_table ? 'Active' : 'Inactive') - ]), - E('tr', { 'class': 'tr' }, [ - E('td', { 'class': 'td' }, 'IPv6 Table'), - E('td', { 'class': 'td' }, nftStats.ipv6_table ? 'Active' : 'Inactive') - ]), - E('tr', { 'class': 'tr' }, [ - E('td', { 'class': 'td' }, 'Blocked IPv4'), - E('td', { 'class': 'td' }, String(nftStats.blocked_ipv4 || 0)) - ]), - E('tr', { 'class': 'tr' }, [ - E('td', { 'class': 'td' }, 'Blocked IPv6'), - E('td', { 'class': 'td' }, String(nftStats.blocked_ipv6 || 0)) - ]) - ]) - ]), - - // Service Controls - E('div', { 'class': 'cbi-section' }, [ - E('h3', { 'class': 'cbi-section-title' }, 'Service Controls'), - E('div', { 'style': 'text-align: center;' }, [ - E('button', { - 'class': 'btn cbi-button cbi-button-apply', - 'click': ui.createHandlerFn(this, function() { - return this.api.controlService('crowdsec', 'restart').then(function() { - ui.addNotification(null, E('p', 'CrowdSec restarted'), 'info'); - window.location.reload(); - }); - }) - }, 'Restart CrowdSec'), - ' ', - E('button', { - 'class': 'btn cbi-button cbi-button-apply', - 'click': ui.createHandlerFn(this, function() { - return this.api.controlService('crowdsec-firewall-bouncer', 'restart').then(function() { - ui.addNotification(null, E('p', 'Bouncer restarted'), 'info'); - window.location.reload(); - }); - }) - }, 'Restart Bouncer'), - ' ', - E('button', { - 'class': 'btn cbi-button cbi-button-action', - 'click': ui.createHandlerFn(this, function() { - return this.api.updateHub().then(function(res) { - if (res.success) - ui.addNotification(null, E('p', res.message), 'info'); - else - ui.addNotification(null, E('p', res.error), 'warning'); - }); - }) - }, 'Update Hub') - ]) - ]) - ]); - - return view; - }, - - handleSaveApply: null, - handleSave: null, - handleReset: null -}); diff --git a/package/secubox/luci-app-secubox-crowdsec/htdocs/luci-static/resources/view/secubox-crowdsec/decisions.js b/package/secubox/luci-app-secubox-crowdsec/htdocs/luci-static/resources/view/secubox-crowdsec/decisions.js deleted file mode 100644 index 371e7c39..00000000 --- a/package/secubox/luci-app-secubox-crowdsec/htdocs/luci-static/resources/view/secubox-crowdsec/decisions.js +++ /dev/null @@ -1,139 +0,0 @@ -'use strict'; -'require view'; -'require dom'; -'require ui'; -'require secubox-crowdsec/api as api'; - -return view.extend({ - api: null, - - load: function() { - this.api = new api(); - return this.api.getDecisions(); - }, - - renderDecisionsTable: function(decisions) { - var self = this; - - if (!decisions || decisions.length === 0) { - return E('p', { 'class': 'alert-message' }, 'No active decisions'); - } - - var rows = decisions.map(function(d) { - return E('tr', { 'class': 'tr' }, [ - E('td', { 'class': 'td' }, d.value || d.ip || '-'), - E('td', { 'class': 'td' }, d.type || 'ban'), - E('td', { 'class': 'td' }, d.scope || 'ip'), - E('td', { 'class': 'td' }, d.duration || '-'), - E('td', { 'class': 'td' }, d.origin || '-'), - E('td', { 'class': 'td' }, d.scenario || d.reason || '-'), - E('td', { 'class': 'td' }, [ - E('button', { - 'class': 'btn cbi-button cbi-button-remove', - 'click': ui.createHandlerFn(self, function(ev) { - return self.api.deleteDecision(d.value || d.ip, d.id).then(function(res) { - if (res.success) { - ui.addNotification(null, E('p', res.message), 'info'); - window.location.reload(); - } else { - ui.addNotification(null, E('p', res.error), 'warning'); - } - }); - }) - }, 'Delete') - ]) - ]); - }); - - return E('table', { 'class': 'table cbi-section-table' }, [ - E('tr', { 'class': 'tr table-titles' }, [ - E('th', { 'class': 'th' }, 'IP/Range'), - E('th', { 'class': 'th' }, 'Type'), - E('th', { 'class': 'th' }, 'Scope'), - E('th', { 'class': 'th' }, 'Duration'), - E('th', { 'class': 'th' }, 'Origin'), - E('th', { 'class': 'th' }, 'Reason'), - E('th', { 'class': 'th' }, 'Actions') - ]) - ].concat(rows)); - }, - - render: function(data) { - var decisions = data.decisions || []; - var self = this; - - var view = E('div', { 'class': 'cbi-map' }, [ - E('h2', { 'class': 'cbi-map-title' }, 'CrowdSec Decisions'), - E('div', { 'class': 'cbi-map-descr' }, 'Active bans and security decisions'), - - // Add Decision Form - E('div', { 'class': 'cbi-section' }, [ - E('h3', { 'class': 'cbi-section-title' }, 'Add Manual Ban'), - E('div', { 'class': 'cbi-value' }, [ - E('label', { 'class': 'cbi-value-title' }, 'IP Address'), - E('div', { 'class': 'cbi-value-field' }, [ - E('input', { 'type': 'text', 'id': 'ban-ip', 'class': 'cbi-input-text', 'placeholder': '192.168.1.100' }) - ]) - ]), - E('div', { 'class': 'cbi-value' }, [ - E('label', { 'class': 'cbi-value-title' }, 'Duration'), - E('div', { 'class': 'cbi-value-field' }, [ - E('select', { 'id': 'ban-duration', 'class': 'cbi-input-select' }, [ - E('option', { 'value': '1h' }, '1 hour'), - E('option', { 'value': '4h' }, '4 hours'), - E('option', { 'value': '24h', 'selected': 'selected' }, '24 hours'), - E('option', { 'value': '7d' }, '7 days'), - E('option', { 'value': '30d' }, '30 days'), - E('option', { 'value': '365d' }, '1 year') - ]) - ]) - ]), - E('div', { 'class': 'cbi-value' }, [ - E('label', { 'class': 'cbi-value-title' }, 'Reason'), - E('div', { 'class': 'cbi-value-field' }, [ - E('input', { 'type': 'text', 'id': 'ban-reason', 'class': 'cbi-input-text', 'placeholder': 'Manual ban via LuCI', 'value': 'Manual ban via LuCI' }) - ]) - ]), - E('div', { 'class': 'cbi-value' }, [ - E('label', { 'class': 'cbi-value-title' }, ' '), - E('div', { 'class': 'cbi-value-field' }, [ - E('button', { - 'class': 'btn cbi-button cbi-button-apply', - 'click': ui.createHandlerFn(this, function() { - var ip = document.getElementById('ban-ip').value; - var duration = document.getElementById('ban-duration').value; - var reason = document.getElementById('ban-reason').value; - - if (!ip) { - ui.addNotification(null, E('p', 'Please enter an IP address'), 'warning'); - return; - } - - return this.api.addDecision(ip, duration, reason, 'ban').then(function(res) { - if (res.success) { - ui.addNotification(null, E('p', res.message), 'info'); - window.location.reload(); - } else { - ui.addNotification(null, E('p', res.error), 'warning'); - } - }); - }) - }, 'Add Ban') - ]) - ]) - ]), - - // Active Decisions - E('div', { 'class': 'cbi-section' }, [ - E('h3', { 'class': 'cbi-section-title' }, 'Active Decisions (' + decisions.length + ')'), - E('div', { 'id': 'decisions-table' }, this.renderDecisionsTable(decisions)) - ]) - ]); - - return view; - }, - - handleSaveApply: null, - handleSave: null, - handleReset: null -}); diff --git a/package/secubox/luci-app-secubox-crowdsec/htdocs/luci-static/resources/view/secubox-crowdsec/settings.js b/package/secubox/luci-app-secubox-crowdsec/htdocs/luci-static/resources/view/secubox-crowdsec/settings.js deleted file mode 100644 index e4cc05ac..00000000 --- a/package/secubox/luci-app-secubox-crowdsec/htdocs/luci-static/resources/view/secubox-crowdsec/settings.js +++ /dev/null @@ -1,116 +0,0 @@ -'use strict'; -'require view'; -'require dom'; -'require ui'; -'require uci'; -'require form'; -'require secubox-crowdsec/api as api'; - -return view.extend({ - api: null, - - load: function() { - this.api = new api(); - return Promise.all([ - uci.load('crowdsec'), - this.api.getConfig() - ]); - }, - - render: function(data) { - var config = data[1] || {}; - var m, s, o; - - m = new form.Map('crowdsec', 'CrowdSec Settings', - 'Configure CrowdSec security engine and firewall bouncer settings.'); - - // Main CrowdSec settings - s = m.section(form.TypedSection, 'crowdsec', 'CrowdSec Engine'); - s.anonymous = true; - - o = s.option(form.Flag, 'enabled', 'Enable CrowdSec', - 'Enable or disable the CrowdSec security engine'); - o.default = '1'; - o.rmempty = false; - - // Bouncer settings - s = m.section(form.TypedSection, 'bouncer', 'Firewall Bouncer'); - s.anonymous = true; - - o = s.option(form.Flag, 'enabled', 'Enable Bouncer', - 'Enable the firewall bouncer to block malicious IPs'); - o.default = '1'; - o.rmempty = false; - - o = s.option(form.Flag, 'ipv4', 'IPv4 Blocking', - 'Enable IPv4 address blocking'); - o.default = '1'; - o.rmempty = false; - - o = s.option(form.Flag, 'ipv6', 'IPv6 Blocking', - 'Enable IPv6 address blocking'); - o.default = '1'; - o.rmempty = false; - - o = s.option(form.ListValue, 'deny_action', 'Deny Action', - 'Action to take when blocking an IP'); - o.value('drop', 'Drop (silent)'); - o.value('reject', 'Reject (with response)'); - o.default = 'drop'; - - o = s.option(form.Flag, 'deny_log', 'Log Blocked IPs', - 'Log blocked connections to system log'); - o.default = '1'; - o.rmempty = false; - - o = s.option(form.ListValue, 'update_frequency', 'Update Frequency', - 'How often to fetch new decisions from LAPI'); - o.value('5s', '5 seconds'); - o.value('10s', '10 seconds'); - o.value('30s', '30 seconds'); - o.value('1m', '1 minute'); - o.default = '10s'; - - // Acquisition settings - s = m.section(form.TypedSection, 'acquisition', 'Log Acquisition'); - s.anonymous = true; - - o = s.option(form.Flag, 'syslog_enabled', 'Syslog', - 'Monitor system logs via syslog-ng'); - o.default = '1'; - o.rmempty = false; - - o = s.option(form.Flag, 'firewall_enabled', 'Firewall Logs', - 'Monitor nftables/iptables firewall logs'); - o.default = '1'; - o.rmempty = false; - - o = s.option(form.Flag, 'ssh_enabled', 'SSH Logs', - 'Monitor SSH authentication attempts'); - o.default = '1'; - o.rmempty = false; - - o = s.option(form.Flag, 'http_enabled', 'HTTP Logs', - 'Monitor HTTP server logs (if applicable)'); - o.default = '0'; - o.rmempty = false; - - // Hub settings - s = m.section(form.TypedSection, 'hub', 'Hub Settings'); - s.anonymous = true; - - o = s.option(form.Value, 'collections', 'Default Collections', - 'Space-separated list of collections to install'); - o.default = 'crowdsecurity/linux crowdsecurity/sshd crowdsecurity/iptables'; - - o = s.option(form.ListValue, 'update_interval', 'Hub Update Interval', - 'How often to check for hub updates (days)'); - o.value('1', 'Daily'); - o.value('7', 'Weekly'); - o.value('30', 'Monthly'); - o.value('0', 'Never'); - o.default = '7'; - - return m.render(); - } -}); diff --git a/package/secubox/luci-app-secubox-crowdsec/root/etc/config/crowdsec b/package/secubox/luci-app-secubox-crowdsec/root/etc/config/crowdsec deleted file mode 100644 index 95313df8..00000000 --- a/package/secubox/luci-app-secubox-crowdsec/root/etc/config/crowdsec +++ /dev/null @@ -1,25 +0,0 @@ -config crowdsec 'crowdsec' - option enabled '1' - option data_dir '/srv/crowdsec/data' - option db_path '/srv/crowdsec/data/crowdsec.db' - -config acquisition 'acquisition' - option syslog_enabled '1' - option firewall_enabled '1' - option ssh_enabled '1' - option http_enabled '0' - -config hub 'hub' - option auto_install '1' - option collections 'crowdsecurity/linux crowdsecurity/sshd crowdsecurity/iptables' - option update_interval '7' - -config bouncer 'bouncer' - option enabled '1' - option ipv4 '1' - option ipv6 '1' - option deny_action 'drop' - option deny_log '1' - option update_frequency '10s' - option filter_input '1' - option filter_forward '1' diff --git a/package/secubox/luci-app-secubox-crowdsec/root/usr/libexec/rpcd/luci.secubox-crowdsec b/package/secubox/luci-app-secubox-crowdsec/root/usr/libexec/rpcd/luci.secubox-crowdsec deleted file mode 100755 index f79de565..00000000 --- a/package/secubox/luci-app-secubox-crowdsec/root/usr/libexec/rpcd/luci.secubox-crowdsec +++ /dev/null @@ -1,691 +0,0 @@ -#!/bin/sh -# SPDX-License-Identifier: MIT -# SecuBox CrowdSec RPCD Backend -# Copyright (C) 2025 CyberMind.fr - Gandalf - -. /lib/functions.sh -. /usr/share/libubox/jshn.sh - -CSCLI="/usr/bin/cscli" -CSCLI_TIMEOUT=10 - -# Execution cscli avec timeout pour eviter les blocages -run_cscli() { - timeout "$CSCLI_TIMEOUT" "$CSCLI" "$@" 2>/dev/null -} - -# ============================================================================= -# FONCTIONS STATUS -# ============================================================================= - -# Statut global des services -get_status() { - json_init - - # Service CrowdSec - if pgrep -x crowdsec >/dev/null 2>&1; then - json_add_string "crowdsec" "running" - else - json_add_string "crowdsec" "stopped" - fi - - # Service Bouncer - if pgrep -f "crowdsec-firewall-bouncer" >/dev/null 2>&1; then - json_add_string "bouncer" "running" - else - json_add_string "bouncer" "stopped" - fi - - # Service syslog-ng - if pgrep -f "syslog-ng" >/dev/null 2>&1; then - json_add_string "syslog" "running" - else - json_add_string "syslog" "stopped" - fi - - # Version CrowdSec - local version - version=$(run_cscli version 2>/dev/null | grep "version:" | awk '{print $2}') - json_add_string "version" "${version:-unknown}" - - # LAPI status - local lapi_status="unavailable" - if [ -x "$CSCLI" ]; then - if run_cscli lapi status >/dev/null 2>&1; then - lapi_status="available" - fi - fi - json_add_string "lapi_status" "$lapi_status" - - # CAPI status - local capi_status="unknown" - local capi_output - capi_output=$(run_cscli capi status 2>/dev/null) - if echo "$capi_output" | grep -qi "online\|connected"; then - capi_status="connected" - elif echo "$capi_output" | grep -qi "offline\|disconnected"; then - capi_status="disconnected" - fi - json_add_string "capi_status" "$capi_status" - - # Tables nftables - local nft_ipv4=0 - local nft_ipv6=0 - if command -v nft >/dev/null 2>&1; then - nft list tables 2>/dev/null | grep -q "ip crowdsec" && nft_ipv4=1 - nft list tables 2>/dev/null | grep -q "ip6 crowdsec6" && nft_ipv6=1 - fi - json_add_boolean "nftables_ipv4" "$nft_ipv4" - json_add_boolean "nftables_ipv6" "$nft_ipv6" - - json_dump -} - -# ============================================================================= -# FONCTIONS DECISIONS -# ============================================================================= - -# Liste des decisions actives -get_decisions() { - if [ ! -x "$CSCLI" ]; then - echo '{"decisions":[],"error":"cscli not found"}' - return - fi - - if ! pgrep -x crowdsec >/dev/null 2>&1; then - echo '{"decisions":[],"error":"crowdsec not running"}' - return - fi - - local output - output=$(run_cscli decisions list -o json) - if [ -z "$output" ] || [ "$output" = "null" ]; then - echo '{"decisions":[]}' - else - echo "{\"decisions\":$output}" - fi -} - -# Ajouter une decision (ban) -add_decision() { - local ip="$1" - local duration="${2:-24h}" - local reason="${3:-Ban manuel via LuCI}" - local decision_type="${4:-ban}" - - json_init - - if [ -z "$ip" ]; then - json_add_boolean "success" 0 - json_add_string "error" "IP requise" - json_dump - return - fi - - local result - result=$(run_cscli decisions add --ip "$ip" --duration "$duration" --reason "$reason" --type "$decision_type" 2>&1) - - if [ $? -eq 0 ]; then - json_add_boolean "success" 1 - json_add_string "message" "IP $ip bannie pour $duration" - logger -t "secubox-crowdsec" "Ban manuel: $ip pour $duration - $reason" - else - json_add_boolean "success" 0 - json_add_string "error" "$result" - fi - - json_dump -} - -# Supprimer une decision (unban) -delete_decision() { - local ip="$1" - local decision_id="$2" - - json_init - - local result - if [ -n "$decision_id" ]; then - result=$(run_cscli decisions delete --id "$decision_id" 2>&1) - elif [ -n "$ip" ]; then - result=$(run_cscli decisions delete --ip "$ip" 2>&1) - else - json_add_boolean "success" 0 - json_add_string "error" "IP ou ID decision requis" - json_dump - return - fi - - if [ $? -eq 0 ]; then - json_add_boolean "success" 1 - json_add_string "message" "Decision supprimee" - logger -t "secubox-crowdsec" "Unban: ${ip:-ID:$decision_id}" - else - json_add_boolean "success" 0 - json_add_string "error" "$result" - fi - - json_dump -} - -# ============================================================================= -# FONCTIONS ALERTES -# ============================================================================= - -# Liste des alertes -get_alerts() { - local limit="${1:-50}" - local since="${2:-24h}" - - if [ ! -x "$CSCLI" ]; then - echo '{"alerts":[],"error":"cscli not found"}' - return - fi - - local output - output=$(run_cscli alerts list -o json --limit "$limit" --since "$since") - if [ -z "$output" ] || [ "$output" = "null" ]; then - echo '{"alerts":[]}' - else - echo "{\"alerts\":$output}" - fi -} - -# ============================================================================= -# FONCTIONS METRIQUES -# ============================================================================= - -# Metriques globales -get_metrics() { - if [ ! -x "$CSCLI" ]; then - echo '{"error":"cscli not found"}' - return - fi - - local output - output=$(run_cscli metrics -o json) - if [ -z "$output" ]; then - echo '{}' - else - echo "$output" - fi -} - -# Statistiques resumees -get_stats() { - json_init - - if [ ! -x "$CSCLI" ] || ! pgrep -x crowdsec >/dev/null 2>&1; then - json_add_int "total_decisions" 0 - json_add_int "alerts_24h" 0 - json_add_int "bouncers" 0 - json_add_int "parsers" 0 - json_add_int "scenarios" 0 - json_dump - return - fi - - # Compter les decisions actives - local decisions_count - decisions_count=$(run_cscli decisions list -o json 2>/dev/null | grep -c '"id"' || echo "0") - json_add_int "total_decisions" "${decisions_count:-0}" - - # Compter les alertes 24h - local alerts_count - alerts_count=$(run_cscli alerts list -o json --since 24h 2>/dev/null | grep -c '"id"' || echo "0") - json_add_int "alerts_24h" "${alerts_count:-0}" - - # Compter les bouncers - local bouncers_count - bouncers_count=$(run_cscli bouncers list -o json 2>/dev/null | grep -c '"name"' || echo "0") - json_add_int "bouncers" "${bouncers_count:-0}" - - # Compter les parsers installes - local parsers_count - parsers_count=$(run_cscli parsers list 2>/dev/null | grep -c "INSTALLED" || echo "0") - json_add_int "parsers" "${parsers_count:-0}" - - # Compter les scenarios installes - local scenarios_count - scenarios_count=$(run_cscli scenarios list 2>/dev/null | grep -c "INSTALLED" || echo "0") - json_add_int "scenarios" "${scenarios_count:-0}" - - json_dump -} - -# ============================================================================= -# FONCTIONS COLLECTIONS -# ============================================================================= - -# Liste des collections -get_collections() { - if [ ! -x "$CSCLI" ]; then - echo '{"collections":[]}' - return - fi - - local output - output=$(run_cscli collections list -o json) - if [ -z "$output" ] || [ "$output" = "null" ]; then - echo '{"collections":[]}' - else - echo "{\"collections\":$output}" - fi -} - -# Installer une collection -install_collection() { - local collection="$1" - - json_init - - if [ -z "$collection" ]; then - json_add_boolean "success" 0 - json_add_string "error" "Nom de collection requis" - json_dump - return - fi - - local result - result=$(run_cscli collections install "$collection" 2>&1) - - if [ $? -eq 0 ]; then - json_add_boolean "success" 1 - json_add_string "message" "Collection $collection installee" - logger -t "secubox-crowdsec" "Collection installee: $collection" - else - json_add_boolean "success" 0 - json_add_string "error" "$result" - fi - - json_dump -} - -# Desinstaller une collection -remove_collection() { - local collection="$1" - - json_init - - if [ -z "$collection" ]; then - json_add_boolean "success" 0 - json_add_string "error" "Nom de collection requis" - json_dump - return - fi - - local result - result=$(run_cscli collections remove "$collection" 2>&1) - - if [ $? -eq 0 ]; then - json_add_boolean "success" 1 - json_add_string "message" "Collection $collection supprimee" - logger -t "secubox-crowdsec" "Collection supprimee: $collection" - else - json_add_boolean "success" 0 - json_add_string "error" "$result" - fi - - json_dump -} - -# ============================================================================= -# FONCTIONS HUB -# ============================================================================= - -# Mise a jour du hub -update_hub() { - json_init - - local result - result=$(run_cscli hub update 2>&1) - - if [ $? -eq 0 ]; then - json_add_boolean "success" 1 - json_add_string "message" "Hub mis a jour" - logger -t "secubox-crowdsec" "Hub mis a jour" - else - json_add_boolean "success" 0 - json_add_string "error" "$result" - fi - - json_dump -} - -# Upgrade du hub -upgrade_hub() { - json_init - - run_cscli hub update >/dev/null 2>&1 - local result - result=$(run_cscli hub upgrade 2>&1) - - if [ $? -eq 0 ]; then - json_add_boolean "success" 1 - json_add_string "message" "Hub upgrade effectue" - logger -t "secubox-crowdsec" "Hub upgrade effectue" - else - json_add_boolean "success" 0 - json_add_string "error" "$result" - fi - - json_dump -} - -# ============================================================================= -# FONCTIONS BOUNCERS -# ============================================================================= - -# Liste des bouncers -get_bouncers() { - if [ ! -x "$CSCLI" ]; then - echo '{"bouncers":[]}' - return - fi - - local output - output=$(run_cscli bouncers list -o json) - if [ -z "$output" ] || [ "$output" = "null" ]; then - echo '{"bouncers":[]}' - else - echo "{\"bouncers\":$output}" - fi -} - -# ============================================================================= -# FONCTIONS SERVICES -# ============================================================================= - -# Controle des services -control_service() { - local service="$1" - local action="$2" - - json_init - - case "$service" in - crowdsec|syslog-ng|crowdsec-firewall-bouncer) - ;; - *) - json_add_boolean "success" 0 - json_add_string "error" "Service invalide: $service" - json_dump - return - ;; - esac - - case "$action" in - start|stop|restart|enable|disable) - ;; - *) - json_add_boolean "success" 0 - json_add_string "error" "Action invalide: $action" - json_dump - return - ;; - esac - - if /etc/init.d/"$service" "$action" >/dev/null 2>&1; then - json_add_boolean "success" 1 - json_add_string "message" "Service $service: $action OK" - logger -t "secubox-crowdsec" "Service $service: $action" - else - json_add_boolean "success" 0 - json_add_string "error" "Echec $action sur $service" - fi - - json_dump -} - -# ============================================================================= -# FONCTIONS NFTABLES -# ============================================================================= - -# Statistiques nftables -get_nftables_stats() { - json_init - - if ! command -v nft >/dev/null 2>&1; then - json_add_boolean "available" 0 - json_add_string "error" "nftables non disponible" - json_dump - return - fi - - json_add_boolean "available" 1 - - # Table IPv4 - local ipv4_exists=0 - if nft list table ip crowdsec >/dev/null 2>&1; then - ipv4_exists=1 - fi - json_add_boolean "ipv4_table" "$ipv4_exists" - - # Table IPv6 - local ipv6_exists=0 - if nft list table ip6 crowdsec6 >/dev/null 2>&1; then - ipv6_exists=1 - fi - json_add_boolean "ipv6_table" "$ipv6_exists" - - # Compter les IPs bloquees - local ipv4_count=0 - local ipv6_count=0 - - if [ "$ipv4_exists" = "1" ]; then - ipv4_count=$(nft list set ip crowdsec crowdsec-blacklists 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | wc -l || echo "0") - fi - if [ "$ipv6_exists" = "1" ]; then - ipv6_count=$(nft list set ip6 crowdsec6 crowdsec6-blacklists 2>/dev/null | grep -c ':' || echo "0") - fi - - json_add_int "blocked_ipv4" "$ipv4_count" - json_add_int "blocked_ipv6" "$ipv6_count" - - json_dump -} - -# Liste des IPs bloquees -get_blocked_ips() { - json_init - json_add_array "ipv4" - - if nft list set ip crowdsec crowdsec-blacklists 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+(/[0-9]+)?' > /tmp/blocked_ips.tmp; then - while read -r ip; do - json_add_string "" "$ip" - done < /tmp/blocked_ips.tmp - rm -f /tmp/blocked_ips.tmp - fi - - json_close_array - - json_add_array "ipv6" - # IPv6 plus complexe, on simplifie - json_close_array - - json_dump -} - -# ============================================================================= -# FONCTIONS CONFIGURATION -# ============================================================================= - -# Lire la configuration UCI -get_config() { - json_init - - # Section crowdsec - local enabled=$(uci -q get crowdsec.crowdsec.enabled || echo "1") - json_add_string "enabled" "$enabled" - - # Section bouncer - local bouncer_enabled=$(uci -q get crowdsec.bouncer.enabled || echo "1") - local ipv4=$(uci -q get crowdsec.bouncer.ipv4 || echo "1") - local ipv6=$(uci -q get crowdsec.bouncer.ipv6 || echo "1") - local deny_action=$(uci -q get crowdsec.bouncer.deny_action || echo "drop") - local deny_log=$(uci -q get crowdsec.bouncer.deny_log || echo "1") - local update_freq=$(uci -q get crowdsec.bouncer.update_frequency || echo "10s") - - json_add_object "bouncer" - json_add_string "enabled" "$bouncer_enabled" - json_add_string "ipv4" "$ipv4" - json_add_string "ipv6" "$ipv6" - json_add_string "deny_action" "$deny_action" - json_add_string "deny_log" "$deny_log" - json_add_string "update_frequency" "$update_freq" - json_close_object - - # Section acquisition - local syslog_enabled=$(uci -q get crowdsec.acquisition.syslog_enabled || echo "1") - local firewall_enabled=$(uci -q get crowdsec.acquisition.firewall_enabled || echo "1") - local ssh_enabled=$(uci -q get crowdsec.acquisition.ssh_enabled || echo "1") - local http_enabled=$(uci -q get crowdsec.acquisition.http_enabled || echo "0") - - json_add_object "acquisition" - json_add_string "syslog_enabled" "$syslog_enabled" - json_add_string "firewall_enabled" "$firewall_enabled" - json_add_string "ssh_enabled" "$ssh_enabled" - json_add_string "http_enabled" "$http_enabled" - json_close_object - - json_dump -} - -# Sauvegarder la configuration UCI -save_config() { - local key="$1" - local value="$2" - - json_init - - if [ -z "$key" ]; then - json_add_boolean "success" 0 - json_add_string "error" "Cle requise" - json_dump - return - fi - - if uci set "crowdsec.$key=$value" && uci commit crowdsec; then - json_add_boolean "success" 1 - json_add_string "message" "Configuration sauvegardee" - logger -t "secubox-crowdsec" "Config: $key=$value" - else - json_add_boolean "success" 0 - json_add_string "error" "Echec sauvegarde configuration" - fi - - json_dump -} - -# ============================================================================= -# DISPATCHER PRINCIPAL -# ============================================================================= - -case "$1" in - list) - cat << 'EOF' -{ - "status":{}, - "decisions":{}, - "add_decision":{"ip":"string","duration":"string","reason":"string","type":"string"}, - "delete_decision":{"ip":"string","decision_id":"string"}, - "alerts":{"limit":"number","since":"string"}, - "metrics":{}, - "stats":{}, - "collections":{}, - "install_collection":{"collection":"string"}, - "remove_collection":{"collection":"string"}, - "update_hub":{}, - "upgrade_hub":{}, - "bouncers":{}, - "control_service":{"service":"string","action":"string"}, - "nftables_stats":{}, - "blocked_ips":{}, - "config":{}, - "save_config":{"key":"string","value":"string"} -} -EOF - ;; - call) - case "$2" in - status) - get_status - ;; - decisions) - get_decisions - ;; - add_decision) - read -r input - ip=$(echo "$input" | jsonfilter -e '@.ip' 2>/dev/null) - duration=$(echo "$input" | jsonfilter -e '@.duration' 2>/dev/null) - reason=$(echo "$input" | jsonfilter -e '@.reason' 2>/dev/null) - dtype=$(echo "$input" | jsonfilter -e '@.type' 2>/dev/null) - add_decision "$ip" "$duration" "$reason" "$dtype" - ;; - delete_decision) - read -r input - ip=$(echo "$input" | jsonfilter -e '@.ip' 2>/dev/null) - decision_id=$(echo "$input" | jsonfilter -e '@.decision_id' 2>/dev/null) - delete_decision "$ip" "$decision_id" - ;; - alerts) - read -r input - limit=$(echo "$input" | jsonfilter -e '@.limit' 2>/dev/null) - since=$(echo "$input" | jsonfilter -e '@.since' 2>/dev/null) - get_alerts "${limit:-50}" "${since:-24h}" - ;; - metrics) - get_metrics - ;; - stats) - get_stats - ;; - collections) - get_collections - ;; - install_collection) - read -r input - collection=$(echo "$input" | jsonfilter -e '@.collection' 2>/dev/null) - install_collection "$collection" - ;; - remove_collection) - read -r input - collection=$(echo "$input" | jsonfilter -e '@.collection' 2>/dev/null) - remove_collection "$collection" - ;; - update_hub) - update_hub - ;; - upgrade_hub) - upgrade_hub - ;; - bouncers) - get_bouncers - ;; - control_service) - read -r input - service=$(echo "$input" | jsonfilter -e '@.service' 2>/dev/null) - action=$(echo "$input" | jsonfilter -e '@.action' 2>/dev/null) - control_service "$service" "$action" - ;; - nftables_stats) - get_nftables_stats - ;; - blocked_ips) - get_blocked_ips - ;; - config) - get_config - ;; - save_config) - read -r input - key=$(echo "$input" | jsonfilter -e '@.key' 2>/dev/null) - value=$(echo "$input" | jsonfilter -e '@.value' 2>/dev/null) - save_config "$key" "$value" - ;; - *) - echo '{"error":"Unknown method"}' - ;; - esac - ;; -esac diff --git a/package/secubox/luci-app-secubox-crowdsec/root/usr/share/luci/menu.d/luci-app-secubox-crowdsec.json b/package/secubox/luci-app-secubox-crowdsec/root/usr/share/luci/menu.d/luci-app-secubox-crowdsec.json deleted file mode 100644 index 308a849b..00000000 --- a/package/secubox/luci-app-secubox-crowdsec/root/usr/share/luci/menu.d/luci-app-secubox-crowdsec.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "admin/services/secubox-crowdsec": { - "title": "CrowdSec", - "order": 80, - "action": { - "type": "firstchild" - }, - "depends": { - "acl": ["luci-app-secubox-crowdsec"], - "uci": { "crowdsec": true } - } - }, - "admin/services/secubox-crowdsec/dashboard": { - "title": "Dashboard", - "order": 10, - "action": { - "type": "view", - "path": "secubox-crowdsec/dashboard" - } - }, - "admin/services/secubox-crowdsec/decisions": { - "title": "Decisions", - "order": 20, - "action": { - "type": "view", - "path": "secubox-crowdsec/decisions" - } - }, - "admin/services/secubox-crowdsec/alerts": { - "title": "Alerts", - "order": 30, - "action": { - "type": "view", - "path": "secubox-crowdsec/alerts" - } - }, - "admin/services/secubox-crowdsec/collections": { - "title": "Collections", - "order": 40, - "action": { - "type": "view", - "path": "secubox-crowdsec/collections" - } - }, - "admin/services/secubox-crowdsec/settings": { - "title": "Settings", - "order": 50, - "action": { - "type": "view", - "path": "secubox-crowdsec/settings" - } - } -} diff --git a/package/secubox/luci-app-secubox-crowdsec/root/usr/share/rpcd/acl.d/luci-app-secubox-crowdsec.json b/package/secubox/luci-app-secubox-crowdsec/root/usr/share/rpcd/acl.d/luci-app-secubox-crowdsec.json deleted file mode 100644 index b022d4a3..00000000 --- a/package/secubox/luci-app-secubox-crowdsec/root/usr/share/rpcd/acl.d/luci-app-secubox-crowdsec.json +++ /dev/null @@ -1,47 +0,0 @@ -{ - "luci-app-secubox-crowdsec": { - "description": "Grant access to SecuBox CrowdSec Dashboard", - "read": { - "ubus": { - "luci.secubox-crowdsec": [ - "status", - "decisions", - "alerts", - "metrics", - "stats", - "collections", - "bouncers", - "nftables_stats", - "blocked_ips", - "config" - ] - }, - "uci": [ - "crowdsec" - ], - "file": { - "/etc/crowdsec/*": ["read"], - "/etc/crowdsec/acquis.d/*": ["read"], - "/var/log/crowdsec*.log": ["read"], - "/tmp/log/*.log": ["read"] - } - }, - "write": { - "ubus": { - "luci.secubox-crowdsec": [ - "add_decision", - "delete_decision", - "install_collection", - "remove_collection", - "update_hub", - "upgrade_hub", - "control_service", - "save_config" - ] - }, - "uci": [ - "crowdsec" - ] - } - } -}