diff --git a/.gitea/workflows/deploy.yml b/.gitea/workflows/deploy.yml index 48bda75..ad203c9 100644 --- a/.gitea/workflows/deploy.yml +++ b/.gitea/workflows/deploy.yml @@ -25,13 +25,13 @@ jobs: chmod 600 "$HOME/.kube/config" kubectl config current-context - # __PUBLIC_IP__ placeholder in jvb manifest needs the actual public - # IP of darkember. Inject from a repo secret so the manifest stays - # generic in git. - - name: Patch JVB public IP + # __PUBLIC_IP__ placeholder lives in JVB + coturn manifests — both + # advertise their public address so clients can reach them. Single + # sed pass over the directory keeps the secret out of git. + - name: Patch __PUBLIC_IP__ in manifests run: | test -n "${{ secrets.DARKEMBER_PUBLIC_IP }}" || (echo "secret DARKEMBER_PUBLIC_IP missing" && exit 1) - sed -i "s|__PUBLIC_IP__|${{ secrets.DARKEMBER_PUBLIC_IP }}|g" infra/k3s/60-jvb.yaml + sed -i "s|__PUBLIC_IP__|${{ secrets.DARKEMBER_PUBLIC_IP }}|g" infra/k3s/60-jvb.yaml infra/k3s/80-coturn.yaml - name: Apply manifests # 20-secrets.yaml is intentionally NOT applied — secret must be @@ -45,6 +45,7 @@ jobs: kubectl apply -f infra/k3s/50-web.yaml kubectl apply -f infra/k3s/60-jvb.yaml kubectl apply -f infra/k3s/70-ingress.yaml + kubectl apply -f infra/k3s/80-coturn.yaml # ConfigMap-only changes don't restart pods on their own, so a # deploy that just edits 10-config.yaml would otherwise leave the @@ -59,6 +60,7 @@ jobs: kubectl -n jitsi rollout status deployment/jicofo --timeout=3m kubectl -n jitsi rollout status deployment/jitsi-web --timeout=3m kubectl -n jitsi rollout status deployment/jvb --timeout=3m + kubectl -n jitsi rollout status deployment/coturn --timeout=3m - name: Smoke-check run: | diff --git a/infra/k3s/10-config.yaml b/infra/k3s/10-config.yaml index f4f105e..4514e5d 100644 --- a/infra/k3s/10-config.yaml +++ b/infra/k3s/10-config.yaml @@ -43,8 +43,17 @@ data: # === Videobridge brewery (where jicofo finds JVBs over XMPP) === JVB_BREWERY_MUC: "jvbbrewery" - # === STUN — default Jitsi-hosted STUN servers; ok for getting started === - JVB_STUN_SERVERS: "meet-jit-si-turnrelay.jitsi.net:443" + # === STUN/TURN — our own coturn (deploy 80-coturn.yaml). JVB itself + # uses STUN to discover its public-side mapping; clients additionally + # learn the TURN endpoints from Prosody via mod_external_services and + # fall back to relay when direct UDP doesn't reach JVB:10001 (typical + # for mobile-carrier NATs). === + JVB_STUN_SERVERS: "meet.it.financeflow.de:3478" + TURN_HOST: "meet.it.financeflow.de" + TURNS_HOST: "meet.it.financeflow.de" + TURN_PORT: "3478" + TURNS_PORT: "5349" + TURN_TRANSPORT: "udp,tcp" # === UX / lockdown === # Pre-join page on — gives joiners a chance to set audio/video before @@ -58,3 +67,12 @@ data: ENABLE_CLOSE_PAGE: "0" ENABLE_TRANSCRIPTIONS: "0" ENABLE_RECORDING: "0" + + # === Bandwidth defaults — keep things sane on mobile === + # Cap outgoing video at 480p so even slow connections can stream. + # Users on fat pipes can still manually bump it via the toolbar. + RESOLUTION: "480" + RESOLUTION_MIN: "180" + # In rooms with >5 people, new joiners start with video muted — + # saves bandwidth in larger team meetings, easy 1-click to enable. + START_VIDEO_MUTED: "5" diff --git a/infra/k3s/20-secrets.yaml.example b/infra/k3s/20-secrets.yaml.example index 5c26342..47b26f1 100644 --- a/infra/k3s/20-secrets.yaml.example +++ b/infra/k3s/20-secrets.yaml.example @@ -25,3 +25,7 @@ stringData: # kubectl -n embertime exec -it deploy/embertime-postgres -- \ # psql -U embertime -t -c "select meeting_jwt_secret from app_settings" JWT_APP_SECRET: "REPLACE_WITH_VALUE_FROM_EMBERTIME" + # HMAC secret shared between coturn and Prosody. Prosody mints + # time-limited TURN credentials; coturn validates with the same key. + # Generate fresh via generate-secrets.sh. + TURN_CREDENTIALS_SECRET: "REPLACE_WITH_32_RANDOM_CHARS" diff --git a/infra/k3s/80-coturn.yaml b/infra/k3s/80-coturn.yaml new file mode 100644 index 0000000..bb29331 --- /dev/null +++ b/infra/k3s/80-coturn.yaml @@ -0,0 +1,162 @@ +# coturn — STUN+TURN relay for clients whose network can't reach JVB +# directly. Hosted on the same node as JVB; hostNetwork: true so the +# TURN listening ports bind on the public NIC. The TLS cert is the +# same one cert-manager already issues for the jitsi-web Ingress — +# we mount the jitsi-tls Secret as a volume. Watch out: when the cert +# renews, this pod must be restarted to pick up the new file (the +# weekly CronJob below handles that). +# +# Auth model: HMAC time-limited credentials. coturn validates with +# `use-auth-secret` + `static-auth-secret=`; +# Prosody hands out matching credentials per session via +# mod_external_services. Both read the secret from the same k8s +# Secret entry so they stay in lockstep. +# +# Port plan (mirrors required FritzBox forwards): +# UDP 3478 STUN + TURN (cleartext) +# TCP 3478 TURN over TCP — first cleartext fallback +# UDP 5349 TURN over DTLS +# TCP 5349 TURN over TLS — works through most firewalls +# UDP 50000-50100 relay range — actual media flows on these +apiVersion: apps/v1 +kind: Deployment +metadata: + name: coturn + namespace: jitsi +spec: + replicas: 1 + strategy: + type: Recreate + selector: + matchLabels: { app: coturn } + template: + metadata: + labels: { app: coturn } + spec: + hostNetwork: true + dnsPolicy: ClusterFirstWithHostNet + containers: + - name: coturn + image: coturn/coturn:4.7-alpine + # Inline-template the config so we can interpolate the env-var + # secret without an extra ConfigMap-then-envsubst dance. + command: ["/bin/sh", "-c"] + args: + - | + set -eu + cat > /tmp/turnserver.conf <