feat(packages): Sync source packages with MOCHAbin deployed configs

Session 101 source sync:

secubox-streamlit:
- API uses sudo -n for LXC commands (NoNewPrivileges workaround)
- Added systemd drop-in for NoNewPrivileges=false
- Added sudoers config for secubox user LXC access
- Added example config file
- postinst creates config dir, example config, LXC symlink

secubox-metablogizer:
- Added example config file
- Updated debian/rules for config installation

TOML configs saved:
- streamlit.toml (35 apps, 29 instances)
- metablogizer.toml (151 sites)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
CyberMind-FR 2026-05-06 09:21:25 +02:00
parent d234100568
commit b28e98f298
12 changed files with 2218 additions and 6 deletions

View File

@ -5,6 +5,72 @@
## 2026-05-06
### Session 101 — C3BOX Network Recovery + HAProxy LXC Routing
**Goal:** Establish network connectivity between C3BOX and MOCHAbin for migration
**Completed:**
1. **C3BOX Network Recovery**
- Fixed eth2 NO-CARRIER issue (was on wrong interface)
- C3BOX lan0@eth1 connected to MOCHAbin lan0 (DSA switch)
- IP assigned on br-lan: 192.168.255.201 (original) + .10 (secondary)
- Connectivity established: C3BOX ↔ MOCHAbin via 192.168.255.x
2. **Migration Archive Imported**
- 93 SSL certificates copied to /data/haproxy/certs/
- 99 nginx secubox.d configs available
- LXC container configs imported
3. **HAProxy LXC Routing Added**
- Created backends: lxc_gitea, lxc_nextcloud, lxc_matrix
- ACL routing for gitea.gk2.secubox.in → 10.100.0.40:3000
- ACL routing for nextcloud.gk2.secubox.in → 10.100.0.20:80
- ACL routing for matrix.gk2.secubox.in → 10.100.0.30:8008
4. **Routing Verified**
- gk2.secubox.in → 200 (WebUI)
- gitea.gk2.secubox.in → 200 (LXC)
- nextcloud.gk2.secubox.in → 302 (LXC redirect)
- blog.cybermind.fr → 200 (nginx_vhosts)
- Unknown domains → 503 (correct fallback)
5. **Metablogizer Migration COMPLETE**
- 166 sites synced from C3BOX (/srv/metablogizer/sites/)
- 60 sites emancipated (published) with nginx + HAProxy routing
- UCI config converted to nginx server blocks (per-port)
- Fixed HAProxy ACL order (metablog backends vs nginx_vhosts)
- All sites accessible from internet with correct content
**TODO (noted for later):**
- Implement mitmproxy WAF container (like C3BOX architecture)
- HAProxy cacert + vhost SSL verification
- Metablogizer TOML config conversion
### Session 101 continued — Source Package Sync
**Goal:** Sync source packages with deployed working configurations
**Completed:**
1. **secubox-streamlit package updated:**
- API main.py: Added `sudo -n` for LXC commands (NoNewPrivileges workaround)
- Added systemd drop-in: `debian/secubox-streamlit.service.d/allow-lxc.conf`
- Added sudoers config: `sudoers.d/secubox-streamlit`
- Added example config: `config/streamlit.toml.example`
- Updated postinst: Creates config dir, example config, LXC symlink
- Updated debian/rules to install new files
2. **secubox-metablogizer package updated:**
- Added example config: `config/metablogizer.toml.example`
- Updated debian/rules to install example config
3. **TOML configs saved:**
- `.claude/configs/streamlit.toml` (35 apps, 29 instances)
- `.claude/configs/metablogizer.toml` (151 sites)
---
### Session 100 — MOCHAbin Migration SUCCESS
**Goal:** Complete C3BOX → SecuBox-DEB migration with proper WAF and routing

View File

