2020-05-29 17:49:23 +02:00
|
|
|
|
# Installation
|
|
|
|
|
|
2020-09-24 19:08:25 +02:00
|
|
|
|
## Build the server binary
|
2020-04-29 18:31:54 +02:00
|
|
|
|
|
|
|
|
|
CGO_ENABLED=0 go build -ldflags='-s -w'
|
|
|
|
|
|
2020-09-24 19:08:25 +02:00
|
|
|
|
## Create a server certificate
|
2020-04-29 18:31:54 +02:00
|
|
|
|
|
2020-05-03 15:09:02 +02:00
|
|
|
|
mkdir data
|
2020-04-29 18:31:54 +02:00
|
|
|
|
openssl req -newkey rsa:2048 -nodes -keyout data/key.pem -x509 -days 365 -out data/cert.pem
|
|
|
|
|
|
2020-09-24 19:08:25 +02:00
|
|
|
|
## Set the server administrator credentials
|
|
|
|
|
|
|
|
|
|
This step is optional.
|
2020-04-29 18:31:54 +02:00
|
|
|
|
|
|
|
|
|
echo 'god:topsecret' > data/passwd
|
|
|
|
|
|
2021-01-08 15:36:23 +01:00
|
|
|
|
## Set up an ICE server
|
2020-09-24 19:08:25 +02:00
|
|
|
|
|
2021-01-08 15:36:23 +01:00
|
|
|
|
ICE is the NAT and firewall traversal protocol used by WebRTC. ICE uses
|
|
|
|
|
a variety of techniques for establishing a flow in the presence of
|
|
|
|
|
a firewall; the two most effective techniques, STUN and TURN, require help
|
|
|
|
|
from an external server. Whether you need a helping server depends both
|
|
|
|
|
on your firewalling setup and on the networks of your users; for
|
|
|
|
|
production use, you should probably use your own TURN server.
|
2020-11-09 00:23:44 +01:00
|
|
|
|
|
2021-01-08 15:36:23 +01:00
|
|
|
|
### No ICE server
|
|
|
|
|
|
|
|
|
|
If Galène is not firewalled (high-numbered ports are accessible from the
|
|
|
|
|
Internet) and none of your users are on a restrictive network, then you
|
|
|
|
|
need no ICE servers. There is nothing to do, skip to *Set up a group*
|
|
|
|
|
below.
|
|
|
|
|
|
|
|
|
|
### STUN server
|
|
|
|
|
|
|
|
|
|
If Galène might be behind a firewall (high-numbered ports might or might
|
|
|
|
|
not be accessible from the Internet), but none of your clients are on
|
|
|
|
|
a restrictive network, then a STUN server is enough. It is usually safe
|
|
|
|
|
to use a third-party STUN server, although doing that might violate the
|
|
|
|
|
privacy of your users. Your `data/ice-servers.json` file should look like
|
|
|
|
|
this:
|
2020-11-09 00:23:44 +01:00
|
|
|
|
|
|
|
|
|
[
|
|
|
|
|
{
|
2021-01-01 17:54:48 +01:00
|
|
|
|
"urls": [
|
2021-01-08 15:36:23 +01:00
|
|
|
|
"stun:stun.example.org"
|
|
|
|
|
]
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
### TURN server
|
|
|
|
|
|
|
|
|
|
In practice, some of your users will be on restrictive networks: many
|
|
|
|
|
enterprise networks only allow outgoing TCP to ports 80 and 443;
|
|
|
|
|
university networks tend to additionally allow outgoing traffic to port
|
|
|
|
|
1194. For best performance, your TURN server should be located close to
|
|
|
|
|
Galène and close to your users, so you will want to run your own (I use
|
|
|
|
|
*coturn*, but other implementations of TURN should work too).
|
|
|
|
|
|
|
|
|
|
Your `ice-servers.json` should look like this, where `username` and
|
|
|
|
|
`secret` are identical to the ones in your TURN server's configuration:
|
|
|
|
|
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
"urls": [
|
|
|
|
|
"turn:turn.example.org:443",
|
|
|
|
|
"turn:turn.example.org:443?transport=tcp"
|
2021-01-01 17:54:48 +01:00
|
|
|
|
],
|
2021-01-01 23:50:34 +01:00
|
|
|
|
"username": "galene",
|
|
|
|
|
"credential": "secret"
|
2021-01-01 22:20:58 +01:00
|
|
|
|
}
|
2020-11-09 00:23:44 +01:00
|
|
|
|
]
|
|
|
|
|
|
2021-01-08 15:36:23 +01:00
|
|
|
|
If you use coturn's `use-auth-secret` option, then your `ice-servers.json`
|
|
|
|
|
should look like this:
|
|
|
|
|
|
|
|
|
|
[
|
|
|
|
|
{
|
|
|
|
|
"urls": [
|
|
|
|
|
"turn:turn.example.com:443",
|
|
|
|
|
"turn:turn.example.com:443?transport=tcp"
|
|
|
|
|
],
|
|
|
|
|
"username": "galene",
|
|
|
|
|
"credential": "secret",
|
|
|
|
|
"credentialType": "hmac-sha1"
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
|
|
|
|
|
For redundancy, you may set up multiple TURN servers, and ICE will use the
|
|
|
|
|
first one that works.
|
2020-04-29 18:31:54 +02:00
|
|
|
|
|
2020-09-24 19:08:25 +02:00
|
|
|
|
## Set up a group
|
|
|
|
|
|
2021-01-08 15:36:23 +01:00
|
|
|
|
A group is set up by creating a file `groups/name.json`.
|
2020-04-29 18:31:54 +02:00
|
|
|
|
|
|
|
|
|
mkdir groups
|
2021-01-08 15:36:23 +01:00
|
|
|
|
vi groups/groupname.json
|
|
|
|
|
|
|
|
|
|
A group with a single operator and no password for ordinary users looks
|
|
|
|
|
like this:
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
"op": [{"username": "jch", "password": "1234"}],
|
|
|
|
|
"presenter": [{}]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
A group with one operator and two users looks like this:
|
2020-04-29 18:31:54 +02:00
|
|
|
|
|
|
|
|
|
{
|
2021-01-08 15:36:23 +01:00
|
|
|
|
"op": [{"username": "jch", "password": "1234"}],
|
|
|
|
|
"presenter": [
|
|
|
|
|
{"username": "mom", "password": "0000"},
|
|
|
|
|
{
|
|
|
|
|
"username": "dad",
|
|
|
|
|
"password": "Pójdźże, kiń tę chmurność w głąb flaszy!"
|
|
|
|
|
}
|
|
|
|
|
]
|
2020-04-29 18:31:54 +02:00
|
|
|
|
}
|
2021-01-08 15:36:23 +01:00
|
|
|
|
|
|
|
|
|
More options are described under *Details of group definitions* below.
|
2020-04-29 18:31:54 +02:00
|
|
|
|
|
2021-01-08 15:36:23 +01:00
|
|
|
|
## Test locally
|
2020-09-24 19:08:25 +02:00
|
|
|
|
|
2021-01-08 15:36:23 +01:00
|
|
|
|
./galene &
|
|
|
|
|
|
2021-01-11 19:33:09 +01:00
|
|
|
|
You should be able to access Galène at `https://localhost:8443`. Connect
|
|
|
|
|
to the group that you have just set up in two distinct browser windows,
|
|
|
|
|
then press *Ready* in one of the two; you should see a video in the other.
|
|
|
|
|
|
|
|
|
|
If you have set up a TURN server, type */relay-test* in the chat box; if
|
|
|
|
|
the TURN server is properly configured, you should see a message saying
|
|
|
|
|
that the relay test has been successful. (The relay test will fail if you
|
|
|
|
|
didn't configure a TURN server; this is normal, and nothing to worry
|
|
|
|
|
about.)
|
2020-04-29 18:31:54 +02:00
|
|
|
|
|
2021-01-08 15:36:23 +01:00
|
|
|
|
## Deploy to your server
|
2020-04-29 18:31:54 +02:00
|
|
|
|
|
2021-01-08 15:36:23 +01:00
|
|
|
|
Set up a user *galene* on your server, then do:
|
|
|
|
|
|
|
|
|
|
rsync -a galene static data groups galene@server.example.org:
|
|
|
|
|
|
|
|
|
|
Now run the binary on the server:
|
2020-04-29 18:31:54 +02:00
|
|
|
|
|
2020-12-06 19:43:17 +01:00
|
|
|
|
ssh galene@server.example.org
|
2021-01-08 15:36:23 +01:00
|
|
|
|
ulimit -n 65536
|
2020-12-06 19:43:17 +01:00
|
|
|
|
nohup ./galene &
|
2020-04-29 18:31:54 +02:00
|
|
|
|
|
2020-09-24 19:08:25 +02:00
|
|
|
|
If you are using *runit*, use a script like the following:
|
|
|
|
|
|
|
|
|
|
#!/bin/sh
|
|
|
|
|
exec 2>&1
|
2020-12-06 19:43:17 +01:00
|
|
|
|
cd ~galene
|
2020-12-19 02:37:07 +01:00
|
|
|
|
ulimit -n 65536
|
2020-12-06 19:43:17 +01:00
|
|
|
|
exec setuidgid galene ./galene
|
2020-09-24 19:08:25 +02:00
|
|
|
|
|
2021-01-08 15:36:23 +01:00
|
|
|
|
If you are using *systemd*:
|
2020-12-19 02:37:07 +01:00
|
|
|
|
|
|
|
|
|
[Unit]
|
|
|
|
|
Description=Galene
|
|
|
|
|
After=network.target
|
|
|
|
|
|
|
|
|
|
[Service]
|
|
|
|
|
Type=simple
|
|
|
|
|
WorkingDirectory=/home/galene
|
|
|
|
|
User=galene
|
|
|
|
|
Group=galene
|
|
|
|
|
ExecStart=/home/galene/galene
|
|
|
|
|
LimitNOFILE=65536
|
|
|
|
|
|
|
|
|
|
[Install]
|
|
|
|
|
WantedBy=multi-user.target
|
2020-05-29 17:49:23 +02:00
|
|
|
|
|
2021-01-08 15:36:23 +01:00
|
|
|
|
# Usage
|
|
|
|
|
|
|
|
|
|
## Locations
|
2020-05-31 23:16:08 +02:00
|
|
|
|
|
|
|
|
|
There is a landing page at the root of the server. It contains a form
|
|
|
|
|
for typing the name of a group, and a clickable list of public groups.
|
|
|
|
|
|
|
|
|
|
Groups are available under `/group/groupname`. You may share this URL
|
|
|
|
|
with others, there is no need to go through the landing page.
|
|
|
|
|
|
|
|
|
|
Recordings can be accessed under `/recordings/groupname`. This is only
|
|
|
|
|
available to the administrator of the group.
|
|
|
|
|
|
|
|
|
|
Some statistics are available under `/stats`. This is only available to
|
|
|
|
|
the server administrator.
|
|
|
|
|
|
2021-01-08 15:36:23 +01:00
|
|
|
|
## Side menu
|
|
|
|
|
|
|
|
|
|
There is a menu on the right of the user interface. This allows choosing
|
|
|
|
|
the camera and microphone and setting the video throughput. The
|
|
|
|
|
*Blackboard mode* checkbox increases resolution and sacrifices framerate
|
|
|
|
|
in favour of image quality. The *Play local file* dialog allows streaming
|
|
|
|
|
a video from a local file.
|
2020-05-31 23:16:08 +02:00
|
|
|
|
|
2021-01-08 15:36:23 +01:00
|
|
|
|
## Commands
|
|
|
|
|
|
|
|
|
|
Typing a line starting with a slash `/` in the chat dialogue causes
|
|
|
|
|
a command to be sent to the server. Type `/help` to get the list of
|
|
|
|
|
available commands; the output depends on whether you are an operator or
|
|
|
|
|
not.
|
2020-05-29 17:49:23 +02:00
|
|
|
|
|
|
|
|
|
|
2021-01-08 15:36:23 +01:00
|
|
|
|
# Details of group definitions
|
|
|
|
|
|
|
|
|
|
Groups are defined by files in the `./groups` directory (this may be
|
|
|
|
|
configured by the `-groups` command-line option, try `./galene -help`).
|
|
|
|
|
The definition for the group called *groupname* is in the file
|
|
|
|
|
`groups/groupname.json` and does not contain the group name, which makes
|
|
|
|
|
it easy to copy or link group definitions. You may use subdirectories:
|
|
|
|
|
a file `groups/teaching/networking.json` defines a group called
|
|
|
|
|
*teching/networking*.
|
|
|
|
|
|
|
|
|
|
Every group definition file contains a JSON directory with the following
|
2021-01-01 17:54:48 +01:00
|
|
|
|
fields. All fields are optional, but unless you specify at least one user
|
|
|
|
|
definition (`op`, `presenter`, or `other`), nobody will be able to join
|
|
|
|
|
the group.
|
2020-05-29 17:49:23 +02:00
|
|
|
|
|
|
|
|
|
- `op`, `presenter`, `other`: each of these is an array of user
|
|
|
|
|
definitions (see below) and specifies the users allowed to connect
|
|
|
|
|
respectively with operator privileges, with presenter privileges, and
|
|
|
|
|
as passive listeners;
|
2020-05-30 01:18:00 +02:00
|
|
|
|
- `public`: if true, then the group is visible on the landing page;
|
2020-09-24 22:03:41 +02:00
|
|
|
|
- `description`: a human-readable description of the group; this is
|
|
|
|
|
displayed on the landing page for public groups;
|
2021-01-13 23:00:48 +01:00
|
|
|
|
- `contact`: a human-readable contact for this group, such as an e-mail
|
|
|
|
|
address;
|
|
|
|
|
- `comment`: a human-readable string;
|
2020-05-29 17:49:23 +02:00
|
|
|
|
- `max-clients`: the maximum number of clients that may join the group at
|
|
|
|
|
a time;
|
2020-10-08 14:38:33 +02:00
|
|
|
|
- `max-history-age`: the time, in seconds, during which chat history is
|
|
|
|
|
kept (default 14400, i.e. 4 hours);
|
2020-05-30 01:18:00 +02:00
|
|
|
|
- `allow-recording`: if true, then recording is allowed in this group;
|
2021-01-08 15:36:23 +01:00
|
|
|
|
- `allow-anonymous`: if true, then users may connect with an empty username;
|
2020-11-22 19:54:54 +01:00
|
|
|
|
- `allow-subgroups`: if true, then subgroups of the form `group/subgroup`
|
2021-01-08 15:36:23 +01:00
|
|
|
|
are automatically created when first accessed;
|
2020-09-10 13:55:57 +02:00
|
|
|
|
- `redirect`: if set, then attempts to join the group will be redirected
|
2021-01-08 15:36:23 +01:00
|
|
|
|
to the given URL; most other fields are ignored in this case;
|
2020-12-25 17:33:44 +01:00
|
|
|
|
- `codecs`: this is a list of codecs allowed in this group. The default
|
2021-01-08 15:36:23 +01:00
|
|
|
|
is `["vp8", "opus"]`.
|
|
|
|
|
|
|
|
|
|
Supported video codecs include:
|
|
|
|
|
|
|
|
|
|
- `"vp8"` (compatible with all supported browsers);
|
|
|
|
|
- `"vp9"` (better video quality than `"vp8"`, but incompatible with
|
|
|
|
|
older versions of Mac OS);
|
|
|
|
|
- `"h264"` (incompatible with Debian, Ubuntu, and some Android devices,
|
|
|
|
|
recording is not supported).
|
|
|
|
|
|
|
|
|
|
Supported audio codecs include `"opus"`, `"g722"`, `"pcmu"` and `"pcma"`.
|
|
|
|
|
There is no good reason to use anything except Opus.
|
2020-05-29 17:49:23 +02:00
|
|
|
|
|
|
|
|
|
A user definition is a dictionary with the following fields:
|
|
|
|
|
|
|
|
|
|
- `username`: the username of the user; if omitted, any username is
|
|
|
|
|
allowed;
|
2020-11-29 14:26:42 +01:00
|
|
|
|
- `password`: if omitted, then no password is required. Otherwise, this
|
|
|
|
|
can either be a string, specifying a plain text password, or
|
2020-12-06 19:43:17 +01:00
|
|
|
|
a dictionary generated by the `galene-password-generator` utility.
|
2020-05-29 17:49:23 +02:00
|
|
|
|
|
2020-11-29 14:26:42 +01:00
|
|
|
|
For example,
|
2020-05-29 17:49:23 +02:00
|
|
|
|
|
2021-01-08 15:36:23 +01:00
|
|
|
|
{"username": "jch", "password": "1234"}
|
|
|
|
|
|
|
|
|
|
specifies user *jch* with password *1234*, while
|
|
|
|
|
|
|
|
|
|
{"password": "1234"}
|
2020-05-29 17:49:23 +02:00
|
|
|
|
|
2021-01-08 15:36:23 +01:00
|
|
|
|
specifies that any (non-empty) username will do, and
|
2020-05-29 17:49:23 +02:00
|
|
|
|
|
2021-01-08 15:36:23 +01:00
|
|
|
|
{}
|
2020-05-29 17:49:23 +02:00
|
|
|
|
|
2021-01-08 15:36:23 +01:00
|
|
|
|
allows any (non-empty) username with any password.
|
|
|
|
|
|
|
|
|
|
If you don't wish to store cleartext passwords on the server, you may
|
|
|
|
|
generate hashed password with the `galene-password-generator` utility. A
|
|
|
|
|
user entry with a hashed password looks like this:
|
2020-05-29 17:49:23 +02:00
|
|
|
|
|
2020-11-29 14:26:42 +01:00
|
|
|
|
{
|
|
|
|
|
"username": "jch",
|
|
|
|
|
"password": {
|
|
|
|
|
"type": "pbkdf2",
|
|
|
|
|
"hash": "sha-256",
|
|
|
|
|
"key": "f591c35604e6aef572851d9c3543c812566b032b6dc083c81edd15cc24449913",
|
|
|
|
|
"salt": "92bff2ace56fe38f",
|
|
|
|
|
"iterations": 4096
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-05-29 17:49:23 +02:00
|
|
|
|
|
|
|
|
|
--- Juliusz Chroboczek <https://www.irif.fr/~jch/>
|