lk-jwt-service listens on 0.0.0.0:8071, so anything on the LAN can mint LiveKit tokens directly and skip the voice-limit / audio-only guard #24

Open
opened 2026-10-05 18:41:35 -04:00 by jared · 0 comments
Owner

The README describes voice-limit-guard (:8070) as the enforcement point for per-room participant caps and publish permissions "for ALL clients", fronting lk-jwt-service, which was moved to :8071. But the drop-in sets LIVEKIT_JWT_BIND=:8071, i.e. all interfaces, and the port table lists 8071 | lk-jwt-service (behind guard) | 0.0.0.0.

Checked from another LAN host (10.10.10.58), TCP connect only, no HTTP request sent:

10.10.10.29:8070 open   (guard)
10.10.10.29:8071 open   (lk-jwt-service, unguarded)

Internet clients reach only /sfu/get and /get_token through NPM → :8070. But any LAN host, VPN user or compromised container can POST http://10.10.10.29:8071/sfu/get with their OpenID token and get an unrestricted JWT: no max_users check, and the full canPublishSources even in audio-only rooms. The guard is also fail-open by design (any error passes the upstream token through), so :8071 is the only hard boundary.

Fix:

  • LIVEKIT_JWT_BIND=127.0.0.1:8071, since the guard runs on the same LXC. Or a host firewall rule allowing :8071 from 127.0.0.1 only.
  • Update the README's port table, and add a Kuma check that :8071 is not reachable from the LAN.
  • While there, the same table shows other internals on 0.0.0.0 that may not need LAN exposure: LiveKit metrics 6789, Hookshot metrics 9004, node_exporter 9100 and matrix-admin exporter 9101. Worth a pass if Prometheus scrapes from a known host.

Found in the October 2026 client audit (Playwright against a local Synapse + LiveKit stack, plus code reading). Code refs are at cinny 30e2dbcd, cinny-desktop 8f67ec7, element-call 3b0050e8.

The README describes `voice-limit-guard` (`:8070`) as the enforcement point for per-room participant caps and publish permissions "for ALL clients", fronting `lk-jwt-service`, which was moved to `:8071`. But the drop-in sets `LIVEKIT_JWT_BIND=:8071`, i.e. **all interfaces**, and the port table lists `8071 | lk-jwt-service (behind guard) | 0.0.0.0`. **Checked from another LAN host (10.10.10.58), TCP connect only, no HTTP request sent:** ``` 10.10.10.29:8070 open (guard) 10.10.10.29:8071 open (lk-jwt-service, unguarded) ``` Internet clients reach only `/sfu/get` and `/get_token` through NPM → `:8070`. But any LAN host, VPN user or compromised container can `POST http://10.10.10.29:8071/sfu/get` with their OpenID token and get an unrestricted JWT: no `max_users` check, and the full `canPublishSources` even in audio-only rooms. The guard is also fail-open by design (any error passes the upstream token through), so `:8071` is the only hard boundary. **Fix:** - `LIVEKIT_JWT_BIND=127.0.0.1:8071`, since the guard runs on the same LXC. Or a host firewall rule allowing `:8071` from 127.0.0.1 only. - Update the README's port table, and add a Kuma check that `:8071` is *not* reachable from the LAN. - While there, the same table shows other internals on `0.0.0.0` that may not need LAN exposure: LiveKit metrics `6789`, Hookshot metrics `9004`, `node_exporter` `9100` and `matrix-admin exporter` `9101`. Worth a pass if Prometheus scrapes from a known host. --- _Found in the October 2026 client audit (Playwright against a local Synapse + LiveKit stack, plus code reading). Code refs are at cinny `30e2dbcd`, cinny-desktop `8f67ec7`, element-call `3b0050e8`._
jared added the infrapriority: lowsecurity labels 2026-10-05 18:41:35 -04:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: LotusGuild/matrix#24