Files
uPortal/docs/jitsi-nginx-config.md
T
2026-10-15 23:25:46 +03:00

248 lines
8.1 KiB
Markdown

# Jitsi & Nginx Deployment Configuration
## Overview
This document covers the configuration required to deploy **uPortal** alongside a **Docker-based Jitsi Meet** instance behind **Nginx**.
---
## Environment Variables (.env)
These are the variables used by uPortal for Jitsi integration. Copy them into your `.env` file:
| Variable | Required | Default | Purpose |
|----------|----------|---------|---------|
| `JITSI_SECRET` | **Yes** | — | HMAC-SHA256 shared secret for signing JWTs. Must match the secret configured in Jitsi's prosody (`JWT_APP_SECRET`). Generate with: `openssl rand -hex 32` |
| `JITSI_APP_ID` | **Yes** | — | Jitsi App ID. Used as the `iss` (issuer) and `aud` (audience) in JWTs. Match this to `JWT_APP_ID` in Jitsi's prosody config. |
| `JITSI_DOMAIN` | No | `meet.jit.si` | Your Jitsi Meet instance FQDN (e.g. `meet.example.com`). |
| `SITE_DOMAIN` | No | — | The public domain of the portal (e.g. `portal.example.com`). Required for absolute avatar URLs in JWT context — otherwise Jitsi cannot fetch avatars. |
| `TRUSTED_DOMAIINS` | No | — | Comma-separated domains that bypass the external-redirect warning. Add your Jitsi domain here to skip the interstitial page. |
> **Note on `TRUSTED_DOMAIINS`**: The variable name contains a typo (double "I") — this matches the codebase, so keep it as-is.
### Minimal .env for Jitsi
```
JITSI_SECRET=your-64-char-hex-secret-here
JITSI_APP_ID=my_uportal_app
JITSI_DOMAIN=meet.example.com
SITE_DOMAIN=portal.example.com
TRUSTED_DOMAIINS=meet.example.com
```
---
## Docker-Based Jitsi Meet Setup
### 1. Prerequisites
- Docker and Docker Compose installed
- A server with a public IP and domain names pointing to it
- Ports 80 and 443 accessible (or behind Nginx)
### 2. Clone Jitsi Meet Docker
```bash
git clone https://github.com/jitsi/docker-jitsi-meet.git
cd docker-jitsi-meet
cp env.example .env
```
### 3. Configure Jitsi `.env`
Edit the Jitsi `.env` file. The critical settings for JWT authentication:
```bash
# --- Domain / TLS ---
CONFIG=~/.jitsi-meet-cfg
HTTP_PORT=8000
HTTPS_PORT=8443
# --- JWT Authentication (REQUIRED for uPortal integration) ---
ENABLE_AUTH=1
AUTH_TYPE=jwt
JWT_APP_ID=my_uportal_app # Must match uPortal's JITSI_APP_ID
JWT_APP_SECRET=your-64-char-hex-secret-here # Must match uPortal's JITSI_SECRET
JWT_ACCEPTED_ISSUERS=my_uportal_app
JWT_ACCEPTED_AUDIENCES=my_uportal_app
JWT_ASAP_KEYSERVER=
JWT_ALLOW_EMPTY=false
# --- Optional: Disable guest access ---
# JWT_AUTH_MODULE=jwt
```
### 4. Start Jitsi Meet
```bash
docker compose up -d
```
This starts the full Jitsi stack: prosody (XMPP), jicofo (focus), jvb (video bridge), and web (nginx frontend).
---
## Nginx Reverse Proxy Configuration
### Sample `/etc/nginx/sites-available/uportal`
```nginx
server {
listen 80;
server_name portal.example.com meet.example.com;
return 301 https://$server_name$request_uri;
}
# uPortal
server {
listen 443 ssl http2;
server_name portal.example.com;
ssl_certificate /etc/letsencrypt/live/portal.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/portal.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8080;
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 $scheme;
}
location /static/ {
proxy_pass http://127.0.0.1:8080;
expires 7d;
add_header Cache-Control "public, immutable";
}
}
# Jitsi Meet
server {
listen 443 ssl http2;
server_name meet.example.com;
ssl_certificate /etc/letsencrypt/live/meet.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/meet.example.com/privkey.pem;
location / {
proxy_pass http://127.0.0.1:8000; # Jitsi HTTP port
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 $scheme;
# WebSocket support (required for Jitsi)
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
# Large file uploads (recording, streaming)
client_max_body_size 10M;
client_body_buffer_size 128k;
}
# Long-polling for Jitsi
location ~ ^/(xmpp|http-bind) {
proxy_pass http://127.0.0.1:8000;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
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 $scheme;
proxy_read_timeout 300s;
proxy_send_timeout 300s;
}
}
```
### Key Points
- **uPortal** runs on `127.0.0.1:8080` (use `docker compose run -p 127.0.0.1:8080:8080` or systemd service)
- **Jitsi** HTTP port is `8000` (from Jitsi's `HTTP_PORT=8000`), proxied on a different subdomain
- **WebSocket** support is essential for Jitsi video/audio — the `Upgrade` and `Connection` headers must be forwarded
- Both services can share the same Nginx instance with different `server_name` blocks
---
## Docker Compose for uPortal
The project includes [`docker-compose.yml`](../docker-compose.yml) and [`Dockerfile`](../Dockerfile) for containerized deployment:
```yaml
version: '3.8'
services:
uportal:
build:
context: .
dockerfile: Dockerfile
container_name: uportal
ports:
- "127.0.0.1:8080:8080" # Bind to loopback behind Nginx
volumes:
- ./data:/app # Persist SQLite database locally
environment:
LOG_LEVEL: info
JITSI_SECRET: your-64-char-hex-secret-here
JITSI_APP_ID: my_uportal_app
JITSI_DOMAIN: meet.example.com
SITE_DOMAIN: portal.example.com
TRUSTED_DOMAIINS: meet.example.com
restart: unless-stopped
```
> Bind to `127.0.0.1:8080` (not `0.0.0.0:8080`) when running behind Nginx on the same host to avoid direct external access.
---
## Architecture Diagram
```
Internet
│
├── portal.example.com:443 ──┐
│ Nginx (reverse proxy) │
│ ├── / → uPortal :8080 │
│ └── /static/ → uPortal │
│ │
└── meet.example.com:443 ────┤
Nginx (reverse proxy) │
├── / → Jitsi Web :8000 │
├── /xmpp → Jitsi │
└── /http-bind → Jitsi │
│
Docker Network │
├── uPortal container │
│ ├── uportal binary │
│ ├── uportal.db (SQLite) │
│ ├── templates/ │
│ └── static/ │
│ │
└── jitsi-docker stack │
├── prosody (XMPP) │
├── jicofo (focus) │
├── jvb (video bridge) │
└── web (nginx front) │
│
┌──────────────────────────────┘
│
JWT Signing: uPortal signs JWT with JITSI_SECRET
→ Redirects user to meet.example.com/room?jwt=...
→ Jitsi verifies JWT with matching JWT_APP_SECRET
```
---
## Troubleshooting
| Symptom | Likely Cause | Fix |
|---------|-------------|-----|
| "Jitsi secret is not configured" | `JITSI_SECRET` not set in uPortal `.env` | Set the variable |
| "Jitsi app id is not configured" | `JITSI_APP_ID` not set | Set the variable |
| "Authentication failed" in Jitsi | JWT secret mismatch between uPortal and Jitsi | Verify `JITSI_SECRET` = `JWT_APP_SECRET` |
| "Invalid issuer" in Jitsi logs | `JITSI_APP_ID` ≠ `JWT_APP_ID` | Make them match |
| Avatar not showing in Jitsi | `SITE_DOMAIN` not set, or avatar missing | Set `SITE_DOMAIN` to the portal's public URL |
| External redirect warning page appears | Jitsi domain not in `TRUSTED_DOMAIINS` | Add the domain |