mirror of
https://github.com/CyberMind-FR/secubox-deb.git
synced 2026-07-29 15:37:03 +00:00
* feat(secubox-lyrion): expose LMS web UI on LAN IP + lyrion.maegia.tv (closes #248) Operator asked "make lyrion available with local ip ui". Ships nginx/lyrion-vhost.conf to /etc/nginx/sites-available/lyrion.conf with two server blocks: 1. **LAN direct** — `listen 0.0.0.0:9000`, accepts the LAN IP / .local / localhost / catch-all. No TLS, no WAF. Operator hits `http://192.168.1.200:9000/` and lands on LMS at root. 2. **Public chain** — `listen 0.0.0.0:9080`, server_name lyrion.maegia.tv. Ships with the mandatory `^~ /.well-known/acme-challenge/` block per the #246 lesson; operator wires HAProxy ACL + cert + mitmproxy route separately when ready. Why two blocks instead of one: LMS uses absolute asset paths (/skin/EN/..., /html/..., …) so it cannot live under a subpath without rewriting every asset reference. Each block proxies to LXC `10.100.0.100:9000` at root. Operator enables with: ln -s ../sites-available/lyrion.conf /etc/nginx/sites-enabled/ systemctl reload nginx Board-validated 2026-05-20: $ curl -I http://192.168.1.200:9000/ HTTP/1.1 302 Found Server: nginx/1.22.1 Location: /settings/server/wizard.html Set-Cookie: Squeezebox-albumView=; path=/ Closes #248 * fix(secubox-lyrion): disable LMS protectSettings on install (ref #248) Board-validated v1.0.2 LAN UI returned 403 on /settings/server/wizard.html because LMS's `protectSettings: 1` (Slim/Web/HTTP.pm:1040) rejects any peer that is the LXC bridge gateway OR on a "different network" — both true for traffic arriving through nginx reverse-proxy. install-lxc.sh now writes two LMS prefs after the .deb install: protectSettings: 0 allowedHosts: 127.0.0.1,10.100.0.*,192.168.* Access control is enforced upstream (HAProxy + mitmproxy + Authelia SSO), so disabling LMS's local guard is safe. Idempotent — re-running `lyrionctl install` just rewrites both prefs. Diagnostic that surfaced the bug: /var/log/squeezeboxserver/server.log → Access to settings pages is restricted to the local network or localhost: 10.100.0.1 -> 10.100.0.100 (settings/server/wizard.html) * feat(secubox-lyrion): slimproto DNAT for LAN/WiFi players (ref #248) LMS slimproto (TCP+UDP :3483) lives inside the LXC at 10.100.0.100, on br-lxc which is not routed from the LAN. WiFi players (e.g. on 192.168.8.x) reaching the board via the default gateway can talk to the web UI on :9000 (nginx already binds 0.0.0.0) but not to slimproto, so they can't actually play. install-lxc.sh now drops a persistent rule file at /etc/nftables.d/secubox-lyrion-dnat.nft loaded by nftables.service: table inet secubox-lyrion { chain prerouting { type nat hook prerouting priority dstnat; policy accept; iifname "lan0" tcp dport 3483 dnat ip to 10.100.0.100:3483 iifname "lan0" udp dport 3483 dnat ip to 10.100.0.100:3483 } } LAN interface defaults to `lan0`; override via SECUBOX_LAN_IFACE. Idempotent: re-running `lyrionctl install` deletes the live table and re-applies from the file (verified by removing both on the board and running the install verb). Board-validated 2026-05-20: $ nc -zv 192.168.1.200 3483 Connection to 192.168.1.200 3483 port [tcp/*] succeeded! Note: UDP player auto-discovery broadcasts do NOT cross subnets without router-side IP-helper. Players on a different VLAN/SSID must be told the board's LAN IP manually as the server. --------- Co-authored-by: CyberMind-FR <gandalf@Gk2.net>
This commit is contained in:
parent
322809d2a6
commit
6ad9d7262a
|
|
@ -1,3 +1,50 @@
|
|||
secubox-lyrion (1.0.3-1~bookworm1) bookworm; urgency=medium
|
||||
|
||||
* install-lxc.sh: new ensure_slimproto_dnat() step writes a persistent
|
||||
nftables file /etc/nftables.d/secubox-lyrion-dnat.nft with a
|
||||
PREROUTING DNAT rule routing host LAN traffic on TCP+UDP :3483 to
|
||||
the Lyrion LXC. Lets WiFi/LAN players (Squeezelite, iPeng, …) reach
|
||||
the LMS server without bridging the LXC network into the LAN.
|
||||
Default LAN interface is `lan0` — override via SECUBOX_LAN_IFACE.
|
||||
Idempotent: re-running `lyrionctl install` deletes the live table
|
||||
and re-applies from the file.
|
||||
* Note: UDP player auto-discovery broadcasts do NOT cross subnets
|
||||
without router-side IP-helper. Players on a different VLAN/SSID
|
||||
must be configured manually with the board's LAN IP as the server.
|
||||
|
||||
-- Gerald KERMA <devel@cybermind.fr> Wed, 20 May 2026 21:00:00 +0200
|
||||
|
||||
secubox-lyrion (1.0.2-1~bookworm1) bookworm; urgency=medium
|
||||
|
||||
* Ship nginx/lyrion-vhost.conf to /etc/nginx/sites-available/lyrion.conf
|
||||
so the operator can expose the LMS web UI on the LAN IP directly.
|
||||
Two server blocks:
|
||||
1. LAN direct — listen :9000, accepts 192.168.1.200 / lyrion.local /
|
||||
localhost / _ ; proxies to LXC root. No TLS, no WAF.
|
||||
2. Public chain — listen :9080, server_name lyrion.maegia.tv, with
|
||||
the mandatory ^~ /.well-known/acme-challenge/ block (cf. #246).
|
||||
Operator enables with:
|
||||
ln -s ../sites-available/lyrion.conf /etc/nginx/sites-enabled/
|
||||
systemctl reload nginx
|
||||
After the symlink, http://192.168.1.200:9000/ serves the LMS web UI
|
||||
directly. Closes #248.
|
||||
* Note: subpath proxy (/lyrion/ on the canonical hub) is unsuitable
|
||||
for LMS because LMS uses absolute asset paths (/skin/EN/..., /html/..., …).
|
||||
LMS must live at vhost root.
|
||||
* install-lxc.sh: new configure_lms_for_proxy() step writes two LMS prefs
|
||||
after the .deb install so the LAN UI works on first hit:
|
||||
- protectSettings: 0 (LMS otherwise 403s every /settings/* page
|
||||
when the peer is the bridge gateway / a "different network" —
|
||||
both true through nginx reverse-proxy; access control is enforced
|
||||
upstream by HAProxy + mitmproxy + Authelia).
|
||||
- allowedHosts: 127.0.0.1,10.100.0.*,192.168.*
|
||||
Was discovered post-board-validation when the wizard.html redirect
|
||||
landed on a 403 with `Access to settings pages is restricted to the
|
||||
local network or localhost: 10.100.0.1 -> 10.100.0.100` in
|
||||
/var/log/squeezeboxserver/server.log.
|
||||
|
||||
-- Gerald KERMA <devel@cybermind.fr> Wed, 20 May 2026 20:00:00 +0200
|
||||
|
||||
secubox-lyrion (1.0.1-1~bookworm1) bookworm; urgency=medium
|
||||
|
||||
* debian/rules: chmod 755 install-lxc.sh after cp -r (cp does not always
|
||||
|
|
|
|||
|
|
@ -18,6 +18,9 @@ override_dh_auto_install:
|
|||
# nginx snippet — install to BOTH secubox.d/ and secubox-routes.d/ (per v2.11.1 lesson)
|
||||
install -d debian/secubox-lyrion/etc/nginx/secubox.d
|
||||
[ -f nginx/lyrion.conf ] && cp nginx/lyrion.conf debian/secubox-lyrion/etc/nginx/secubox.d/ && (install -d debian/secubox-lyrion/etc/nginx/secubox-routes.d && cp nginx/lyrion.conf debian/secubox-lyrion/etc/nginx/secubox-routes.d/) || true
|
||||
# Public + LAN vhost (sites-available — operator opts in via ln -s)
|
||||
install -d debian/secubox-lyrion/etc/nginx/sites-available
|
||||
[ -f nginx/lyrion-vhost.conf ] && cp nginx/lyrion-vhost.conf debian/secubox-lyrion/etc/nginx/sites-available/lyrion.conf || true
|
||||
# Config example
|
||||
install -d debian/secubox-lyrion/etc/secubox
|
||||
[ -f conf/lyrion.toml.example ] && cp conf/lyrion.toml.example debian/secubox-lyrion/etc/secubox/lyrion.toml.example || true
|
||||
|
|
|
|||
|
|
@ -60,6 +60,37 @@ ensure_masquerade() {
|
|||
fi
|
||||
}
|
||||
|
||||
# Slimproto (the LMS↔player protocol) lives on TCP+UDP :3483 inside the LXC.
|
||||
# Without a DNAT on the host's LAN interface, LAN/WiFi players can't reach it
|
||||
# (the LXC bridge 10.100.0.0/24 is not routed from the LAN). We drop a
|
||||
# persistent rule file in /etc/nftables.d/ so it survives reboots — the
|
||||
# systemd nftables.service loads everything in that directory.
|
||||
ensure_slimproto_dnat() {
|
||||
local nft_file="/etc/nftables.d/secubox-lyrion-dnat.nft"
|
||||
local lan_iface="${SECUBOX_LAN_IFACE:-lan0}"
|
||||
log "Ensuring slimproto DNAT ${lan_iface}:3483 → ${LXC_IP}:3483 ..."
|
||||
install -d -m 0755 /etc/nftables.d
|
||||
cat > "$nft_file" <<NFT
|
||||
# /etc/nftables.d/secubox-lyrion-dnat.nft
|
||||
# DNAT slimproto (TCP+UDP :3483) from LAN to the Lyrion LXC.
|
||||
# Lets WiFi/LAN players (Squeezelite, iPeng, …) reach the LMS server
|
||||
# without bridging the LXC network into the LAN.
|
||||
# Generated by secubox-lyrion install-lxc.sh (#248).
|
||||
|
||||
table inet secubox-lyrion {
|
||||
chain prerouting {
|
||||
type nat hook prerouting priority dstnat; policy accept;
|
||||
iifname "${lan_iface}" tcp dport 3483 dnat ip to ${LXC_IP}:3483 comment "lyrion-slimproto-tcp"
|
||||
iifname "${lan_iface}" udp dport 3483 dnat ip to ${LXC_IP}:3483 comment "lyrion-slimproto-udp"
|
||||
}
|
||||
}
|
||||
NFT
|
||||
chmod 0644 "$nft_file"
|
||||
# Re-apply just this table (idempotent — flushes our own table first).
|
||||
nft 'delete table inet secubox-lyrion' 2>/dev/null || true
|
||||
nft -f "$nft_file" 2>&1
|
||||
}
|
||||
|
||||
lxc_state() {
|
||||
lxc-info -n "$LXC_NAME" -P "$LXC_PATH" 2>/dev/null \
|
||||
| awk -F: '/^State:/ { gsub(/ /,"",$2); print tolower($2) }'
|
||||
|
|
@ -164,6 +195,39 @@ INNER
|
|||
}
|
||||
|
||||
|
||||
configure_lms_for_proxy() {
|
||||
# LMS has a `protectSettings: 1` default that 403s the settings pages
|
||||
# whenever the request peer is the LXC bridge gateway (10.100.0.1) or on
|
||||
# a "different network" — both true when traffic arrives through nginx
|
||||
# reverse-proxy from the LAN. We disable it here because access control
|
||||
# is enforced upstream (HAProxy + mitmproxy WAF + Authelia SSO).
|
||||
# Also widen `allowedHosts` so the LAN can reach the UI even before
|
||||
# the settings-protection lift takes effect.
|
||||
log "Configuring LMS prefs for reverse-proxy access ..."
|
||||
local prefs=/var/lib/squeezeboxserver/prefs/server.prefs
|
||||
lxc-attach -n "$LXC_NAME" -P "$LXC_PATH" -- systemctl stop lyrionmusicserver 2>/dev/null || true
|
||||
lxc-attach -n "$LXC_NAME" -P "$LXC_PATH" -- bash -e <<INNER
|
||||
prefs=$prefs
|
||||
if [ -f "\$prefs" ]; then
|
||||
# protectSettings off — see comment in install-lxc.sh
|
||||
if grep -q '^protectSettings' "\$prefs"; then
|
||||
sed -i 's/^protectSettings:.*/protectSettings: 0/' "\$prefs"
|
||||
else
|
||||
echo 'protectSettings: 0' >> "\$prefs"
|
||||
fi
|
||||
# Widen allowedHosts to cover the LAN + bridge + localhost
|
||||
if grep -q '^allowedHosts' "\$prefs"; then
|
||||
sed -i 's|^allowedHosts:.*|allowedHosts: 127.0.0.1,10.100.0.*,192.168.*|' "\$prefs"
|
||||
else
|
||||
echo 'allowedHosts: 127.0.0.1,10.100.0.*,192.168.*' >> "\$prefs"
|
||||
fi
|
||||
fi
|
||||
INNER
|
||||
lxc-attach -n "$LXC_NAME" -P "$LXC_PATH" -- systemctl start lyrionmusicserver 2>/dev/null \
|
||||
|| lxc-attach -n "$LXC_NAME" -P "$LXC_PATH" -- systemctl start logitechmediaserver 2>/dev/null || true
|
||||
}
|
||||
|
||||
|
||||
mark_provisioned() {
|
||||
install -d -m 0755 -o secubox -g secubox "$STATE_DIR"
|
||||
date -Iseconds > "$SENTINEL"
|
||||
|
|
@ -180,12 +244,17 @@ main() {
|
|||
wait_for_network
|
||||
ensure_resolv
|
||||
install_lyrion_in_lxc
|
||||
configure_lms_for_proxy
|
||||
ensure_slimproto_dnat
|
||||
mark_provisioned
|
||||
log "OK — LXC '$LXC_NAME' at $LXC_IP, Lyrion provisioned + running."
|
||||
log " · Web admin : http://$LXC_IP:$LYRION_HTTP_PORT/"
|
||||
log " · JSON-RPC : http://$LXC_IP:9090/"
|
||||
log " · slimproto : tcp://$LXC_IP:3483/ (players auto-discover)"
|
||||
log " · Reverse proxy: /lyrion/ on the canonical hub vhost"
|
||||
log " · Web admin (LXC) : http://$LXC_IP:$LYRION_HTTP_PORT/"
|
||||
log " · JSON-RPC : http://$LXC_IP:9090/"
|
||||
log " · slimproto (LXC) : tcp://$LXC_IP:3483/"
|
||||
log " · slimproto (LAN) : tcp://<board-ip>:3483/ (DNAT'd; WiFi players OK)"
|
||||
log " · LAN UI : http://<board-ip>:9000/ (enable nginx vhost)"
|
||||
log " ln -s ../sites-available/lyrion.conf /etc/nginx/sites-enabled/"
|
||||
log " systemctl reload nginx"
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
|
|
|||
87
packages/secubox-lyrion/nginx/lyrion-vhost.conf
Normal file
87
packages/secubox-lyrion/nginx/lyrion-vhost.conf
Normal file
|
|
@ -0,0 +1,87 @@
|
|||
# /etc/nginx/sites-available/lyrion.conf — Lyrion Music Server vhost
|
||||
# Installed by secubox-lyrion (#248). Operator enables with:
|
||||
# ln -s ../sites-available/lyrion.conf /etc/nginx/sites-enabled/
|
||||
# systemctl reload nginx
|
||||
#
|
||||
# Two server blocks:
|
||||
# 1. LAN direct (port 9000) — http://192.168.1.200:9000/ → LXC
|
||||
# No TLS, no WAF. For internal use only.
|
||||
# 2. Public chain (port 9080) — Browser → HAProxy:443 (cert) → mitmproxy WAF
|
||||
# → nginx:9080 (lyrion.maegia.tv) → LXC. Operator wires cert + HAProxy
|
||||
# ACL + mitmproxy route separately (see #246 yacy for the recipe).
|
||||
#
|
||||
# Lyrion Music Server uses absolute paths internally (/skin/EN/..., /html/..., …)
|
||||
# so it MUST live at vhost root — never proxy it under a subpath without
|
||||
# rewriting the assets, or skins will 404 / 502.
|
||||
#
|
||||
# Player support (Squeezelite, iPeng, …) needs slimproto on TCP:3483. That is
|
||||
# OUTSIDE this nginx vhost — operator adds nftables DNAT 192.168.1.200:3483 →
|
||||
# 10.100.0.100:3483 (and matching FORWARD rule for the LXC bridge).
|
||||
|
||||
# ── 1. LAN direct (no TLS) ─────────────────────────────────────────────────
|
||||
server {
|
||||
listen 0.0.0.0:9000;
|
||||
server_name 192.168.1.200 lyrion.local localhost _;
|
||||
|
||||
# Shared assets — banner JS, error pages
|
||||
location /shared/ {
|
||||
add_header Access-Control-Allow-Origin "*" always;
|
||||
alias /usr/share/secubox/www/shared/;
|
||||
}
|
||||
|
||||
# LMS web UI proxied to the LXC at root
|
||||
location / {
|
||||
proxy_pass http://10.100.0.100:9000/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto http;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
proxy_buffering off;
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
|
||||
access_log /var/log/nginx/lyrion_lan_access.log;
|
||||
error_log /var/log/nginx/lyrion_lan_error.log;
|
||||
}
|
||||
|
||||
# ── 2. Public lyrion.maegia.tv (via HAProxy + mitmproxy) ───────────────────
|
||||
server {
|
||||
listen 0.0.0.0:9080;
|
||||
server_name lyrion.maegia.tv;
|
||||
|
||||
# ACME HTTP-01 challenge — MUST come before the catch-all proxy so
|
||||
# acme.sh renewals win (see #246 yacy fix for the same reason).
|
||||
location ^~ /.well-known/acme-challenge/ {
|
||||
root /usr/share/secubox/www;
|
||||
try_files $uri =404;
|
||||
}
|
||||
|
||||
# Health Banner injection (consistent with yacy.maegia.tv / auth.maegia.tv)
|
||||
sub_filter_types text/html;
|
||||
sub_filter_once on;
|
||||
sub_filter "</body>" '<script src="/shared/health-banner.js"></script></body>';
|
||||
|
||||
# Shared assets — banner JS, error pages
|
||||
location /shared/ {
|
||||
add_header Access-Control-Allow-Origin "*" always;
|
||||
alias /usr/share/secubox/www/shared/;
|
||||
}
|
||||
|
||||
# LMS web UI proxied to the LXC at root
|
||||
location / {
|
||||
proxy_pass http://10.100.0.100:9000/;
|
||||
proxy_set_header Host $host;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Forwarded-Proto https;
|
||||
proxy_http_version 1.1;
|
||||
proxy_set_header Connection "";
|
||||
proxy_buffering off;
|
||||
proxy_read_timeout 300s;
|
||||
}
|
||||
|
||||
access_log /var/log/nginx/lyrion_access.log;
|
||||
error_log /var/log/nginx/lyrion_error.log;
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user