secubox-openwrt/package/secubox/PUNK-EXPOSURE.md
CyberMind-FR 297917e79a feat(jellyfin): Add secubox-app-jellyfin and luci-app-jellyfin packages
Docker-based Jellyfin media server with UCI config (port, image, media
paths, GPU transcoding), procd init, jellyfinctl CLI, and LuCI frontend
with status/config/logs view.

Also adds Punk Exposure Engine architectural README documenting the
Peek/Poke/Emancipate service exposure model and DNS provider API
roadmap. CLAUDE.md updated with architectural directive.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-04 14:50:59 +01:00

244 lines
9.8 KiB
Markdown

# Punk Exposure Engine
## Vision
Every SecuBox node is a **generative station** — it discovers what runs locally, and offers a unified flow to make any service reachable through all available channels: Tor .onion, classical DNS/HTTPS, P2P mesh, or all three at once.
Three verbs define the workflow:
- **Peek** — Scan, discover, look around. What's running? What's exposed? What domains are mapped? What peers are online?
- **Poke** — Target a service. Pick the exposure channels. Configure the linking flow.
- **Emancipate** — Activate. The service becomes reachable. DNS records are created, certificates are issued, .onion addresses are generated, mesh peers are notified.
## Architecture
```
EMANCIPATE
|
+----------------+----------------+
| | |
Tor Layer DNS/SSL Layer Mesh Layer
(.onion) (HTTPS+ACME) (P2P peers)
| | |
tor-shield haproxyctl secubox-p2p
hidden svc + dns-provider-api + gossip sync
+ acme.sh
|
DNS Provider APIs
(OVH, Gandi, Cloudflare)
|
A/AAAA/CNAME records
created programmatically
```
## Components
### Existing (already built)
| Component | Package | What it does |
|-----------|---------|--------------|
| Service scanner | `secubox-app-exposure` | `netstat` scan enriched with UCI/Docker/process names |
| Tor exposure | `secubox-app-tor` + `secubox-app-exposure` | `tor_add()` creates hidden service dir + torrc entry |
| SSL/HAProxy exposure | `secubox-app-haproxy` + `secubox-app-exposure` | `ssl_add()` creates HAProxy backend + vhost + ACME cert |
| ACME certificates | `secubox-app-haproxy` | `acme.sh` with HTTP-01 webroot validation via port 8402 |
| VHost manager | `luci-app-vhost-manager` | Nginx-based vhost CRUD with ACME + templates |
| P2P mesh | `secubox-p2p` | mDNS discovery, WireGuard mesh, service registry, gossip chain |
| Master-Link | `secubox-master-link` | Hierarchical node onboarding with HMAC tokens + blockchain audit |
| Service registry | `luci-app-service-registry` | Aggregates services across mesh, health checks, landing page |
| Exposure dashboard | `luci-app-exposure` | Single-table KISS view: scan + Tor/SSL toggles per service |
### Missing (to build)
| Component | Purpose | Priority |
|-----------|---------|----------|
| **DNS provider API** | Programmatic DNS record management (OVH, Gandi, Cloudflare) | **High** |
| **DNS-01 ACME** | Wildcard certs + domains without port 80 access | **High** |
| **Unified Poke flow** | Single action to expose service on all channels | Medium |
| **Peek aggregation** | Combined view: local scan + mesh peers + DNS records + Tor | Medium |
| **Emancipate orchestrator** | Atomic multi-channel activation with rollback | Medium |
## DNS Provider API Integration
### Design
New package: `secubox-app-dns-provider`
```
package/secubox/secubox-app-dns-provider/
files/etc/config/dns-provider # UCI: provider type, API keys, zone
files/etc/init.d/dns-provider # (optional) cron for record sync
files/usr/sbin/dnsctl # CLI: record add/rm/list/sync
files/usr/lib/secubox/dns/ # Provider adapters
ovh.sh # OVH API (app key + secret + consumer key)
gandi.sh # Gandi LiveDNS (API key)
cloudflare.sh # Cloudflare (API token + zone ID)
```
### UCI config
```uci
config dns_provider 'main'
option enabled '1'
option provider 'ovh' # ovh | gandi | cloudflare
option zone 'example.com' # managed DNS zone
config ovh 'ovh'
option endpoint 'ovh-eu' # ovh-eu | ovh-ca | ovh-us
option app_key ''
option app_secret ''
option consumer_key ''
config gandi 'gandi'
option api_key ''
config cloudflare 'cloudflare'
option api_token ''
option zone_id ''
```
### dnsctl commands
```
dnsctl list # List all DNS records in zone
dnsctl add A myservice 1.2.3.4 # Create A record
dnsctl add CNAME blog mycdn.net # Create CNAME
dnsctl rm A myservice # Remove record
dnsctl sync # Sync local vhosts to DNS records
dnsctl verify myservice.example.com # Check DNS propagation
```
### acme.sh DNS-01 integration
Once `dnsctl` works, enable DNS-01 challenges in `haproxyctl cert add`:
```sh
# Current (HTTP-01 only):
acme.sh --issue -d "$domain" --webroot /var/www/acme-challenge
# New (DNS-01 via provider):
provider=$(uci -q get dns-provider.main.provider)
case "$provider" in
ovh)
export OVH_END_POINT=$(uci -q get dns-provider.ovh.endpoint)
export OVH_APPLICATION_KEY=$(uci -q get dns-provider.ovh.app_key)
export OVH_APPLICATION_SECRET=$(uci -q get dns-provider.ovh.app_secret)
export OVH_CONSUMER_KEY=$(uci -q get dns-provider.ovh.consumer_key)
acme.sh --issue -d "$domain" --dns dns_ovh
;;
gandi)
export GANDI_LIVEDNS_KEY=$(uci -q get dns-provider.gandi.api_key)
acme.sh --issue -d "$domain" --dns dns_gandi_livedns
;;
cloudflare)
export CF_Token=$(uci -q get dns-provider.cloudflare.api_token)
export CF_Zone_ID=$(uci -q get dns-provider.cloudflare.zone_id)
acme.sh --issue -d "$domain" --dns dns_cf
;;
esac
```
This unlocks **wildcard certificates** (`*.example.com`) and domains behind firewalls without port 80.
## The Emancipate Flow
When a user pokes a service and chooses "Emancipate", the orchestrator runs all selected channels atomically:
```
User selects: Gitea (port 3001) → Emancipate [Tor + DNS + Mesh]
1. Tor channel:
secubox-exposure tor add gitea 3001 80
→ .onion address generated
2. DNS channel:
dnsctl add A gitea <public-ip>
haproxyctl vhost add gitea.example.com 3001
haproxyctl cert add gitea.example.com --dns
→ HTTPS live at gitea.example.com
3. Mesh channel:
secubox-p2p publish gitea 3001 "Gitea"
gossip_sync
→ All mesh peers discover the service
4. Registry update:
Service registry refreshed
Landing page regenerated
Exposure dashboard shows all three badges
```
### Rollback on failure
If any channel fails, previously completed channels are not torn down — they remain active. The failure is reported, and the user can retry or remove individual channels via the Exposure dashboard toggles.
## Peek: What Exists Today
The current Exposure dashboard (`luci-app-exposure/services.js`) already implements Peek:
- Scans all listening ports via `netstat -tlnp`
- Enriches with real names from uhttpd, streamlit, Docker, glances configs
- Cross-references Tor hidden services by backend port
- Cross-references HAProxy vhosts by backend port
- Shows toggle switches for Tor and SSL per service
### What Peek needs next
- **DNS records column**: Show which services have DNS A/CNAME records via `dnsctl list`
- **Mesh visibility column**: Show which services are published to mesh peers
- **Multi-node view**: Aggregate services across all mesh peers (already available via `secubox-p2p get_shared_services`)
## Poke: What Exists Today
The toggle switches in the Exposure dashboard are already "Poke" actions:
- Toggle Tor ON → modal → service name + onion port → Enable
- Toggle SSL ON → modal → service name + domain → Enable
### What Poke needs next
- **DNS toggle**: Third toggle column for DNS record management
- **Emancipate button**: "Expose everywhere" single action per service
- **Provider selection**: Choose which DNS zone/provider for the domain
## Integration Points with Existing Packages
| Package | Integration | Direction |
|---------|------------|-----------|
| `secubox-app-exposure` | Peek scan + Tor/SSL add/remove | Already working |
| `secubox-app-haproxy` | HAProxy vhost + ACME cert | Already working |
| `secubox-app-tor` | Hidden service lifecycle | Already working |
| `secubox-p2p` | Service publish + gossip sync | Add `publish` RPC call |
| `luci-app-exposure` | Dashboard: add DNS column + Emancipate button | Frontend extension |
| `secubox-app-dns-provider` | **NEW**: DNS record CRUD via provider APIs | To build |
| `luci-app-dns-provider` | **NEW**: LuCI config for provider credentials | To build |
## Implementation Order
1. **`secubox-app-dns-provider`** — CLI tool + UCI config + provider adapters (OVH first)
2. **DNS-01 in haproxyctl** — Wire `dnsctl` into ACME flow as alternative to HTTP-01
3. **`luci-app-dns-provider`** — LuCI frontend for provider configuration
4. **Exposure dashboard DNS column** — Add DNS toggle + `dnsctl` integration
5. **Emancipate flow** — Unified orchestrator in `secubox-exposure emancipate`
6. **Mesh publish integration** — Wire `secubox-p2p publish` into Emancipate
## Naming Convention
The project uses punk/DIY metaphors:
| Term | Meaning | Technical equivalent |
|------|---------|---------------------|
| **Peek** | Discover, scan, observe | `secubox-exposure scan` + service registry |
| **Poke** | Target, configure, aim | Toggle switches + modal config |
| **Emancipate** | Activate, make free, expose | Multi-channel atomic activation |
| **Station** | A SecuBox node | One OpenWrt device running the mesh |
| **Generative** | Each station can create new endpoints | Docker apps + exposure channels |
## Security Considerations
- DNS provider API keys stored in UCI with restricted ACL
- ACME private keys in `/etc/acme/` with 600 permissions
- Tor hidden service keys in `/var/lib/tor/` owned by tor:tor
- Emancipate flow never exposes 127.0.0.1-only services (guard in scan)
- DNS records only created for services the user explicitly Pokes
- Rollback does NOT auto-delete — user must explicitly remove exposure