@ -1,9 +1,113 @@
# WIP — Work In Progress
*Mis à jour : 2026-05-05 (Session 98)*
*Mis à jour : 2026-05-06 (Session 101)*
---
## 🔄 En cours — MOCHAbin Full Migration (HAProxy/WAF/Services)
## 🔄 En cours — Session 101: MOCHAbin Migration COMPLETE
### ✅ Fait cette session
1. **Metablogizer migration complète**
- Config TOML: `.claude/configs/metablogizer.toml` (151 sites)
- Déployée: `/etc/secubox/metablogizer/config.toml`
- 59 sites publiés synchronisés avec HAProxy backends
- API: 165 sites total, 59 publiés
- Sites internet OK (gandalf.maegia.tv, etc.)
2. **Streamlit migration complète**
- Container LXC: `/data/lxc/streamlit` (mSSD), symlink `/var/lib/lxc/streamlit`
- IP: 10.100.0.50 (br-lxc)
- Config TOML déployée: `/etc/secubox/streamlit.toml` (35 apps, 29 instances)
- 22+ instances running dans LXC
- HAProxy backends configurés pour tous les domaines emancipated
- WebUI: container status = running, 29 instances configurées
- Accès internet: https://pix.gk2.secubox.in/, etc.
3. **Configs TOML sauvegardées**
- `.claude/configs/metablogizer.toml`
- `.claude/configs/streamlit.toml`
### ✅ Corrections API appliquées
- NoNewPrivileges=false pour permettre sudo lxc-*
- Sudoers: secubox peut exécuter lxc-info, lxc-attach, streamlitctl
- API utilise sudo pour les commandes LXC
---
## 📋 Guidelines de développement (Session 101)
### Confinement LXC obligatoire
Tous les services apportés par les modules SecuBox doivent être confinés dans des conteneurs LXC:
- Chaque module a son propre LXC (`/data/lxc/<module>`)
- Symlink vers `/var/lib/lxc/<module>` pour compatibilité lxc-*
- Le module expose des ports IP vers le nginx/HAProxy du système hôte
- Exemple: streamlit LXC expose ports 8501-8599, HAProxy route les vhosts
### Pattern xxxctl
Chaque module doit avoir un outil CLI `<module>ctl`:
- `streamlitctl` - gestion container, apps, instances
- `metablogizerctl` - gestion sites, publish, sync
- Format de sortie JSON pour intégration API
- Commandes: install, start, stop, status, list, etc.
### Bundle system (à implémenter)
Pour archivage, backup, mesh publish, migration:
- Chaque service exportable en bundle autonome
- Inclut: content, config, requirements, vhost, cert
---
### ⬜ À faire — Bundle system
Voir section détaillée ci-dessous.
---
## 📦 Plan: Bundle System pour archivage/distribution
Structure cible:
```
/srv/secubox/bundles/<service>/<name>/
├── content/ # Fichiers
├── config.toml # Config
├── requirements.txt
├── vhost.conf
├── cert/ # SSL si emancipated
└── manifest.json # Version, checksums
```
Objectifs: archivage, backup, mesh publish, migration simplifiée.
---
## 🔄 Previous — MOCHAbin Full Migration (HAProxy/WAF/Services)
### Status (Session 99-100)
**Export COMPLETE** - Archive ready at `/tmp/c3box-migration-20260506.tar.gz`
✅ Step 1 Complete: Export from C3BOX
- 93 SSL certificates exported
- 99 nginx secubox.d configs exported
- HAProxy config exported
- 4 LXC container configs exported (gitea, mail, matrix, nextcloud)
- Services content exported (metablogizer/streamlit dirs empty on source)
⬜ Step 2: MOCHAbin network setup (MOCHAbin currently unreachable at 192.168.255.10)
⬜ Step 3: Import migration archive
⬜ Step 4: Run `haproxyctl migrate`
⬜ Step 5-8: LXC, WAF, verification
**To continue when MOCHAbin is online:**
```bash
# Transfer archive
scp /tmp/c3box-migration-20260506.tar.gz root@192.168.255.10:/tmp/
# On MOCHAbin
tar xzf /tmp/c3box-migration-20260506.tar.gz -C /tmp/
cp -a /tmp/c3box-export/secrets/certs/* /data/haproxy/certs/
haproxyctl generate
systemctl reload haproxy
```
### Context
Previous attempt (Session 97) failed due to incomplete HAProxy migration:

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,547 @@
# Streamlit TOML config
# Converted from UCI: 35 apps, 29 instances
[main]
enabled = true
http_port = "8501"
http_host = "0.0.0.0"
data_path = "/srv/streamlit"
memory_limit = "4G"
active_app = "secubox_control"
[server]
headless = "true"
browser_gather_usage_stats = "false"
theme_base = "dark"
theme_primary_color = "#0ff"
[gitea]
enabled = true
user = "gandalf"
url = "http://192.168.255.1:3001"
# token = "..." # Configure in /etc/secubox/secrets/
[apps.metabolizer]
name = "metabolizer"
path = "metabolizer.py"
enabled = true
repo = "gandalf/streamlit-metabolizer"
gitea_synced = "2026-02-10T07:00:53+01:00"
[apps.bazi_webapp]
name = "Bazi Web"
path = "bazi_webapp.py"
enabled = true
[apps.bazi_calculator]
name = "bazi_calculator"
path = "bazi_calculator.py"
enabled = true
repo = "gandalf/streamlit-bazi_calculator"
gitea_synced = "2026-02-10T07:12:30+01:00"
[apps.sappix]
name = "Sappix Test"
path = "sappix.py"
enabled = true
[apps.tong_shu_app]
name = "tong_shu_app"
path = "tong_shu_app.py"
enabled = true
[apps.files_295]
name = "bweek"
path = "/srv/streamlit/apps/files_295/bazi_weekly_app.py"
enabled = true
repo = "gandalf/streamlit-files_295"
gitea_synced = "2026-02-10T07:00:49+01:00"
[apps.wuyun_liuqi]
name = "wuyun_liuqi"
path = "tong_shu_app.py"
enabled = true
repo = "gandalf/streamlit-wuyun_liuqi"
gitea_synced = "2026-02-15T08:20:28+01:00"
[apps.bazi_weekly_app]
name = "bazi_weekly_app"
path = "bazi_weekly_app.py"
enabled = true
[apps.bazi_calculator_1]
name = "bazi_calculator_1"
path = "bazi_calculator_1.py"
enabled = true
[apps.bazi_complete]
name = "bazi_complete"
path = "bazi_complete.py"
enabled = true
[apps.files_299]
name = "dash"
path = "/srv/streamlit/apps/files_299/app.py"
enabled = true
repo = "gandalf/streamlit-files_299"
gitea_synced = "2026-02-10T07:00:50+01:00"
[apps.yijing_oracle]
name = "yijing_oracle"
path = "/srv/streamlit/apps/yijing_oracle/yijing-oracle/app.py"
enabled = true
repo = "gandalf/streamlit-yijing_oracle"
gitea_synced = "2026-02-10T07:01:16+01:00"
[apps.test_upload]
name = "test_upload"
path = "test_upload.py"
enabled = true
[apps.yijing_generator_v2]
name = "yijing_generator_v2"
path = "/srv/streamlit/apps/yijing_generator_v2/yijing-oracle/generator.py"
enabled = true
repo = "gandalf/streamlit-yijing_generator_v2"
gitea_synced = "2026-02-10T07:01:13+01:00"
[apps.reupload_test]
name = "reupload_test"
path = "reupload_test.py"
enabled = true
[apps.test_app]
name = "test_app"
path = "test_app.py"
enabled = true
[apps.Francetv_magazine]
name = "Francetv_magazine"
path = "/srv/streamlit/apps/Francetv_magazine/francetv_magazine.py"
enabled = true
repo = "gandalf/streamlit-Francetv_magazine"
gitea_synced = "2026-02-12T07:57:49+01:00"
[apps.cineposter_fixed]
name = "cineposter_fixed"
path = "/srv/streamlit/apps/cineposter_fixed/app.py"
enabled = true
repo = "gandalf/streamlit-cineposter_fixed"
gitea_synced = "2026-02-15T08:20:23+01:00"
[apps.pdf_slideshow]
name = "pdf_slideshow"
path = "pdf_slideshow.py"
enabled = true
repo = "gandalf/streamlit-pdf_slideshow"
gitea_synced = "2026-02-15T08:20:24+01:00"
[apps.pharmacopoeia_secubox]
name = "pharmacopoeia_secubox"
path = "/srv/streamlit/apps/pharmacopoeia_secubox/app.py"
enabled = true
repo = "gandalf/streamlit-pharmacopoeia_secubox"
gitea_synced = "2026-02-15T08:20:26+01:00"
[apps.Photo_cloud_streamlit_main]
name = "Photo_cloud_streamlit_main"
path = "/srv/streamlit/apps/Photo_cloud_streamlit_main/photo_cloud_streamlit.py"
enabled = true
repo = "gandalf/streamlit-Photo_cloud_streamlit_main"
gitea_synced = "2026-02-14T06:18:31+01:00"
[apps.cc_osint]
name = "cc_osint"
path = "/srv/streamlit/apps/cc_osint/app.py"
enabled = true
repo = "gandalf/streamlit-cc_osint"
gitea_synced = "2026-02-15T05:06:28+01:00"
[apps.generix]
name = "generix"
path = "generix.py"
enabled = true
repo = "gandalf/streamlit-generix"
gitea_synced = "2026-02-16T05:33:08+01:00"
[apps.prompt_forge]
name = "prompt_forge"
path = "prompt_forge.py"
enabled = true
[apps.fanzine_gk2_lumiere_1]
name = "fanzine_gk2_lumiere_1"
path = "fanzine_gk2_lumiere_1.py"
enabled = true
[apps.gk2_lumiere]
name = "gk2_lumiere"
path = "gk2_lumiere.py"
enabled = true
[apps.cybermind_fanzine]
name = "cybermind_fanzine"
path = "cybermind_fanzine.py"
enabled = true
app = "cybermind_fanzine"
port = "8529"
emancipated = true
emancipated_at = "2026-02-20T19:12:45+01:00"
domain = "cybermind_fanzine.gk2.secubox.in"
[apps.tamagochi_gol]
name = "tamagochi_gol"
path = "tamagochi_gol.py"
enabled = true
[apps.exit_test]
name = "exit_test"
path = "exit_test.py"
enabled = true
[apps.final_fix_test]
name = "final_fix_test"
path = "final_fix_test.py"
enabled = true
[apps.alerte_depot]
name = "alerte_depot"
path = "alerte_depot"
enabled = true
[apps.diapvid]
name = "diapvid"
path = "/srv/streamlit/apps/diapvid/diaporama_video_v2.py"
enabled = true
app = "diapvid"
port = "8519"
autostart = true
repo = "gandalf/streamlit-diapvid"
gitea_synced = "2026-03-02T11:09:32+01:00"
emancipated = true
domain = "diapvid.gk2.secubox.in"
emancipated_at = "2026-03-04T07:30:04+01:00"
[apps.files_51]
name = "files_51"
path = "files_51.py"
enabled = true
app = "files_51"
port = "8530"
autostart = true
emancipated = true
emancipated_at = "2026-03-02T11:12:27+01:00"
domain = "files_51.gk2.secubox.in"
waf_enabled = true
[apps.enhance_app]
name = "enhance_app"
path = "enhance_app.py"
enabled = true
app = "enhance_app"
port = "8531"
autostart = true
emancipated = true
emancipated_at = "2026-03-02T18:22:52+01:00"
domain = "enhance-app.gk2.secubox.in"
waf_enabled = true
[apps.yijing_360]
name = "yijing_360"
path = "/srv/streamlit/apps/yijing_360/mc360_component.py"
enabled = true
repo = "gandalf/streamlit-yijing_360"
gitea_synced = "2026-03-31T12:15:43+02:00"
[instances.pix]
name = "bazi_calculator"
app = "bazi_calculator"
port = "8506"
enabled = true
autostart = true
emancipated = true
emancipated_at = "2026-02-13T08:52:09+01:00"
domain = "pix.gk2.secubox.in"
[instances.wuyun]
name = "wuyun"
app = "wuyun_liuqi"
port = "8503"
enabled = true
autostart = true
emancipated = true
emancipated_at = "2026-02-13T08:53:16+01:00"
domain = "wuyun.gk2.secubox.in"
[instances.yling]
name = "yling"
app = "yijing"
port = "8501"
enabled = true
autostart = true
emancipated = true
emancipated_at = "2026-02-13T08:54:22+01:00"
domain = "yling.gk2.secubox.in"
[instances.BASIC]
name = "BASIC"
app = "bazi_complete"
port = "8509"
enabled = true
autostart = true
emancipated = true
emancipated_at = "2026-02-13T08:58:20+01:00"
domain = "basic.gk2.secubox.in"
[instances.secubox_evolution]
enabled = true
app = "secubox_evolution"
port = "8510"
emancipated = true
emancipated_at = "2026-02-13T08:47:14+01:00"
domain = "secubox_evolution.gk2.secubox.in"
name = "secubox_evolution"
path = "/srv/streamlit/apps/secubox_evolution/app.py"
repo = "gandalf/streamlit-secubox_evolution"
gitea_synced = "2026-02-15T10:43:54+01:00"
[instances.secubox_control]
app = "secubox_control"
port = "8511"
enabled = true
emancipated = true
emancipated_at = "2026-02-13T08:48:20+01:00"
domain = "secubox_control.gk2.secubox.in"
[instances.console]
name = "console"
app = "secubox_console"
port = "8515"
enabled = true
emancipated = true
emancipated_at = "2026-02-13T08:59:48+01:00"
domain = "console.gk2.secubox.in"
[instances.hermes]
name = "hermes"
app = "yijing_generator_v2"
port = "8512"
enabled = true
autostart = true
emancipated = true
emancipated_at = "2026-02-13T09:01:01+01:00"
domain = "hermes.gk2.secubox.in"
[instances.fabricator]
name = "fabricator"
app = "fabricator"
port = "8520"
enabled = true
emancipated = true
emancipated_at = "2026-02-07T09:37:57+01:00"
domain = "fabricator.gk2.secubox.in"
repo = "gandalf/streamlit-fabricator"
gitea_synced = "2026-02-10T07:00:47+01:00"
[instances.yijing360]
name = "yijing360"
app = "yijing-360"
port = "8521"
enabled = true
emancipated = true
emancipated_at = "2026-02-13T09:02:06+01:00"
domain = "yijing360.gk2.secubox.in"
[instances.ftvm]
name = "ftvm"
app = "Francetv_magazine"
port = "8522"
enabled = true
autostart = true
emancipated = true
emancipated_at = "2026-02-13T09:03:05+01:00"
domain = "ftvm.gk2.secubox.in"
[instances.cpf]
name = "cpf"
app = "cineposter_fixed"
port = "8523"
enabled = true
autostart = true
emancipated = true
emancipated_at = "2026-02-13T09:04:14+01:00"
domain = "cpf.gk2.secubox.in"
[instances.pdf]
name = "pdf"
app = "pdf_slideshow"
port = "8524"
enabled = true
autostart = true
emancipated = true
emancipated_at = "2026-02-13T09:05:41+01:00"
domain = "pdf.gk2.secubox.in"
[instances.papyrus]
name = "papyrus"
app = "pharmacopoeia_secubox"
port = "8525"
enabled = true
autostart = true
emancipated = true
emancipated_at = "2026-02-13T09:07:15+01:00"
domain = "papyrus.gk2.secubox.in"
[instances.pc]
name = "pc"
app = "Photo_cloud_streamlit_main"
port = "8526"
enabled = true
autostart = true
emancipated = true
emancipated_at = "2026-02-25T09:35:17+01:00"
domain = "pc.gk2.secubox.in"
[instances.osint]
name = "osint"
app = "cc_osint"
port = "8527"
enabled = true
autostart = true
emancipated = true
emancipated_at = "2026-02-25T11:12:09+01:00"
domain = "osint.gk2.secubox.in"
[instances.swg]
name = "swg"
app = "generix"
port = "8513"
enabled = true
autostart = true
emancipated = true
emancipated_at = "2026-02-16T05:34:52+01:00"
domain = "swg.gk2.secubox.in"
[instances.generik]
name = "generik"
app = "files_19"
port = "8528"
enabled = true
autostart = true
emancipated = true
emancipated_at = "2026-02-25T09:38:33+01:00"
domain = "generik.gk2.secubox.in"
[instances.prompt]
name = "prompt"
app = "prompt_forge"
port = "8502"
enabled = true
autostart = true
emancipated = true
emancipated_at = "2026-02-19T07:57:15+01:00"
domain = "pf.gk2.secubox.in"
[instances.fanzine]
name = "fanzine"
app = "fanzine_gk2_lumiere_1"
port = "8504"
enabled = true
autostart = true
emancipated = true
emancipated_at = "2026-02-20T15:18:55+01:00"
domain = "fan.gk2.secubox.in"
[instances.gk2]
name = "gk2"
app = "gk2_lumiere"
port = "8505"
enabled = true
autostart = true
emancipated = true
emancipated_at = "2026-02-20T15:18:44+01:00"
domain = "gk2.gk2.secubox.in"
[instances.cybfan]
name = "cybfan"
app = "cybermind_fanzine"
port = "8529"
enabled = true
autostart = true
emancipated = true
emancipated_at = "2026-02-20T18:29:24+01:00"
domain = "cybfan.gk2.secubox.in"
[instances.tam]
name = "tam"
app = "tamagochi_gol"
port = "8514"
enabled = true
autostart = true
emancipated = true
emancipated_at = "2026-02-21T09:29:18+01:00"
domain = "tam.gk2.secubox.in"
[instances.files_40]
name = "files_40"
path = "files_40.py"
enabled = true
app = "files_40"
port = "8516"
autostart = true
emancipated = true
emancipated_at = "2026-02-25T09:43:35+01:00"
domain = "files_40.gk2.secubox.in"
[instances.files_42]
name = "files_42"
app = "files_42"
port = "8517"
enabled = true
path = "files_42/app.py"
emancipated = true
emancipated_at = "2026-02-21T14:19:50+01:00"
domain = "files.gk2.secubox.in"
[instances.test2]
name = "Test2"
app = "bazi_calculator"
port = "8598"
enabled = true
autostart = true
emancipated = true
emancipated_at = "2026-02-25T11:29:48+01:00"
domain = "test2new.gk2.secubox.in"
waf_enabled = true
[instances.alerte_depot_inst]
name = "alerte_depot"
app = "alerte_depot"
port = "8518"
enabled = true
autostart = true
domain = "alerte.gk2.secubox.in"
emancipated = true
[instances.entamoir_1]
name = "entamoir_1"
path = "entamoir_1.py"
enabled = true
app = "entamoir_1"
port = "8507"
autostart = true
[instances.secubox_report_streamlit]
name = "secubox_report_streamlit"
path = "secubox_report_streamlit.py"
enabled = true
app = "secubox_report_streamlit"
port = "8508"
autostart = true
[api]
# Secrets removed - configure in /etc/secubox/secrets/streamlit.env
# google_client_id = "..."
# google_client_secret = "..."
redirect_base = "https://files.gk2.secubox.in"
# anthropic_key = "..."

