feat: Add comprehensive permission management system (v0.3.1)

Implement three-tier permission management across all SecuBox modules:

**1. Package-Level Permissions (PKG_FILE_MODES)**
- Add PKG_FILE_MODES to all 15 module Makefiles
- RPCD scripts: 755 (executable)
- CSS/JS/JSON files: 644 (default, no config needed)
- Ensures correct permissions at installation time

**2. Runtime Permission Fix**
- New script: /usr/libexec/secubox/fix-permissions.sh
- RPCD method: luci.secubox fix_permissions
- UI control: "🔧 Fix Perms" button in Quick Actions
- Fixes all permissions and restarts services

**3. Automation & Documentation**
- secubox-tools/add-pkg-file-modes.sh: Auto-configure PKG_FILE_MODES
- PERMISSIONS-GUIDE.md: Comprehensive permissions guide
- MODULE-ENABLE-DISABLE-DESIGN.md: Enable/disable system design doc
- Updated Makefile template with PKG_FILE_MODES pattern

**Modules Updated:**
- luci-app-auth-guardian
- luci-app-bandwidth-manager
- luci-app-cdn-cache
- luci-app-client-guardian
- luci-app-crowdsec-dashboard
- luci-app-ksm-manager
- luci-app-media-flow
- luci-app-netdata-dashboard
- luci-app-netifyd-dashboard
- luci-app-network-modes
- luci-app-secubox (+ fix-permissions.sh script)
- luci-app-system-hub
- luci-app-traffic-shaper
- luci-app-vhost-manager
- luci-app-wireguard-dashboard

**Benefits:**
- No more manual permission fixes after installation
- Users can fix permissions from UI without SSH access
- Proper OpenWrt package management compliance
- Automated detection and configuration

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2025-12-28 02:19:30 +01:00
parent 33f3b89393
commit a53e5f7068
27 changed files with 1515 additions and 82 deletions

View File

@ -0,0 +1,356 @@
# Module Enable/Disable Design Document
**Version:** 0.3.1
**Date:** 2025-12-27
**Author:** Claude Code + CyberMind
## 🎯 Objectif
Remplacer la logique **start/stop** des modules SecuBox par une logique **enable/disable** (activé/désactivé), car les modules sont des **plugins installés** qu'on souhaite activer ou désactiver, plutôt que des services qu'on démarre ou arrête ponctuellement.
## 📋 Changements Conceptuels
### Avant (v0.2.x)
```
Module installé → peut être "Running" ou "Stopped"
Actions: Start / Stop / Restart
État affiché: "Running" (vert) ou "Stopped" (gris)
```
### Après (v0.3.1+)
```
Module installé → peut être "Enabled" ou "Disabled"
Actions: Enable / Disable
État affiché: "Activé" (vert) ou "Désactivé" (gris)
Info complémentaire: "Service running" (si enabled + running)
```
## 🏗️ Architecture Technique
### 1. Configuration UCI
Chaque module dans `/etc/config/secubox` aura un champ `enabled`:
```uci
config module 'crowdsec'
option name 'CrowdSec Dashboard'
option package 'luci-app-crowdsec-dashboard'
option config 'crowdsec'
option category 'security'
option enabled '1' # NEW: 1 = activé, 0 = désactivé
option icon '🛡️'
option color '#ef4444'
```
### 2. Méthodes RPCD (`luci.secubox`)
#### Anciennes méthodes (DEPRECATED)
- ❌ `start_module(module_id)` → démarre le service
- ❌ `stop_module(module_id)` → arrête le service
- ❌ `restart_module(module_id)` → redémarre le service
#### Nouvelles méthodes (v0.3.1+)
```javascript
// Active un module (config UCI + démarrage service)
enable_module(module_id)
→ uci set secubox.${module}.enabled='1'
→ uci commit secubox
→ /etc/init.d/${service} enable
→ /etc/init.d/${service} start
→ return { success: true, message: "Module activé" }
// Désactive un module (config UCI + arrêt service)
disable_module(module_id)
→ uci set secubox.${module}.enabled='0'
→ uci commit secubox
→ /etc/init.d/${service} disable
→ /etc/init.d/${service} stop
→ return { success: true, message: "Module désactivé" }
// Vérifie si un module est activé
check_module_enabled(module_id)
→ return uci get secubox.${module}.enabled == '1'
// Vérifie si le service tourne (info complémentaire)
check_service_running(module_id)
→ return pgrep -f ${service} > /dev/null
```
### 3. Structure de données retournée
```json
{
"modules": [
{
"id": "crowdsec",
"name": "CrowdSec Dashboard",
"category": "security",
"installed": true,
"enabled": true, // État principal (config UCI)
"running": true, // État du service (info)
"status": "active", // enabled + running = "active"
"icon": "🛡️",
"color": "#ef4444"
},
{
"id": "netdata",
"name": "Netdata Monitoring",
"category": "monitoring",
"installed": true,
"enabled": false, // Module désactivé
"running": false,
"status": "disabled", // Status affiché
"icon": "📊",
"color": "#22c55e"
}
]
}
```
### 4. États Possibles
| enabled | running | status | Badge UI | Description |
|---------|---------|----------|---------------|-------------|
| `true` | `true` | `active` | ✓ Activé | Module activé et service tourne |
| `true` | `false` | `error` | ⚠️ Erreur | Module activé mais service arrêté (problème) |
| `false` | `false` | `disabled` | ○ Désactivé | Module désactivé (état normal) |
| `false` | `true` | `unknown` | ? Inconnu | État incohérent (rare) |
## 🎨 Interface Utilisateur
### Dashboard Principal (SecuBox Hub)
**Avant:**
```
[CrowdSec Dashboard] ● Running [Stop] [Restart]
[Netdata Monitor] ○ Stopped [Start]
```
**Après:**
```
[CrowdSec Dashboard] ✓ Activé [Désactiver]
[Netdata Monitor] ○ Désactivé [Activer]
```
### Module Individual Card
```html
<div class="module-card enabled">
<div class="module-header">
<span class="module-icon">🛡️</span>
<span class="module-name">CrowdSec Dashboard</span>
<span class="module-badge enabled">✓ Activé</span>
</div>
<div class="module-status">
<span class="status-dot running"></span>
<span>Service en cours d'exécution</span>
</div>
<div class="module-actions">
<button class="btn-disable">Désactiver</button>
</div>
</div>
```
### Classes CSS
```css
/* Module states */
.module-badge.enabled {
background: linear-gradient(135deg, #22c55e, #16a34a);
color: white;
}
.module-badge.disabled {
background: var(--sh-bg-secondary);
color: var(--sh-text-muted);
}
.module-badge.error {
background: linear-gradient(135deg, #f59e0b, #d97706);
color: white;
}
/* Status indicators */
.status-dot.running {
background: #22c55e;
animation: pulse 2s infinite;
}
.status-dot.stopped {
background: #94a3b8;
}
```
## 📝 API JavaScript
### Fichier: `secubox/api.js`
```javascript
// Anciennes méthodes (DEPRECATED - à supprimer)
startModule: callStartModule, // DEPRECATED
stopModule: callStopModule, // DEPRECATED
restartModule: callRestartModule, // DEPRECATED
// Nouvelles méthodes (v0.3.1+)
enableModule: callEnableModule, // NEW
disableModule: callDisableModule, // NEW
// Déclarations RPC
var callEnableModule = rpc.declare({
object: 'luci.secubox',
method: 'enable_module',
params: ['module_id'],
expect: { success: false, message: '' }
});
var callDisableModule = rpc.declare({
object: 'luci.secubox',
method: 'disable_module',
params: ['module_id'],
expect: { success: false, message: '' }
});
```
## 🔄 Migration des Données
### Script de migration (à exécuter une fois)
```bash
#!/bin/sh
# migrate-to-enable-disable.sh
. /lib/functions.sh
migrate_module() {
local module="$1"
local running=$(pgrep -f "$module" > /dev/null && echo "1" || echo "0")
# Si le service tourne actuellement, on l'active
if [ "$running" = "1" ]; then
uci set secubox.${module}.enabled='1'
else
# Sinon, on le désactive par défaut
uci set secubox.${module}.enabled='0'
fi
}
# Migrer tous les modules
config_load secubox
config_foreach migrate_module module
uci commit secubox
echo "Migration completed"
```
## 📚 Documentation Utilisateur
### README.md (à ajouter)
```markdown
## Gestion des Modules
Les modules SecuBox sont des plugins installés qui peuvent être **activés** ou **désactivés**.
### Activer un module
- Cliquez sur le bouton **"Activer"** sur la carte du module
- Le module sera configuré pour démarrer automatiquement au boot
- Le service associé démarrera immédiatement
### Désactiver un module
- Cliquez sur le bouton **"Désactiver"** sur la carte du module
- Le module ne démarrera plus automatiquement au boot
- Le service associé s'arrêtera immédiatement
### États des modules
| Badge | Signification |
|-------|---------------|
| ✓ Activé | Module activé et service en cours d'exécution |
| ⚠️ Erreur | Module activé mais service arrêté (vérifier les logs) |
| ○ Désactivé | Module désactivé (normal) |
**Note:** Les modules restent installés même lorsqu'ils sont désactivés. Pour les supprimer complètement, utilisez le gestionnaire de paquets APK.
```
## 🧪 Tests à Effectuer
### Tests Unitaires RPCD
```bash
# Test enable_module
ubus call luci.secubox enable_module '{"module_id":"crowdsec"}'
# Expected: {"success":true,"message":"Module activé"}
# Vérifier config UCI
uci get secubox.crowdsec.enabled
# Expected: 1
# Vérifier service
/etc/init.d/crowdsec enabled && echo "OK" || echo "FAIL"
pgrep crowdsec && echo "Running" || echo "Not running"
# Test disable_module
ubus call luci.secubox disable_module '{"module_id":"crowdsec"}'
# Expected: {"success":true,"message":"Module désactivé"}
# Vérifier
uci get secubox.crowdsec.enabled
# Expected: 0
```
### Tests Interface
1. ✅ Ouvrir le dashboard SecuBox
2. ✅ Vérifier que les modules affichent "Activé" ou "Désactivé"
3. ✅ Cliquer sur "Désactiver" → badge passe à "○ Désactivé"
4. ✅ Cliquer sur "Activer" → badge passe à "✓ Activé"
5. ✅ Vérifier que le service démarre/s'arrête réellement
6. ✅ Rafraîchir la page → état persiste (UCI)
## 📦 Modules Affectés
### SecuBox Hub (`luci-app-secubox`)
**Fichiers à modifier:**
- ✅ `root/usr/libexec/rpcd/luci.secubox` - Backend RPCD
- ✅ `htdocs/luci-static/resources/secubox/api.js` - API JS
- ✅ `htdocs/luci-static/resources/view/secubox/dashboard.js` - Dashboard
- ✅ `htdocs/luci-static/resources/view/secubox/modules.js` - Module list
- ✅ `htdocs/luci-static/resources/secubox/dashboard.css` - Styles
- ✅ `root/usr/share/rpcd/acl.d/luci-app-secubox.json` - ACL permissions
- ✅ `README.md` - Documentation
### System Hub (`luci-app-system-hub`)
**Fichiers à modifier:**
- ✅ `htdocs/luci-static/resources/view/system-hub/components.js` - Vue composants
- ✅ `htdocs/luci-static/resources/view/system-hub/services.js` - Vue services
- ✅ `README.md` - Documentation
## 🎯 Bénéfices
1. **Clarté conceptuelle**: "Activer/Désactiver" est plus clair que "Démarrer/Arrêter" pour des plugins
2. **Persistance**: L'état persiste après redémarrage (UCI + init.d enable/disable)
3. **Cohérence**: Tous les modules suivent la même logique
4. **Meilleure UX**: L'utilisateur comprend qu'il active/désactive des fonctionnalités
5. **Alignement OpenWrt**: Utilise les mécanismes natifs (`/etc/init.d/${service} enable/disable`)
## 🔜 Prochaines Étapes
- [x] Créer ce document de design
- [ ] Implémenter les modifications RPCD
- [ ] Mettre à jour l'API JavaScript
- [ ] Mettre à jour les interfaces UI
- [ ] Mettre à jour les ACL permissions
- [ ] Créer script de migration UCI
- [ ] Mettre à jour la documentation
- [ ] Tester sur router de test
- [ ] Déployer en production
---
**Maintainer:** CyberMind <contact@cybermind.fr>
**License:** Apache-2.0

