feat(livekit-guard): enforce per-room call permissions (screenshare/camera)
Extend voice-limit-guard to enforce a per-room publish-source policy (io.lotus.room_quality allow_screenshare/allow_camera) for ALL Matrix clients, alongside the existing participant limit. - At token issue, re-sign the LiveKit JWT's canPublishSources to drop forbidden sources (microphone always kept). Verifies our own secret signed the token first and fails open on mismatch, so a secret drift can never mint a token the SFU rejects. Limit check and source policy are independent (one's outage can't skip the other). - Live (mid-call) enforcement: a background reconcile loop calls LiveKit UpdateParticipant to revoke a forbidden source from participants who joined before the policy changed -- which unpublishes their in-progress screenshare/camera server-side within ~3s and blocks re-publish. Only removes sources (never grants), preserves other permission flags, fails open, and runs as a daemon thread that cannot crash or block token issuance. - Endpoint-specific room-id extraction (/get_token->room_id, /sfu/get->room) so a client sending both keys can't get a different room's policy applied. - Auto-deploy the guard on LXC 151 (py_compile-gated, backup + rollback). - Unit tests: JWT re-sign/verify + tamper, secret-mismatch, source narrowing, reconcile (never-grant / preserve-flags / disable-on-empty), fail-open. Numeric bitrate/fps caps are NOT server-enforceable on an SFU (LiveKit forwards, never transcodes) and remain a Lotus-client-cooperative setting; the screenshare/camera permission is the hard cross-client lever. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
#!/bin/bash
|
||||
# Auto-deploy script for LXC 151 (matrix homeserver)
|
||||
# Handles: hookshot transformation functions, livekit service file (graceful), matrixbot
|
||||
# Handles: hookshot transformation functions, livekit service file (graceful),
|
||||
# voice-limit-guard (livekit policy sidecar), matrixbot
|
||||
# Triggered by: Gitea webhook on push to main
|
||||
set -euo pipefail
|
||||
|
||||
@@ -46,6 +47,48 @@ else
|
||||
echo "Restart pending — will apply when no active calls."
|
||||
fi
|
||||
|
||||
# Voice-limit / quality guard (fronts lk-jwt-service on :8070)
|
||||
if echo "$CHANGED" | grep -qE '^livekit/voice-limit-guard\.py|^systemd/voice-limit-guard\.service'; then
|
||||
echo "Deploying voice-limit-guard..."
|
||||
# Validate syntax BEFORE swapping so a broken edit can never wedge token
|
||||
# issuance (a syntax error would crash the guard and block all joins).
|
||||
if python3 -m py_compile "$REPO_DIR/livekit/voice-limit-guard.py"; then
|
||||
GUARD_DST="/opt/voice-limit-guard/voice-limit-guard.py"
|
||||
GUARD_BAK="/opt/voice-limit-guard/voice-limit-guard.py.bak"
|
||||
# Back up the last-known-good guard so a runtime-broken (but
|
||||
# syntactically valid) deploy can self-heal — a dead guard breaks
|
||||
# ALL new joins, so we must never leave it down.
|
||||
[ -f "$GUARD_DST" ] && cp "$GUARD_DST" "$GUARD_BAK"
|
||||
install -D -m644 "$REPO_DIR/livekit/voice-limit-guard.py" "$GUARD_DST"
|
||||
if echo "$CHANGED" | grep -q '^systemd/voice-limit-guard\.service'; then
|
||||
install -m644 "$REPO_DIR/systemd/voice-limit-guard.service" /etc/systemd/system/voice-limit-guard.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
# Restarting the guard only affects joins in a ~1s window (established
|
||||
# calls talk directly to livekit-server); it does not drop calls.
|
||||
# `|| true` so a non-zero restart can't abort the deploy under set -e.
|
||||
systemctl restart voice-limit-guard || true
|
||||
sleep 1
|
||||
if systemctl is-active --quiet voice-limit-guard; then
|
||||
echo "voice-limit-guard restarted successfully."
|
||||
elif [ -f "$GUARD_BAK" ]; then
|
||||
echo "ERROR: new voice-limit-guard failed to start — rolling back to last-known-good."
|
||||
cp "$GUARD_BAK" "$GUARD_DST"
|
||||
systemctl restart voice-limit-guard || true
|
||||
sleep 1
|
||||
if systemctl is-active --quiet voice-limit-guard; then
|
||||
echo "voice-limit-guard rolled back and running."
|
||||
else
|
||||
echo "CRITICAL: voice-limit-guard down after rollback — manual intervention needed."
|
||||
fi
|
||||
else
|
||||
echo "CRITICAL: voice-limit-guard failed to start and no backup to roll back to."
|
||||
fi
|
||||
else
|
||||
echo "ERROR: py_compile failed on voice-limit-guard.py — skipping deploy."
|
||||
fi
|
||||
fi
|
||||
|
||||
# Matrixbot source files
|
||||
if echo "$CHANGED" | grep -q '^matrixbot/'; then
|
||||
echo "Deploying matrixbot changes..."
|
||||
|
||||
Reference in New Issue
Block a user