Add host firewall script for LiveKit ports
This commit is contained in:
@@ -72,12 +72,27 @@ docker compose -f infra/docker-compose.yml up -d
|
|||||||
| API | 8080 |
|
| API | 8080 |
|
||||||
| LiveKit | 7880 |
|
| LiveKit | 7880 |
|
||||||
|
|
||||||
|
If the host runs a firewall, open the LiveKit ports:
|
||||||
|
|
||||||
|
```
|
||||||
|
sudo ./infra/firewall.sh
|
||||||
|
```
|
||||||
|
|
||||||
|
It allows 7880 and 7881 TCP, 3478 UDP and 50000-50100 UDP from your LAN, handling ufw and
|
||||||
|
firewalld. Pass `--dry-run` to see the commands first, `--subnet` to name a different network or
|
||||||
|
`--open-to-all` to drop the source restriction. The website and API need no rules, since Docker
|
||||||
|
publishes those ports with its own firewall rules. LiveKit runs on the host network and does not
|
||||||
|
get that treatment, so a firewalled host will load the site and then fail to connect to a room.
|
||||||
|
|
||||||
Sign in at `/admin` with the bootstrap admin details from your `.env`.
|
Sign in at `/admin` with the bootstrap admin details from your `.env`.
|
||||||
|
|
||||||
If you are serving anything other than localhost, set `PUBLIC_API_URL` and
|
If you are serving anything other than localhost, set `PUBLIC_API_URL`, `PUBLIC_LIVEKIT_URL` and
|
||||||
`PUBLIC_LIVEKIT_URL` in `infra/.env` to addresses your devices can actually reach, and change
|
`PUBLIC_WEB_URL` in `infra/.env` to addresses your devices can actually reach, add
|
||||||
`turn.domain` in `infra/livekit.yaml` to the same host. LiveKit reads that file literally, so it
|
`PUBLIC_WEB_URL` to `CORS_ORIGINS`, and change `turn.domain` in `infra/livekit.yaml` to the same
|
||||||
does not pick up environment variables.
|
host. LiveKit reads that file literally, so it does not pick up environment variables.
|
||||||
|
|
||||||
|
Leaving `CORS_ORIGINS` on its localhost default while browsing by LAN address is the usual cause
|
||||||
|
of requests failing with a missing `Access-Control-Allow-Origin` header.
|
||||||
|
|
||||||
The LiveKit container uses host networking, because WebRTC needs to advertise an address your
|
The LiveKit container uses host networking, because WebRTC needs to advertise an address your
|
||||||
devices can reach and a container on a bridge network has none. That is Linux only; on macOS or
|
devices can reach and a container on a bridge network has none. That is Linux only; on macOS or
|
||||||
@@ -128,7 +143,8 @@ Set in `infra/.env`:
|
|||||||
| `PIN_ROTATION_SECONDS` | How often join codes change, 30 to 60 |
|
| `PIN_ROTATION_SECONDS` | How often join codes change, 30 to 60 |
|
||||||
| `PIN_GRACE_SECONDS` | How long an old code keeps working after it changes |
|
| `PIN_GRACE_SECONDS` | How long an old code keeps working after it changes |
|
||||||
| `PUBLIC_API_URL` / `PUBLIC_LIVEKIT_URL` | Addresses browsers and kiosks use to reach you |
|
| `PUBLIC_API_URL` / `PUBLIC_LIVEKIT_URL` | Addresses browsers and kiosks use to reach you |
|
||||||
| `CORS_ORIGINS` | Browser origins allowed to call the API. Kiosks are exempt, since a desktop app has no fixed web origin |
|
| `PUBLIC_WEB_URL` | The address the website is served on. Must match what you type in the browser, scheme included, or server rendered pages disagree with the browser |
|
||||||
|
| `CORS_ORIGINS` | Browser origins allowed to call the API, comma separated. Must include `PUBLIC_WEB_URL`. Kiosks are exempt, since a desktop app has no fixed web origin |
|
||||||
|
|
||||||
Video encoding on the kiosk is tunable per device, without rebuilding:
|
Video encoding on the kiosk is tunable per device, without rebuilding:
|
||||||
|
|
||||||
|
|||||||
Executable
+153
@@ -0,0 +1,153 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SUBNET=""
|
||||||
|
OPEN_TO_ALL="no"
|
||||||
|
DRY_RUN="no"
|
||||||
|
|
||||||
|
log() { printf '\033[1;34m==>\033[0m %s\n' "$*"; }
|
||||||
|
warn() { printf '\033[1;33m==>\033[0m %s\n' "$*" >&2; }
|
||||||
|
fail() { printf '\033[1;31m==>\033[0m %s\n' "$*" >&2; exit 1; }
|
||||||
|
|
||||||
|
usage() {
|
||||||
|
cat <<'USAGE'
|
||||||
|
PiStation host firewall setup
|
||||||
|
|
||||||
|
sudo ./infra/firewall.sh [options]
|
||||||
|
|
||||||
|
Opens the ports LiveKit needs. The web and API containers publish their ports
|
||||||
|
through Docker, which writes its own rules and is not affected by the host
|
||||||
|
firewall. LiveKit runs on the host network, so it is.
|
||||||
|
|
||||||
|
Options
|
||||||
|
--subnet <cidr> Allow only this network, defaults to the LAN behind your default route
|
||||||
|
--open-to-all Allow from any address instead of a single network
|
||||||
|
--dry-run Print the commands without running them
|
||||||
|
--help Show this message
|
||||||
|
USAGE
|
||||||
|
}
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--subnet) SUBNET="${2:-}"; shift 2 ;;
|
||||||
|
--subnet=*) SUBNET="${1#*=}"; shift ;;
|
||||||
|
--open-to-all) OPEN_TO_ALL="yes"; shift ;;
|
||||||
|
--dry-run) DRY_RUN="yes"; shift ;;
|
||||||
|
--help|-h) usage; exit 0 ;;
|
||||||
|
*) fail "unknown option: $1" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
PORTS_TCP=(7880 7881)
|
||||||
|
PORTS_UDP=(3478)
|
||||||
|
RANGE_UDP_START=50000
|
||||||
|
RANGE_UDP_END=50100
|
||||||
|
|
||||||
|
detect_subnet() {
|
||||||
|
local interface
|
||||||
|
interface="$(ip route show default 2>/dev/null | awk '/default/ {print $5; exit}')"
|
||||||
|
[[ -n "$interface" ]] || return 1
|
||||||
|
ip -o -f inet route show scope link dev "$interface" 2>/dev/null | awk '{print $1; exit}'
|
||||||
|
}
|
||||||
|
|
||||||
|
run() {
|
||||||
|
if [[ "$DRY_RUN" == "yes" ]]; then
|
||||||
|
printf ' %s\n' "$*"
|
||||||
|
else
|
||||||
|
"$@" >/dev/null
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
if [[ "$OPEN_TO_ALL" == "no" && -z "$SUBNET" ]]; then
|
||||||
|
SUBNET="$(detect_subnet || true)"
|
||||||
|
[[ -n "$SUBNET" ]] || fail "could not work out your LAN subnet, pass --subnet or --open-to-all"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$DRY_RUN" == "no" && $EUID -ne 0 ]]; then
|
||||||
|
fail "run this with sudo, or pass --dry-run to see what it would do"
|
||||||
|
fi
|
||||||
|
|
||||||
|
apply_ufw() {
|
||||||
|
if [[ "$OPEN_TO_ALL" == "yes" ]]; then
|
||||||
|
for port in "${PORTS_TCP[@]}"; do
|
||||||
|
run ufw allow "${port}/tcp"
|
||||||
|
done
|
||||||
|
for port in "${PORTS_UDP[@]}"; do
|
||||||
|
run ufw allow "${port}/udp"
|
||||||
|
done
|
||||||
|
run ufw allow "${RANGE_UDP_START}:${RANGE_UDP_END}/udp"
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
|
||||||
|
for port in "${PORTS_TCP[@]}"; do
|
||||||
|
run ufw allow from "$SUBNET" to any port "$port" proto tcp
|
||||||
|
done
|
||||||
|
for port in "${PORTS_UDP[@]}"; do
|
||||||
|
run ufw allow from "$SUBNET" to any port "$port" proto udp
|
||||||
|
done
|
||||||
|
run ufw allow from "$SUBNET" to any port "${RANGE_UDP_START}:${RANGE_UDP_END}" proto udp
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_firewalld() {
|
||||||
|
if [[ "$OPEN_TO_ALL" == "yes" ]]; then
|
||||||
|
for port in "${PORTS_TCP[@]}"; do
|
||||||
|
run firewall-cmd --permanent "--add-port=${port}/tcp"
|
||||||
|
done
|
||||||
|
for port in "${PORTS_UDP[@]}"; do
|
||||||
|
run firewall-cmd --permanent "--add-port=${port}/udp"
|
||||||
|
done
|
||||||
|
run firewall-cmd --permanent "--add-port=${RANGE_UDP_START}-${RANGE_UDP_END}/udp"
|
||||||
|
else
|
||||||
|
for port in "${PORTS_TCP[@]}"; do
|
||||||
|
run firewall-cmd --permanent "--add-rich-rule=rule family=ipv4 source address=${SUBNET} port port=${port} protocol=tcp accept"
|
||||||
|
done
|
||||||
|
for port in "${PORTS_UDP[@]}"; do
|
||||||
|
run firewall-cmd --permanent "--add-rich-rule=rule family=ipv4 source address=${SUBNET} port port=${port} protocol=udp accept"
|
||||||
|
done
|
||||||
|
run firewall-cmd --permanent "--add-rich-rule=rule family=ipv4 source address=${SUBNET} port port=${RANGE_UDP_START}-${RANGE_UDP_END} protocol=udp accept"
|
||||||
|
fi
|
||||||
|
run firewall-cmd --reload
|
||||||
|
}
|
||||||
|
|
||||||
|
print_manual() {
|
||||||
|
cat <<MANUAL
|
||||||
|
No supported firewall front end was found. If you are running nftables or
|
||||||
|
iptables directly, allow these from ${SUBNET:-any address}:
|
||||||
|
|
||||||
|
tcp ${PORTS_TCP[*]}
|
||||||
|
udp ${PORTS_UDP[*]}
|
||||||
|
udp ${RANGE_UDP_START}-${RANGE_UDP_END}
|
||||||
|
MANUAL
|
||||||
|
}
|
||||||
|
|
||||||
|
if command -v ufw >/dev/null 2>&1; then
|
||||||
|
BACKEND="ufw"
|
||||||
|
elif command -v firewall-cmd >/dev/null 2>&1; then
|
||||||
|
BACKEND="firewalld"
|
||||||
|
else
|
||||||
|
BACKEND="none"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$BACKEND" == "ufw" && $EUID -eq 0 ]] && ! ufw status 2>/dev/null | grep -q "^Status: active"; then
|
||||||
|
warn "ufw is installed but not active, the rules will apply once you run: ufw enable"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [[ "$OPEN_TO_ALL" == "yes" ]]; then
|
||||||
|
log "opening LiveKit ports to any address"
|
||||||
|
else
|
||||||
|
log "opening LiveKit ports to $SUBNET"
|
||||||
|
fi
|
||||||
|
|
||||||
|
case "$BACKEND" in
|
||||||
|
ufw) apply_ufw ;;
|
||||||
|
firewalld) apply_firewalld ;;
|
||||||
|
none) print_manual; exit 0 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
log "done via $BACKEND"
|
||||||
|
echo
|
||||||
|
echo " tcp 7880 LiveKit signalling, the websocket browsers connect to"
|
||||||
|
echo " tcp 7881 LiveKit media over TCP, used where UDP is blocked"
|
||||||
|
echo " udp 3478 TURN relay"
|
||||||
|
echo " udp 50000-50100 LiveKit media, the normal path for audio and video"
|
||||||
|
echo
|
||||||
Reference in New Issue
Block a user