229
PERMISSIONS-GUIDE.md Normal file
View File

@ -0,0 +1,229 @@
# OpenWrt Package Permissions Guide
**Version:** 0.3.1
**Date:** 2025-12-28
**Author:** CyberMind
## 🎯 Objectif
Assurer que tous les fichiers des packages SecuBox ont les **bonnes permissions** dès l'installation, sans nécessiter de correction manuelle.
## 📋 Permissions Requises
### Fichiers Exécutables (755)
Ces fichiers **DOIVENT** avoir les permissions d'exécution:
```
-rwxr-xr-x (755)
```
**Liste des fichiers:**
- `/usr/libexec/rpcd/luci.*` - Scripts RPCD backend
- `/usr/libexec/secubox/*.sh` - Scripts utilitaires
- `/etc/init.d/*` - Scripts d'initialisation
- `/etc/uci-defaults/*` - Scripts de configuration initiale
### Fichiers Non-Exécutables (644)
Ces fichiers **NE DOIVENT PAS** être exécutables:
```
-rw-r--r-- (644)
```
**Liste des fichiers:**
- `/www/luci-static/resources/**/*.js` - Fichiers JavaScript
- `/www/luci-static/resources/**/*.css` - Fichiers CSS
- `/usr/share/rpcd/acl.d/*.json` - Permissions ACL
- `/usr/share/luci/menu.d/*.json` - Définitions de menu
- `/etc/config/*` - Fichiers de configuration UCI
## 🔧 Configuration dans le Makefile
### Méthode Recommandée: PKG_FILE_MODES
OpenWrt supporte la variable `PKG_FILE_MODES` pour définir les permissions des fichiers lors de l'installation du package.
**Syntaxe:**
```makefile
PKG_FILE_MODES:=/path/to/file:permissions
```
**Exemple complet:**
```makefile
include $(TOPDIR)/rules.mk
PKG_NAME:=luci-app-example
PKG_VERSION:=0.3.1
PKG_RELEASE:=1
PKG_LICENSE:=Apache-2.0
PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>
LUCI_TITLE:=LuCI - Example Module
LUCI_DESCRIPTION:=Example SecuBox module
LUCI_DEPENDS:=+luci-base +rpcd
LUCI_PKGARCH:=all
# File permissions (RPCD scripts must be executable)
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.example:755
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature
```
### Plusieurs Fichiers Exécutables
Si vous avez plusieurs fichiers exécutables:
```makefile
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.example:755 \
/usr/libexec/example/helper.sh:755 \
/etc/init.d/example:755
```
**Note:** Utilisez `\` pour continuer sur la ligne suivante.
## 📦 Modules SecuBox avec PKG_FILE_MODES
### luci-app-secubox
```makefile
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.secubox:755 \
/usr/libexec/secubox/fix-permissions.sh:755
```
### luci-app-system-hub
```makefile
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.system-hub:755
```
### luci-app-network-modes
```makefile
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.network-modes:755
```
## 🧪 Vérification
### Lors du Développement
Avant de déployer un package, vérifiez les permissions:
```bash
# Vérifier les scripts RPCD
ls -l root/usr/libexec/rpcd/luci.*
# Vérifier les scripts helper
ls -l root/usr/libexec/*/
# Vérifier les fichiers web
find root/www -type f -name "*.js" -o -name "*.css" | xargs ls -l
```
### Après Installation du Package
Vérifiez que les permissions sont correctes sur le routeur:
```bash
# RPCD scripts doivent être 755
ls -l /usr/libexec/rpcd/luci.*
# Fichiers web doivent être 644
ls -l /www/luci-static/resources/secubox/*.js
ls -l /www/luci-static/resources/secubox/*.css
```
## 🛠️ Script de Vérification Automatique
Un script de vérification est inclus dans `luci-app-secubox`:
```bash
# Vérifier et corriger toutes les permissions
/usr/libexec/secubox/fix-permissions.sh
# Via ubus
ubus call luci.secubox fix_permissions
# Via l'interface web
Dashboard → Quick Actions → "🔧 Fix Perms"
```
## ⚠️ Erreurs Communes
### 1. RPCD Script Non-Exécutable
**Symptôme:**
```bash
ubus call luci.example status
# Command failed: Permission denied
```
**Cause:** Le script RPCD n'a pas les permissions 755
**Solution:**
```makefile
# Ajouter dans le Makefile
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.example:755
```
### 2. Fichiers Web Exécutables
**Symptôme:** Fichiers JavaScript/CSS avec permissions 755
**Cause:** Mauvaise manipulation ou script mal configuré
**Solution:** Les fichiers web sont 644 par défaut avec LuCI, pas besoin de les spécifier dans PKG_FILE_MODES
### 3. Script Helper Non-Exécutable
**Symptôme:**
```bash
/usr/libexec/example/helper.sh
# -bash: /usr/libexec/example/helper.sh: Permission denied
```
**Solution:**
```makefile
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.example:755 \
/usr/libexec/example/helper.sh:755
```
## 📚 Références
- **LuCI Build System:** `$(TOPDIR)/feeds/luci/luci.mk`
- **OpenWrt Package Build:** https://openwrt.org/docs/guide-developer/packages
- **PKG_FILE_MODES:** https://openwrt.org/docs/guide-developer/build-system/use-buildsystem#build_system_variables
## ✅ Checklist Pré-Déploiement
Avant de créer un package `.ipk` ou `.apk`:
- [ ] Tous les scripts RPCD ont 755 dans PKG_FILE_MODES
- [ ] Tous les scripts helper ont 755 dans PKG_FILE_MODES
- [ ] Les fichiers web (JS/CSS) ne sont PAS dans PKG_FILE_MODES (ils sont 644 par défaut)
- [ ] Les fichiers ACL/Menu ne sont PAS dans PKG_FILE_MODES (ils sont 644 par défaut)
- [ ] Le Makefile utilise `include $(TOPDIR)/feeds/luci/luci.mk`
- [ ] PKG_FILE_MODES est défini AVANT le `include $(TOPDIR)/feeds/luci/luci.mk`
## 🔄 Migration des Modules Existants
Pour ajouter PKG_FILE_MODES à un module existant:
```bash
cd luci-app-mymodule
# Éditer le Makefile
vi Makefile
# Ajouter avant 'include $(TOPDIR)/feeds/luci/luci.mk'
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.mymodule:755
# Reconstruire le package
make package/luci-app-mymodule/clean
make package/luci-app-mymodule/compile
```
---
**Maintainer:** CyberMind <contact@cybermind.fr>
**License:** Apache-2.0

View File

@ -11,6 +11,10 @@ LUCI_DESCRIPTION:=Comprehensive authentication and session management with capti
LUCI_DEPENDS:=+luci-base +rpcd +nodogsplash
LUCI_PKGARCH:=all
# File permissions (RPCD scripts must be executable)
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.auth-guardian:755
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -11,6 +11,10 @@ LUCI_DESCRIPTION:=Advanced bandwidth management with QoS rules, client quotas, a
LUCI_DEPENDS:=+luci-base +rpcd +tc +kmod-sched-core +kmod-sched-cake +kmod-ifb +sqm-scripts +iptables +iptables-mod-conntrack-extra +ip-full
LUCI_PKGARCH:=all
# File permissions (RPCD scripts must be executable)
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.bandwidth-manager:755
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -11,6 +11,10 @@ LUCI_DESCRIPTION:=Dashboard for managing local CDN caching proxy on OpenWrt
LUCI_DEPENDS:=+luci-base +rpcd
LUCI_PKGARCH:=all
# File permissions (RPCD scripts must be executable)
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.cdn-cache:755
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -20,6 +20,10 @@ LUCI_DEPENDS:=+luci-base +luci-app-secubox +luci-lib-jsonc +rpcd +rpcd-mod-luci
LUCI_PKGARCH:=all
# File permissions (RPCD scripts must be executable)
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.client-guardian:755
include $(TOPDIR)/feeds/luci/luci.mk
define Package/$(PKG_NAME)/conffiles

View File

@ -20,6 +20,10 @@ LUCI_DEPENDS:=+luci-base +luci-app-secubox +crowdsec +luci-lib-jsonc +rpcd +rpcd
LUCI_PKGARCH:=all
# File permissions (RPCD scripts must be executable)
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.crowdsec-dashboard:755
include $(TOPDIR)/feeds/luci/luci.mk
define Package/$(PKG_NAME)/conffiles

View File

@ -16,6 +16,10 @@ LUCI_PKGARCH:=all
PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>
PKG_LICENSE:=Apache-2.0
# File permissions (RPCD scripts must be executable)
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.ksm-manager:755
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -14,6 +14,10 @@ LUCI_DESCRIPTION:=Real-time detection and monitoring of streaming services (Netf
LUCI_DEPENDS:=+luci-base +rpcd +netifyd +luci-app-netifyd-dashboard
LUCI_PKGARCH:=all
# File permissions (RPCD scripts must be executable)
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.media-flow:755
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -20,6 +20,10 @@ LUCI_DEPENDS:=+luci-base +luci-app-secubox +luci-lib-jsonc +rpcd +rpcd-mod-luci
LUCI_PKGARCH:=all
# File permissions (RPCD scripts must be executable)
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.netdata-dashboard:755
include $(TOPDIR)/feeds/luci/luci.mk
define Package/$(PKG_NAME)/conffiles

View File

@ -20,6 +20,10 @@ LUCI_DEPENDS:=+luci-base +luci-app-secubox +luci-lib-jsonc +rpcd +rpcd-mod-luci
LUCI_PKGARCH:=all
# File permissions (RPCD scripts must be executable)
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.netifyd-dashboard:755
include $(TOPDIR)/feeds/luci/luci.mk
define Package/$(PKG_NAME)/conffiles

View File

@ -20,6 +20,9 @@ LUCI_DEPENDS:=+luci-base +luci-app-secubox +luci-lib-jsonc +rpcd +rpcd-mod-luci
LUCI_PKGARCH:=all
# File permissions (RPCD scripts must be executable)
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.network-modes:755
include $(TOPDIR)/feeds/luci/luci.mk
define Package/$(PKG_NAME)/conffiles

View File

@ -63,12 +63,102 @@ return baseclass.extend({
callGetAvailableModes(),
callGetInterfaces()
]).then(function(results) {
var status = results[0] || {};
var currentMode = results[1] || {};
// Merge current_mode into status for compatibility
status.current_mode = currentMode.mode || 'router';
status.interfaces = (results[3] || {}).interfaces || [];
return {
status: results[0] || {},
current_mode: results[1] || { mode: '' },
available_modes: results[2] || { modes: [] },
interfaces: results[3] || { interfaces: [] }
status: status,
modes: results[2] || { modes: [] }
};
});
},
// Get static information about a mode
getModeInfo: function(mode) {
var modeInfo = {
router: {
id: 'router',
name: 'Router Mode',
icon: '🏠',
description: 'Traditional home/office router with NAT, firewall, and DHCP server. Ideal for connecting multiple devices to the internet.',
features: [
'NAT and firewall enabled',
'DHCP server for LAN clients',
'Port forwarding and DMZ',
'QoS and traffic shaping'
]
},
bridge: {
id: 'bridge',
name: 'Bridge Mode',
icon: '🌉',
description: 'Transparent layer-2 forwarding without NAT. All devices appear on the same network segment.',
features: [
'Layer-2 transparent bridging',
'No NAT or routing',
'STP/RSTP support',
'VLAN tagging support'
]
},
accesspoint: {
id: 'accesspoint',
name: 'Access Point',
icon: '📡',
description: 'WiFi access point with wired uplink. Extends your existing network wirelessly.',
features: [
'WiFi hotspot functionality',
'Wired uplink to main router',
'Multiple SSID support',
'Fast roaming (802.11r/k/v)'
]
},
relay: {
id: 'relay',
name: 'Repeater/Extender',
icon: '🔁',
description: 'WiFi to WiFi repeating to extend wireless coverage. Connects wirelessly to upstream network.',
features: [
'WiFi range extension',
'Wireless uplink (WDS/Relay)',
'Rebroadcast on same or different SSID',
'Signal amplification'
]
},
sniffer: {
id: 'sniffer',
name: 'Sniffer Mode',
icon: '🔍',
description: 'Network monitoring and packet capture mode for security analysis and troubleshooting.',
features: [
'Promiscuous mode capture',
'WiFi monitor mode',
'pcap/pcapng output',
'Integration with Wireshark'
]
}
};
return modeInfo[mode] || {
id: mode,
name: mode.charAt(0).toUpperCase() + mode.slice(1),
icon: '⚙️',
description: 'Unknown mode',
features: []
};
},
// Format uptime seconds to human readable
formatUptime: function(seconds) {
if (!seconds || seconds < 0) return '0d 0h 0m';
var days = Math.floor(seconds / 86400);
var hours = Math.floor((seconds % 86400) / 3600);
var minutes = Math.floor((seconds % 3600) / 60);
return days + 'd ' + hours + 'h ' + minutes + 'm';
}
});

View File

@ -11,6 +11,10 @@ LUCI_DESCRIPTION:=Central control hub for all SecuBox modules. Provides unified
LUCI_DEPENDS:=+luci-base +rpcd +curl +jq
LUCI_PKGARCH:=all
# File permissions (RPCD scripts must be executable)
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.secubox:755 \
/usr/libexec/secubox/fix-permissions.sh:755
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot

View File

@ -8,7 +8,7 @@
* RPCD object: luci.secubox
*/
// Version: 0.2.2
// Version: 0.3.1
var callStatus = rpc.declare({
object: 'luci.secubox',
@ -54,6 +54,28 @@ var callRestartModule = rpc.declare({
params: ['module']
});
// NEW v0.3.1: Enable/Disable module methods
var callEnableModule = rpc.declare({
object: 'luci.secubox',
method: 'enable_module',
params: ['module'],
expect: { success: false, message: '' }
});
var callDisableModule = rpc.declare({
object: 'luci.secubox',
method: 'disable_module',
params: ['module'],
expect: { success: false, message: '' }
});
var callCheckModuleEnabled = rpc.declare({
object: 'luci.secubox',
method: 'check_module_enabled',
params: ['module'],
expect: { enabled: false }
});
var callHealth = rpc.declare({
object: 'luci.secubox',
method: 'health',
@ -110,6 +132,12 @@ var callClearAlerts = rpc.declare({
expect: { }
});
var callFixPermissions = rpc.declare({
object: 'luci.secubox',
method: 'fix_permissions',
expect: { success: false, message: '', output: '' }
});
function formatUptime(seconds) {
if (!seconds) return '0s';
var d = Math.floor(seconds / 86400);
@ -133,9 +161,15 @@ return baseclass.extend({
getModules: callModules,
getModulesByCategory: callModulesByCategory,
getModuleInfo: callModuleInfo,
// DEPRECATED: Use enable/disable instead
startModule: callStartModule,
stopModule: callStopModule,
restartModule: callRestartModule,
// NEW v0.3.1: Enable/Disable methods
enableModule: callEnableModule,
disableModule: callDisableModule,
checkModuleEnabled: callCheckModuleEnabled,
// Health & diagnostics
getHealth: callHealth,
getDiagnostics: callDiagnostics,
getSystemHealth: callSystemHealth,
@ -145,6 +179,8 @@ return baseclass.extend({
getTheme: callGetTheme,
dismissAlert: callDismissAlert,
clearAlerts: callClearAlerts,
fixPermissions: callFixPermissions,
// Utilities
formatUptime: formatUptime,
formatBytes: formatBytes
});

View File

@ -1,4 +1,5 @@
/* SecuBox Dashboard Styles * Version: 0.3.0
/* SecuBox Dashboard Styles
* Version: 0.3.1
*/
.secubox-dashboard {
@ -301,6 +302,28 @@
display: inline-block;
}
/* Module Status Indicators (v0.3.1) */
.secubox-status-dot.secubox-status-active {
background: #22c55e;
box-shadow: 0 0 8px rgba(34, 197, 94, 0.5);
animation: pulse 2s infinite;
}
.secubox-status-dot.secubox-status-disabled {
background: #94a3b8;
}
.secubox-status-dot.secubox-status-error {
background: #f59e0b;
box-shadow: 0 0 8px rgba(245, 158, 11, 0.5);
}
.secubox-status-dot.secubox-status-unknown {
background: #94a3b8;
opacity: 0.5;
}
/* DEPRECATED: Keeping for backward compatibility */
.secubox-status-dot.secubox-status-running {
background: #22c55e;
box-shadow: 0 0 8px rgba(34, 197, 94, 0.5);
@ -334,6 +357,27 @@
color: var(--sb-text-muted);
}
/* Module Status Colors (v0.3.1) */
.secubox-module-active .secubox-module-mini-status {
color: #22c55e;
font-weight: 500;
}
.secubox-module-disabled .secubox-module-mini-status {
color: #94a3b8;
}
.secubox-module-error .secubox-module-mini-status {
color: #f59e0b;
font-weight: 500;
}
.secubox-module-unknown .secubox-module-mini-status {
color: #94a3b8;
opacity: 0.7;
}
/* DEPRECATED: Keeping for backward compatibility */
.secubox-module-running .secubox-module-mini-status {
color: #22c55e;
}

View File

@ -256,10 +256,20 @@ return view.extend({
};
var moduleCards = filteredModules.map(function(module) {
var isRunning = module.running;
var statusClass = isRunning ? 'running' : 'stopped';
var status = module.status || 'unknown';
var statusClass = status;
var dashboardPath = modulePaths[module.id] || ('admin/secubox/' + module.id);
// Status label mapping (v0.3.1)
var statusLabels = {
'active': '✓ Activé',
'disabled': '○ Désactivé',
'error': '⚠️ Erreur',
'unknown': '? Inconnu'
};
var statusLabel = statusLabels[status] || '○ Désactivé';
return E('a', {
'href': L.url(dashboardPath),
'class': 'secubox-module-link secubox-module-' + statusClass
@ -272,13 +282,12 @@ return view.extend({
E('span', { 'class': 'secubox-module-mini-icon' }, module.icon || '📦'),
E('span', {
'class': 'secubox-status-dot secubox-status-' + statusClass,
'title': isRunning ? 'Running' : 'Stopped'
'title': statusLabel
})
]),
E('div', { 'class': 'secubox-module-mini-body' }, [
E('div', { 'class': 'secubox-module-mini-name' }, module.name || module.id),
E('div', { 'class': 'secubox-module-mini-status' },
isRunning ? '● Running' : '○ Stopped')
E('div', { 'class': 'secubox-module-mini-status' }, statusLabel)
])
])
]);
@ -367,10 +376,20 @@ return view.extend({
};
var moduleCards = filteredModules.map(function(module) {
var isRunning = module.running;
var statusClass = isRunning ? 'running' : 'stopped';
var status = module.status || 'unknown';
var statusClass = status;
var dashboardPath = modulePaths[module.id] || ('admin/secubox/' + module.id);
// Status label mapping (v0.3.1)
var statusLabels = {
'active': '✓ Activé',
'disabled': '○ Désactivé',
'error': '⚠️ Erreur',
'unknown': '? Inconnu'
};
var statusLabel = statusLabels[status] || '○ Désactivé';
return E('a', {
'href': L.url(dashboardPath),
'class': 'secubox-module-link secubox-module-' + statusClass
@ -383,13 +402,12 @@ return view.extend({
E('span', { 'class': 'secubox-module-mini-icon' }, module.icon || '📦'),
E('span', {
'class': 'secubox-status-dot secubox-status-' + statusClass,
'title': isRunning ? 'Running' : 'Stopped'
'title': statusLabel
})
]),
E('div', { 'class': 'secubox-module-mini-body' }, [
E('div', { 'class': 'secubox-module-mini-name' }, module.name || module.id),
E('div', { 'class': 'secubox-module-mini-status' },
isRunning ? '● Running' : '○ Stopped')
E('div', { 'class': 'secubox-module-mini-status' }, statusLabel)
])
])
]);
@ -443,6 +461,20 @@ return view.extend({
]);
});
// Add Fix Permissions button (v0.3.1)
buttons.push(
E('button', {
'class': 'secubox-action-btn',
'style': 'border-color: #f97316',
'click': function() {
self.executeFixPermissions();
}
}, [
E('span', { 'class': 'secubox-action-icon' }, '🔧'),
E('span', { 'class': 'secubox-action-label' }, 'Fix Perms')
])
);
return E('div', { 'class': 'secubox-card' }, [
E('h3', { 'class': 'secubox-card-title' }, '⚡ Quick Actions'),
E('div', { 'class': 'secubox-actions-grid' }, buttons)
@ -474,6 +506,38 @@ return view.extend({
});
},
executeFixPermissions: function() {
var self = this;
ui.showModal(_('Fixing Permissions'), [
E('p', { 'class': 'spinning' }, _('Fixing file permissions and restarting services...'))
]);
API.fixPermissions().then(function(result) {
ui.hideModal();
if (result && result.success) {
ui.addNotification(null, E('p', '✓ Permissions fixed successfully'), 'info');
// Show output in console
if (result.output) {
console.log('Fix Permissions Output:\n' + result.output);
}
// Refresh data after fixing permissions
setTimeout(function() {
self.refreshData().then(function() {
self.updateDynamicElements();
});
}, 2000);
} else {
ui.addNotification(null, E('p', '✗ ' + (result.message || 'Failed to fix permissions')), 'error');
if (result.output) {
console.error('Fix Permissions Error:\n' + result.output);
}
}
}).catch(function(err) {
ui.hideModal();
ui.addNotification(null, E('p', 'Error: ' + err.message), 'error');
});
},
renderAlerts: function(alerts) {
if (!alerts || alerts.length === 0) {
return E('div', { 'class': 'secubox-card' }, [

View File

@ -66,8 +66,8 @@ return view.extend({
renderHeader: function(modules) {
var total = modules.length;
var installed = modules.filter(function(m) { return m.installed; }).length;
var running = modules.filter(function(m) { return m.running; }).length;
var stopped = installed - running;
var enabled = modules.filter(function(m) { return m.enabled; }).length;
var disabled = installed - enabled;
return E('div', { 'class': 'secubox-page-header' }, [
E('div', {}, [
@ -81,12 +81,12 @@ return view.extend({
E('span', { 'class': 'secubox-stat-label' }, 'Total')
]),
E('div', { 'class': 'secubox-stat-badge secubox-stat-success' }, [
E('span', { 'class': 'secubox-stat-value' }, running),
E('span', { 'class': 'secubox-stat-label' }, 'Running')
E('span', { 'class': 'secubox-stat-value' }, enabled),
E('span', { 'class': 'secubox-stat-label' }, 'Activés')
]),
E('div', { 'class': 'secubox-stat-badge secubox-stat-warning' }, [
E('span', { 'class': 'secubox-stat-value' }, stopped),
E('span', { 'class': 'secubox-stat-label' }, 'Stopped')
E('span', { 'class': 'secubox-stat-value' }, disabled),
E('span', { 'class': 'secubox-stat-label' }, 'Désactivés')
]),
E('div', { 'class': 'secubox-stat-badge secubox-stat-muted' }, [
E('span', { 'class': 'secubox-stat-value' }, total - installed),
@ -144,9 +144,20 @@ return view.extend({
renderModuleCard: function(module) {
var self = this;
var isRunning = module.running;
var status = module.status || 'unknown';
var isInstalled = module.installed;
var statusClass = isRunning ? 'running' : (isInstalled ? 'stopped' : 'not-installed');
var statusClass = isInstalled ? status : 'not-installed';
// Status label mapping (v0.3.1)
var statusLabels = {
'active': '✓ Activé',
'disabled': '○ Désactivé',
'error': '⚠️ Erreur',
'unknown': '? Inconnu',
'not-installed': '- Not Installed'
};
var statusLabel = isInstalled ? (statusLabels[status] || '○ Désactivé') : statusLabels['not-installed'];
return E('div', {
'class': 'secubox-module-card secubox-module-' + statusClass,
@ -166,7 +177,7 @@ return view.extend({
]),
E('div', {
'class': 'secubox-status-indicator secubox-status-' + statusClass,
'title': isRunning ? 'Running' : (isInstalled ? 'Stopped' : 'Not Installed')
'title': statusLabel
})
]),
@ -184,7 +195,7 @@ return view.extend({
E('span', { 'class': 'secubox-detail-label' }, 'Status:'),
E('span', {
'class': 'secubox-detail-value secubox-status-text-' + statusClass
}, isRunning ? '● Running' : (isInstalled ? '○ Stopped' : '- Not Installed'))
}, statusLabel)
])
])
]),
@ -207,36 +218,24 @@ return view.extend({
}, '📥 Install')
);
} else {
// Start/Stop button
if (module.running) {
// Enable/Disable button (v0.3.1)
if (module.enabled) {
actions.push(
E('button', {
'class': 'secubox-btn secubox-btn-danger secubox-btn-sm',
'click': function() {
self.stopModule(module);
self.disableModule(module);
}
}, '⏹️ Stop')
}, '⏹️ Désactiver')
);
} else {
actions.push(
E('button', {
'class': 'secubox-btn secubox-btn-success secubox-btn-sm',
'click': function() {
self.startModule(module);
self.enableModule(module);
}
}, '▶️ Start')
);
}
// Restart button (only if running)
if (module.running) {
actions.push(
E('button', {
'class': 'secubox-btn secubox-btn-warning secubox-btn-sm',
'click': function() {
self.restartModule(module);
}
}, '🔄 Restart')
}, '▶️ Activer')
);
}
@ -286,69 +285,65 @@ return view.extend({
return icons[category] || icons['other'];
},
startModule: function(module) {
// Enable module (v0.3.1)
enableModule: function(module) {
var self = this;
ui.showModal(_('Starting Module'), [
E('p', {}, _('Starting') + ' ' + module.name + '...')
ui.showModal(_('Activation du module'), [
E('p', {}, 'Activation de ' + module.name + '...')
]);
API.startModule(module.id).then(function(result) {
API.enableModule(module.id).then(function(result) {
ui.hideModal();
if (result && result.success !== false) {
ui.addNotification(null, E('p', module.name + ' started successfully'), 'info');
ui.addNotification(null, E('p', module.name + ' activé avec succès'), 'info');
self.refreshData().then(function() {
self.updateModulesGrid();
});
} else {
ui.addNotification(null, E('p', 'Failed to start ' + module.name), 'error');
ui.addNotification(null, E('p', 'Échec de l\'activation de ' + module.name), 'error');
}
}).catch(function(err) {
ui.hideModal();
ui.addNotification(null, E('p', 'Error: ' + err.message), 'error');
ui.addNotification(null, E('p', 'Erreur: ' + err.message), 'error');
});
},
stopModule: function(module) {
// Disable module (v0.3.1)
disableModule: function(module) {
var self = this;
ui.showModal(_('Stopping Module'), [
E('p', {}, _('Stopping') + ' ' + module.name + '...')
ui.showModal(_('Désactivation du module'), [
E('p', {}, 'Désactivation de ' + module.name + '...')
]);
API.stopModule(module.id).then(function(result) {
API.disableModule(module.id).then(function(result) {
ui.hideModal();
if (result && result.success !== false) {
ui.addNotification(null, E('p', module.name + ' stopped successfully'), 'info');
ui.addNotification(null, E('p', module.name + ' désactivé avec succès'), 'info');
self.refreshData().then(function() {
self.updateModulesGrid();
});
} else {
ui.addNotification(null, E('p', 'Failed to stop ' + module.name), 'error');
ui.addNotification(null, E('p', 'Échec de la désactivation de ' + module.name), 'error');
}
}).catch(function(err) {
ui.hideModal();
ui.addNotification(null, E('p', 'Error: ' + err.message), 'error');
ui.addNotification(null, E('p', 'Erreur: ' + err.message), 'error');
});
},
// DEPRECATED: Keeping for backward compatibility
startModule: function(module) {
return this.enableModule(module);
},
stopModule: function(module) {
return this.disableModule(module);
},
restartModule: function(module) {
var self = this;
ui.showModal(_('Restarting Module'), [
E('p', {}, _('Restarting') + ' ' + module.name + '...')
]);
API.restartModule(module.id).then(function(result) {
ui.hideModal();
if (result && result.success !== false) {
ui.addNotification(null, E('p', module.name + ' restarted successfully'), 'info');
self.refreshData().then(function() {
self.updateModulesGrid();
});
} else {
ui.addNotification(null, E('p', 'Failed to restart ' + module.name), 'error');
}
}).catch(function(err) {
ui.hideModal();
ui.addNotification(null, E('p', 'Error: ' + err.message), 'error');
return this.disableModule(module).then(function() {
return self.enableModule(module);
});
},

View File

@ -44,14 +44,25 @@ check_module_installed() {
fi
}
# Check if a module is enabled (UCI config)
check_module_enabled() {
local module="$1"
local enabled
config_load secubox
config_get enabled "$module" enabled "1"
echo "$enabled"
}
# Check if a module service is running
check_module_running() {
local module="$1"
local config
config_load secubox
config_get config "$module" config ""
case "$module" in
crowdsec)
pgrep -f crowdsec > /dev/null 2>&1 && echo "1" || echo "0"
@ -79,6 +90,22 @@ check_module_running() {
esac
}
# Determine module status based on enabled + running
get_module_status() {
local enabled="$1"
local running="$2"
if [ "$enabled" = "1" ] && [ "$running" = "1" ]; then
echo "active"
elif [ "$enabled" = "1" ] && [ "$running" = "0" ]; then
echo "error"
elif [ "$enabled" = "0" ] && [ "$running" = "0" ]; then
echo "disabled"
else
echo "unknown"
fi
}
# Get overall system status
get_status() {
local total=0
@ -159,7 +186,9 @@ get_modules() {
config_get version "$module" version "0.0.9"
local is_installed=$(check_module_installed "$module")
local is_enabled=$(check_module_enabled "$module")
local is_running=$(check_module_running "$module")
local status=$(get_module_status "$is_enabled" "$is_running")
# Get real version from opkg if installed
if [ "$is_installed" = "1" ] && [ -n "$package" ]; then
@ -178,7 +207,9 @@ get_modules() {
json_add_string "config" "$config"
json_add_string "version" "$version"
json_add_boolean "installed" "$is_installed"
json_add_boolean "enabled" "$is_enabled"
json_add_boolean "running" "$is_running"
json_add_string "status" "$status"
json_add_boolean "in_uci" "1"
json_close_object
@ -213,7 +244,9 @@ get_modules() {
esac
local clean_module=$(echo "$module_id" | sed 's/_/-/g')
local is_enabled=$(check_module_enabled "$module_id")
local is_running=$(check_module_running "$module_id")
local status=$(get_module_status "$is_enabled" "$is_running")
json_add_object ""
json_add_string "id" "$module_id"
@ -226,7 +259,9 @@ get_modules() {
json_add_string "config" "$clean_module"
json_add_string "version" "$version"
json_add_boolean "installed" "1"
json_add_boolean "enabled" "$is_enabled"
json_add_boolean "running" "$is_running"
json_add_string "status" "$status"
json_add_boolean "in_uci" "0"
json_close_object
done
@ -265,7 +300,9 @@ get_modules_by_category() {
config_get version "$module" version "0.0.9"
local is_installed=$(check_module_installed "$module")
local is_enabled=$(check_module_enabled "$module")
local is_running=$(check_module_running "$module")
local status=$(get_module_status "$is_enabled" "$is_running")
# Get real version from opkg if installed
if [ "$is_installed" = "1" ] && [ -n "$package" ]; then
@ -282,7 +319,9 @@ get_modules_by_category() {
json_add_string "version" "$version"
json_add_string "package" "$package"
json_add_boolean "installed" "$is_installed"
json_add_boolean "enabled" "$is_enabled"
json_add_boolean "running" "$is_running"
json_add_string "status" "$status"
json_add_boolean "in_uci" "1"
json_close_object
@ -321,7 +360,9 @@ get_modules_by_category() {
name=$(echo "$package" | sed 's/^luci-app-//' | sed 's/-dashboard$//' | sed 's/-/ /g' | sed 's/\b\(.\)/\u\1/g')
local is_installed="1" # Must be installed if detected
local is_enabled=$(check_module_enabled "$module_id")
local is_running=$(check_module_running "$module_id")
local status=$(get_module_status "$is_enabled" "$is_running")
json_add_object ""
json_add_string "id" "$module_id"
@ -333,7 +374,9 @@ get_modules_by_category() {
json_add_string "package" "$package"
json_add_string "category" "$mod_category"
json_add_boolean "installed" "$is_installed"
json_add_boolean "enabled" "$is_enabled"
json_add_boolean "running" "$is_running"
json_add_string "status" "$status"
json_add_boolean "in_uci" "0"
json_close_object
fi
@ -361,7 +404,9 @@ get_module_info() {
config_get version "$module" version "0.0.9"
local is_installed=$(check_module_installed "$module")
local is_enabled=$(check_module_enabled "$module")
local is_running=$(check_module_running "$module")
local status=$(get_module_status "$is_enabled" "$is_running")
json_init
json_add_string "id" "$module"
@ -374,7 +419,9 @@ get_module_info() {
json_add_string "config" "$config"
json_add_string "version" "$version"
json_add_boolean "installed" "$is_installed"
json_add_boolean "enabled" "$is_enabled"
json_add_boolean "running" "$is_running"
json_add_string "status" "$status"
json_dump
}
@ -422,14 +469,14 @@ stop_module() {
fi
}
# Restart a module
# Restart a module (DEPRECATED - use disable/enable instead)
restart_module() {
local module="$1"
local config
config_load secubox
config_get config "$module" config ""
if [ -x "/etc/init.d/${config}" ]; then
/etc/init.d/${config} restart
json_init
@ -444,6 +491,64 @@ restart_module() {
fi
}
# Enable a module (NEW v0.3.1)
enable_module() {
local module="$1"
local config
config_load secubox
config_get config "$module" config ""
# Set enabled flag in UCI
uci set secubox.${module}.enabled='1'
uci commit secubox
# Enable and start service if init script exists
if [ -x "/etc/init.d/${config}" ]; then
/etc/init.d/${config} enable
/etc/init.d/${config} start
json_init
json_add_boolean "success" 1
json_add_string "message" "Module activé"
json_dump
else
json_init
json_add_boolean "success" 0
json_add_string "message" "Init script not found"
json_dump
fi
}
# Disable a module (NEW v0.3.1)
disable_module() {
local module="$1"
local config
config_load secubox
config_get config "$module" config ""
# Set disabled flag in UCI
uci set secubox.${module}.enabled='0'
uci commit secubox
# Disable and stop service if init script exists
if [ -x "/etc/init.d/${config}" ]; then
/etc/init.d/${config} stop
/etc/init.d/${config} disable
json_init
json_add_boolean "success" 1
json_add_string "message" "Module désactivé"
json_dump
else
json_init
json_add_boolean "success" 0
json_add_string "message" "Init script not found"
json_dump
fi
}
# Get health report
get_health() {
json_init
@ -752,12 +857,16 @@ get_dashboard_data() {
config_get color "$module" color "#64748b"
local is_installed=$(check_module_installed "$module")
local is_enabled="0"
local is_running="0"
local status="disabled"
total=$((total + 1))
if [ "$is_installed" = "1" ]; then
is_enabled=$(check_module_enabled "$module")
is_running=$(check_module_running "$module")
status=$(get_module_status "$is_enabled" "$is_running")
installed=$((installed + 1))
[ "$is_running" = "1" ] && running=$((running + 1))
fi
@ -770,7 +879,9 @@ get_dashboard_data() {
json_add_string "icon" "$icon"
json_add_string "color" "$color"
json_add_boolean "installed" "$is_installed"
json_add_boolean "enabled" "$is_enabled"
json_add_boolean "running" "$is_running"
json_add_string "status" "$status"
json_close_object
done
json_close_array
@ -844,6 +955,34 @@ clear_alerts() {
json_dump
}
# Fix permissions (v0.3.1)
fix_permissions() {
local fix_script="/usr/libexec/secubox/fix-permissions.sh"
if [ ! -x "$fix_script" ]; then
json_init
json_add_boolean "success" 0
json_add_string "message" "Fix script not found or not executable"
json_dump
return 1
fi
# Run the fix script and capture output
local output=$($fix_script 2>&1)
local exit_code=$?
json_init
if [ $exit_code -eq 0 ]; then
json_add_boolean "success" 1
json_add_string "message" "Permissions fixed successfully"
else
json_add_boolean "success" 0
json_add_string "message" "Failed to fix permissions"
fi
json_add_string "output" "$output"
json_dump
}
# Main dispatcher
case "$1" in
list)
@ -867,6 +1006,15 @@ case "$1" in
json_add_object "restart_module"
json_add_string "module" "string"
json_close_object
json_add_object "enable_module"
json_add_string "module" "string"
json_close_object
json_add_object "disable_module"
json_add_string "module" "string"
json_close_object
json_add_object "check_module_enabled"
json_add_string "module" "string"
json_close_object
json_add_object "health"
json_close_object
json_add_object "diagnostics"
@ -887,6 +1035,8 @@ case "$1" in
json_close_object
json_add_object "clear_alerts"
json_close_object
json_add_object "fix_permissions"
json_close_object
json_dump
;;
call)
@ -927,6 +1077,27 @@ case "$1" in
json_get_var module module ""
restart_module "$module"
;;
enable_module)
read -r input
json_load "$input"
json_get_var module module ""
enable_module "$module"
;;
disable_module)
read -r input
json_load "$input"
json_get_var module module ""
disable_module "$module"
;;
check_module_enabled)
read -r input
json_load "$input"
json_get_var module module ""
local enabled=$(check_module_enabled "$module")
json_init
json_add_boolean "enabled" "$enabled"
json_dump
;;
health)
get_health
;;
@ -960,6 +1131,9 @@ case "$1" in
clear_alerts)
clear_alerts
;;
fix_permissions)
fix_permissions
;;
*)
echo '{"error":"Unknown method"}'
;;

View File

@ -0,0 +1,169 @@
#!/bin/sh
# SPDX-License-Identifier: Apache-2.0
# SecuBox Permissions Fix Script
# Automatically fixes file permissions for SecuBox modules
# Copyright (C) 2025 CyberMind.fr
set -e
LOG_TAG="secubox-fix-perms"
log_info() {
logger -t "$LOG_TAG" -p info "$1"
echo "[INFO] $1"
}
log_error() {
logger -t "$LOG_TAG" -p err "$1"
echo "[ERROR] $1" >&2
}
fix_rpcd_permissions() {
log_info "Fixing RPCD script permissions..."
local rpcd_scripts="
/usr/libexec/rpcd/luci.secubox
/usr/libexec/rpcd/luci.system-hub
/usr/libexec/rpcd/luci.network-modes
/usr/libexec/rpcd/luci.crowdsec-dashboard
/usr/libexec/rpcd/luci.netdata-dashboard
/usr/libexec/rpcd/luci.netifyd-dashboard
/usr/libexec/rpcd/luci.wireguard-dashboard
/usr/libexec/rpcd/luci.client-guardian
/usr/libexec/rpcd/luci.bandwidth-manager
/usr/libexec/rpcd/luci.auth-guardian
/usr/libexec/rpcd/luci.media-flow
/usr/libexec/rpcd/luci.vhost-manager
/usr/libexec/rpcd/luci.traffic-shaper
/usr/libexec/rpcd/luci.cdn-cache
/usr/libexec/rpcd/luci.ksm-manager
"
local count=0
for script in $rpcd_scripts; do
if [ -f "$script" ]; then
chmod 755 "$script" 2>/dev/null && count=$((count + 1))
fi
done
log_info "Fixed $count RPCD scripts (755)"
}
fix_web_permissions() {
log_info "Fixing web resources permissions..."
# Fix CSS files
local css_count=0
if [ -d "/www/luci-static/resources" ]; then
css_count=$(find /www/luci-static/resources -type f -name "*.css" -exec chmod 644 {} \; -print 2>/dev/null | wc -l)
fi
log_info "Fixed $css_count CSS files (644)"
# Fix JS files
local js_count=0
if [ -d "/www/luci-static/resources" ]; then
js_count=$(find /www/luci-static/resources -type f -name "*.js" -exec chmod 644 {} \; -print 2>/dev/null | wc -l)
fi
log_info "Fixed $js_count JS files (644)"
}
fix_config_permissions() {
log_info "Fixing configuration file permissions..."
local count=0
# Fix ACL files
for acl in /usr/share/rpcd/acl.d/luci-app-*.json; do
if [ -f "$acl" ]; then
chmod 644 "$acl" 2>/dev/null && count=$((count + 1))
fi
done
# Fix menu files
for menu in /usr/share/luci/menu.d/luci-app-*.json; do
if [ -f "$menu" ]; then
chmod 644 "$menu" 2>/dev/null && count=$((count + 1))
fi
done
log_info "Fixed $count config files (644)"
}
restart_services() {
log_info "Restarting services..."
if /etc/init.d/rpcd restart >/dev/null 2>&1; then
log_info "RPCD restarted successfully"
else
log_error "Failed to restart RPCD"
fi
if /etc/init.d/uhttpd restart >/dev/null 2>&1; then
log_info "uHTTPd restarted successfully"
else
log_error "Failed to restart uHTTPd"
fi
}
verify_permissions() {
log_info "Verifying permissions..."
local errors=0
# Check RPCD scripts
for script in /usr/libexec/rpcd/luci.*; do
if [ -f "$script" ]; then
local perms=$(ls -l "$script" 2>/dev/null | cut -c1-10)
if [ "$perms" != "-rwxr-xr-x" ]; then
log_error "Invalid permissions on $script: $perms (expected -rwxr-xr-x)"
errors=$((errors + 1))
fi
fi
done
# Check critical files
for file in /www/luci-static/resources/secubox/api.js /www/luci-static/resources/secubox/dashboard.css; do
if [ -f "$file" ]; then
local perms=$(ls -l "$file" 2>/dev/null | cut -c1-10)
if [ "$perms" != "-rw-r--r--" ]; then
log_error "Invalid permissions on $file: $perms (expected -rw-r--r--)"
errors=$((errors + 1))
fi
fi
done
if [ $errors -eq 0 ]; then
log_info "All permissions verified successfully"
return 0
else
log_error "Found $errors permission errors"
return 1
fi
}
main() {
echo "================================================"
echo " SecuBox Permissions Fix Script v0.3.1"
echo "================================================"
echo ""
fix_rpcd_permissions
fix_web_permissions
fix_config_permissions
echo ""
restart_services
echo ""
verify_permissions
echo ""
echo "================================================"
echo " Permissions fix completed!"
echo "================================================"
}
# Run if executed directly
if [ "${0##*/}" = "fix-permissions.sh" ]; then
main "$@"
fi

View File

@ -8,6 +8,7 @@
"modules",
"modules_by_category",
"module_info",
"check_module_enabled",
"health",
"diagnostics",
"get_system_health",
@ -30,9 +31,12 @@
"start_module",
"stop_module",
"restart_module",
"enable_module",
"disable_module",
"quick_action",
"dismiss_alert",
"clear_alerts"
"clear_alerts",
"fix_permissions"
],
"uci": [
"set",

View File

@ -11,6 +11,9 @@ LUCI_DESCRIPTION:=Central system control with monitoring, services, logs, and ba
LUCI_DEPENDS:=+luci-base +rpcd +coreutils +coreutils-base64
LUCI_PKGARCH:=all
# File permissions (RPCD scripts must be executable)
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.system-hub:755
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -10,6 +10,10 @@ LUCI_TITLE:=Traffic Shaper - Advanced QoS Control
LUCI_DESCRIPTION:=Advanced traffic shaping with TC/CAKE for precise bandwidth control
LUCI_DEPENDS:=+luci-base +rpcd +tc +kmod-sched-core +kmod-sched-cake
# File permissions (RPCD scripts must be executable)
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.traffic-shaper:755
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -14,6 +14,10 @@ LUCI_DESCRIPTION:=Nginx reverse proxy manager with Let's Encrypt SSL certificate
LUCI_DEPENDS:=+luci-base +rpcd +nginx-ssl +acme +curl
LUCI_PKGARCH:=all
# File permissions (RPCD scripts must be executable)
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.vhost-manager:755
include $(TOPDIR)/feeds/luci/luci.mk
# call BuildPackage - OpenWrt buildroot signature

View File

@ -20,6 +20,10 @@ LUCI_DEPENDS:=+luci-base +luci-app-secubox +luci-lib-jsonc +rpcd +rpcd-mod-luci
LUCI_PKGARCH:=all
# File permissions (RPCD scripts must be executable)
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.wireguard-dashboard:755
include $(TOPDIR)/feeds/luci/luci.mk
define Package/$(PKG_NAME)/conffiles

View File

@ -0,0 +1,209 @@
#!/bin/bash
# Add PKG_FILE_MODES to OpenWrt Makefiles
# Automatically detects RPCD scripts and adds correct permissions
#
# Usage: ./add-pkg-file-modes.sh [module-path]
# ./add-pkg-file-modes.sh --all (process all luci-app-* modules)
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[1;33m'
BLUE='\033[0;34m'
NC='\033[0m' # No Color
log_info() {
echo -e "${BLUE}[INFO]${NC} $1"
}
log_success() {
echo -e "${GREEN}[OK]${NC} $1"
}
log_warning() {
echo -e "${YELLOW}[WARN]${NC} $1"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1"
}
# Detect RPCD scripts in a module
detect_rpcd_scripts() {
local module_path="$1"
local rpcd_dir="$module_path/root/usr/libexec/rpcd"
if [ ! -d "$rpcd_dir" ]; then
return
fi
find "$rpcd_dir" -type f -name "luci.*" 2>/dev/null | while read script; do
basename "$script"
done
}
# Detect helper scripts in a module
detect_helper_scripts() {
local module_path="$1"
local helper_dir="$module_path/root/usr/libexec"
if [ ! -d "$helper_dir" ]; then
return
fi
# Find .sh files in subdirectories (not in rpcd/)
find "$helper_dir" -type f -name "*.sh" ! -path "*/rpcd/*" 2>/dev/null | while read script; do
echo "$script" | sed "s|^$module_path/root||"
done
}
# Generate PKG_FILE_MODES line
generate_pkg_file_modes() {
local module_name="$1"
local rpcd_scripts="$2"
local helper_scripts="$3"
local modes=""
# Add RPCD scripts
for script in $rpcd_scripts; do
if [ -z "$modes" ]; then
modes="/usr/libexec/rpcd/$script:755"
else
modes="$modes \\\\\n\\t/usr/libexec/rpcd/$script:755"
fi
done
# Add helper scripts
for script in $helper_scripts; do
if [ -z "$modes" ]; then
modes="$script:755"
else
modes="$modes \\\\\n\\t$script:755"
fi
done
if [ -n "$modes" ]; then
echo -e "# File permissions (RPCD scripts must be executable)\nPKG_FILE_MODES:=$modes"
fi
}
# Check if Makefile already has PKG_FILE_MODES
has_pkg_file_modes() {
local makefile="$1"
grep -q "^PKG_FILE_MODES:=" "$makefile" 2>/dev/null
}
# Add PKG_FILE_MODES to Makefile
add_to_makefile() {
local makefile="$1"
local pkg_file_modes="$2"
if [ ! -f "$makefile" ]; then
log_error "Makefile not found: $makefile"
return 1
fi
# Check if already exists
if has_pkg_file_modes "$makefile"; then
log_warning "PKG_FILE_MODES already exists in $makefile"
return 0
fi
# Find the line with 'include $(TOPDIR)/feeds/luci/luci.mk'
if ! grep -q "include.*luci.mk" "$makefile"; then
log_error "Cannot find luci.mk include in $makefile"
return 1
fi
# Create backup
cp "$makefile" "$makefile.bak"
# Insert PKG_FILE_MODES before the include line
awk -v modes="$pkg_file_modes" '
/^include.*luci\.mk/ {
print ""
print modes
print ""
}
{ print }
' "$makefile.bak" > "$makefile"
log_success "Added PKG_FILE_MODES to $makefile"
rm -f "$makefile.bak"
}
# Process a single module
process_module() {
local module_path="$1"
local module_name=$(basename "$module_path")
log_info "Processing $module_name..."
# Detect scripts
local rpcd_scripts=$(detect_rpcd_scripts "$module_path")
local helper_scripts=$(detect_helper_scripts "$module_path")
if [ -z "$rpcd_scripts" ] && [ -z "$helper_scripts" ]; then
log_warning "No executable scripts found in $module_name"
return 0
fi
# Generate PKG_FILE_MODES
local pkg_file_modes=$(generate_pkg_file_modes "$module_name" "$rpcd_scripts" "$helper_scripts")
if [ -z "$pkg_file_modes" ]; then
log_warning "Could not generate PKG_FILE_MODES for $module_name"
return 0
fi
# Add to Makefile
local makefile="$module_path/Makefile"
add_to_makefile "$makefile" "$pkg_file_modes"
# Show what was added
echo ""
echo " Scripts found:"
[ -n "$rpcd_scripts" ] && echo " RPCD: $(echo $rpcd_scripts | tr '\n' ' ')"
[ -n "$helper_scripts" ] && echo " Helper: $(echo $helper_scripts | tr '\n' ' ')"
echo ""
}
# Main
main() {
echo "================================================"
echo " PKG_FILE_MODES Auto-Configurator v0.3.1"
echo "================================================"
echo ""
if [ "$1" = "--all" ]; then
log_info "Processing all luci-app-* modules..."
echo ""
find "$PROJECT_ROOT" -maxdepth 1 -type d -name "luci-app-*" | sort | while read module_path; do
process_module "$module_path"
done
elif [ -n "$1" ] && [ -d "$1" ]; then
process_module "$1"
else
log_error "Usage: $0 [module-path] | --all"
log_info "Examples:"
log_info " $0 ../luci-app-secubox"
log_info " $0 --all"
exit 1
fi
echo ""
echo "================================================"
echo " Processing complete!"
echo "================================================"
}
main "$@"

View File

@ -14,9 +14,14 @@ PKG_MAINTAINER:=CyberMind <contact@cybermind.fr>
# LuCI specific
LUCI_TITLE:=LuCI - Package Description
LUCI_DESCRIPTION:=Detailed description of what this package does
LUCI_DEPENDS:=+luci-base
LUCI_DEPENDS:=+luci-base +rpcd
LUCI_PKGARCH:=all
# File permissions (CRITICAL: RPCD scripts MUST be executable)
# CSS/JS files are 644 by default (correct)
# Only specify files that need non-default permissions (755 for executables)
PKG_FILE_MODES:=/usr/libexec/rpcd/luci.PACKAGE_NAME:755
# Include LuCI build system
include $(TOPDIR)/feeds/luci/luci.mk