View File

@ -0,0 +1,24 @@
# SecuBox MetaBlogizer Configuration
# Location: /etc/secubox/metablogizer/config.toml
[main]
enabled = true
runtime = "nginx"
sites_root = "/srv/metablogizer/sites"
port_base = "8900"
# Gitea integration (optional)
[gitea]
enabled = false
url = "http://localhost:3001"
user = "admin"
# token = "your_gitea_token"
# Site definitions
# [sites.mysite]
# name = "mysite"
# domain = "mysite.example.com"
# port = "8900"
# enabled = true
# emancipated = true
# ssl = true

View File

@ -11,3 +11,6 @@ override_dh_auto_install:
[ -d menu.d ] && cp -r menu.d/. debian/secubox-metablogizer/usr/share/secubox/menu.d/ || true
install -d debian/secubox-metablogizer/usr/sbin
[ -d sbin ] && install -m 755 sbin/* debian/secubox-metablogizer/usr/sbin/ || true
# Example config
install -d debian/secubox-metablogizer/usr/share/doc/secubox-metablogizer/examples
[ -f config/metablogizer.toml.example ] && install -m 644 config/metablogizer.toml.example debian/secubox-metablogizer/usr/share/doc/secubox-metablogizer/examples/ || true

View File

@ -32,10 +32,13 @@ def _cfg():
def _run_ctl(*args, timeout: int = 30) -> dict:
"""Run streamlitctl and return parsed JSON or error."""
"""Run streamlitctl and return parsed JSON or error.
Uses sudo -n for non-interactive sudo (requires sudoers config).
"""
try:
result = subprocess.run(
[CTL, *args],
["sudo", "-n", CTL, *args],
capture_output=True, text=True, timeout=timeout
)
if result.stdout.strip().startswith("{"):
@ -48,9 +51,12 @@ def _run_ctl(*args, timeout: int = 30) -> dict:
def _lxc_running() -> bool:
"""Check if LXC container is running."""
"""Check if LXC container is running.
Uses sudo -n for non-interactive sudo (requires sudoers config).
"""
result = subprocess.run(
["lxc-info", "-n", LXC_NAME, "-s"],
["sudo", "-n", "lxc-info", "-n", LXC_NAME, "-s"],
capture_output=True, text=True
)
return "RUNNING" in result.stdout

View File

@ -0,0 +1,39 @@
# SecuBox Streamlit Platform Configuration
# Location: /etc/secubox/streamlit.toml
[main]
enabled = true
http_port = "8501"
http_host = "0.0.0.0"
data_path = "/srv/streamlit"
memory_limit = "4G"
[server]
headless = "true"
browser_gather_usage_stats = "false"
theme_base = "dark"
theme_primary_color = "#0ff"
# Gitea integration (optional)
[gitea]
enabled = false
user = "admin"
url = "http://localhost:3001"
# token = "your_gitea_token"
# App definitions
# [apps.myapp]
# name = "My App"
# path = "myapp.py"
# enabled = true
# repo = "user/repo"
# Instance definitions (running instances with domain routing)
# [instances.myinstance]
# name = "myinstance"
# app = "myapp"
# port = "8501"
# enabled = true
# autostart = true
# emancipated = true
# domain = "myapp.example.com"

View File

@ -7,6 +7,22 @@ case "$1" in
install -d -o secubox -g secubox -m 750 /run/secubox /var/lib/secubox
install -d -o secubox -g secubox -m 755 /srv/streamlit/apps /srv/streamlit/logs
install -d -o secubox -g secubox -m 755 /var/log/secubox
install -d -o root -g root -m 755 /etc/secubox
# Create example config if none exists
if [ ! -f /etc/secubox/streamlit.toml ]; then
if [ -f /usr/share/doc/secubox-streamlit/examples/streamlit.toml.example ]; then
cp /usr/share/doc/secubox-streamlit/examples/streamlit.toml.example /etc/secubox/streamlit.toml
chown root:secubox /etc/secubox/streamlit.toml
chmod 640 /etc/secubox/streamlit.toml
fi
fi
# Create LXC symlink if container exists on /data (mSSD)
if [ -d /data/lxc/streamlit ] && [ ! -e /var/lib/lxc/streamlit ]; then
ln -sf /data/lxc/streamlit /var/lib/lxc/streamlit
fi
systemctl daemon-reload
systemctl enable secubox-streamlit.service
systemctl start secubox-streamlit.service || true

View File

@ -16,3 +16,12 @@ override_dh_auto_install:
install -d debian/secubox-streamlit/usr/sbin
[ -d sbin ] && install -m 755 sbin/* debian/secubox-streamlit/usr/sbin/ || true
[ -d scripts ] && install -m 755 scripts/* debian/secubox-streamlit/usr/sbin/ || true
# Sudoers config for LXC access
install -d debian/secubox-streamlit/etc/sudoers.d
[ -f sudoers.d/secubox-streamlit ] && install -m 440 sudoers.d/secubox-streamlit debian/secubox-streamlit/etc/sudoers.d/ || true
# Systemd service drop-in for NoNewPrivileges
install -d debian/secubox-streamlit/usr/lib/systemd/system/secubox-streamlit.service.d
[ -f debian/secubox-streamlit.service.d/allow-lxc.conf ] && install -m 644 debian/secubox-streamlit.service.d/allow-lxc.conf debian/secubox-streamlit/usr/lib/systemd/system/secubox-streamlit.service.d/ || true
# Example config
install -d debian/secubox-streamlit/usr/share/doc/secubox-streamlit/examples
[ -f config/streamlit.toml.example ] && install -m 644 config/streamlit.toml.example debian/secubox-streamlit/usr/share/doc/secubox-streamlit/examples/ || true

View File

@ -0,0 +1,4 @@
# Allow sudo for LXC commands
# Required because NoNewPrivileges=true prevents setuid binaries (like sudo)
[Service]
NoNewPrivileges=false

View File

@ -0,0 +1,7 @@
# SecuBox Streamlit - Allow secubox user to manage LXC containers
# Required for API to control streamlit LXC container
secubox ALL=(root) NOPASSWD: /usr/bin/lxc-info
secubox ALL=(root) NOPASSWD: /usr/bin/lxc-attach
secubox ALL=(root) NOPASSWD: /usr/bin/lxc-start
secubox ALL=(root) NOPASSWD: /usr/bin/lxc-stop
secubox ALL=(root) NOPASSWD: /usr/sbin/streamlitctl