mirror of
https://github.com/samjage/matrix.git
synced 2026-06-06 02:40:43 +00:00
first pass
This commit is contained in:
@@ -0,0 +1,58 @@
|
|||||||
|
# ============================================================
|
||||||
|
# Matrix Stack — Environment Configuration
|
||||||
|
# Copy this file to .env and fill in all values
|
||||||
|
# cp .env.example .env
|
||||||
|
# ============================================================
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# DOMAIN
|
||||||
|
# Your base domain. matrix.DOMAIN_NAME and livekit.DOMAIN_NAME
|
||||||
|
# will be derived from this automatically.
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
DOMAIN_NAME=
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# SERVER
|
||||||
|
# The public IPv4 address of this server.
|
||||||
|
# Used by LiveKit and coturn to advertise themselves externally.
|
||||||
|
# curl -4 ifconfig.me
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
EXTERNAL_IP=
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# TURN SECRET
|
||||||
|
# Shared secret between coturn and tuwunel for TURN auth.
|
||||||
|
# Must match on both sides — do not set them separately.
|
||||||
|
# openssl rand -hex 32
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
STATIC_AUTH_SECRET=
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# LIVEKIT API CREDENTIALS
|
||||||
|
# Key/secret pair for LiveKit. Used by the server, lk-jwt-service,
|
||||||
|
# and must match the LIVEKIT_KEYS entry in livekit.yaml if set there.
|
||||||
|
# API_KEY: openssl rand -hex 16
|
||||||
|
# API_SECRET: openssl rand -hex 32
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
API_KEY=
|
||||||
|
API_SECRET=
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# DOCKER NETWORK
|
||||||
|
# The external Docker network shared across your stacks.
|
||||||
|
# Create it first if it doesn't exist: docker network create <name>
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
NETWORK_NAME=backbone
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
# IMAGE TAG
|
||||||
|
# Docker image tag to deploy. Use 'latest' for development.
|
||||||
|
# Set to a specific SHA or version tag for production stability.
|
||||||
|
# ------------------------------------------------------------
|
||||||
|
GIT_SHA=latest
|
||||||
Executable
+5
@@ -0,0 +1,5 @@
|
|||||||
|
.env
|
||||||
|
coturn/certs/
|
||||||
|
*.pem
|
||||||
|
*.key
|
||||||
|
rotation.log
|
||||||
@@ -0,0 +1,305 @@
|
|||||||
|
# 🧩 Matrix Stack
|
||||||
|
|
||||||
|
> Self-hosted Matrix homeserver with native voice & video calling, TURN relay, and LiveKit WebRTC integration.
|
||||||
|
|
||||||
|
Built on **[Tuwunel](https://github.com/jevolk/tuwunel)** — a fast, modern Matrix homeserver — with full [MSC4143](https://github.com/matrix-org/matrix-spec-proposals/pull/4143) support for in-room calls via LiveKit.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📦 Services
|
||||||
|
|
||||||
|
| Container | Image | Role |
|
||||||
|
|---|---|---|
|
||||||
|
| `tuwunel` | `jevolk/tuwunel` | Matrix homeserver |
|
||||||
|
| `livekit` | `livekit/livekit-server` | WebRTC SFU — voice & video |
|
||||||
|
| `lk-jwt-service` | `ghcr.io/element-hq/lk-jwt-service` | Issues LiveKit tokens to Matrix clients |
|
||||||
|
| `coturn` | `coturn/coturn` | TURN/STUN relay for NAT traversal |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Configuration
|
||||||
|
|
||||||
|
### 🔑 Environment variables
|
||||||
|
|
||||||
|
Copy `.env.example` to `.env` and fill in all values before starting the stack.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cp .env.example .env
|
||||||
|
```
|
||||||
|
|
||||||
|
| Variable | Description |
|
||||||
|
|---|---|
|
||||||
|
| `DOMAIN_NAME` | Your base domain — e.g. `example.com` |
|
||||||
|
| `EXTERNAL_IP` | Your server's public IPv4 address |
|
||||||
|
| `STATIC_AUTH_SECRET` | Shared TURN secret between coturn and tuwunel |
|
||||||
|
| `API_KEY` | LiveKit API key |
|
||||||
|
| `API_SECRET` | LiveKit API secret |
|
||||||
|
| `NETWORK_NAME` | External Docker network shared across stacks — default `backbone` |
|
||||||
|
| `GIT_SHA` | Image tag to deploy — use `latest` for development |
|
||||||
|
|
||||||
|
**Generating secrets:**
|
||||||
|
|
||||||
|
| Variable | Command |
|
||||||
|
|---|---|
|
||||||
|
| `STATIC_AUTH_SECRET` | `openssl rand -hex 32` |
|
||||||
|
| `API_SECRET` | `openssl rand -hex 32` |
|
||||||
|
| `API_KEY` | `openssl rand -hex 16` |
|
||||||
|
| `EXTERNAL_IP` | `curl -4 ifconfig.me` |
|
||||||
|
|
||||||
|
> ⚠️ `.env` is gitignored and must **never** be committed.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🔁 Secret rotation
|
||||||
|
|
||||||
|
`scripts/rotate-secrets.sh` regenerates secrets in `.env` and restarts only the affected containers.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bash scripts/rotate-secrets.sh # rotate everything
|
||||||
|
bash scripts/rotate-secrets.sh --turn # TURN secret only (restarts coturn + tuwunel)
|
||||||
|
bash scripts/rotate-secrets.sh --livekit # LiveKit keys only (restarts livekit + lk-jwt-service)
|
||||||
|
```
|
||||||
|
|
||||||
|
> ⚠️ Active calls and sessions will be dropped on rotation. Run during a maintenance window.
|
||||||
|
|
||||||
|
Rotation events are logged to `scripts/rotation.log` (gitignored).
|
||||||
|
|
||||||
|
**To run on a schedule** — add a cron entry on the host:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
crontab -e
|
||||||
|
```
|
||||||
|
|
||||||
|
```cron
|
||||||
|
# Rotate all Matrix secrets every 90 days at 3am
|
||||||
|
0 3 1 */3 * cd /opt/stacks/matrix && bash scripts/rotate-secrets.sh --all >> /var/log/matrix-rotation.log 2>&1
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📝 Variable substitution — what works where
|
||||||
|
|
||||||
|
Docker Compose substitutes `${VAR}` **only inside `docker-compose.yml`**. Config files mounted as volumes are read as plain text — variables are not expanded inside them.
|
||||||
|
|
||||||
|
| File | Substitution | How secrets get in |
|
||||||
|
|---|---|---|
|
||||||
|
| `docker-compose.yml` | ✅ Yes | Directly from `.env` |
|
||||||
|
| `tuwunel/tuwunel.toml` | ❌ No | `server_name` is a required field — update it to your domain. `TUWUNEL_SERVER_NAME` in compose overrides it at runtime but the field must be present |
|
||||||
|
| `coturn/turnserver.conf` | ❌ No | Secret and realm passed as CLI args in compose |
|
||||||
|
| `livekit/livekit.yaml` | ❌ No | API keys passed via `LIVEKIT_KEYS` env var in compose |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Deployment
|
||||||
|
|
||||||
|
### Prerequisites
|
||||||
|
|
||||||
|
Both of these must exist before the stack will start — Docker will refuse to create containers otherwise.
|
||||||
|
|
||||||
|
**1. Create your Docker network** (set `NETWORK_NAME` in `.env` to match):
|
||||||
|
```bash
|
||||||
|
docker network create backbone
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Create the tuwunel data volume:**
|
||||||
|
```bash
|
||||||
|
docker volume create matrix_tuwunel-data
|
||||||
|
```
|
||||||
|
|
||||||
|
### Start the stack
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔀 Reverse Proxy
|
||||||
|
|
||||||
|
This stack does not run its own Caddy instance. Routing is handled by a shared Caddy container on the `backbone` Docker network. Add the following blocks to your Caddyfile.
|
||||||
|
|
||||||
|
> 💡 **Always use `handle` blocks.** Do not mix bare `respond` directives with a `handle {}` catch-all in the same site block — Caddy's `handle {}` will intercept all requests before a bare `respond` can fire.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🌐 Base domain — client & server discovery
|
||||||
|
|
||||||
|
Serves the `.well-known/matrix` endpoints that tell clients where the homeserver and LiveKit service are. Required for login and calling to work.
|
||||||
|
|
||||||
|
```caddy
|
||||||
|
your.domain {
|
||||||
|
handle /.well-known/matrix/server {
|
||||||
|
header Content-Type application/json
|
||||||
|
header Access-Control-Allow-Origin *
|
||||||
|
respond `{"m.server":"matrix.your.domain:443"}`
|
||||||
|
}
|
||||||
|
handle /.well-known/matrix/client {
|
||||||
|
header Content-Type application/json
|
||||||
|
header Access-Control-Allow-Origin *
|
||||||
|
respond `{
|
||||||
|
"m.homeserver": {
|
||||||
|
"base_url": "https://matrix.your.domain"
|
||||||
|
},
|
||||||
|
"org.matrix.msc4143.rtc_foci": [
|
||||||
|
{
|
||||||
|
"type": "livekit",
|
||||||
|
"livekit_service_url": "https://livekit.your.domain"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}`
|
||||||
|
}
|
||||||
|
handle {
|
||||||
|
respond 404
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🏠 Matrix homeserver
|
||||||
|
|
||||||
|
Routes Matrix API traffic to tuwunel. WebSocket headers are required for client sync connections.
|
||||||
|
|
||||||
|
```caddy
|
||||||
|
matrix.your.domain {
|
||||||
|
handle /_version {
|
||||||
|
respond `{"sha":"{env.GIT_SHA}"}`
|
||||||
|
}
|
||||||
|
handle /_matrix/* {
|
||||||
|
reverse_proxy tuwunel:6167 {
|
||||||
|
header_up Upgrade {http.request.header.Upgrade}
|
||||||
|
header_up Connection {http.request.header.Connection}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handle /_synapse/* {
|
||||||
|
reverse_proxy tuwunel:6167 {
|
||||||
|
header_up Upgrade {http.request.header.Upgrade}
|
||||||
|
header_up Connection {http.request.header.Connection}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
handle {
|
||||||
|
respond 404
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 🔗 Federation — port 8448
|
||||||
|
|
||||||
|
Handles server-to-server federation traffic. Requires port `8448` exposed on your Caddy container.
|
||||||
|
|
||||||
|
```caddy
|
||||||
|
matrix.your.domain:8448 {
|
||||||
|
reverse_proxy tuwunel:6167
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
# caddy docker-compose.yml
|
||||||
|
ports:
|
||||||
|
- "8448:8448"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📹 LiveKit
|
||||||
|
|
||||||
|
Routes JWT token requests to `lk-jwt-service` and all WebRTC traffic to the LiveKit server.
|
||||||
|
|
||||||
|
LiveKit runs with `network_mode: host` and is **not** on the Docker network — it is reached via `host.docker.internal`. This requires the following in your Caddy compose:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
extra_hosts:
|
||||||
|
- "host.docker.internal:host-gateway"
|
||||||
|
```
|
||||||
|
|
||||||
|
```caddy
|
||||||
|
livekit.your.domain {
|
||||||
|
handle /sfu/* {
|
||||||
|
reverse_proxy lk-jwt-service:8080
|
||||||
|
}
|
||||||
|
handle /livekit/jwt* {
|
||||||
|
reverse_proxy lk-jwt-service:8080
|
||||||
|
}
|
||||||
|
handle {
|
||||||
|
reverse_proxy host.docker.internal:7880 {
|
||||||
|
header_up Upgrade {http.request.header.Upgrade}
|
||||||
|
header_up Connection {http.request.header.Connection}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
> ⚠️ WebSocket headers on the LiveKit catch-all are **required**. Omitting them causes `MISSING_MATRIX_RTC_TRANSPORT` errors on clients.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📋 Network topology
|
||||||
|
|
||||||
|
```
|
||||||
|
┌─────────────────────────────┐
|
||||||
|
│ backbone network │
|
||||||
|
│ │
|
||||||
|
Client ──HTTPS──▶ │ Caddy ──▶ tuwunel:6167 │
|
||||||
|
│ ──▶ lk-jwt-service │
|
||||||
|
│ ──▶ host.docker.internal:7880 (livekit)
|
||||||
|
└─────────────────────────────┘
|
||||||
|
|
||||||
|
host network (UDP): livekit (ports 7880, 7881, 50000-60000)
|
||||||
|
coturn (ports 3478, 5349, 49152-65535)
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 📁 Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
.
|
||||||
|
├── tuwunel/
|
||||||
|
│ └── tuwunel.toml # Homeserver config — hardcode server_name here
|
||||||
|
├── livekit/
|
||||||
|
│ └── livekit.yaml # LiveKit ports and RTC config
|
||||||
|
├── coturn/
|
||||||
|
│ ├── turnserver.conf # TURN config — no secrets, those go in compose
|
||||||
|
│ └── certs/ # Drop fullchain.pem + privkey.pem here to enable TURNS (gitignored)
|
||||||
|
├── docker-compose.yml
|
||||||
|
├── .env # Secrets — gitignored, never commit
|
||||||
|
└── .env.example # Template — safe to commit
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔒 Security notes
|
||||||
|
|
||||||
|
- `coturn` blocks RFC1918 ranges in `turnserver.conf` to prevent TURN relay abuse
|
||||||
|
- Matrix registration is disabled by default in `tuwunel/tuwunel.toml`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Enabling TURNS (TURN over TLS)
|
||||||
|
|
||||||
|
TURNS encrypts relay traffic on port `5349`. The stack is pre-wired for it — `coturn/certs/` is already mounted into the container and `TUWUNEL_TURN_URIS` already advertises the `turns:` URI to clients. It just needs certs and the config uncommented.
|
||||||
|
|
||||||
|
**To enable:**
|
||||||
|
|
||||||
|
1. Drop your certificates into `coturn/certs/`:
|
||||||
|
```
|
||||||
|
coturn/certs/fullchain.pem
|
||||||
|
coturn/certs/privkey.pem
|
||||||
|
```
|
||||||
|
Certs must be valid for `turn.DOMAIN_NAME`. If you have a wildcard cert (`*.yourdomain.com`) it will work as-is.
|
||||||
|
|
||||||
|
2. Uncomment the TLS lines in `coturn/turnserver.conf`:
|
||||||
|
```
|
||||||
|
tls-listening-port=5349
|
||||||
|
cert=/etc/coturn/certs/fullchain.pem
|
||||||
|
pkey=/etc/coturn/certs/privkey.pem
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Restart coturn:
|
||||||
|
```bash
|
||||||
|
docker compose up -d coturn
|
||||||
|
```
|
||||||
|
|
||||||
|
> `coturn/certs/` is gitignored — your private key will never be committed.
|
||||||
Executable
+21
@@ -0,0 +1,21 @@
|
|||||||
|
use-auth-secret
|
||||||
|
|
||||||
|
listening-port=3478
|
||||||
|
# tls-listening-port=5349
|
||||||
|
# cert=/etc/coturn/certs/fullchain.pem
|
||||||
|
# pkey=/etc/coturn/certs/privkey.pem
|
||||||
|
|
||||||
|
min-port=49152
|
||||||
|
max-port=65535
|
||||||
|
|
||||||
|
verbose
|
||||||
|
fingerprint
|
||||||
|
no-multicast-peers
|
||||||
|
|
||||||
|
# Block RFC1918 ranges to prevent TURN relay abuse
|
||||||
|
denied-peer-ip=10.0.0.0-10.255.255.255
|
||||||
|
denied-peer-ip=192.168.0.0-192.168.255.255
|
||||||
|
denied-peer-ip=172.16.0.0-172.31.255.255
|
||||||
|
|
||||||
|
# static-auth-secret and realm are passed via CLI args in docker-compose.yml
|
||||||
|
# Do not put secrets in this file — it does not support variable substitution
|
||||||
Executable
+65
@@ -0,0 +1,65 @@
|
|||||||
|
services:
|
||||||
|
tuwunel:
|
||||||
|
image: jevolk/tuwunel:latest
|
||||||
|
container_name: tuwunel
|
||||||
|
restart: unless-stopped
|
||||||
|
networks:
|
||||||
|
- backbone
|
||||||
|
environment:
|
||||||
|
TUWUNEL_CONFIG: /etc/tuwunel/tuwunel.toml
|
||||||
|
TUWUNEL_SERVER_NAME: ${DOMAIN_NAME}
|
||||||
|
TUWUNEL_TURN_URIS: '["turn:turn.${DOMAIN_NAME}:3478?transport=udp","turn:turn.${DOMAIN_NAME}:3478?transport=tcp","turns:turn.${DOMAIN_NAME}:5349?transport=tcp"]'
|
||||||
|
TUWUNEL_TURN_SECRET: ${STATIC_AUTH_SECRET}
|
||||||
|
volumes:
|
||||||
|
- matrix_tuwunel-data:/var/lib/tuwunel
|
||||||
|
- ./tuwunel/tuwunel.toml:/etc/tuwunel/tuwunel.toml:ro
|
||||||
|
coturn:
|
||||||
|
image: coturn/coturn:latest
|
||||||
|
container_name: coturn
|
||||||
|
restart: unless-stopped
|
||||||
|
network_mode: host
|
||||||
|
volumes:
|
||||||
|
- ./coturn/turnserver.conf:/etc/coturn/turnserver.conf:ro
|
||||||
|
- ./coturn/certs:/etc/coturn/certs:ro
|
||||||
|
command:
|
||||||
|
- -c
|
||||||
|
- /etc/coturn/turnserver.conf
|
||||||
|
- --static-auth-secret=${STATIC_AUTH_SECRET}
|
||||||
|
- --external-ip=${EXTERNAL_IP}
|
||||||
|
- --realm=turn.${DOMAIN_NAME}
|
||||||
|
|
||||||
|
livekit:
|
||||||
|
image: livekit/livekit-server:latest
|
||||||
|
container_name: livekit
|
||||||
|
restart: unless-stopped
|
||||||
|
network_mode: host
|
||||||
|
volumes:
|
||||||
|
- ./livekit/livekit.yaml:/etc/livekit.yaml:ro
|
||||||
|
command:
|
||||||
|
- --config
|
||||||
|
- /etc/livekit.yaml
|
||||||
|
- --node-ip=${EXTERNAL_IP}
|
||||||
|
environment:
|
||||||
|
LIVEKIT_KEYS: "${API_KEY}: ${API_SECRET}"
|
||||||
|
|
||||||
|
lk-jwt-service:
|
||||||
|
image: ghcr.io/element-hq/lk-jwt-service:latest
|
||||||
|
container_name: lk-jwt-service
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
LIVEKIT_URL: wss://livekit.${DOMAIN_NAME}
|
||||||
|
LIVEKIT_KEY: ${API_KEY}
|
||||||
|
LIVEKIT_SECRET: ${API_SECRET}
|
||||||
|
LIVEKIT_FULL_ACCESS_HOMESERVERS: ${DOMAIN_NAME}
|
||||||
|
networks:
|
||||||
|
- backbone
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
matrix_tuwunel-data:
|
||||||
|
external: true
|
||||||
|
name: matrix_tuwunel-data
|
||||||
|
|
||||||
|
networks:
|
||||||
|
backbone:
|
||||||
|
external: true
|
||||||
|
name: ${NETWORK_NAME}
|
||||||
Executable
+8
@@ -0,0 +1,8 @@
|
|||||||
|
port: 7880
|
||||||
|
rtc:
|
||||||
|
tcp_port: 7881
|
||||||
|
port_range_start: 50000
|
||||||
|
port_range_end: 60000
|
||||||
|
use_external_ip: true
|
||||||
|
logging:
|
||||||
|
level: info
|
||||||
@@ -0,0 +1,35 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================
|
||||||
|
# check-image-updates.sh
|
||||||
|
# Checks all running containers for newer images and caches
|
||||||
|
# results to /tmp/.docker-updates for the MOTD to display.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# bash scripts/check-image-updates.sh
|
||||||
|
#
|
||||||
|
# Add to cron for automatic checks (e.g. daily at 3am):
|
||||||
|
# 0 3 * * * bash /opt/stacks/matrix/scripts/check-image-updates.sh
|
||||||
|
# =============================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
CACHE="/tmp/.docker-updates"
|
||||||
|
TMP=$(mktemp)
|
||||||
|
|
||||||
|
echo "# generated $(date -u +"%Y-%m-%dT%H:%M:%SZ")" > "$TMP"
|
||||||
|
|
||||||
|
while IFS='|' read -r name image; do
|
||||||
|
OUTPUT=$(docker pull "$image" 2>&1 || true)
|
||||||
|
if echo "$OUTPUT" | grep -q "Downloaded newer image"; then
|
||||||
|
echo "$name → $image" >> "$TMP"
|
||||||
|
fi
|
||||||
|
done < <(docker ps --format '{{.Names}}|{{.Image}}' | sort)
|
||||||
|
|
||||||
|
mv "$TMP" "$CACHE"
|
||||||
|
|
||||||
|
UPDATES=$(grep -v '^#' "$CACHE" | grep -c . || true)
|
||||||
|
if [ "$UPDATES" -gt 0 ]; then
|
||||||
|
echo "✅ $UPDATES image(s) updated — cache written to $CACHE"
|
||||||
|
else
|
||||||
|
echo "✅ All images current — cache written to $CACHE"
|
||||||
|
fi
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# =============================================================
|
||||||
|
# rotate-secrets.sh
|
||||||
|
# Regenerates TURN and LiveKit secrets in .env and restarts
|
||||||
|
# only the affected containers.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# bash scripts/rotate-secrets.sh # rotate all
|
||||||
|
# bash scripts/rotate-secrets.sh --turn # rotate TURN secret only
|
||||||
|
# bash scripts/rotate-secrets.sh --livekit # rotate LiveKit keys only
|
||||||
|
#
|
||||||
|
# ⚠️ Active calls and sessions WILL be dropped on rotation.
|
||||||
|
# Run during a maintenance window or when the server is idle.
|
||||||
|
# =============================================================
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
ENV_FILE="$SCRIPT_DIR/../.env"
|
||||||
|
|
||||||
|
if [ ! -f "$ENV_FILE" ]; then
|
||||||
|
echo "❌ .env not found at $ENV_FILE"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
rotate_turn() {
|
||||||
|
echo "🔄 Rotating TURN secret..."
|
||||||
|
NEW_SECRET=$(openssl rand -hex 32)
|
||||||
|
sed -i "s|^STATIC_AUTH_SECRET=.*|STATIC_AUTH_SECRET=$NEW_SECRET|" "$ENV_FILE"
|
||||||
|
echo "✅ STATIC_AUTH_SECRET updated"
|
||||||
|
|
||||||
|
echo "🔁 Restarting coturn and tuwunel..."
|
||||||
|
docker compose --env-file "$ENV_FILE" -f "$SCRIPT_DIR/../docker-compose.yml" \
|
||||||
|
up -d --force-recreate coturn tuwunel
|
||||||
|
echo "✅ coturn and tuwunel restarted"
|
||||||
|
}
|
||||||
|
|
||||||
|
rotate_livekit() {
|
||||||
|
echo "🔄 Rotating LiveKit API credentials..."
|
||||||
|
NEW_KEY=$(openssl rand -hex 16)
|
||||||
|
NEW_SECRET=$(openssl rand -hex 32)
|
||||||
|
sed -i "s|^API_KEY=.*|API_KEY=$NEW_KEY|" "$ENV_FILE"
|
||||||
|
sed -i "s|^API_SECRET=.*|API_SECRET=$NEW_SECRET|" "$ENV_FILE"
|
||||||
|
echo "✅ API_KEY and API_SECRET updated"
|
||||||
|
|
||||||
|
echo "🔁 Restarting livekit and lk-jwt-service..."
|
||||||
|
docker compose --env-file "$ENV_FILE" -f "$SCRIPT_DIR/../docker-compose.yml" \
|
||||||
|
up -d --force-recreate livekit lk-jwt-service
|
||||||
|
echo "✅ livekit and lk-jwt-service restarted"
|
||||||
|
}
|
||||||
|
|
||||||
|
log_rotation() {
|
||||||
|
echo "📝 Logging rotation event..."
|
||||||
|
echo "[$(date -u +"%Y-%m-%dT%H:%M:%SZ")] $1" >> "$SCRIPT_DIR/../scripts/rotation.log"
|
||||||
|
}
|
||||||
|
|
||||||
|
case "${1:-all}" in
|
||||||
|
--turn)
|
||||||
|
rotate_turn
|
||||||
|
log_rotation "TURN secret rotated"
|
||||||
|
;;
|
||||||
|
--livekit)
|
||||||
|
rotate_livekit
|
||||||
|
log_rotation "LiveKit credentials rotated"
|
||||||
|
;;
|
||||||
|
all|--all)
|
||||||
|
rotate_turn
|
||||||
|
rotate_livekit
|
||||||
|
log_rotation "All secrets rotated"
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
echo "Usage: $0 [--turn | --livekit | --all]"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "🎉 Rotation complete. Previous secrets are gone — update any external clients if needed."
|
||||||
Executable
+21
@@ -0,0 +1,21 @@
|
|||||||
|
[global]
|
||||||
|
# This value is overridden at runtime by TUWUNEL_SERVER_NAME in docker-compose.yml,
|
||||||
|
# which pulls from DOMAIN_NAME in your .env. You do not need to edit this file.
|
||||||
|
server_name = "your.domain"
|
||||||
|
|
||||||
|
database_path = "/var/lib/tuwunel/db"
|
||||||
|
port = 6167
|
||||||
|
address = "0.0.0.0"
|
||||||
|
|
||||||
|
allow_registration = false
|
||||||
|
allow_federation = true
|
||||||
|
|
||||||
|
max_request_size = 20_000_000
|
||||||
|
trusted_servers = ["matrix.org"]
|
||||||
|
|
||||||
|
# Hardening
|
||||||
|
allow_inbound_profile_lookup_federation_requests = false
|
||||||
|
allow_device_name_federation = false
|
||||||
|
encryption_enabled_by_default_for_room_type = "all"
|
||||||
|
|
||||||
|
turn_ttl = 86400
|
||||||
Reference in New Issue
Block a user