2024-11-12 15:38:05 +01:00
|
|
|
---
|
|
|
|
gitea: none
|
|
|
|
include_toc: true
|
|
|
|
---
|
|
|
|
|
2025-01-07 13:42:48 +01:00
|
|
|
# Overview
|
|
|
|
|
|
|
|
Element Call consists of a few parts, you don't have to host all of them
|
|
|
|
yourself. In this document, we're going to host everything ourselves, so
|
|
|
|
here's what you need.
|
|
|
|
|
2025-01-07 13:45:14 +01:00
|
|
|
* **lk-jwt**. This authenticates Synapse users to LiveKit.
|
|
|
|
* **LiveKit**. This is the "SFU", which actually handles the audio and video, and does TURN.
|
2025-01-08 14:51:11 +01:00
|
|
|
* **Element Call widget**. This is basically the webapplication, the user interface.
|
2025-01-07 13:42:48 +01:00
|
|
|
|
2025-01-07 13:50:44 +01:00
|
|
|
As mentioned in the [checklist](../checklist.md) you need to define these
|
|
|
|
three entries in DNS and get certificates for them:
|
|
|
|
|
|
|
|
* `turn.example.com`
|
|
|
|
* `livekit.example.com`
|
|
|
|
* `call.example.com`
|
|
|
|
|
2025-01-08 14:51:11 +01:00
|
|
|
You may already have DNS and TLS for `turn.example.com`, as it is also used
|
|
|
|
for [coturn](../coturn).
|
|
|
|
|
2025-01-07 13:50:44 +01:00
|
|
|
For more inspiraten, check https://sspaeth.de/2024/11/sfu/
|
|
|
|
|
2025-01-07 13:42:48 +01:00
|
|
|
|
|
|
|
# LiveKit {#livekit}
|
|
|
|
|
|
|
|
The actual SFU, Selective Forwarding Unit, is LiveKit; this is the part that
|
|
|
|
handles the audio and video feeds and also does TURN (this TURN-functionality
|
|
|
|
does not support the legacy calls, you'll need [coturn](coturn) for that).
|
|
|
|
|
|
|
|
Downloading and installing is easy: download the [binary from
|
|
|
|
Github](https://github.com/livekit/livekit/releases/download/v1.8.0/livekit_1.8.0_linux_amd64.tar.gz)
|
|
|
|
to /usr/local/bin, chown it to root:root and you're done.
|
|
|
|
|
|
|
|
The quickest way to do precisely that, is to run the script:
|
|
|
|
|
|
|
|
```
|
|
|
|
curl -sSL https://get.livekit.io | bash
|
|
|
|
```
|
|
|
|
|
|
|
|
You can do this as a normal user, it will use sudo to do its job.
|
|
|
|
|
2025-01-07 17:17:05 +01:00
|
|
|
While you're at it, you might consider installing the cli tool as well, you
|
|
|
|
can use it -for example- to generate tokens so you can [test LiveKit's
|
|
|
|
connectivity](https://livekit.io/connection-test):
|
|
|
|
|
|
|
|
```
|
|
|
|
curl -sSL https://get.livekit.io/cli | bash
|
|
|
|
```
|
|
|
|
|
|
|
|
Configuring LiveKit is [documented
|
2025-01-07 13:42:48 +01:00
|
|
|
here](https://docs.livekit.io/home/self-hosting/deployment/). We're going to
|
|
|
|
run LiveKit under authorization of user `turnserver`, the same users we use
|
|
|
|
for [coturn](coturn). This user is created when installing coturn, so if you
|
|
|
|
haven't installed that, you should create the user yourself:
|
|
|
|
|
|
|
|
```
|
|
|
|
adduser --system turnserver
|
|
|
|
```
|
|
|
|
|
2025-01-07 17:17:05 +01:00
|
|
|
## Configure {#keysecret}
|
|
|
|
|
|
|
|
Start by creating a key and secret:
|
2025-01-07 13:42:48 +01:00
|
|
|
|
|
|
|
```
|
|
|
|
livekit-server generate-keys
|
|
|
|
```
|
|
|
|
|
|
|
|
This key and secret have to be fed to lk-jwt-service too, [see here](#jwtconfig).
|
2025-01-07 17:00:35 +01:00
|
|
|
Create the directory for LiveKit's configuration:
|
|
|
|
|
|
|
|
```
|
|
|
|
mkdir /etc/livekit
|
|
|
|
chown root:turnserver /etc/livekit
|
|
|
|
chmod 750 /etc/livekit
|
|
|
|
```
|
|
|
|
|
2025-01-07 13:42:48 +01:00
|
|
|
Create a configuration file for livekit, `/etc/livekit/livekit.yaml`:
|
|
|
|
|
|
|
|
```
|
|
|
|
port: 7880
|
|
|
|
bind_addresses:
|
|
|
|
- ::1
|
|
|
|
rtc:
|
|
|
|
tcp_port: 7881
|
|
|
|
port_range_start: 50000
|
|
|
|
port_range_end: 60000
|
|
|
|
use_external_ip: true
|
|
|
|
enable_loopback_candidate: false
|
|
|
|
turn:
|
|
|
|
enabled: true
|
|
|
|
domain: livekit.example.com
|
|
|
|
cert_file: /etc/coturn/ssl/fullchain.pem
|
|
|
|
key_file: /etc/coturn/ssl/privkey.pem
|
|
|
|
tls_port: 5349
|
|
|
|
udp_port: 3478
|
|
|
|
external_tls: true
|
|
|
|
keys:
|
2025-01-08 14:51:11 +01:00
|
|
|
# KEY: SECRET were generated by "livekit-server generate-keys"
|
2025-01-07 13:42:48 +01:00
|
|
|
<KEY>: <SECRET>
|
|
|
|
```
|
|
|
|
|
2025-01-07 17:00:35 +01:00
|
|
|
Being a bit paranoid: make sure LiveKit can only read this file, not write it:
|
|
|
|
|
|
|
|
```
|
|
|
|
chown root:turnserver /etc/livekit/livekit.yaml
|
|
|
|
chmod 640 /etc/livekit/livekit.yaml
|
|
|
|
```
|
|
|
|
|
2025-01-08 14:51:11 +01:00
|
|
|
Port `7880` is forwarded by nginx: authentication is also done there, and that
|
|
|
|
bit has to be forwarded to `lk-jwt-service` on port `8080`. Therefore, we
|
|
|
|
listen only on localhost.
|
|
|
|
|
|
|
|
The TURN ports are the normal, default ones. If you also use coturn, make sure
|
|
|
|
it doesn't use the same ports as LiveKit. Also, make sure you open the correct
|
|
|
|
ports in the [firewall](../firewall).
|
|
|
|
|
|
|
|
|
2025-01-07 17:10:59 +01:00
|
|
|
## TLS certificate
|
|
|
|
|
2025-01-07 17:00:35 +01:00
|
|
|
The TLS-certificate files are not in the usual place under
|
2025-01-07 13:42:48 +01:00
|
|
|
`/etc/letsencrypt/live`, see [DNS and
|
2025-01-07 17:00:35 +01:00
|
|
|
certificate](../coturn/README.md#dnscert) under coturn why that is.
|
2025-01-07 13:42:48 +01:00
|
|
|
|
|
|
|
As stated before, we use the same user as for coturn. Because this user does
|
|
|
|
not have the permission to read private keys under `/etc/letsencrypt`, we copy
|
|
|
|
those files to a place where it can read them. For coturn we copy them to
|
|
|
|
`/etc/coturn/ssl`, and if you use coturn and have this directory, LiveKit can
|
|
|
|
read them there too.
|
|
|
|
|
|
|
|
If you don't have coturn installed, you should create a directory under
|
|
|
|
`/etc/livekit` and copy the files to there. Modify the `livekit.yaml` file and
|
|
|
|
the [script to copy the files](../coturn/README.md#fixssl) to use that
|
2025-01-08 14:51:11 +01:00
|
|
|
directory. Don't forget to update the `renew_hook` in Letsencrypt if you do.
|
2025-01-07 13:42:48 +01:00
|
|
|
|
|
|
|
The LiveKit API listens on localhost, IPv6, port 7880. Traffic to this port is
|
|
|
|
forwarded from port 443 by nginx, which handles TLS, so it shouldn't be reachable
|
|
|
|
from the outside world.
|
|
|
|
|
|
|
|
See [LiveKit's config documentation](https://github.com/livekit/livekit/blob/master/config-sample.yaml)
|
|
|
|
for more options.
|
|
|
|
|
2025-01-07 17:10:59 +01:00
|
|
|
|
|
|
|
## Systemd
|
|
|
|
|
2025-01-07 13:42:48 +01:00
|
|
|
Now define a systemd servicefile, like this:
|
|
|
|
|
|
|
|
```
|
|
|
|
[Unit]
|
|
|
|
Description=LiveKit Server
|
|
|
|
After=network.target
|
|
|
|
Documentation=https://docs.livekit.io
|
|
|
|
|
|
|
|
[Service]
|
|
|
|
User=turnserver
|
|
|
|
Group=turnserver
|
|
|
|
LimitNOFILE=500000
|
|
|
|
Restart=on-failure
|
|
|
|
WorkingDirectory=/etc/livekit
|
|
|
|
ExecStart=/usr/local/bin/livekit-server --config /etc/livekit/livekit.yaml
|
|
|
|
|
|
|
|
[Install]
|
|
|
|
WantedBy=multi-user.target
|
|
|
|
```
|
|
|
|
|
|
|
|
Enable and start it.
|
|
|
|
|
2025-01-07 17:00:35 +01:00
|
|
|
Clients don't know about LiveKit yet, you'll have to give them the information
|
|
|
|
via the `.well-known/matrix/client`: add this bit to it to point them at the
|
|
|
|
SFU:
|
2024-11-18 17:31:38 +01:00
|
|
|
|
|
|
|
```
|
|
|
|
"org.matrix.msc4143.rtc_foci": [
|
|
|
|
{
|
|
|
|
"type": "livekit",
|
2024-12-04 10:18:18 +01:00
|
|
|
"livekit_service_url": "https://livekit.example.com"
|
2024-11-18 17:31:38 +01:00
|
|
|
}
|
|
|
|
]
|
|
|
|
```
|
|
|
|
|
|
|
|
Make sure it is served as `application/json`, just like the other .well-known
|
|
|
|
files.
|
|
|
|
|
|
|
|
|
2025-01-08 14:51:11 +01:00
|
|
|
# lk-jwt-service {#lkjwt}
|
|
|
|
|
|
|
|
lk-jwt-service is a small Go program that handles authorization tokens for use with LiveKit.
|
|
|
|
You'll need a Go compiler, but the one Debian provides is too old (at the time
|
|
|
|
of writing this, at least), so we'll install the latest one manually. Check
|
|
|
|
[the Go website](https://go.dev/dl/) to see which version is the latest, at
|
|
|
|
the time of writing it's 1.23.3, so we'll install that:
|
2024-11-18 17:31:38 +01:00
|
|
|
|
|
|
|
```
|
2025-01-08 14:51:11 +01:00
|
|
|
wget https://go.dev/dl/go1.23.3.linux-amd64.tar.gz
|
|
|
|
tar xvfz go1.23.3.linux-amd64.tar.gz
|
|
|
|
cd go/bin
|
|
|
|
export PATH=`pwd`:$PATH
|
|
|
|
cd
|
2024-11-18 17:31:38 +01:00
|
|
|
```
|
|
|
|
|
2025-01-08 14:51:11 +01:00
|
|
|
This means you now have the latest Go compiler in your path, but it's not
|
|
|
|
installed system-wide. If you want that, copy the whole `go` directory to
|
|
|
|
`/usr/local` and add `/usr/local/go/bin` to everybody's $PATH.
|
2024-11-18 17:31:38 +01:00
|
|
|
|
2025-01-08 14:51:11 +01:00
|
|
|
Get the latest lk-jwt-service source code and comile it (preferably *NOT* as root):
|
2024-11-18 18:36:24 +01:00
|
|
|
|
|
|
|
```
|
|
|
|
git clone https://github.com/element-hq/lk-jwt-service.git
|
|
|
|
cd lk-jwt-service
|
|
|
|
go build -o lk-jwt-service
|
|
|
|
```
|
|
|
|
|
|
|
|
Now, compile:
|
|
|
|
|
|
|
|
```
|
|
|
|
cd lk-jwt-service
|
|
|
|
go build -o lk-jwt-service
|
|
|
|
```
|
|
|
|
|
|
|
|
Copy and chown the binary to `/usr/local/sbin` (yes: as root):
|
|
|
|
|
|
|
|
```
|
|
|
|
cp ~user/lk-jwt-service/lk-jwt-service /usr/local/sbin
|
|
|
|
chown root:root /usr/local/sbin/lk-jwt-service
|
|
|
|
```
|
|
|
|
|
2025-01-08 14:51:11 +01:00
|
|
|
|
2025-01-07 17:10:59 +01:00
|
|
|
## Systemd
|
|
|
|
|
2024-11-18 18:36:24 +01:00
|
|
|
Create a service file for systemd, something like this:
|
|
|
|
|
|
|
|
```
|
|
|
|
# This thing does authorization for Element Call
|
|
|
|
|
|
|
|
[Unit]
|
|
|
|
Description=LiveKit JWT Service
|
|
|
|
After=network.target
|
|
|
|
|
|
|
|
[Service]
|
|
|
|
Restart=always
|
|
|
|
User=www-data
|
|
|
|
Group=www-data
|
2024-12-04 11:29:34 +01:00
|
|
|
WorkingDirectory=/etc/lk-jwt-service
|
2024-11-18 18:36:24 +01:00
|
|
|
EnvironmentFile=/etc/lk-jwt-service/config
|
|
|
|
ExecStart=/usr/local/sbin/lk-jwt-service
|
|
|
|
|
|
|
|
[Install]
|
|
|
|
WantedBy=multi-user.target
|
|
|
|
```
|
|
|
|
|
2025-01-07 17:10:59 +01:00
|
|
|
## Configuration {#jwtconfig}
|
|
|
|
|
2024-12-04 11:29:34 +01:00
|
|
|
We read the options from `/etc/lk-jwt-service/config`,
|
2024-11-18 18:36:24 +01:00
|
|
|
which we make read-only for group `www-data` and non-accessible by anyone
|
|
|
|
else.
|
|
|
|
|
|
|
|
```
|
|
|
|
mkdir /etc/lk-jwt-service
|
|
|
|
vi /etc/lk-jwt-service/config
|
2025-01-08 14:51:11 +01:00
|
|
|
chgrp -R root:www-data /etc/lk-jwt-service
|
|
|
|
chmod 750 /etc/lk-jwt-service
|
2024-11-18 18:36:24 +01:00
|
|
|
```
|
|
|
|
|
2025-01-07 17:10:59 +01:00
|
|
|
This is what you should put into that config file,
|
2025-01-07 13:42:48 +01:00
|
|
|
`/etc/lk-jwt-service/config`. The `LIVEKIT_SECRET` and `LIVEKIT_KEY` are the
|
|
|
|
ones you created while [configuring LiveKit](#keysecret).
|
2024-11-18 18:36:24 +01:00
|
|
|
|
|
|
|
```
|
2024-12-04 10:18:18 +01:00
|
|
|
LIVEKIT_URL=wss://livekit.example.com
|
2024-11-18 18:36:24 +01:00
|
|
|
LIVEKIT_SECRET=xxx
|
|
|
|
LIVEKIT_KEY=xxx
|
|
|
|
LK_JWT_PORT=8080
|
|
|
|
```
|
|
|
|
|
2025-01-08 14:51:11 +01:00
|
|
|
Change the permission accordingly:
|
|
|
|
|
|
|
|
```
|
|
|
|
chown root:www-data /etc/lk-jwt-service/config
|
|
|
|
chmod 640 /etc/lk-jwt-service/config
|
|
|
|
```
|
|
|
|
|
2024-11-18 18:36:24 +01:00
|
|
|
Now enable and start this thing:
|
|
|
|
|
|
|
|
```
|
|
|
|
systemctl enable --now lk-jwt-service
|
|
|
|
```
|
|
|
|
|
2025-01-08 14:51:11 +01:00
|
|
|
|
2024-11-20 20:12:42 +01:00
|
|
|
# Element Call widget {#widget}
|
|
|
|
|
|
|
|
This is a Node.js thingy, so start by installing yarn. Unfortunately both npm
|
|
|
|
and `yarnpkg` in Debian are antique, so we need to update them after installation.
|
|
|
|
Install Node.js and upgrade everything. Do not do this as root, we'll only
|
|
|
|
need to "compile" Element Call once.
|
|
|
|
|
|
|
|
See [the Node.js
|
|
|
|
website](https://nodejs.org/en/download/package-manager/current) for
|
|
|
|
instructions.
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.0/install.sh | bash
|
|
|
|
```
|
|
|
|
|
|
|
|
Exit and login again to set some environment variables (yes, the installation
|
|
|
|
changes .bashrc). Then install and upgrade:
|
|
|
|
|
|
|
|
```
|
|
|
|
nvm install 23
|
|
|
|
sudo apt install yarnpkg
|
|
|
|
/usr/share/nodejs/yarn/bin/yarn set version stable
|
|
|
|
/usr/share/nodejs/yarn/bin/yarn install
|
|
|
|
```
|
|
|
|
|
2025-01-08 14:51:11 +01:00
|
|
|
Yes, this whole Node.js, yarn and npm thing is a mess. Better documentation
|
|
|
|
could be written, but for now this will have to do.
|
|
|
|
|
2024-11-20 20:12:42 +01:00
|
|
|
Now clone the Element Call repository and "compile" stuff (again: not as
|
|
|
|
root):
|
|
|
|
|
|
|
|
```
|
|
|
|
git clone https://github.com/element-hq/element-call.git
|
|
|
|
cd element-call
|
|
|
|
/usr/share/nodejs/yarn/bin/yarn
|
|
|
|
/usr/share/nodejs/yarn/bin/yarn build
|
|
|
|
```
|
|
|
|
|
2025-01-08 14:51:11 +01:00
|
|
|
If it successfully compiles (warnings are more or less ok, errors aren't), you will
|
|
|
|
find the whole shebang under "dist". Copy that to `/var/www/element-call` and point
|
|
|
|
nginx to it ([see nginx](../nginx#callwidget)).
|
|
|
|
|
2024-11-20 20:12:42 +01:00
|
|
|
|
2025-01-07 17:10:59 +01:00
|
|
|
## Configuring
|
|
|
|
|
2024-11-25 15:11:53 +01:00
|
|
|
It needs a tiny bit of configuring. The default configuration under `config/config.sample.json`
|
|
|
|
is a good place to start, copy it to `/etc/element-call` and change where
|
|
|
|
necessary:
|
|
|
|
|
|
|
|
```
|
|
|
|
{
|
|
|
|
"default_server_config": {
|
|
|
|
"m.homeserver": {
|
2024-12-04 10:18:18 +01:00
|
|
|
"base_url": "https://matrix.example.com",
|
|
|
|
"server_name": "example.com"
|
2024-11-25 15:11:53 +01:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
"livekit": {
|
2024-12-04 10:18:18 +01:00
|
|
|
"livekit_service_url": "https://livekit.example.com"
|
2024-11-25 15:11:53 +01:00
|
|
|
},
|
|
|
|
|
|
|
|
"features": {
|
|
|
|
"feature_use_device_session_member_events": true
|
|
|
|
},
|
|
|
|
|
|
|
|
"eula": "https://www.example.com/online-EULA.pdf"
|
|
|
|
}
|
|
|
|
```
|
2025-01-07 17:00:35 +01:00
|
|
|
|
|
|
|
Now tell the clients about this widget. Create
|
|
|
|
`.well-known/element/element.json`, which is opened by Element Web, Element Desktop
|
2025-01-08 15:45:09 +01:00
|
|
|
and ElementX to find the Element Call widget. It should look this:
|
2025-01-07 17:00:35 +01:00
|
|
|
|
|
|
|
```
|
|
|
|
{
|
|
|
|
"call": {
|
|
|
|
"widget_url": "https://call.example.com"
|
|
|
|
}
|
|
|
|
}
|
|
|
|
```
|
|
|
|
|