feat(vhosts-checker): Dark theme UI with emoji status and fixed route detection
- Fix jshn boolean handling (use 1/0 instead of "true"/"false") - Rework UI with dark theme compatible styling - Add emoji-based status indicators (🔗🔒🛡️✅) - Simplify interface with async Load More pagination - Update README.md to v0.18.0 with 86 modules Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
686fe113c5
commit
5b8c4cd52c
390
README.md
390
README.md
@ -1,194 +1,211 @@
|
||||
# SecuBox - Security Suite for OpenWrt
|
||||
|
||||
**Version:** 0.17.0 🎉 **First Public Release**
|
||||
**Last Updated:** 2026-01-31
|
||||
**Status:** Production Ready
|
||||
**Modules:** 38 LuCI Applications
|
||||
**Version:** 0.18.0
|
||||
**Last Updated:** 2026-03-04
|
||||
**Status:** Production Ready
|
||||
**Modules:** 86 LuCI Applications
|
||||
|
||||
[](https://github.com/CyberMind-FR/secubox-openwrt/actions/workflows/build-openwrt-packages.yml)
|
||||
[](LICENSE)
|
||||
[](https://github.com/CyberMind-FR/secubox-openwrt/releases)
|
||||
|
||||
## 🎉 First Public Release
|
||||
|
||||
SecuBox v0.17.0 marks the **First Public Release** of the project. All core features are now stable and ready for production deployment.
|
||||
|
||||
### What's Ready
|
||||
|
||||
- ✅ **38 LuCI modules** — Complete security and network management suite
|
||||
- ✅ **Three-Loop Security Architecture** — Operational, Tactical, and Strategic defense layers
|
||||
- ✅ **CrowdSec Integration** — Real-time threat intelligence and automated blocking
|
||||
- ✅ **Deep Packet Inspection** — netifyd/nDPId protocol analysis
|
||||
- ✅ **WireGuard VPN** — Encrypted mesh connectivity
|
||||
- ✅ **Multi-architecture support** — ARM64, ARM32, MIPS, x86
|
||||
|
||||
### Coming Next (v0.18+)
|
||||
|
||||
- 🔵 **P2P Hub** — Decentralized threat intelligence sharing
|
||||
- 🔵 **did:plc Identity** — Self-sovereign node identity for trust networks
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
|
||||
SecuBox is a comprehensive security and network management suite for OpenWrt, providing a unified ecosystem of 38 specialized dashboards and tools. All modules are compiled automatically for multiple OpenWrt architectures via GitHub Actions.
|
||||
SecuBox is a comprehensive security and network management suite for OpenWrt, providing a unified ecosystem of 86 specialized dashboards and tools. The platform implements a **Four-Layer Architecture** for defense in depth, featuring AI-powered threat analysis, P2P mesh networking, and multi-channel service exposure.
|
||||
|
||||
**Website:** [secubox.maegia.tv](https://secubox.maegia.tv)
|
||||
**Website:** [secubox.maegia.tv](https://secubox.maegia.tv)
|
||||
**Publisher:** [CyberMind.fr](https://cybermind.fr)
|
||||
|
||||
---
|
||||
|
||||
## Three-Loop Security Architecture
|
||||
|
||||
SecuBox implements a **Three-Loop Security Model** for defense in depth:
|
||||
## Four-Layer Architecture
|
||||
|
||||
```
|
||||
┌────────────────────────────────────────────────────────┐
|
||||
│ LOOP 3: STRATEGIC │
|
||||
│ (Hours → Days) │
|
||||
│ ┌────────────────────────────────────────────────┐ │
|
||||
│ │ LOOP 2: TACTICAL │ │
|
||||
│ │ (Minutes → Hours) │ │
|
||||
│ │ ┌────────────────────────────────────────┐ │ │
|
||||
│ │ │ LOOP 1: OPERATIONAL │ │ │
|
||||
│ │ │ (Milliseconds → Seconds) │ │ │
|
||||
│ │ │ DETECT → DECIDE → BLOCK │ │ │
|
||||
│ │ └────────────────────────────────────────┘ │ │
|
||||
│ │ CORRELATE → ANALYZE → ADAPT │ │
|
||||
│ └────────────────────────────────────────────────┘ │
|
||||
│ AGGREGATE → ANTICIPATE → EVOLVE │
|
||||
└────────────────────────────────────────────────────────┘
|
||||
+============================================================+
|
||||
| LAYER 4: MESH NETWORKING |
|
||||
| MirrorNet / P2P Hub / Services Mirrors |
|
||||
| +--------------------------------------------------------+ |
|
||||
| | LAYER 3: AI GATEWAY | |
|
||||
| | MCP Server / Threat Analyst / DNS Guard | |
|
||||
| | +----------------------------------------------------+ | |
|
||||
| | | LAYER 2: TACTICAL | | |
|
||||
| | | CrowdSec / WAF / Scenarios | | |
|
||||
| | | +------------------------------------------------+ | | |
|
||||
| | | | LAYER 1: OPERATIONAL | | | |
|
||||
| | | | fw4 / DPI / Bouncer / HAProxy | | | |
|
||||
| | | +------------------------------------------------+ | | |
|
||||
| | +----------------------------------------------------+ | |
|
||||
| +--------------------------------------------------------+ |
|
||||
+============================================================+
|
||||
```
|
||||
|
||||
| Loop | Function | SecuBox Modules |
|
||||
|------|----------|-----------------|
|
||||
| **Loop 1** | Real-time blocking | nftables/fw4, netifyd DPI, CrowdSec Bouncer |
|
||||
| **Loop 2** | Pattern correlation | CrowdSec Agent/LAPI, Scenarios, Netdata |
|
||||
| **Loop 3** | Threat intelligence | CrowdSec CAPI, Blocklists, P2P Hub (v0.18+) |
|
||||
|
||||
See [DOCS/THREE-LOOP-ARCHITECTURE.md](DOCS/THREE-LOOP-ARCHITECTURE.md) for detailed analysis.
|
||||
| Layer | Function | Time Scale | SecuBox Components |
|
||||
|-------|----------|------------|-------------------|
|
||||
| **Layer 1** | Real-time blocking | ms → seconds | nftables/fw4, netifyd DPI, CrowdSec Bouncer |
|
||||
| **Layer 2** | Pattern correlation | minutes → hours | CrowdSec Agent/LAPI, mitmproxy WAF, Scenarios |
|
||||
| **Layer 3** | AI analysis | minutes → hours | MCP Server, Threat Analyst, DNS Guard |
|
||||
| **Layer 4** | Mesh networking | continuous | P2P Hub, MirrorBox, Services Registry |
|
||||
|
||||
---
|
||||
|
||||
## SecuBox Modules
|
||||
## Key Features
|
||||
|
||||
### SecuBox Core (5 modules)
|
||||
### Security
|
||||
|
||||
| Module | Version | Description |
|
||||
|--------|---------|-------------|
|
||||
| **luci-app-secubox** | 0.7.1 | Central dashboard/Hub for all SecuBox modules |
|
||||
| **luci-app-secubox-portal** | 0.7.0 | Unified entry point with tabbed navigation |
|
||||
| **luci-app-secubox-admin** | 1.0.0 | Admin control center with appstore and monitoring |
|
||||
| **luci-app-secubox-bonus** | 0.2.0 | Documentation, local repo, and app store |
|
||||
| **luci-app-system-hub** | 0.5.1 | Central system control with logs and backup |
|
||||
- **CrowdSec Integration** — Real-time threat intelligence, CAPI enrollment, auto-banning
|
||||
- **mitmproxy WAF** — HTTPS inspection with CVE detection, sensitivity-based auto-ban
|
||||
- **Deep Packet Inspection** — netifyd/nDPId protocol analysis
|
||||
- **MAC Guardian** — WiFi MAC spoofing detection with CrowdSec integration
|
||||
- **DNS Guard** — AI-powered DGA, tunneling, and anomaly detection
|
||||
|
||||
### Security & Threat Management (9 modules)
|
||||
### AI Gateway
|
||||
|
||||
| Module | Version | Description |
|
||||
|--------|---------|-------------|
|
||||
| **luci-app-crowdsec-dashboard** | 0.7.0 | Real-time CrowdSec security monitoring |
|
||||
| **luci-app-secubox-security-threats** | 1.0.0 | Unified netifyd DPI + CrowdSec intelligence |
|
||||
| **luci-app-client-guardian** | 0.4.0 | Network access, captive portal, parental controls |
|
||||
| **luci-app-auth-guardian** | 0.4.0 | OAuth2/OIDC authentication, voucher system |
|
||||
| **luci-app-exposure** | 1.0.0 | Service exposure manager |
|
||||
| **luci-app-tor-shield** | 1.0.0 | Tor anonymization dashboard |
|
||||
| **luci-app-mitmproxy** | 0.4.0 | HTTPS traffic inspection |
|
||||
| **luci-app-cyberfeed** | 0.1.1 | Cyberpunk RSS feed aggregator |
|
||||
| **luci-app-ksm-manager** | 0.4.0 | Cryptographic key/HSM management |
|
||||
- **MCP Server** — Model Context Protocol for Claude Desktop integration
|
||||
- **Threat Analyst** — Autonomous AI agent for threat analysis and rule generation
|
||||
- **LocalAI** — Self-hosted LLM with model management
|
||||
|
||||
### Deep Packet Inspection (2 modules)
|
||||
### Mesh Networking
|
||||
|
||||
| Module | Version | Description |
|
||||
|--------|---------|-------------|
|
||||
| **luci-app-ndpid** | 1.1.2 | nDPId deep packet inspection dashboard |
|
||||
| **luci-app-secubox-netifyd** | 1.2.1 | netifyd DPI with real-time flow monitoring |
|
||||
- **P2P Hub** — Decentralized peer discovery with globe visualization
|
||||
- **MirrorBox** — Distributed service catalog with auto-sync
|
||||
- **App Store** — P2P package distribution across mesh peers
|
||||
- **Master Link** — Secure mesh onboarding with dynamic IPK generation
|
||||
|
||||
### Network & Connectivity (8 modules)
|
||||
### Service Exposure
|
||||
|
||||
| Module | Version | Description |
|
||||
|--------|---------|-------------|
|
||||
| **luci-app-vhost-manager** | 0.5.0 | Nginx reverse proxy with Let's Encrypt SSL |
|
||||
| **luci-app-haproxy** | 1.0.0 | Load balancer with vhosts and SSL |
|
||||
| **luci-app-wireguard-dashboard** | 0.7.0 | WireGuard VPN monitoring |
|
||||
| **luci-app-network-modes** | 0.5.0 | Sniffer, AP, Relay, Router modes |
|
||||
| **luci-app-network-tweaks** | 1.0.0 | Auto Proxy DNS & Hosts from vhosts |
|
||||
| **luci-app-mqtt-bridge** | 0.4.0 | USB-to-MQTT IoT hub |
|
||||
| **luci-app-cdn-cache** | 0.5.0 | Content delivery optimization |
|
||||
| **luci-app-media-flow** | 0.6.4 | Streaming detection (Netflix, YouTube, Spotify) |
|
||||
- **Punk Exposure** — Multi-channel service emancipation (Tor + DNS/SSL + Mesh)
|
||||
- **HAProxy** — Load balancer with webroot ACME, auto-SSL
|
||||
- **Tor Shield** — .onion hidden services with split-routing
|
||||
|
||||
### Bandwidth & Traffic Management (2 modules)
|
||||
### Media & Content
|
||||
|
||||
| Module | Version | Description |
|
||||
|--------|---------|-------------|
|
||||
| **luci-app-bandwidth-manager** | 0.5.0 | QoS rules, client quotas, SQM integration |
|
||||
| **luci-app-traffic-shaper** | 0.4.0 | TC/CAKE traffic shaping |
|
||||
- **Jellyfin** — LXC media server with setup wizard
|
||||
- **Lyrion** — Music server with CIFS integration
|
||||
- **Zigbee2MQTT** — LXC Alpine container for IoT
|
||||
- **Domoticz** — Home automation with MQTT bridge
|
||||
|
||||
### Content & Web Platforms (5 modules)
|
||||
---
|
||||
|
||||
| Module | Version | Description |
|
||||
|--------|---------|-------------|
|
||||
| **luci-app-gitea** | 1.0.0 | Gitea Platform management |
|
||||
| **luci-app-hexojs** | 1.0.0 | Hexo static site generator |
|
||||
| **luci-app-metabolizer** | 1.0.0 | Metabolizer CMS support |
|
||||
| **luci-app-magicmirror2** | 0.4.0 | MagicMirror2 smart display |
|
||||
| **luci-app-mmpm** | 0.2.0 | MagicMirror Package Manager |
|
||||
## SecuBox Modules (86 Total)
|
||||
|
||||
### AI/LLM & Analytics (4 modules)
|
||||
### Core (6 modules)
|
||||
|
||||
| Module | Version | Description |
|
||||
|--------|---------|-------------|
|
||||
| **luci-app-localai** | 0.1.0 | LocalAI LLM management |
|
||||
| **luci-app-ollama** | 0.1.0 | Ollama LLM management |
|
||||
| **luci-app-glances** | 1.0.0 | Glances system monitoring |
|
||||
| **luci-app-netdata-dashboard** | 0.5.0 | Real-time Netdata monitoring |
|
||||
| Module | Description |
|
||||
|--------|-------------|
|
||||
| luci-app-secubox | Central dashboard/Hub |
|
||||
| luci-app-secubox-portal | Unified entry point with tabs |
|
||||
| luci-app-secubox-admin | Admin control center |
|
||||
| secubox-app-bonus | App store and documentation |
|
||||
| luci-app-system-hub | System control with backup |
|
||||
| luci-theme-secubox | KISS UI theme |
|
||||
|
||||
### Streaming & Data Processing (2 modules)
|
||||
### Security (15 modules)
|
||||
|
||||
| Module | Version | Description |
|
||||
|--------|---------|-------------|
|
||||
| **luci-app-streamlit** | 1.0.0 | Streamlit Platform management |
|
||||
| **luci-app-picobrew** | 1.0.0 | PicoBrew Server management |
|
||||
| Module | Description |
|
||||
|--------|-------------|
|
||||
| luci-app-crowdsec-dashboard | CrowdSec monitoring |
|
||||
| luci-app-security-threats | Unified netifyd + CrowdSec |
|
||||
| luci-app-client-guardian | Captive portal, parental controls |
|
||||
| luci-app-auth-guardian | OAuth2/OIDC, vouchers |
|
||||
| luci-app-exposure | Service exposure manager |
|
||||
| luci-app-tor-shield | Tor anonymization |
|
||||
| luci-app-mitmproxy | HTTPS inspection WAF |
|
||||
| luci-app-mac-guardian | WiFi MAC security |
|
||||
| luci-app-dns-guard | AI-powered DNS anomaly |
|
||||
| luci-app-waf | Web Application Firewall |
|
||||
| luci-app-threat-analyst | AI threat analysis |
|
||||
| luci-app-ksm-manager | Key/HSM management |
|
||||
| luci-app-master-link | Mesh onboarding |
|
||||
| luci-app-routes-status | VHosts route checker |
|
||||
| secubox-mcp-server | MCP protocol server |
|
||||
|
||||
### IoT & Smart Devices (1 module)
|
||||
### Network (12 modules)
|
||||
|
||||
| Module | Version | Description |
|
||||
|--------|---------|-------------|
|
||||
| **luci-app-zigbee2mqtt** | 1.0.0 | Zigbee2MQTT docker management |
|
||||
| Module | Description |
|
||||
|--------|-------------|
|
||||
| luci-app-haproxy | Load balancer with SSL |
|
||||
| luci-app-wireguard-dashboard | WireGuard VPN |
|
||||
| luci-app-vhost-manager | Nginx reverse proxy |
|
||||
| luci-app-network-modes | Sniffer/AP/Relay/Router |
|
||||
| luci-app-network-tweaks | DNS & proxy controls |
|
||||
| luci-app-dns-provider | DNS provider API |
|
||||
| luci-app-cdn-cache | CDN optimization |
|
||||
| luci-app-bandwidth-manager | QoS and quotas |
|
||||
| luci-app-traffic-shaper | TC/CAKE shaping |
|
||||
| luci-app-mqtt-bridge | USB-to-MQTT IoT |
|
||||
| luci-app-media-flow | Streaming detection |
|
||||
| luci-app-netdiag | Network diagnostics |
|
||||
|
||||
### DPI (2 modules)
|
||||
|
||||
| Module | Description |
|
||||
|--------|-------------|
|
||||
| luci-app-ndpid | nDPId deep packet inspection |
|
||||
| luci-app-netifyd | netifyd flow monitoring |
|
||||
|
||||
### P2P Mesh (4 modules)
|
||||
|
||||
| Module | Description |
|
||||
|--------|-------------|
|
||||
| luci-app-p2p | P2P Hub with MirrorBox |
|
||||
| luci-app-service-registry | Service catalog |
|
||||
| luci-app-device-intel | Device intelligence |
|
||||
| secubox-content-pkg | Content distribution |
|
||||
|
||||
### AI/LLM (4 modules)
|
||||
|
||||
| Module | Description |
|
||||
|--------|-------------|
|
||||
| luci-app-localai | LocalAI v3.9.0 |
|
||||
| luci-app-ollama | Ollama LLM |
|
||||
| luci-app-glances | System monitoring |
|
||||
| luci-app-netdata-dashboard | Netdata real-time |
|
||||
|
||||
### Media (7 modules)
|
||||
|
||||
| Module | Description |
|
||||
|--------|-------------|
|
||||
| luci-app-jellyfin | Media server (LXC) |
|
||||
| luci-app-lyrion | Music server |
|
||||
| luci-app-zigbee2mqtt | Zigbee gateway (LXC) |
|
||||
| luci-app-domoticz | Home automation (LXC) |
|
||||
| luci-app-ksmbd | SMB/CIFS shares |
|
||||
| luci-app-smbfs | Remote mount manager |
|
||||
| luci-app-magicmirror2 | Smart display |
|
||||
|
||||
### Content Platforms (6 modules)
|
||||
|
||||
| Module | Description |
|
||||
|--------|-------------|
|
||||
| luci-app-gitea | Git platform |
|
||||
| luci-app-hexojs | Static site generator |
|
||||
| luci-app-metablogizer | Metabolizer CMS |
|
||||
| luci-app-streamlit | Streamlit apps |
|
||||
| luci-app-picobrew | PicoBrew server |
|
||||
| luci-app-jitsi | Video conferencing |
|
||||
|
||||
### Remote Access (3 modules)
|
||||
|
||||
| Module | Description |
|
||||
|--------|-------------|
|
||||
| luci-app-rustdesk | RustDesk relay |
|
||||
| luci-app-guacamole | Clientless desktop |
|
||||
| luci-app-simplex | SimpleX Chat |
|
||||
|
||||
### *Plus 27 additional supporting packages...*
|
||||
|
||||
---
|
||||
|
||||
## Supported Architectures
|
||||
|
||||
### ARM 64-bit (AArch64)
|
||||
|
||||
| Target | Devices |
|
||||
|--------|---------|
|
||||
| `aarch64-cortex-a53` | ESPRESSObin, BananaPi R64 |
|
||||
| `aarch64-cortex-a72` | MOCHAbin, Raspberry Pi 4, NanoPi R4S |
|
||||
| `mediatek-filogic` | GL.iNet MT3000, BananaPi R3 |
|
||||
| `rockchip-armv8` | NanoPi R4S/R5S, FriendlyARM |
|
||||
| `bcm27xx-bcm2711` | Raspberry Pi 4, Compute Module 4 |
|
||||
|
||||
### ARM 32-bit
|
||||
|
||||
| Target | Devices |
|
||||
|--------|---------|
|
||||
| `arm-cortex-a7-neon` | Orange Pi, BananaPi, Allwinner |
|
||||
| `arm-cortex-a9-neon` | Linksys WRT, Turris Omnia |
|
||||
| `qualcomm-ipq40xx` | Google WiFi, Zyxel NBG6617 |
|
||||
|
||||
### MIPS
|
||||
|
||||
| Target | Devices |
|
||||
|--------|---------|
|
||||
| `mips-24kc` | TP-Link Archer, Ubiquiti |
|
||||
| `mipsel-24kc` | Xiaomi, GL.iNet, Netgear |
|
||||
|
||||
### x86
|
||||
|
||||
| Target | Devices |
|
||||
|--------|---------|
|
||||
| `x86-64` | PC, VMs, Docker, Proxmox |
|
||||
| Architecture | Targets | Example Devices |
|
||||
|--------------|---------|-----------------|
|
||||
| **ARM64** | aarch64-cortex-a53/a72, mediatek-filogic, rockchip-armv8 | MOCHAbin, NanoPi R4S/R5S, GL.iNet MT3000, Raspberry Pi 4 |
|
||||
| **ARM32** | arm-cortex-a7/a9-neon, qualcomm-ipq40xx | Turris Omnia, Google WiFi |
|
||||
| **MIPS** | mips-24kc, mipsel-24kc | TP-Link Archer, Xiaomi |
|
||||
| **x86** | x86-64 | PC, VMs, Docker, Proxmox |
|
||||
|
||||
---
|
||||
|
||||
@ -196,12 +213,9 @@ See [DOCS/THREE-LOOP-ARCHITECTURE.md](DOCS/THREE-LOOP-ARCHITECTURE.md) for detai
|
||||
|
||||
### From Pre-built Packages
|
||||
|
||||
Download from [GitHub Releases](https://github.com/CyberMind-FR/secubox-openwrt/releases):
|
||||
|
||||
```bash
|
||||
opkg update
|
||||
opkg install luci-app-secubox-portal_*.ipk
|
||||
opkg install luci-app-system-hub_*.ipk
|
||||
opkg install luci-app-crowdsec-dashboard_*.ipk
|
||||
```
|
||||
|
||||
@ -213,73 +227,53 @@ cd ~/openwrt-sdk/package/
|
||||
git clone https://github.com/CyberMind-FR/secubox-openwrt.git secubox
|
||||
|
||||
# Build
|
||||
cd ~/openwrt-sdk/
|
||||
make package/secubox/luci-app-secubox-portal/compile V=s
|
||||
```
|
||||
|
||||
### Add as OpenWrt Feed
|
||||
|
||||
Add to `feeds.conf.default`:
|
||||
### Add as Feed
|
||||
|
||||
```
|
||||
src-git secubox https://github.com/CyberMind-FR/secubox-openwrt.git
|
||||
```
|
||||
|
||||
Then:
|
||||
|
||||
```bash
|
||||
./scripts/feeds update secubox
|
||||
./scripts/feeds install -a -p secubox
|
||||
make menuconfig # Select modules under LuCI > Applications
|
||||
make V=s
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Repository Structure
|
||||
## MCP Integration (Claude Desktop)
|
||||
|
||||
```
|
||||
secubox-openwrt/
|
||||
├── package/secubox/ # All 38 SecuBox LuCI packages
|
||||
├── secubox-tools/ # Build tools and local SDK
|
||||
├── DOCS/ # Documentation
|
||||
│ ├── THREE-LOOP-ARCHITECTURE.md # Security model analysis
|
||||
│ ├── DEVELOPMENT-GUIDELINES.md
|
||||
│ ├── QUICK-START.md
|
||||
│ └── VALIDATION-GUIDE.md
|
||||
└── .github/workflows/ # CI/CD
|
||||
SecuBox includes an MCP server for AI integration:
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"secubox": {
|
||||
"command": "ssh",
|
||||
"args": ["root@192.168.255.1", "/usr/bin/secubox-mcp"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## OpenWrt Compatibility
|
||||
|
||||
| Version | Status | Package Format |
|
||||
|---------|--------|----------------|
|
||||
| 25.x | Testing | `.apk` |
|
||||
| 24.10.x | **Recommended** | `.ipk` |
|
||||
| 23.05.x | Supported | `.ipk` |
|
||||
**Available tools:** `crowdsec.alerts`, `crowdsec.decisions`, `waf.logs`, `dns.queries`, `network.flows`, `system.metrics`, `wireguard.status`, `ai.analyze_threats`, `ai.cve_lookup`, `ai.suggest_waf_rules`
|
||||
|
||||
---
|
||||
|
||||
## Roadmap
|
||||
|
||||
| Phase | Version | Status | Focus |
|
||||
|-------|---------|--------|-------|
|
||||
| **Core Mesh** | v0.17 | ✅ Released | Loops 1+2 complete |
|
||||
| **Service Mesh** | v0.18 | 🔵 In Progress | P2P Hub foundation |
|
||||
| **Intelligence Mesh** | v0.19 | ⚪ Planned | Full P2P intelligence |
|
||||
| **AI Mesh** | v0.20 | ⚪ Planned | ML in Loop 2 |
|
||||
| **Certification** | v1.0 | ⚪ Planned | ANSSI certification |
|
||||
| Version | Status | Focus |
|
||||
|---------|--------|-------|
|
||||
| **v0.17** | Released | Core Mesh, 38 modules |
|
||||
| **v0.18** | Current | P2P Hub, AI Gateway, 86 modules |
|
||||
| **v0.19** | Planned | Full P2P intelligence |
|
||||
| **v1.0** | Planned | ANSSI certification |
|
||||
|
||||
---
|
||||
|
||||
## Links
|
||||
|
||||
* **Website**: [secubox.maegia.tv](https://secubox.maegia.tv)
|
||||
* **GitHub**: [github.com/CyberMind-FR/secubox-openwrt](https://github.com/CyberMind-FR/secubox-openwrt)
|
||||
* **Publisher**: [CyberMind.fr](https://cybermind.fr)
|
||||
* **Issues**: [GitHub Issues](https://github.com/CyberMind-FR/secubox-openwrt/issues)
|
||||
- **Website**: [secubox.maegia.tv](https://secubox.maegia.tv)
|
||||
- **GitHub**: [github.com/CyberMind-FR/secubox-openwrt](https://github.com/CyberMind-FR/secubox-openwrt)
|
||||
- **Publisher**: [CyberMind.fr](https://cybermind.fr)
|
||||
- **Issues**: [GitHub Issues](https://github.com/CyberMind-FR/secubox-openwrt/issues)
|
||||
|
||||
---
|
||||
|
||||
@ -289,20 +283,10 @@ Apache-2.0 © 2024-2026 CyberMind.fr
|
||||
|
||||
---
|
||||
|
||||
## Contributing
|
||||
|
||||
1. Fork the repository
|
||||
2. Create a feature branch (`git checkout -b feature/amazing-feature`)
|
||||
3. Commit your changes (`git commit -m 'Add amazing feature'`)
|
||||
4. Push to the branch (`git push origin feature/amazing-feature`)
|
||||
5. Open a Pull Request
|
||||
|
||||
---
|
||||
|
||||
## Author
|
||||
|
||||
**Gandalf** - [CyberMind.fr](https://cybermind.fr)
|
||||
|
||||
**Ex Tenebris, Lux Securitas**
|
||||
|
||||
🇫🇷 Made with love in France
|
||||
Made in France
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
var callStatus = rpc.declare({
|
||||
object: 'luci.routes-status',
|
||||
method: 'status',
|
||||
params: ['offset', 'limit'],
|
||||
expect: { }
|
||||
});
|
||||
|
||||
@ -25,46 +26,36 @@ var callAddRoute = rpc.declare({
|
||||
});
|
||||
|
||||
return view.extend({
|
||||
allVhosts: [],
|
||||
currentOffset: 0,
|
||||
pageSize: 50,
|
||||
totalVhosts: 0,
|
||||
statusData: null,
|
||||
|
||||
load: function() {
|
||||
return callStatus();
|
||||
return callStatus(0, 50);
|
||||
},
|
||||
|
||||
renderHeaderChip: function(icon, label, value, tone) {
|
||||
var display = (value == null ? '—' : value).toString();
|
||||
return E('div', { 'class': 'sh-header-chip' + (tone ? ' ' + tone : '') }, [
|
||||
E('span', { 'class': 'sh-chip-icon' }, icon),
|
||||
E('div', { 'class': 'sh-chip-text' }, [
|
||||
E('span', { 'class': 'sh-chip-label' }, label),
|
||||
E('strong', {}, display)
|
||||
])
|
||||
]);
|
||||
},
|
||||
|
||||
renderPill: function(text, type) {
|
||||
var colors = {
|
||||
success: '#4CAF50',
|
||||
warning: '#ff9800',
|
||||
danger: '#f44336',
|
||||
info: '#2196F3',
|
||||
muted: '#9e9e9e'
|
||||
};
|
||||
// Emoji-based status pill
|
||||
pill: function(emoji, label, ok) {
|
||||
return E('span', {
|
||||
'style': 'display:inline-block;padding:2px 8px;margin:2px;border-radius:4px;color:#fff;background:' + (colors[type] || colors.muted) + ';font-size:0.8em;font-weight:500;'
|
||||
}, text);
|
||||
'class': 'vhost-pill' + (ok ? ' ok' : ' warn'),
|
||||
'title': label
|
||||
}, emoji);
|
||||
},
|
||||
|
||||
handleSync: function() {
|
||||
ui.showModal(_('Syncing Routes...'), [
|
||||
E('p', { 'class': 'spinning' }, _('Please wait...'))
|
||||
ui.showModal(_('Syncing...'), [
|
||||
E('p', { 'class': 'spinning' }, _('Syncing routes from HAProxy...'))
|
||||
]);
|
||||
|
||||
callSyncRoutes().then(function(res) {
|
||||
ui.hideModal();
|
||||
if (res && res.success) {
|
||||
ui.addNotification(null, E('p', {}, _('Routes synchronized successfully')), 'success');
|
||||
ui.addNotification(null, E('p', {}, '✅ ' + _('Routes synchronized')), 'success');
|
||||
location.reload();
|
||||
} else {
|
||||
ui.addNotification(null, E('p', {}, _('Error: ') + (res.error || 'Unknown error')), 'error');
|
||||
ui.addNotification(null, E('p', {}, '❌ ' + (res.error || 'Sync failed')), 'error');
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -74,11 +65,11 @@ return view.extend({
|
||||
|
||||
ui.showModal(_('Add Route'), [
|
||||
E('div', { 'class': 'cbi-section' }, [
|
||||
E('p', {}, _('Add mitmproxy route for: %s').format(domain)),
|
||||
E('p', {}, _('Add mitmproxy route for: ') + domain),
|
||||
E('div', { 'class': 'cbi-value' }, [
|
||||
E('label', { 'class': 'cbi-value-title' }, _('Backend Port')),
|
||||
E('label', { 'class': 'cbi-value-title' }, _('Port')),
|
||||
E('div', { 'class': 'cbi-value-field' }, [
|
||||
E('input', { 'type': 'number', 'id': 'route-port', 'value': port || '443', 'style': 'width:100px;' })
|
||||
E('input', { 'type': 'number', 'id': 'route-port', 'value': port || '443', 'style': 'width:80px;' })
|
||||
])
|
||||
])
|
||||
]),
|
||||
@ -93,61 +84,102 @@ return view.extend({
|
||||
self.doAddRoute(domain, p);
|
||||
}
|
||||
},
|
||||
'style': 'margin-left:10px;'
|
||||
}, _('Add Route'))
|
||||
'style': 'margin-left:8px;'
|
||||
}, _('Add'))
|
||||
])
|
||||
]);
|
||||
},
|
||||
|
||||
doAddRoute: function(domain, port) {
|
||||
ui.showModal(_('Adding Route...'), [
|
||||
E('p', { 'class': 'spinning' }, _('Please wait...'))
|
||||
ui.showModal(_('Adding...'), [
|
||||
E('p', { 'class': 'spinning' }, _('Adding route...'))
|
||||
]);
|
||||
|
||||
callAddRoute(domain, port).then(function(res) {
|
||||
ui.hideModal();
|
||||
if (res && res.success) {
|
||||
ui.addNotification(null, E('p', {}, _('Route added successfully')), 'success');
|
||||
ui.addNotification(null, E('p', {}, '✅ ' + _('Route added')), 'success');
|
||||
location.reload();
|
||||
} else {
|
||||
ui.addNotification(null, E('p', {}, _('Error: ') + (res.error || 'Unknown error')), 'error');
|
||||
ui.addNotification(null, E('p', {}, '❌ ' + (res.error || 'Failed')), 'error');
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
renderVhostRow: function(vhost) {
|
||||
handleLoadMore: function() {
|
||||
var self = this;
|
||||
var missingRoutes = !vhost.has_route_out || !vhost.has_route_in;
|
||||
var btn = document.getElementById('load-more-btn');
|
||||
var spinner = document.getElementById('load-spinner');
|
||||
|
||||
return E('tr', {}, [
|
||||
if (btn) btn.style.display = 'none';
|
||||
if (spinner) spinner.style.display = 'block';
|
||||
|
||||
this.currentOffset += this.pageSize;
|
||||
|
||||
callStatus(this.currentOffset, this.pageSize).then(function(data) {
|
||||
if (spinner) spinner.style.display = 'none';
|
||||
|
||||
var newVhosts = data.vhosts || [];
|
||||
if (newVhosts.length > 0) {
|
||||
self.allVhosts = self.allVhosts.concat(newVhosts);
|
||||
|
||||
var tbody = document.getElementById('vhosts-tbody');
|
||||
if (tbody) {
|
||||
newVhosts.forEach(function(v) {
|
||||
tbody.appendChild(self.renderRow(v));
|
||||
});
|
||||
}
|
||||
|
||||
var counter = document.getElementById('vhosts-count');
|
||||
if (counter) {
|
||||
counter.textContent = self.allVhosts.length + '/' + self.totalVhosts;
|
||||
}
|
||||
}
|
||||
|
||||
if (self.allVhosts.length < self.totalVhosts && btn) {
|
||||
btn.style.display = 'inline-block';
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
renderRow: function(v) {
|
||||
var self = this;
|
||||
var needsRoute = !v.has_route_out || !v.has_route_in;
|
||||
|
||||
return E('tr', { 'class': 'vhost-row' }, [
|
||||
// Domain
|
||||
E('td', {}, [
|
||||
E('a', {
|
||||
'href': 'https://' + vhost.domain,
|
||||
'href': 'https://' + v.domain,
|
||||
'target': '_blank',
|
||||
'style': 'color:#1976D2;text-decoration:none;font-weight:500;'
|
||||
}, vhost.domain)
|
||||
'class': 'vhost-domain'
|
||||
}, v.domain)
|
||||
]),
|
||||
E('td', {}, vhost.backend || '-'),
|
||||
E('td', {}, [
|
||||
vhost.has_route_out ? this.renderPill('OUT', 'success') : this.renderPill('OUT', 'warning'),
|
||||
vhost.has_route_in ? this.renderPill('IN', 'success') : this.renderPill('IN', 'warning')
|
||||
// Status indicators (emoji-based)
|
||||
E('td', { 'class': 'vhost-status' }, [
|
||||
// Routes
|
||||
this.pill(v.has_route_out && v.has_route_in ? '🔗' : '⚠️',
|
||||
'Routes: ' + (v.has_route_out ? 'OUT✓' : 'OUT✗') + ' ' + (v.has_route_in ? 'IN✓' : 'IN✗'),
|
||||
v.has_route_out && v.has_route_in),
|
||||
// SSL
|
||||
this.pill(v.ssl_status === 'valid' ? '🔒' : v.ssl_status === 'expiring' ? '⏰' : '🔓',
|
||||
'SSL: ' + v.ssl_status,
|
||||
v.ssl_status === 'valid'),
|
||||
// WAF
|
||||
this.pill(v.waf_bypass ? '🚫' : '🛡️',
|
||||
v.waf_bypass ? 'WAF Bypass' : 'WAF Active',
|
||||
!v.waf_bypass),
|
||||
// Active
|
||||
this.pill(v.active ? '✅' : '⏸️',
|
||||
v.active ? 'Active' : 'Inactive',
|
||||
v.active)
|
||||
]),
|
||||
// Action
|
||||
E('td', {}, [
|
||||
vhost.ssl_status === 'valid' ? this.renderPill('SSL', 'success') :
|
||||
vhost.ssl_status === 'expiring' ? this.renderPill('Expiring', 'warning') :
|
||||
vhost.ssl_status === 'expired' ? this.renderPill('Expired', 'danger') :
|
||||
this.renderPill('No SSL', 'muted')
|
||||
]),
|
||||
E('td', {}, [
|
||||
vhost.waf_bypass ? this.renderPill('Bypass', 'danger') : this.renderPill('WAF', 'info')
|
||||
]),
|
||||
E('td', {}, [
|
||||
vhost.active ? this.renderPill('Active', 'success') : this.renderPill('Inactive', 'muted'),
|
||||
missingRoutes ? E('button', {
|
||||
'class': 'cbi-button cbi-button-action',
|
||||
'click': function() { self.handleAddRoute(vhost.domain, vhost.backend_port); },
|
||||
'style': 'margin-left:8px;padding:2px 8px;font-size:0.8em;'
|
||||
}, _('+ Route')) : null
|
||||
needsRoute ? E('button', {
|
||||
'class': 'btn btn-sm',
|
||||
'click': function() { self.handleAddRoute(v.domain, v.backend_port || 443); }
|
||||
}, '➕') : null
|
||||
])
|
||||
]);
|
||||
},
|
||||
@ -155,98 +187,100 @@ return view.extend({
|
||||
render: function(data) {
|
||||
var self = this;
|
||||
var vhosts = data.vhosts || [];
|
||||
this.allVhosts = vhosts;
|
||||
this.totalVhosts = data.total || vhosts.length;
|
||||
this.currentOffset = data.offset || 0;
|
||||
this.statusData = data;
|
||||
|
||||
// Sort by domain
|
||||
vhosts.sort(function(a, b) {
|
||||
return a.domain.localeCompare(b.domain);
|
||||
});
|
||||
// Quick stats from first page
|
||||
var stats = {
|
||||
active: vhosts.filter(function(v) { return v.active; }).length,
|
||||
missing: vhosts.filter(function(v) { return !v.has_route_out || !v.has_route_in; }).length,
|
||||
bypass: vhosts.filter(function(v) { return v.waf_bypass; }).length,
|
||||
ssl: vhosts.filter(function(v) { return v.ssl_status === 'valid'; }).length
|
||||
};
|
||||
|
||||
// Stats
|
||||
var totalVhosts = vhosts.length;
|
||||
var activeVhosts = vhosts.filter(function(v) { return v.active; }).length;
|
||||
var missingRoutes = vhosts.filter(function(v) { return !v.has_route_out || !v.has_route_in; }).length;
|
||||
var wafBypassed = vhosts.filter(function(v) { return v.waf_bypass; }).length;
|
||||
var sslValid = vhosts.filter(function(v) { return v.ssl_status === 'valid'; }).length;
|
||||
var content = E('div', { 'class': 'vhosts-checker' }, [
|
||||
// Inline styles for dark theme compatibility
|
||||
E('style', {}, [
|
||||
'.vhosts-checker { font-family: system-ui, sans-serif; }',
|
||||
'.vhosts-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px; flex-wrap: wrap; gap: 12px; }',
|
||||
'.vhosts-title { font-size: 1.4em; font-weight: 600; display: flex; align-items: center; gap: 8px; }',
|
||||
'.vhosts-stats { display: flex; gap: 16px; font-size: 0.9em; opacity: 0.8; }',
|
||||
'.vhosts-stat { display: flex; align-items: center; gap: 4px; }',
|
||||
'.services { display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap; }',
|
||||
'.service-badge { padding: 6px 12px; border-radius: 6px; display: flex; align-items: center; gap: 6px; font-size: 0.85em; background: rgba(255,255,255,0.1); }',
|
||||
'.service-badge.ok { background: rgba(76, 175, 80, 0.2); }',
|
||||
'.service-badge.err { background: rgba(244, 67, 54, 0.2); }',
|
||||
'.vhosts-table { width: 100%; border-collapse: collapse; }',
|
||||
'.vhosts-table th { text-align: left; padding: 8px; opacity: 0.7; font-weight: 500; border-bottom: 1px solid rgba(255,255,255,0.1); }',
|
||||
'.vhost-row td { padding: 8px; border-bottom: 1px solid rgba(255,255,255,0.05); }',
|
||||
'.vhost-domain { color: #64b5f6; text-decoration: none; }',
|
||||
'.vhost-domain:hover { text-decoration: underline; }',
|
||||
'.vhost-status { display: flex; gap: 6px; }',
|
||||
'.vhost-pill { font-size: 1.1em; cursor: help; opacity: 0.9; }',
|
||||
'.vhost-pill.warn { opacity: 0.6; }',
|
||||
'.load-more { text-align: center; padding: 16px; }',
|
||||
'.btn-sm { padding: 4px 8px; font-size: 0.9em; }'
|
||||
].join('\n')),
|
||||
|
||||
var vhostRows = vhosts.map(function(v) { return self.renderVhostRow(v); });
|
||||
|
||||
var content = E('div', { 'class': 'routes-status-page' }, [
|
||||
// KISS Header
|
||||
E('div', { 'class': 'sh-page-header sh-page-header-lite' }, [
|
||||
E('div', {}, [
|
||||
E('h2', { 'class': 'sh-page-title' }, [
|
||||
E('span', { 'class': 'sh-page-title-icon' }, '🔀'),
|
||||
_('Routes Status')
|
||||
]),
|
||||
E('p', { 'class': 'sh-page-subtitle' },
|
||||
_('HAProxy vhosts and mitmproxy route configuration overview.'))
|
||||
// Header
|
||||
E('div', { 'class': 'vhosts-header' }, [
|
||||
E('div', { 'class': 'vhosts-title' }, [
|
||||
'🔀 ', _('VHosts Checker')
|
||||
]),
|
||||
E('div', { 'class': 'sh-header-meta' }, [
|
||||
this.renderHeaderChip('🌐', _('Vhosts'), totalVhosts),
|
||||
this.renderHeaderChip('✅', _('Active'), activeVhosts),
|
||||
this.renderHeaderChip('⚠️', _('Missing Routes'), missingRoutes, missingRoutes > 0 ? 'warn' : ''),
|
||||
this.renderHeaderChip('🛡️', _('WAF Bypass'), wafBypassed, wafBypassed > 0 ? 'warn' : ''),
|
||||
this.renderHeaderChip('🔒', _('SSL Valid'), sslValid)
|
||||
E('div', { 'class': 'vhosts-stats' }, [
|
||||
E('span', { 'class': 'vhosts-stat' }, ['📊 ', this.totalVhosts, ' ', _('total')]),
|
||||
E('span', { 'class': 'vhosts-stat' }, ['✅ ', stats.active, '+', ' ', _('active')]),
|
||||
stats.missing > 0 ? E('span', { 'class': 'vhosts-stat' }, ['⚠️ ', stats.missing, ' ', _('missing routes')]) : null,
|
||||
stats.bypass > 0 ? E('span', { 'class': 'vhosts-stat' }, ['🚫 ', stats.bypass, ' ', _('WAF bypass')]) : null
|
||||
])
|
||||
]),
|
||||
|
||||
// Service Status Cards
|
||||
E('div', { 'class': 'sh-card-grid', 'style': 'display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:16px;margin:20px 0;' }, [
|
||||
E('div', { 'class': 'sh-card', 'style': 'background:#fff;border-radius:8px;padding:16px;box-shadow:0 1px 3px rgba(0,0,0,0.1);' }, [
|
||||
E('div', { 'style': 'display:flex;align-items:center;gap:8px;margin-bottom:8px;' }, [
|
||||
E('span', { 'style': 'font-size:1.5em;' }, '⚖️'),
|
||||
E('strong', {}, 'HAProxy')
|
||||
]),
|
||||
data.haproxy_running ?
|
||||
this.renderPill('Running', 'success') :
|
||||
this.renderPill('Stopped', 'danger')
|
||||
// Service status badges
|
||||
E('div', { 'class': 'services' }, [
|
||||
E('span', { 'class': 'service-badge' + (data.haproxy_running ? ' ok' : ' err') }, [
|
||||
data.haproxy_running ? '✅' : '❌', ' HAProxy'
|
||||
]),
|
||||
E('div', { 'class': 'sh-card', 'style': 'background:#fff;border-radius:8px;padding:16px;box-shadow:0 1px 3px rgba(0,0,0,0.1);' }, [
|
||||
E('div', { 'style': 'display:flex;align-items:center;gap:8px;margin-bottom:8px;' }, [
|
||||
E('span', { 'style': 'font-size:1.5em;' }, '🔍'),
|
||||
E('strong', {}, 'mitmproxy')
|
||||
]),
|
||||
data.mitmproxy_running ?
|
||||
this.renderPill('Running', 'success') :
|
||||
this.renderPill('Stopped', 'danger')
|
||||
E('span', { 'class': 'service-badge' + (data.mitmproxy_running ? ' ok' : ' err') }, [
|
||||
data.mitmproxy_running ? '✅' : '❌', ' mitmproxy'
|
||||
]),
|
||||
E('div', { 'class': 'sh-card', 'style': 'background:#fff;border-radius:8px;padding:16px;box-shadow:0 1px 3px rgba(0,0,0,0.1);' }, [
|
||||
E('div', { 'style': 'display:flex;align-items:center;gap:8px;margin-bottom:8px;' }, [
|
||||
E('span', { 'style': 'font-size:1.5em;' }, '🖥️'),
|
||||
E('strong', {}, _('Host IP'))
|
||||
]),
|
||||
E('code', { 'style': 'background:#f5f5f5;padding:4px 8px;border-radius:4px;' }, data.host_ip || '192.168.255.1')
|
||||
])
|
||||
]),
|
||||
|
||||
// Actions
|
||||
E('div', { 'style': 'margin:20px 0;' }, [
|
||||
E('span', { 'class': 'service-badge' }, ['🖥️ ', data.host_ip || '192.168.255.1']),
|
||||
E('button', {
|
||||
'class': 'cbi-button cbi-button-action',
|
||||
'class': 'btn cbi-button-action',
|
||||
'click': function() { self.handleSync(); },
|
||||
'style': 'margin-right:10px;'
|
||||
}, '🔄 ' + _('Sync Routes from HAProxy'))
|
||||
'style': 'margin-left: auto;'
|
||||
}, '🔄 ' + _('Sync'))
|
||||
]),
|
||||
|
||||
// Vhosts Table
|
||||
E('div', { 'class': 'sh-card', 'style': 'background:#fff;border-radius:8px;padding:16px;box-shadow:0 1px 3px rgba(0,0,0,0.1);overflow-x:auto;' }, [
|
||||
E('h3', { 'style': 'margin:0 0 16px 0;' }, '🌐 ' + _('Virtual Hosts (%d)').format(totalVhosts)),
|
||||
vhosts.length > 0 ?
|
||||
E('table', { 'class': 'table', 'style': 'width:100%;border-collapse:collapse;' }, [
|
||||
E('thead', {}, [
|
||||
E('tr', { 'style': 'background:#f5f5f5;' }, [
|
||||
E('th', { 'style': 'padding:10px;text-align:left;' }, _('Domain')),
|
||||
E('th', { 'style': 'padding:10px;text-align:left;' }, _('Backend')),
|
||||
E('th', { 'style': 'padding:10px;text-align:left;' }, _('Routes')),
|
||||
E('th', { 'style': 'padding:10px;text-align:left;' }, _('SSL')),
|
||||
E('th', { 'style': 'padding:10px;text-align:left;' }, _('WAF')),
|
||||
E('th', { 'style': 'padding:10px;text-align:left;' }, _('Status'))
|
||||
])
|
||||
]),
|
||||
E('tbody', {}, vhostRows)
|
||||
]) :
|
||||
E('p', { 'style': 'color:#666;text-align:center;padding:20px;' }, _('No virtual hosts configured.'))
|
||||
])
|
||||
// Table
|
||||
vhosts.length > 0 ?
|
||||
E('table', { 'class': 'vhosts-table' }, [
|
||||
E('thead', {}, [
|
||||
E('tr', {}, [
|
||||
E('th', {}, _('Domain')),
|
||||
E('th', {}, _('Status')),
|
||||
E('th', { 'style': 'width: 50px;' }, '')
|
||||
])
|
||||
]),
|
||||
E('tbody', { 'id': 'vhosts-tbody' }, vhosts.map(function(v) { return self.renderRow(v); }))
|
||||
]) :
|
||||
E('p', { 'style': 'text-align: center; opacity: 0.6; padding: 20px;' }, _('No vhosts found.')),
|
||||
|
||||
// Load more
|
||||
this.totalVhosts > vhosts.length ? E('div', { 'class': 'load-more' }, [
|
||||
E('button', {
|
||||
'id': 'load-more-btn',
|
||||
'class': 'btn cbi-button-neutral',
|
||||
'click': function() { self.handleLoadMore(); }
|
||||
}, [
|
||||
'📥 ', _('Load More'),
|
||||
' (',
|
||||
E('span', { 'id': 'vhosts-count' }, vhosts.length + '/' + this.totalVhosts),
|
||||
')'
|
||||
]),
|
||||
E('p', { 'id': 'load-spinner', 'class': 'spinning', 'style': 'display: none;' }, _('Loading...'))
|
||||
]) : null
|
||||
]);
|
||||
|
||||
return KissTheme.wrap([content], 'admin/status/vhosts-checker');
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
#!/bin/sh
|
||||
# RPCD handler for Routes Status dashboard
|
||||
# Shows HAProxy vhosts and mitmproxy route configuration status
|
||||
# Optimized with pagination
|
||||
|
||||
. /usr/share/libubox/jshn.sh
|
||||
|
||||
@ -13,11 +14,24 @@ get_host_ip() {
|
||||
uci -q get network.lan.ipaddr || echo "192.168.255.1"
|
||||
}
|
||||
|
||||
# Main status method - optimized to fetch data once
|
||||
# Main status method - optimized with pagination
|
||||
method_status() {
|
||||
local offset=0
|
||||
local limit=50
|
||||
|
||||
# Read JSON input for pagination params
|
||||
read -r input 2>/dev/null
|
||||
if [ -n "$input" ]; then
|
||||
json_load "$input" 2>/dev/null
|
||||
json_get_var offset offset 2>/dev/null
|
||||
json_get_var limit limit 2>/dev/null
|
||||
fi
|
||||
[ -z "$offset" ] && offset=0
|
||||
[ -z "$limit" ] && limit=50
|
||||
|
||||
local host_ip=$(get_host_ip)
|
||||
local haproxy_running=$(pgrep haproxy >/dev/null 2>&1 && echo "true" || echo "false")
|
||||
local mitmproxy_running=$(pgrep -f mitmproxy >/dev/null 2>&1 && echo "true" || echo "false")
|
||||
local haproxy_running=$(pgrep haproxy >/dev/null 2>&1 && echo "1" || echo "0")
|
||||
local mitmproxy_running=$(pgrep -f mitmproxy >/dev/null 2>&1 && echo "1" || echo "0")
|
||||
|
||||
# Fetch vhost list once to temp file
|
||||
local vhost_tmp="/tmp/vhosts_$$"
|
||||
@ -27,41 +41,60 @@ method_status() {
|
||||
touch "$vhost_tmp"
|
||||
fi
|
||||
|
||||
# Count total vhosts
|
||||
local total=$(wc -l < "$vhost_tmp" | tr -d ' ')
|
||||
|
||||
json_init
|
||||
json_add_boolean haproxy_running "$haproxy_running"
|
||||
json_add_boolean mitmproxy_running "$mitmproxy_running"
|
||||
json_add_string host_ip "$host_ip"
|
||||
json_add_int total "$total"
|
||||
json_add_int offset "$offset"
|
||||
json_add_int limit "$limit"
|
||||
|
||||
json_add_array vhosts
|
||||
|
||||
# Process vhost data line by line (using file redirection to avoid subshell)
|
||||
# Process vhost data with pagination
|
||||
local count=0
|
||||
local processed=0
|
||||
while IFS= read -r line; do
|
||||
[ -z "$line" ] && continue
|
||||
|
||||
# Skip until offset
|
||||
if [ "$count" -lt "$offset" ]; then
|
||||
count=$((count + 1))
|
||||
continue
|
||||
fi
|
||||
|
||||
# Stop after limit
|
||||
if [ "$processed" -ge "$limit" ]; then
|
||||
break
|
||||
fi
|
||||
|
||||
# Parse line: " domain.com -> backend_name [enabled] SSL ..."
|
||||
local domain=$(echo "$line" | awk '{print $1}')
|
||||
local backend=$(echo "$line" | awk '{print $3}')
|
||||
local enabled=$(echo "$line" | grep -qF '[enabled]' && echo "true" || echo "false")
|
||||
local enabled=$(echo "$line" | grep -qF '[enabled]' && echo "1" || echo "0")
|
||||
|
||||
[ -z "$domain" ] && continue
|
||||
|
||||
# Check mitmproxy routes (grep for domain in JSON)
|
||||
local has_route_out="false"
|
||||
local has_route_in="false"
|
||||
if [ -f "$MITMPROXY_ROUTES" ] && grep -qF "\"$domain\"" "$MITMPROXY_ROUTES" 2>/dev/null; then
|
||||
has_route_out="true"
|
||||
# Check mitmproxy routes (use 1/0 for jshn booleans)
|
||||
local has_route_out="0"
|
||||
local has_route_in="0"
|
||||
if [ -f "$MITMPROXY_ROUTES" ] && grep "$domain" "$MITMPROXY_ROUTES" >/dev/null 2>&1; then
|
||||
has_route_out="1"
|
||||
fi
|
||||
if [ -f "$MITMPROXY_IN_ROUTES" ] && grep -qF "\"$domain\"" "$MITMPROXY_IN_ROUTES" 2>/dev/null; then
|
||||
has_route_in="true"
|
||||
if [ -f "$MITMPROXY_IN_ROUTES" ] && grep "$domain" "$MITMPROXY_IN_ROUTES" >/dev/null 2>&1; then
|
||||
has_route_in="1"
|
||||
fi
|
||||
|
||||
# Check SSL cert
|
||||
local ssl_status="missing"
|
||||
[ -f "$HAPROXY_CERTS/${domain}.pem" ] && ssl_status="valid"
|
||||
|
||||
# WAF bypass check
|
||||
local waf_bypass="false"
|
||||
[ "$backend" != "mitmproxy_inspector" ] && waf_bypass="true"
|
||||
# WAF bypass check (1=bypassed, 0=waf active)
|
||||
local waf_bypass="0"
|
||||
[ "$backend" != "mitmproxy_inspector" ] && waf_bypass="1"
|
||||
|
||||
json_add_object ""
|
||||
json_add_string domain "$domain"
|
||||
@ -75,6 +108,9 @@ method_status() {
|
||||
json_add_string route_target_in ""
|
||||
json_add_boolean waf_bypass "$waf_bypass"
|
||||
json_close_object
|
||||
|
||||
count=$((count + 1))
|
||||
processed=$((processed + 1))
|
||||
done < "$vhost_tmp"
|
||||
|
||||
json_close_array
|
||||
@ -90,12 +126,12 @@ method_sync_routes() {
|
||||
if [ -x /usr/sbin/mitmproxyctl ]; then
|
||||
result=$(/usr/sbin/mitmproxyctl sync-routes 2>&1)
|
||||
json_init
|
||||
json_add_boolean success true
|
||||
json_add_boolean success 1
|
||||
json_add_string output "$result"
|
||||
json_dump
|
||||
else
|
||||
json_init
|
||||
json_add_boolean success false
|
||||
json_add_boolean success 0
|
||||
json_add_string error "mitmproxyctl not found"
|
||||
json_dump
|
||||
fi
|
||||
@ -113,7 +149,7 @@ method_add_route() {
|
||||
|
||||
if [ -z "$domain" ] || [ -z "$port" ]; then
|
||||
json_init
|
||||
json_add_boolean success false
|
||||
json_add_boolean success 0
|
||||
json_add_string error "Missing domain or port parameter"
|
||||
json_dump
|
||||
return
|
||||
@ -122,7 +158,7 @@ method_add_route() {
|
||||
local host_ip=$(get_host_ip)
|
||||
|
||||
# Add route to both mitmproxy route files
|
||||
local success="true"
|
||||
local success="1"
|
||||
local errors=""
|
||||
|
||||
for routes_file in "$MITMPROXY_ROUTES" "$MITMPROXY_IN_ROUTES"; do
|
||||
@ -134,9 +170,7 @@ method_add_route() {
|
||||
'. + {($d): [$h, $p]}' "$routes_file" > "$tmpfile" 2>/dev/null
|
||||
else
|
||||
# Fallback: manual JSON manipulation
|
||||
# Remove trailing } and add new entry
|
||||
sed 's/}$//' "$routes_file" > "$tmpfile"
|
||||
# Check if file has content (not empty object)
|
||||
if grep -q '": \[' "$routes_file"; then
|
||||
printf ',\n "%s": ["%s", %s]\n}\n' "$domain" "$host_ip" "$port" >> "$tmpfile"
|
||||
else
|
||||
@ -148,14 +182,14 @@ method_add_route() {
|
||||
mv "$tmpfile" "$routes_file"
|
||||
else
|
||||
rm -f "$tmpfile"
|
||||
success="false"
|
||||
success="0"
|
||||
errors="$errors Failed to update $routes_file."
|
||||
fi
|
||||
fi
|
||||
done
|
||||
|
||||
# Restart mitmproxy to apply changes
|
||||
if [ "$success" = "true" ]; then
|
||||
if [ "$success" = "1" ]; then
|
||||
/etc/init.d/mitmproxy restart >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
@ -169,6 +203,8 @@ method_add_route() {
|
||||
list_methods() {
|
||||
json_init
|
||||
json_add_object status
|
||||
json_add_int offset 0
|
||||
json_add_int limit 50
|
||||
json_close_object
|
||||
json_add_object sync_routes
|
||||
json_close_object
|
||||
|
||||
Loading…
Reference in New Issue
Block a user