mirror of
https://github.com/CyberMind-FR/secubox-deb.git
synced 2026-07-29 09:14:33 +00:00
feat(meshtastic): systemd + nginx + menu + private broker (ref #897)
- systemd/secubox-meshtasticd.service (+ debian/ copy): dedicated secubox-meshtastic user/group, dialout for serial, ProtectSystem=strict hardening, DeviceAllow for ttyUSB/ttyACM. No RuntimeDirectory=secubox (ref #896 landmine) — daemon binds its socket directly into the shared 1777 /run/secubox. - nginx/meshtastic.conf: static panel + dedicated-socket API proxy, mirrors secubox-profiles' prefix-preserving proxy_pass convention. - menu.d/71-meshtastic.json: panel entry under the mesh category. - conf/mosquitto-secubox-meshtastic.conf: private shared_grid broker listener bound to the MirrorNet IP (10.10.0.1), anonymous access disabled, creds/ACL left to the operator.
This commit is contained in:
parent
6d5139ec7b
commit
340bb6a650
|
|
@ -0,0 +1,37 @@
|
|||
# SPDX-License-Identifier: LicenseRef-CMSD-1.0
|
||||
# Copyright (c) 2026 CyberMind — Gérald Kerma <devel@cybermind.fr>
|
||||
#
|
||||
# SecuBox-Deb :: secubox-meshtastic :: private "shared_grid" broker.
|
||||
#
|
||||
# Installed as /etc/mosquitto/conf.d/secubox-meshtastic.conf. This listener
|
||||
# federates the "shared_grid" Meshtastic channel(s) between SecuBox nodes
|
||||
# over the encrypted MirrorNet WireGuard mesh ONLY — it is bound to this
|
||||
# node's mesh IP (10.10.0.1), never to 0.0.0.0/:: (see
|
||||
# conf/meshtastic.toml.example: [shared_grid] broker = "10.10.0.1:1883").
|
||||
# The "on_grid" bridge (public/community MQTT, e.g. mqtt.example.org) is a
|
||||
# separate, independent client connection made BY this daemon and is not
|
||||
# affected by this listener.
|
||||
#
|
||||
# This file ships the topology only. Mosquitto itself is NOT started by the
|
||||
# secubox-meshtastic package (it's a shared system daemon other modules may
|
||||
# also use) — the operator must:
|
||||
# 1. apt install mosquitto mosquitto-clients
|
||||
# 2. provision credentials: mosquitto_passwd -c /etc/mosquitto/secubox-meshtastic.passwd <user>
|
||||
# 3. write /etc/mosquitto/secubox-meshtastic.acl (topic ACLs per node/user)
|
||||
# 4. systemctl enable --now mosquitto
|
||||
|
||||
# Replace the default any-interface listener for this purpose: bind ONLY to
|
||||
# the mesh address, on the standard MQTT port.
|
||||
listener 1883 10.10.0.1
|
||||
|
||||
# No anonymous publish/subscribe on the mesh listener.
|
||||
allow_anonymous false
|
||||
|
||||
# Password file — created by the operator (step 2 above). Left commented so
|
||||
# a fresh install fails closed (mosquitto refuses to start rather than
|
||||
# silently allowing anonymous access) until the operator provisions it.
|
||||
#password_file /etc/mosquitto/secubox-meshtastic.passwd
|
||||
|
||||
# Optional per-topic ACL (recommended once multiple nodes/users share the
|
||||
# grid) — created by the operator (step 3 above).
|
||||
#acl_file /etc/mosquitto/secubox-meshtastic.acl
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
# SPDX-License-Identifier: LicenseRef-CMSD-1.0
|
||||
# Copyright (c) 2026 CyberMind — Gérald Kerma <devel@cybermind.fr>
|
||||
# Installed as /etc/systemd/system/secubox-meshtasticd.service — own socket,
|
||||
# own service (never served by the aggregator: a radio/serial-attached daemon
|
||||
# must not depend on something it may later restart, and the aggregator loop
|
||||
# must not block on serial I/O).
|
||||
[Unit]
|
||||
Description=SecuBox Meshtastic — LoRa mesh node daemon (radio/cache/passive/bridge)
|
||||
After=network.target secubox-core.service
|
||||
Wants=secubox-core.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=secubox-meshtastic
|
||||
Group=secubox-meshtastic
|
||||
# Serial device access (/dev/ttyUSB*, /dev/ttyACM*) is granted via the
|
||||
# dialout group PLUS the explicit DeviceAllow rules below (belt+braces:
|
||||
# some udev rulesets restrict ttyUSB/ttyACM perms to group dialout only,
|
||||
# others rely on the cgroup device controller).
|
||||
SupplementaryGroups=dialout
|
||||
WorkingDirectory=/usr/lib/secubox/meshtastic
|
||||
ExecStart=/usr/sbin/secubox-meshtasticd
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
UMask=0007
|
||||
|
||||
# NB (ref #896): do NOT set `RuntimeDirectory=secubox` here. Any unit that
|
||||
# declares RuntimeDirectory=secubox has systemd chown the SHARED /run/secubox
|
||||
# directory to *this unit's* user on every (re)start — which breaks every
|
||||
# other secubox-* service's socket bind the next time this unit restarts.
|
||||
# /run/secubox is provided 1777 root:root by tmpfiles (secubox-core), i.e.
|
||||
# world-writable + sticky, so this daemon creates its own
|
||||
# /run/secubox/meshtastic.sock there directly, with no RuntimeDirectory at
|
||||
# all. If a private scratch dir is ever needed, use
|
||||
# `RuntimeDirectory=secubox-meshtastic` (this unit's OWN dir) — never
|
||||
# `=secubox`.
|
||||
NoNewPrivileges=yes
|
||||
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
ReadWritePaths=/run/secubox /var/cache/secubox/meshtastic /var/log/secubox/meshtastic
|
||||
|
||||
# Serial radio access — PrivateDevices=yes would hide /dev/ttyUSB*/ttyACM*
|
||||
# entirely, so it is deliberately NOT set here.
|
||||
DeviceAllow=char-ttyUSB rw
|
||||
DeviceAllow=char-ttyACM rw
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
9
packages/secubox-meshtastic/menu.d/71-meshtastic.json
Normal file
9
packages/secubox-meshtastic/menu.d/71-meshtastic.json
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"id": "meshtastic",
|
||||
"name": "Meshtastic",
|
||||
"category": "mesh",
|
||||
"icon": "📡",
|
||||
"path": "/meshtastic/",
|
||||
"order": 71,
|
||||
"description": "Meshtastic LoRa mesh node — radio status, channels and grid bridging"
|
||||
}
|
||||
25
packages/secubox-meshtastic/nginx/meshtastic.conf
Normal file
25
packages/secubox-meshtastic/nginx/meshtastic.conf
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
# SPDX-License-Identifier: LicenseRef-CMSD-1.0
|
||||
# Copyright (c) 2026 CyberMind — Gérald Kerma <devel@cybermind.fr>
|
||||
# /etc/nginx/secubox-routes.d/meshtastic.conf — installé par secubox-meshtastic.
|
||||
#
|
||||
# Ce répertoire est celui que le vhost admin inclut réellement
|
||||
# (`include /etc/nginx/secubox-routes.d/*.conf;`).
|
||||
|
||||
# Panel (statique, servi par le vhost admin).
|
||||
location /meshtastic/ {
|
||||
alias /usr/share/secubox/www/meshtastic/;
|
||||
try_files $uri $uri/ /meshtastic/index.html;
|
||||
}
|
||||
|
||||
# API — socket DÉDIÉE, jamais l'aggregator : le daemon radio/série ne doit
|
||||
# pas dépendre de ce qu'il est susceptible de redémarrer, et la boucle de
|
||||
# l'aggregator ne doit pas bloquer sur de l'I/O série (cf. api/daemon.py).
|
||||
#
|
||||
# PAS de `:/` en fin de proxy_pass : l'app enregistre le chemin COMPLET
|
||||
# (@app.get("/api/v1/meshtastic/...")), donc le préfixe doit être préservé.
|
||||
# Le stripper renverrait un chemin tronqué à une app qui ne connaît que
|
||||
# /api/v1/meshtastic/... → 404.
|
||||
location /api/v1/meshtastic/ {
|
||||
proxy_pass http://unix:/run/secubox/meshtastic.sock;
|
||||
include /etc/nginx/snippets/secubox-proxy.conf;
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
# SPDX-License-Identifier: LicenseRef-CMSD-1.0
|
||||
# Copyright (c) 2026 CyberMind — Gérald Kerma <devel@cybermind.fr>
|
||||
# Installed as /etc/systemd/system/secubox-meshtasticd.service — own socket,
|
||||
# own service (never served by the aggregator: a radio/serial-attached daemon
|
||||
# must not depend on something it may later restart, and the aggregator loop
|
||||
# must not block on serial I/O).
|
||||
[Unit]
|
||||
Description=SecuBox Meshtastic — LoRa mesh node daemon (radio/cache/passive/bridge)
|
||||
After=network.target secubox-core.service
|
||||
Wants=secubox-core.service
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=secubox-meshtastic
|
||||
Group=secubox-meshtastic
|
||||
# Serial device access (/dev/ttyUSB*, /dev/ttyACM*) is granted via the
|
||||
# dialout group PLUS the explicit DeviceAllow rules below (belt+braces:
|
||||
# some udev rulesets restrict ttyUSB/ttyACM perms to group dialout only,
|
||||
# others rely on the cgroup device controller).
|
||||
SupplementaryGroups=dialout
|
||||
WorkingDirectory=/usr/lib/secubox/meshtastic
|
||||
ExecStart=/usr/sbin/secubox-meshtasticd
|
||||
Restart=on-failure
|
||||
RestartSec=5
|
||||
UMask=0007
|
||||
|
||||
# NB (ref #896): do NOT set `RuntimeDirectory=secubox` here. Any unit that
|
||||
# declares RuntimeDirectory=secubox has systemd chown the SHARED /run/secubox
|
||||
# directory to *this unit's* user on every (re)start — which breaks every
|
||||
# other secubox-* service's socket bind the next time this unit restarts.
|
||||
# /run/secubox is provided 1777 root:root by tmpfiles (secubox-core), i.e.
|
||||
# world-writable + sticky, so this daemon creates its own
|
||||
# /run/secubox/meshtastic.sock there directly, with no RuntimeDirectory at
|
||||
# all. If a private scratch dir is ever needed, use
|
||||
# `RuntimeDirectory=secubox-meshtastic` (this unit's OWN dir) — never
|
||||
# `=secubox`.
|
||||
NoNewPrivileges=yes
|
||||
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
PrivateTmp=yes
|
||||
ReadWritePaths=/run/secubox /var/cache/secubox/meshtastic /var/log/secubox/meshtastic
|
||||
|
||||
# Serial radio access — PrivateDevices=yes would hide /dev/ttyUSB*/ttyACM*
|
||||
# entirely, so it is deliberately NOT set here.
|
||||
DeviceAllow=char-ttyUSB rw
|
||||
DeviceAllow=char-ttyACM rw
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
Loading…
Reference in New Issue
Block a user