Documentation rework.
This commit is contained in:
@@ -1,291 +0,0 @@
|
||||
# Installation instructions
|
||||
|
||||
## Build the `galene` binary
|
||||
|
||||
Do:
|
||||
|
||||
CGO_ENABLED=0 go build -ldflags='-s -w'
|
||||
|
||||
On Windows, do
|
||||
|
||||
set CGO_ENABLED=0
|
||||
go build -ldflags="-s -w"
|
||||
|
||||
## Set up a group
|
||||
|
||||
Set up a group called *test* by creating a file `groups/test.json`:
|
||||
|
||||
mkdir groups
|
||||
vi groups/test.json
|
||||
|
||||
You may use the following definition:
|
||||
|
||||
{
|
||||
"users":{
|
||||
"admin": {"password": "1234", "permissions": "op"}
|
||||
},
|
||||
"wildcard-user": {
|
||||
"password": {"type": "wildcard"},
|
||||
"permissions": "present"
|
||||
}
|
||||
}
|
||||
|
||||
See the README file for more details about defining groups.
|
||||
|
||||
|
||||
## Optional: install libraries for background blur
|
||||
|
||||
Galene's client uses Google's MediaPipe library to implement background
|
||||
blur. This library is optional, and if it is absent, Galene will
|
||||
disable the menu entries for background blur.
|
||||
|
||||
Optionally install Google's MediaPipe library:
|
||||
```
|
||||
mkdir mediapipe
|
||||
cd mediapipe
|
||||
npm pack @mediapipe/tasks-vision
|
||||
tar xzf mediapipe-tasks-vision-*.tgz
|
||||
rm -f ../static/third-party/tasks-vision
|
||||
mv package ../static/third-party/tasks-vision
|
||||
cd ../static/third-party/tasks-vision
|
||||
mkdir models
|
||||
cd models
|
||||
wget https://storage.googleapis.com/mediapipe-models/image_segmenter/selfie_segmenter/float16/latest/selfie_segmenter.tflite
|
||||
cd ../../../../
|
||||
```
|
||||
|
||||
|
||||
## Test locally
|
||||
|
||||
./galene &
|
||||
|
||||
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.
|
||||
|
||||
|
||||
## Configure your server's firewall
|
||||
|
||||
If your server has a global IPv4 address and there is no firewall, there
|
||||
is nothing to do.
|
||||
|
||||
If your server has a global IPv4 address, then the firewall must allow
|
||||
traffic to and from:
|
||||
|
||||
* TCP port 8443 (or whatever is configured with the `-http` option); and
|
||||
|
||||
* TCP and UDP port 1194 (or whatever is configured with the `-turn` option).
|
||||
|
||||
For good performance, your firewall should allow incoming and outgoing
|
||||
traffic from the UDP ports used for media transfer. By default, these are
|
||||
all high-numbered (ephemeral) ports, but they can be restricted using one
|
||||
of the following options:
|
||||
|
||||
* the `-udp-range port1-port2` option restricts the UDP ports to be in
|
||||
the range from port1 to port2 inclusive; this should be a large range,
|
||||
on the order of a few tens of thousands of ports;
|
||||
|
||||
* the `-udp-range port` option makes the server use just a single port,
|
||||
and demultiplex the traffic in userspace.
|
||||
|
||||
If your server is behind NAT (which is not recommended), then the NAT must
|
||||
forward, at the very least, port 8443 to your server. Ideally, you should
|
||||
configure an external TURN server (see *ICE Servers* below) on a host that
|
||||
is not behind NAT. If that is not possible, then you must use a NAT that
|
||||
supports hairpinning, you must forward port 1194 in addition to port 8443,
|
||||
and you will need to add add the option `-turn 203.0.113.1:1194` to
|
||||
Galène's command line, where `203.0.113.1` is your NAT's external (global)
|
||||
IPv4 address.
|
||||
|
||||
|
||||
## Cross-compile for your server
|
||||
|
||||
This step is only required if your server runs a different OS or has
|
||||
a different CPU than your build machine.
|
||||
|
||||
For a Linux server with an Intel or AMD CPU:
|
||||
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -ldflags='-s -w'
|
||||
|
||||
For a Raspberry Pi 1:
|
||||
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=6 go build -ldflags='-s -w'
|
||||
|
||||
For a BeagleBone or a Raspberry Pi 2 or later:
|
||||
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm GOARM=7 go build -ldflags='-s -w'
|
||||
|
||||
For a 64-bit ARM board (Olimex Olinuxino-A64, Pine64, etc.) or server:
|
||||
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags='-s -w'
|
||||
|
||||
For a 32-bit MIPS board with no hardware floating point (WNDR3800, etc.):
|
||||
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=mips GOMIPS=softfloat go build -ldflags='-s -w'
|
||||
|
||||
|
||||
## Deploy to your server
|
||||
|
||||
Set up a user *galene* on your server, then copy the `galene` binary, and
|
||||
the directories `static`, `data` and `groups`:
|
||||
|
||||
rsync -a galene static data groups galene@server.example.org:
|
||||
|
||||
If you don't have a TLS certificate, Galène will generate a self-signed
|
||||
certificate automatically (and print a warning to the logs). If you have
|
||||
a certificate, install it in the files `data/cert.pem` and `data/key.pem`:
|
||||
|
||||
ssh galene@server.example.org
|
||||
sudo cp /etc/letsencrypt/live/server.example.org/fullchain.pem data/cert.pem
|
||||
sudo cp /etc/letsencrypt/live/server.example.org/privkey.pem data/key.pem
|
||||
sudo chown galene:galene data/*.pem
|
||||
sudo chmod go-rw data/key.pem
|
||||
|
||||
Now arrange to run the binary on the server. If you never reboot your
|
||||
server, it might be as simple as
|
||||
|
||||
ssh galene@server.example.org
|
||||
ulimit -n 65536
|
||||
nohup ./galene &
|
||||
|
||||
If you are using *runit*, use a script like the following:
|
||||
|
||||
#!/bin/sh
|
||||
exec 2>&1
|
||||
cd ~galene
|
||||
ulimit -n 65536
|
||||
exec setuidgid galene ./galene
|
||||
|
||||
If you are using *systemd*:
|
||||
|
||||
[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
|
||||
|
||||
|
||||
# Running behind a reverse proxy
|
||||
|
||||
Galene is designed to be directly exposed to the Internet. In order to
|
||||
run Galene behind a reverse proxy, you might need to make a number of
|
||||
tweaks to your configuration.
|
||||
|
||||
First, you might need to inform Galene of the URL at which users connect
|
||||
(the reverse proxy's URL) by adding an entry `proxyURL` to your
|
||||
`data/config.json` file:
|
||||
|
||||
{
|
||||
"proxyURL": "https://galene.example.org/"
|
||||
}
|
||||
|
||||
Second, and depending on your proxy implementation, you might need to
|
||||
request that the proxy pass WebSocket handshakes to the URL at `ws`; for
|
||||
example, with Nginx, you will need to say something like the following:
|
||||
|
||||
location /ws {
|
||||
proxy_pass ...;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
}
|
||||
|
||||
Finally, in order to avoid TLS termination issues, you may want to run
|
||||
Galene over plain HTTP instead of HTTPS by using the command-line flag
|
||||
`-insecure`.
|
||||
|
||||
Note that even if you're using a reverse proxy, clients will attempt to
|
||||
establish direct UDP flows with Galene and direct TCP connections to
|
||||
Galene's TURN server; see the section on "Configuring your firewall"
|
||||
above.
|
||||
|
||||
|
||||
# Connectivity issues and ICE Servers
|
||||
|
||||
Most connectivity issues are due to an incorrect ICE configuration.
|
||||
|
||||
ICE is the NAT and firewall traversal protocol used by WebRTC. ICE can
|
||||
make use of two kinds of servers to help with NAT traversal: STUN servers,
|
||||
that help punching holes in well-behaved NATs, and TURN servers, that
|
||||
serve as relays for traffic. TURN is a superset of STUN: no STUN server
|
||||
is necessary if one or more TURN servers are available.
|
||||
|
||||
Galène includes an IPv4-only TURN server, which is controlled by the
|
||||
`-turn` command-line option. It has the following behaviour:
|
||||
|
||||
* if its value is set to the empty string `""`, then the built-in server
|
||||
is disabled; in this case, the file `data/ice-servers.json` configures
|
||||
an external TURN server;
|
||||
|
||||
* if its value is a colon followed with a port number, for example
|
||||
`:1194`, then the TURN server will listen on all public IPv4 addresses
|
||||
of the local host, over UDP and TCP; this is the recommended value if
|
||||
the server is not behind NAT, and the firewall allows incoming
|
||||
connections to port 1194;
|
||||
|
||||
* if the value of this option is a socket address, such as
|
||||
`203.0.113.1:1194`, then the TURN server will listen on all addresses
|
||||
of the local host but assume that the address seen by the clients is
|
||||
the one given in the option; this is useful when running behind NAT
|
||||
with port forwarding set up.
|
||||
|
||||
* the default value is `auto`, which behaves like `:1194` if there is no
|
||||
`data/ice-servers.json` file, and like `""` otherwise.
|
||||
|
||||
If the server is not accessible from the Internet, e.g. because of NAT or
|
||||
because it is behind a restrictive firewall, then you should configure
|
||||
a TURN server that runs on a host that is accessible by both Galène and
|
||||
the clients. Disable the built-in TURN server (`-turn ""` or the default
|
||||
`-turn auto`), and provide a working ICE configuration in the file
|
||||
`data/ice-servers.json`. In the case of a single STUN server, it should
|
||||
look like this:
|
||||
|
||||
[
|
||||
{
|
||||
"urls": [
|
||||
"stun:stun.example.org"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
In the case of s single TURN server, the `ice-servers.json` file should
|
||||
look like this:
|
||||
|
||||
[
|
||||
{
|
||||
"urls": [
|
||||
"turn:turn.example.org:443",
|
||||
"turn:turn.example.org:443?transport=tcp"
|
||||
],
|
||||
"username": "galene",
|
||||
"credential": "secret"
|
||||
}
|
||||
]
|
||||
|
||||
It is more secure to use coturn's `use-auth-secret` option. If you do
|
||||
that, then the `ice-servers.json` file 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. If an `ice-servers.json` file is present and
|
||||
Galène's built-in TURN server is enabled, then the external server will be
|
||||
used in preference to the built-in server.
|
||||
@@ -1,408 +0,0 @@
|
||||
Galene is a videoconferencing server that is easy to deploy and requires
|
||||
moderate server resources. It is described at <https://galene.org>.
|
||||
|
||||
|
||||
# Installation
|
||||
|
||||
Quick start:
|
||||
|
||||
git clone https://github.com/jech/galene
|
||||
cd galene
|
||||
CGO_ENABLED=0 go build -ldflags='-s -w'
|
||||
mkdir groups
|
||||
echo '{"users": {"bob": {"password":"1234", "permissions":"op"}}}' > \
|
||||
groups/example.json
|
||||
./galene &
|
||||
|
||||
Point your browser at <https://localhost:8443/group/example/>, ignore the
|
||||
unknown certificate warning, and log in with username "bob" and password
|
||||
"1234".
|
||||
|
||||
See the file INSTALL in this directory for full installation instructions.
|
||||
|
||||
|
||||
# Usage
|
||||
|
||||
## Locations
|
||||
|
||||
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 group operator.
|
||||
|
||||
Some statistics are available under `/stats.json`, with a human-readable
|
||||
version at `/stats.html`. This is only available to the server administrator.
|
||||
|
||||
|
||||
## Main interface
|
||||
|
||||
After logging in, the user is confronted with the main interface.
|
||||
|
||||
### Buttons
|
||||
|
||||
There are up to three buttons at the top. The *Enable*/*Disable* button
|
||||
enables either or both the camera and the microphone (depending on the
|
||||
options set in the side menu, see below). The *Mute* button mutes or
|
||||
unmutes the microphone. The *Share Screen* button shares the screen or
|
||||
a window.
|
||||
|
||||
### 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.
|
||||
|
||||
### User list
|
||||
|
||||
There is a user list on the left. Clicking on a user opens a menu with
|
||||
actions that can be applied to that user. Clicking on ones own username
|
||||
opens a menu with actions that are global to the group.
|
||||
|
||||
### Chat pane
|
||||
|
||||
Double-clicking on a message opens a contextual menu.
|
||||
|
||||
### Text box
|
||||
|
||||
Typing a string in the text box at the bottom of the chat pane sends
|
||||
a broadcast message to all of the users in the group.
|
||||
|
||||
Typing a line starting with a slash `/` in the text box 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.
|
||||
|
||||
|
||||
# The global configuration file
|
||||
|
||||
The server may be configured in the JSON file `data/config.json`. This
|
||||
file may look as follows:
|
||||
|
||||
{
|
||||
"users":{"root": {"password":"secret", "permissions": "admin"}},
|
||||
"canonicalHost": "galene.example.org"
|
||||
}
|
||||
|
||||
or, better, with a hashed password:
|
||||
|
||||
{
|
||||
"users": {
|
||||
"root": {
|
||||
"password":{"type":"bcrypt","key":"$2a$10$bTWW..."},
|
||||
"permissions": "admin"
|
||||
}
|
||||
},
|
||||
"canonicalHost": "galene.example.org"
|
||||
}
|
||||
|
||||
The fields are as follows:
|
||||
|
||||
- `users` defines the users allowed to administer the server, and has the
|
||||
same syntax as user definitions in groups (see below), except that the
|
||||
only meaningful permission is `"admin"`;
|
||||
|
||||
- `writableGroups`: if true, then the API can modify group description
|
||||
files; by default, group files are treated as read-only;
|
||||
|
||||
- `allowOrigin` is an array that contains the list of HTTP origins that
|
||||
are allowed to access the server.
|
||||
|
||||
- `allowAdminOrigin` is like `allowOrigin`, but applies to the
|
||||
administrative interface.
|
||||
|
||||
- `proxyURL`: if running behind a reverse proxy, this specifies the root
|
||||
URL that will be visible outside the proxy.
|
||||
|
||||
- `canonicalHost`: the canonical name of the host running the server;
|
||||
this will cause clients to be redirected if they use a different
|
||||
hostname to access the server.
|
||||
|
||||
|
||||
# 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`; it 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
|
||||
*teaching/networking*.
|
||||
|
||||
|
||||
## Examples
|
||||
|
||||
A typical group definition file looks like this:
|
||||
|
||||
{
|
||||
"users":{
|
||||
"jch": {"password":"1234", "permissions": "op"}
|
||||
},
|
||||
"allow-recording": true,
|
||||
"auto-subgroups": true
|
||||
}
|
||||
|
||||
This defines a group with the operator username *jch* and password *1234*.
|
||||
The `allow-recording` entry says that the operator is allowed to record
|
||||
videos to disk, and the `auto-subgroups` entry says that subgroups will be
|
||||
created automatically. This particular group does not allow password
|
||||
login for ordinary users, and is suitable if you use invitations (see
|
||||
*Stateful Tokens* below) for ordinary users.
|
||||
|
||||
In order to allow password login for ordinary users, add password entries
|
||||
with the permission `present`:
|
||||
|
||||
{
|
||||
"users":{
|
||||
"jch": {"password": "1234", "permissions": "op"},
|
||||
"john": {"password": "secret", "permissions": "present"}
|
||||
}
|
||||
}
|
||||
|
||||
If the group is to be publicly accessible, you may allow logins with any
|
||||
username using the `wildcard-user` entry::
|
||||
|
||||
{
|
||||
"users":{
|
||||
"jch": {"password":"1234", "permissions": "op"}
|
||||
},
|
||||
"wildcard-user": {"password": "1234", "permissions": "present"},
|
||||
"public": true
|
||||
}
|
||||
|
||||
If you want to allow users to use any password, use a wildcard password:
|
||||
|
||||
{
|
||||
"users":{
|
||||
"jch": {"password":"1234", "permissions": "op"}
|
||||
},
|
||||
"wildcard-user":
|
||||
{"password": {"type": "wildcard"}, "permissions": "present"},
|
||||
"public": true
|
||||
}
|
||||
|
||||
## Reference
|
||||
|
||||
Every group definition file contains a single JSON directory (a list of
|
||||
entries between `{` and `}`). 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. The following fields are allowed:
|
||||
|
||||
- `users`: is a dictionary that maps user names to dictionaries with
|
||||
entries `password` and `permissions`; see below for a description of
|
||||
possible permissions;
|
||||
|
||||
- `wildcard-user` is a dictionaries with entries `password` and `permissions`
|
||||
that will be used for usernames with no matching entry in the `users`
|
||||
dictionary;
|
||||
|
||||
- `authKeys`, `authServer` and `authPortal`: see *Authorisation* below;
|
||||
|
||||
- `public`: if true, then the group is listed on the landing page;
|
||||
|
||||
- `displayName`: a human-friendly version of the group name;
|
||||
|
||||
- `description`: a human-readable description of the group; this is
|
||||
displayed on the landing page for public groups;
|
||||
|
||||
- `contact`: a human-readable contact for this group, such as an e-mail
|
||||
address, ignored by the server;
|
||||
|
||||
- `comment`: a human-readable string, ignored by the server;
|
||||
|
||||
- `max-clients`: the maximum number of clients that may join the group at
|
||||
a time;
|
||||
|
||||
- `max-history-age`: the time, in seconds, during which chat history is
|
||||
kept (default 14400, i.e. 4 hours);
|
||||
|
||||
- `not-before` and `expires`: the times (in ISO 8601 or RFC 3339 format)
|
||||
between which joining the group is allowed;
|
||||
|
||||
- `allow-recording`: if true, then recording is allowed in this group;
|
||||
|
||||
- `unrestricted-tokens`: if true, then ordinary users (without the "op"
|
||||
privilege) are allowed to create tokens;
|
||||
|
||||
- `allow-anonymous`: if true, then users may connect with an empty username;
|
||||
|
||||
- `auto-subgroups`: if true, then subgroups of the form `group/subgroup`
|
||||
are automatically created when first accessed;
|
||||
|
||||
- `autolock`: if true, the group will start locked and become locked
|
||||
whenever there are no clients with operator privileges;
|
||||
|
||||
- `autokick`: if true, all clients will be kicked out whenever there are
|
||||
no clients with operator privileges; this is not recommended, prefer
|
||||
the `autolock` option instead;
|
||||
|
||||
- `redirect`: if set, then attempts to join the group will be redirected
|
||||
to the given URL; most other fields are ignored in this case;
|
||||
|
||||
- `codecs`: this is a list of codecs allowed in this group. The default
|
||||
is `["vp8", "opus"]`.
|
||||
|
||||
The value of the `permissions` entry in a user definition can either be an
|
||||
array of individual permissions, as carried by the protocol, or one of
|
||||
the following strings:
|
||||
|
||||
- `op`, a group operator with all rights except administering the group;
|
||||
- `present`, an ordinary user with the right to publish audio and video
|
||||
streams and send chat messages;
|
||||
- `message`, a user with the right to send chat messages;
|
||||
- `observe`, a user that receives media streams and chat messages, but
|
||||
is not allowed to send them;
|
||||
- `caption`, a user with the right to display captions (only);
|
||||
- `admin`, a user with the right to administer the group (only).
|
||||
|
||||
Supported video codecs include:
|
||||
|
||||
- `"vp8"` (compatible with all supported browsers);
|
||||
- `"vp9"` (better video quality, but incompatible with Safari; buggy in
|
||||
Firefox);
|
||||
- `"av1"` (even better video quality, only supported by some browsers,
|
||||
recording is not supported, SVC is not supported);
|
||||
- `"h264"` (incompatible with Debian and with some older Android devices,
|
||||
SVC is not supported).
|
||||
|
||||
Supported audio codecs include `"opus"`, `"g722"`, `"pcmu"` and `"pcma"`.
|
||||
Only Opus can be recorded to disk. There is no good reason to use
|
||||
anything except Opus.
|
||||
|
||||
|
||||
## Client Authorisation
|
||||
|
||||
Galene implements three authorisation methods: a simple username/password
|
||||
authorisation scheme, a scheme using stateful tokens and a mechanism based
|
||||
on cryptographic tokens that are generated by an external server. The
|
||||
former two mechanism are intended to be used in standalone installations,
|
||||
while the server-based mechanism is designed to allow easy integration
|
||||
with an existing authorisation infrastructure (such as LDAP, OAuth2, or
|
||||
even Unix passwords).
|
||||
|
||||
### Password authorisation
|
||||
|
||||
When password authorisation is used, authorised usernames and password are
|
||||
defined directly in the group configuration file, in the `users` and
|
||||
`wildcard-user` entries. The `users` entry is a dictionary that maps user
|
||||
names to user descriptions; the `wildcard-user` is a user description
|
||||
that is used with usernames that don't appear in `users`.
|
||||
|
||||
Every user description is a dictionary with fields `password` and
|
||||
`permissions`. The `password` field may be a literal password string, or
|
||||
a dictionary describing a hashed password or a wildcard. The
|
||||
`permissions` field should be one of `op`, `present`, `message` or
|
||||
`observe`. (An array of Galene's internal permissions is also allowed,
|
||||
but this is not recommended, since internal permissions may vary from
|
||||
version to version).
|
||||
|
||||
For example, the entry
|
||||
|
||||
"users": {"jch": {"password": "1234", "permissions": "op"}}
|
||||
|
||||
specifies that user "jch" may login as operator with password "1234", while
|
||||
|
||||
"wildcard-user": {"password": "1234", "permissions": "present"}
|
||||
|
||||
allows any username with password *1234*. Finally,
|
||||
|
||||
"wildcard-user":
|
||||
{"password": {"type": "wildcard"}, "permissions": "present"}
|
||||
|
||||
allows any username with any password.
|
||||
|
||||
|
||||
### Hashed passwords
|
||||
|
||||
If you don't wish to store cleartext passwords on the server, you may
|
||||
generate hashed passwords with the `galenectl` utility. A user entry with
|
||||
a hashed password looks like this:
|
||||
|
||||
"users": {
|
||||
"jch": {
|
||||
"password": {
|
||||
"type": "pbkdf2",
|
||||
"hash": "sha-256",
|
||||
"key": "f591c35604e6aef572851d9c3543c812566b032b6dc083c81edd15cc24449913",
|
||||
"salt": "92bff2ace56fe38f",
|
||||
"iterations": 4096
|
||||
},
|
||||
"permissions": "op"
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
### Stateful tokens
|
||||
|
||||
Stateful tokens allow to temporarily grant access to a user. In order to
|
||||
generate a stateful token, the group operator types
|
||||
|
||||
/invite user period
|
||||
|
||||
where `user` is the username granted to the temporary user, and `period`
|
||||
is the time period for which the token will be valid (for example `2d`
|
||||
meaning 2 days). The server replies with a link, valid the given time
|
||||
period, that may be sent to the temporary user for example by e-mail.
|
||||
|
||||
Tokens may also be granted without imposing a specific username:
|
||||
|
||||
/invite '' 2d
|
||||
|
||||
Stateful tokens are revokable (use the `/revoke` command) and their
|
||||
lifetime may be extended (use the `/reinvite` command).
|
||||
|
||||
|
||||
### Authorisation servers
|
||||
|
||||
Galene is able to delegate authorisation decisions to an external
|
||||
authorisation server. This makes it possible to integrate Galene with an
|
||||
existing authentication and authorisation infrastructure, such as LDAP,
|
||||
OAuth2 or even Unix passwords.
|
||||
|
||||
When an authorisation server is used, the group configuration file
|
||||
specifies one or more public keys in JWK format (with the restriction that
|
||||
the "alg" key must be specified). In addition, it may specify either an
|
||||
authorisation server or an authorisation portal.
|
||||
|
||||
{
|
||||
"authKeys": [{
|
||||
"kty": "oct",
|
||||
"alg": "HS256",
|
||||
"k": "MYz3IfCq4Yq-UmPdNqWEOdPl4C_m9imHHs9uveDUJGQ",
|
||||
}, {
|
||||
"kty": "EC",
|
||||
"alg": "ES256",
|
||||
"crv": "P-256",
|
||||
"x": "dElK9qBNyCpRXdvJsn4GdjrFzScSzpkz_I0JhKbYC88",
|
||||
"y": "pBhVb37haKvwEoleoW3qxnT4y5bK35_RTP7_RmFKR6Q",
|
||||
}]
|
||||
"authServer": "https://auth.example.org",
|
||||
}
|
||||
|
||||
If multiple keys are provided, then they will all be tried in turn, unless
|
||||
the token includes the "kid" header field, in which case only the
|
||||
specified key will be used.
|
||||
|
||||
If an authorisation server is specified, then the default client, after it
|
||||
prompts for a password, will request a token from the authorisation server
|
||||
and will join the group using token authentication. The password is never
|
||||
communicated to the server.
|
||||
|
||||
If an authorisation portal is specified, then the default client will
|
||||
redirect initial client connections to the authorisation portal. The
|
||||
authorisation portal is expected to authorise the client and then redirect
|
||||
it to Galene with the `username` and `token` query parameters set.
|
||||
|
||||
|
||||
# Further information
|
||||
|
||||
Galène's web page is at <https://galene.org>.
|
||||
|
||||
Answers to common questions and issues are at <https://galene.org/faq.html>.
|
||||
|
||||
|
||||
-- Juliusz Chroboczek <https://www.irif.fr/~jch/>
|
||||
@@ -0,0 +1,45 @@
|
||||
# The Galene videoconferencing system
|
||||
|
||||
Galene is a fully-features videoconferencing system that is easy to deploy
|
||||
and requires very moderate server resources. It is described at
|
||||
<https://galene.org>.
|
||||
|
||||
## Quick start
|
||||
|
||||
```sh
|
||||
git clone https://github.com/jech/galene
|
||||
cd galene
|
||||
CGO_ENABLED=0 go build -ldflags='-s -w'
|
||||
mkdir groups
|
||||
echo '{"users": {"vimes": {"password":"sybil", "permissions":"op"}}}' > groups/night-watch.json
|
||||
./galene &
|
||||
```
|
||||
|
||||
Point your browser at <https://localhost:8443/group/night-watch/>, ignore
|
||||
the unknown certificate warning, and log in with username *vimes* and
|
||||
password *sybil*.
|
||||
|
||||
For full installation instructions, please see the file [galene-install.md][1]
|
||||
in this directory.
|
||||
|
||||
## Documentation
|
||||
|
||||
* [galene-install.md][1]: full installation instructions
|
||||
* [galene.md][2]: usage and administration;
|
||||
* [galene-client.md][3]: writing clients;
|
||||
* [galene-protocol.md][4]: the client protocol;
|
||||
* [galene-api.md][4]: Galene's administrative API.
|
||||
|
||||
## Further information
|
||||
|
||||
Galène's web page is at <https://galene.org>.
|
||||
|
||||
Answers to common questions and issues are at <https://galene.org/faq.html>.
|
||||
|
||||
|
||||
-- Juliusz Chroboczek <https://www.irif.fr/~jch/>
|
||||
|
||||
[1]: <galene-install.md>
|
||||
[2]: <galene.md>
|
||||
[3]: <galene-client.md>
|
||||
[4]: <galene-protocol.md>
|
||||
@@ -0,0 +1,366 @@
|
||||
# Galene installation instructions
|
||||
|
||||
## Basic installation
|
||||
|
||||
### Build the Galene binary
|
||||
|
||||
Say:
|
||||
|
||||
```sh
|
||||
CGO_ENABLED=0 go build -ldflags='-s -w'
|
||||
```
|
||||
|
||||
On Windows, say:
|
||||
|
||||
```dosbat
|
||||
set CGO_ENABLED=0
|
||||
go build -ldflags="-s -w"
|
||||
```
|
||||
|
||||
If your server has a different architecture than the machine on which you
|
||||
are building, set the `GOOS` and `GOARCH` environment variables. For
|
||||
example, in order to compile for a 64-bit ARM system (a Raspberry Pi or an
|
||||
Olimex board, for example), you would say:
|
||||
|
||||
```sh
|
||||
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -ldflags='-s -w'
|
||||
```
|
||||
|
||||
### Optional: install libraries for background blur
|
||||
|
||||
Galene's client uses Google's MediaPipe library to implement background
|
||||
blur. This library is optional, and if it is absent, Galene will
|
||||
disable the menu entries for background blur.
|
||||
|
||||
Optionally install Google's MediaPipe library:
|
||||
|
||||
```sh
|
||||
mkdir mediapipe
|
||||
cd mediapipe
|
||||
npm pack @mediapipe/tasks-vision
|
||||
tar xzf mediapipe-tasks-vision-*.tgz
|
||||
rm -f ../static/third-party/tasks-vision
|
||||
mv package ../static/third-party/tasks-vision
|
||||
cd ../static/third-party/tasks-vision
|
||||
mkdir models
|
||||
cd models
|
||||
wget https://storage.googleapis.com/mediapipe-models/image_segmenter/selfie_segmenter/float16/latest/selfie_segmenter.tflite
|
||||
cd ../../../../
|
||||
```
|
||||
|
||||
### Deploy to your server
|
||||
|
||||
The following instructions assume that your server is called
|
||||
`galene.example.org` and that you have already created a dedicated user
|
||||
called `galene`.
|
||||
|
||||
First, create an empty directory called `groups`:
|
||||
|
||||
```sh
|
||||
mkdir groups
|
||||
```
|
||||
|
||||
Now copy the `galene` binary, and the directories `static`, `data` and
|
||||
`groups` to the server:
|
||||
|
||||
```sh
|
||||
rsync -a galene static data groups galene@galene.example.org:
|
||||
```
|
||||
|
||||
If you don't have a TLS certificate, Galène will generate a self-signed
|
||||
certificate (and print a warning to the logs). If you have a certificate,
|
||||
install it in the files `data/cert.pem` and `data/key.pem`:
|
||||
|
||||
```sh
|
||||
ssh galene@galene.example.org
|
||||
sudo cp /etc/letsencrypt/live/galene.example.org/fullchain.pem data/cert.pem
|
||||
sudo cp /etc/letsencrypt/live/galene.example.org/privkey.pem data/key.pem
|
||||
sudo chown galene:galene data/*.pem
|
||||
chmod go-rw data/key.pem
|
||||
```
|
||||
|
||||
Since certificates are regularly rotated, this should be done in a monthly
|
||||
cron job (or a *SystemD* timer unit, if you're feeling particularly kinky).
|
||||
|
||||
### Run Galene on the server
|
||||
|
||||
Arrange to run the binary on the server. If you never reboot your server,
|
||||
just do:
|
||||
|
||||
```sh
|
||||
ssh galene@galene.example.org
|
||||
ulimit -n 65536
|
||||
nohup ./galene &
|
||||
```
|
||||
|
||||
If you are using *runit*, use a script like the following:
|
||||
|
||||
```sh
|
||||
#!/bin/sh
|
||||
exec 2>&1
|
||||
cd ~galene
|
||||
ulimit -n 65536
|
||||
exec setuidgid galene ./galene
|
||||
```
|
||||
|
||||
If you are using *SystemD*, put the following in
|
||||
`/etc/systemd/system/galene.service`:
|
||||
|
||||
```ini
|
||||
[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
|
||||
```
|
||||
|
||||
### Set up galenectl
|
||||
|
||||
There are two ways to administer a Galene instance: by manually editing
|
||||
JSON files on the server, or by using the `galenectl` utility.
|
||||
The `galenectl` utility is recommended, since it avoids issues with
|
||||
concurrent modifications and is less error-prone than the alternative.
|
||||
|
||||
Build the `galenectl` utility, and copy it somewhere on your path:
|
||||
|
||||
```sh
|
||||
cd galenectl
|
||||
go build -ldflags='-s -w'
|
||||
sudo cp galenectl /usr/local/bin
|
||||
```
|
||||
|
||||
Now create an administrator password, and set up galenectl:
|
||||
|
||||
```sh
|
||||
galenectl -admin-username admin initial-setup
|
||||
```
|
||||
|
||||
This command creates two files: `galenectl.conf` and `config.json`. The
|
||||
former is already at the right place, the latter must be copied to the
|
||||
server's `data/` directory:
|
||||
|
||||
```sh
|
||||
rsync config.json galene@galene.example.org:data/
|
||||
```
|
||||
|
||||
### Group setup
|
||||
|
||||
Create a group:
|
||||
|
||||
```sh
|
||||
galenectl create-group -group city-watch
|
||||
```
|
||||
|
||||
If you didn't install a TLS certificate above, you will need to run
|
||||
`galenectl` with the flag `-insecure`:
|
||||
|
||||
```sh
|
||||
galenectl -insecure create-group -group city-watch
|
||||
```
|
||||
|
||||
Create an "op", a user with group moderation privileges:
|
||||
|
||||
```sh
|
||||
galenectl create-user -group city-watch -user vimes -permissions op
|
||||
```
|
||||
|
||||
Set the new user's password:
|
||||
|
||||
```sh
|
||||
galenectl set-password -group city-watch -user vimes
|
||||
```
|
||||
|
||||
You should now be able to test your Galene installation by pointing a web
|
||||
browser at <https://galene.example.org:8443/group/testing/>.
|
||||
|
||||
Create an ordinary user:
|
||||
|
||||
```sh
|
||||
galenectl create-user -group city-watch -user fred
|
||||
galenectl set-password -group city-watch -user fred
|
||||
```
|
||||
|
||||
Check the results:
|
||||
|
||||
```sh
|
||||
galenectl list-groups
|
||||
galenectl list-users -l -group city-watch
|
||||
```
|
||||
|
||||
Type `galenectl -help`, `galenectl create-group -help`, etc. for more
|
||||
information.
|
||||
|
||||
## Advanced configuration
|
||||
|
||||
Galene is designed to be exposed directly to the internet. If your server
|
||||
is behind a firewall or NAT router, some extra configuration is necessary.
|
||||
|
||||
### Running behind a firewall
|
||||
|
||||
If your server is behind a firewall but has a global IPv4 address (it is
|
||||
not behind NAT), then, at the very minimum, the firewall must allow
|
||||
incoming connections to:
|
||||
|
||||
* TCP port 8443 (or whatever is configured with the `-http` option); and
|
||||
|
||||
* TCP and UDP port 1194 (or whatever is configured with the `-turn` option).
|
||||
|
||||
For good performance, your firewall should allow incoming and outgoing
|
||||
traffic from the UDP ports used for media transfer. By default, these are
|
||||
all high-numbered (ephemeral) ports, but they can be restricted using one
|
||||
of the following options:
|
||||
|
||||
* the `-udp-range port1-port2` option restricts the UDP ports to be in
|
||||
the range from port1 to port2 inclusive; this should be a large range,
|
||||
on the order of a few tens of thousands of ports;
|
||||
|
||||
* the `-udp-range port` option makes the server use just a single port,
|
||||
and demultiplex the traffic in userspace.
|
||||
|
||||
At the time of writing, this mechanism is not quite complete, and you will
|
||||
see Galene attempting to use other ports. Unless you see connection
|
||||
failures, this is nothing to worry about.
|
||||
|
||||
### Running behind NAT
|
||||
|
||||
If your server is behind NAT, then currently the only option is to use
|
||||
a STUN, or, preferably, TURN server on a separate host, one that is not
|
||||
behind NAT. See Section "Connectivity issues and ICE servers" below.
|
||||
|
||||
Galene has some support for running behind NAT without a helpful server,
|
||||
but this has not been exhaustively tested. Please see the section
|
||||
"Connectivity issues and ICE server" below.
|
||||
|
||||
### Running behind a reverse proxy
|
||||
|
||||
Galene is designed to be directly exposed to the Internet. In order to
|
||||
run Galene behind a reverse proxy, you might need to make a number of
|
||||
tweaks to your configuration.
|
||||
|
||||
First, you might need to inform Galene of the URL at which users connect
|
||||
(the reverse proxy's URL) by adding an entry `proxyURL` to your
|
||||
`data/config.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"proxyURL": "https://galene.example.org/"
|
||||
}
|
||||
```
|
||||
|
||||
Second, and depending on your proxy implementation, you might need to
|
||||
request that the proxy pass WebSocket handshakes to the URL at `ws`; for
|
||||
example, with Nginx, you will need to say something like the following:
|
||||
|
||||
```
|
||||
location /ws {
|
||||
proxy_pass ...;
|
||||
proxy_set_header Upgrade $http_upgrade;
|
||||
proxy_set_header Connection "Upgrade";
|
||||
}
|
||||
```
|
||||
|
||||
Finally, in order to avoid TLS termination issues, you may want to run
|
||||
Galene over plain HTTP instead of HTTPS by using the command-line flag
|
||||
`-insecure`.
|
||||
|
||||
Note that even if you're using a reverse proxy, clients will attempt to
|
||||
establish direct UDP flows with Galene and direct TCP connections to
|
||||
Galene's TURN server; see the section on "Configuring your firewall"
|
||||
above.
|
||||
|
||||
## Connectivity issues and ICE servers
|
||||
|
||||
Most connectivity issues are due to an incorrect ICE configuration.
|
||||
|
||||
ICE is the NAT and firewall traversal protocol used by WebRTC. ICE can
|
||||
make use of two kinds of servers to help with NAT traversal: STUN servers,
|
||||
that help punching holes in well-behaved NATs, and TURN servers, that
|
||||
serve as relays for traffic. TURN is a superset of STUN: no STUN server
|
||||
is necessary if one or more TURN servers are available.
|
||||
|
||||
Galène includes an IPv4-only TURN server, which is controlled by the
|
||||
`-turn` command-line option. It has the following behaviour:
|
||||
|
||||
* if its value is set to the empty string `""`, then the built-in server
|
||||
is disabled; in this case, the file `data/ice-servers.json` configures
|
||||
an external TURN server;
|
||||
|
||||
* if its value is a colon followed with a port number, for example
|
||||
`:1194`, then the TURN server will listen on all public IPv4 addresses
|
||||
of the local host, over UDP and TCP; this is the recommended value if
|
||||
the server is not behind NAT, and the firewall allows incoming
|
||||
connections to the TURN port.
|
||||
|
||||
* if the value of this option is a socket address, such as
|
||||
`203.0.113.1:1194`, then the TURN server will listen on all addresses
|
||||
of the local host but assume that the address seen by the clients is
|
||||
the one given in the option; this may be useful when running behind NAT
|
||||
with port forwarding set up.
|
||||
|
||||
* the default value is `auto`, which behaves like `:1194` if there is no
|
||||
`data/ice-servers.json` file, and like `""` otherwise.
|
||||
|
||||
If the server is not accessible from the Internet, e.g. because of NAT or
|
||||
because it is behind a restrictive firewall, then you should configure
|
||||
a TURN server that runs on a host that is accessible by both Galène and
|
||||
the clients. Disable the built-in TURN server (`-turn ""` or the default
|
||||
`-turn auto`), and provide a working ICE configuration in the file
|
||||
`data/ice-servers.json`. In the case of a single STUN server, it should
|
||||
look like this:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"urls": [
|
||||
"stun:stun.example.org"
|
||||
]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
In the case of s single TURN server, the `ice-servers.json` file should
|
||||
look like this:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"urls": [
|
||||
"turn:turn.example.org:443",
|
||||
"turn:turn.example.org:443?transport=tcp"
|
||||
],
|
||||
"username": "galene",
|
||||
"credential": "secret"
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
It is more secure to use coturn's `use-auth-secret` option. If you do
|
||||
that, then the `ice-servers.json` file should look like this:
|
||||
|
||||
```json
|
||||
[
|
||||
{
|
||||
"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. If an `ice-servers.json` file is present and
|
||||
Galène's built-in TURN server is enabled, then the external server will be
|
||||
used in preference to the built-in server.
|
||||
@@ -0,0 +1,575 @@
|
||||
# Galene manual
|
||||
|
||||
Please see the file [galene-install.md][1] for installation instructions.
|
||||
Please see the section "Server administration" below for detailed
|
||||
administration instructions.
|
||||
|
||||
## Usage
|
||||
|
||||
Galene includes a web server that, by default, listens on port 8443.
|
||||
Galene is therefore accessed on URLs such as:
|
||||
|
||||
https://galene.example.org:8443/
|
||||
|
||||
### The landing page
|
||||
|
||||
There is a landing page at the root of the server. The landing page
|
||||
contains a list of all groups marked "public" in their configuration, as
|
||||
well as a form that allows joining an arbitrary group.
|
||||
|
||||
Going through the landing page is not required: you are welcome to point
|
||||
your browser directly at the desired group URL.
|
||||
|
||||
### The group pages
|
||||
|
||||
A group named e.g. *city-watch* has an associated page
|
||||
`/group/city-watch/`. After login, it presents an interface consisting
|
||||
of three panes:
|
||||
|
||||
- the left side pane contains the list of users who have joined the
|
||||
group; every username doubles as a menu of group and user actions;
|
||||
- the middle pane contains the chat messages published to the group;
|
||||
- the main pane, on the right, contains the videos being streamed by
|
||||
users in the group.
|
||||
|
||||
On mobile, only the latter pane is shown by default. An icon at the top
|
||||
left opens the user list, and an icon in the main pane switches to the
|
||||
chat.
|
||||
|
||||
### Buttons
|
||||
|
||||
There are up to three buttons at the top. The most important is The
|
||||
*Enable*/*Disable* button, which switches on or off both the camera and
|
||||
the microphone.
|
||||
|
||||
The *Mute* button mutes or unmutes the microphone; the microphone can be
|
||||
muted remotely by the group moderator, but it cannot be unmuted remotely.
|
||||
|
||||
The *Share screen* button streams the contents of the screen or an
|
||||
individual window.
|
||||
|
||||
### Side menu
|
||||
|
||||
There is a menu on the right of the user interface. It allows switching
|
||||
off the camera (for audio-only sessions), 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 streams a video from a local file.
|
||||
|
||||
### User list
|
||||
|
||||
There is a user list on the left, starting with the current user and
|
||||
continuing with all users that have joined the current group.
|
||||
|
||||
The user list doubles as a set of menu. Clicking on the current user (the
|
||||
first entry in the user list) opens the *group menu*, a menu with actions
|
||||
that apply to the group as a whole. Clicking on a different user opens
|
||||
a *user menu*, a menu that applies to that specific user.
|
||||
|
||||
### Chat pane
|
||||
|
||||
The center pane is a traditional chat interface, with an input form at the
|
||||
bottom and the chat history above it. Chat history is never saved to
|
||||
disk, and is erased after four hours (or whatever is specified in the
|
||||
`"max-history-age"` field of the group definition).
|
||||
|
||||
Double-clicking on a message opens a contextual menu.
|
||||
|
||||
The chat form doubles as a command-line interface, which is especially
|
||||
important for visually-impaired users, and more generally is often faster
|
||||
than navigating the user interface. Commands start with a slash character
|
||||
"`/`". The most important command is `/msg`, which sends a private
|
||||
message to a given user. Type `/help` to display the list of available
|
||||
commands.
|
||||
|
||||
### Inviting users
|
||||
|
||||
In order to generate an invitation link, choose the entry "Invite user" in
|
||||
the group menu. This generates a link of the form
|
||||
|
||||
https://galene.example.org:8443/group/city-watch/?token=XXX
|
||||
|
||||
where the *XXX* part, known as the *token*, is a shared secret. Such
|
||||
a link allows password-less login to the group, and may therefore be
|
||||
shared e.g. over e-mail or instant messaging.
|
||||
|
||||
The invitiation functionality is usually restricted to the moderator;
|
||||
however, groups may be configured with the `"unrestricted-tokens"` option,
|
||||
which allows all users to generate tokens.
|
||||
|
||||
Tokens can be created, modified, and expires using the `/invite`,
|
||||
`/reinvite` and `/revoke` commands.
|
||||
nnnn
|
||||
### File transfer
|
||||
|
||||
Galene includes a peer-to, end-to-end encrypted file transfer protocol.
|
||||
In order to transfer a file, click on the receiver's entry in the user
|
||||
list and choose "Send file".
|
||||
|
||||
### Group moderation
|
||||
|
||||
If a user has the *op* permission (short for *Operator*), then they have
|
||||
access to a number of moderation tools.
|
||||
|
||||
For a moderator, the contextual menu that opens when clicking on an entry
|
||||
in the user list is expanded with commands for muting a user, sending them
|
||||
a warning, retrieving their IP address, or kicking them out from a group.
|
||||
|
||||
The group menu (opened by clicking on one's own entry in the user's list)
|
||||
is extended with options to lock or to unlock a group (a locked group is
|
||||
one that non-operator users cannot join).
|
||||
|
||||
All of the moderation commands are also available as command-line commands
|
||||
(see above), which is helpful when moderating large groups.
|
||||
|
||||
# Server administration
|
||||
|
||||
|
||||
## The global configuration file
|
||||
|
||||
The server may be configured in the JSON file `data/config.json`. This
|
||||
file may look as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"users":{"vetinari": {"password":"lagniappe", "permissions": "admin"}},
|
||||
"canonicalHost": "galene.example.org",
|
||||
"writableGroups": true
|
||||
}
|
||||
```
|
||||
|
||||
or, better, with a hashed password:
|
||||
|
||||
```json
|
||||
{
|
||||
"users": {
|
||||
"vetinari": {
|
||||
"password":{"type":"bcrypt","key":"$2a$10$bTWW..."},
|
||||
"permissions": "admin"
|
||||
}
|
||||
},
|
||||
"canonicalHost": "galene.example.org",
|
||||
"writableGroups": true
|
||||
}
|
||||
```
|
||||
|
||||
The file is initially set up using `galenectl initial-setup`, but may be
|
||||
manually edited at any time (there is no need to restart the server). The
|
||||
fields are as follows:
|
||||
|
||||
- `users` defines the users allowed to administer the server, and has the
|
||||
same syntax as user definitions in groups (see below), except that the
|
||||
only meaningful permission is `"admin"`;
|
||||
|
||||
- `writableGroups`: if true, then the API used by `galenectl` can be used
|
||||
to modify group definitions; if unset or false, then only read-only
|
||||
access is allowed;
|
||||
|
||||
- `allowOrigin` is an array that contains the list of HTTP origins that
|
||||
are allowed to access the server;
|
||||
|
||||
- `allowAdminOrigin` is like `allowOrigin`, but applies to the
|
||||
administrative API (the one used by `galenectl`);
|
||||
|
||||
- `proxyURL`: if running behind a reverse proxy, this specifies the root
|
||||
URL that will be visible outside the proxy;
|
||||
|
||||
- `canonicalHost`: the canonical name of the host running the server;
|
||||
clients that attempt to access the server using a different host name
|
||||
will be redirected to the canonical one.
|
||||
|
||||
|
||||
## Group definitions
|
||||
|
||||
Groups are described by JSON files in the `./groups/` directory. These
|
||||
files are normally administered using the `galenectl` utility, but may
|
||||
also be edited manually (there is no need to restart the server).
|
||||
|
||||
### Managing groups using `galenectl`
|
||||
|
||||
#### Creating, modifying, and suppressing groups
|
||||
|
||||
A group is created using `galenectl create-group`:
|
||||
|
||||
```sh
|
||||
galenectl create-group -group city-watch
|
||||
```
|
||||
|
||||
There are a number of options to customise the behaviour of the group, see
|
||||
`galenectl create-group -help` for a full list. For example, in order to
|
||||
create a group that allows unrestricted creation of tokens, say:
|
||||
|
||||
```sh
|
||||
galenectl create-group -group city-watch -unrestricted-tokens
|
||||
```
|
||||
|
||||
For more advanced configuration, `galenectl create-group` can be invoked
|
||||
with the `-json` flag, in which case it takes a JSON template on standard
|
||||
input. The syntax of a JSON template is just like that of a group
|
||||
definition file (see below), except that it must not contain the fields
|
||||
`users` and `wildcard-user`. For example, in order to create a redirect
|
||||
(see the section "Group description reference" below):
|
||||
|
||||
```sh
|
||||
echo '{"redirect": "https://galene.example.org:8443/group/city-watch/"}' | galenectl create-group -group amcw -json
|
||||
```
|
||||
|
||||
Groups are modified using `galenectl update-group`:
|
||||
|
||||
```sh
|
||||
galenectl update-group -group city-watch -unrestricted-tokens=false
|
||||
```
|
||||
|
||||
If a JSON template is provided to `galenectl update-group`, then it is
|
||||
merged with the existing group configuration. Entries may be suppressed
|
||||
by setting them to `null` in the template:
|
||||
|
||||
```sh
|
||||
echo '{"redirect": null}' | galenectl update-group -group amcw
|
||||
```
|
||||
|
||||
A group is deleted using `galenectl delete-group`:
|
||||
|
||||
```sh
|
||||
galenectl delete-group -group amcw
|
||||
```
|
||||
|
||||
#### Creating, modifying, and suppressing users
|
||||
|
||||
A user entry is created with the `galenectl create-user` command :
|
||||
|
||||
```sh
|
||||
galenectl create-user -group city-watch -user vimes -permissions op
|
||||
```
|
||||
|
||||
If the `-permissions` flag is not specified, it defaults to `present`,
|
||||
meaning that the user can participate in the chat and present videos to
|
||||
the group. The other useful values are `message`, which allows a user
|
||||
to participate in the chat only, and `observe`, which doesn't allow any
|
||||
active participation.
|
||||
|
||||
A user is modified using `galenectl update-user`, and suppressed using
|
||||
`galenectl delete-user`.
|
||||
|
||||
In order to be useful, a user entry needs to be assigned a password. This
|
||||
is done with the `galenectl set-password` command:
|
||||
|
||||
```sh
|
||||
galenectl set-password -group city-watch -user vimes
|
||||
```
|
||||
|
||||
#### The fallback user
|
||||
|
||||
It is sometimes useful to allow multiple users to log-in using the same
|
||||
password. This is achieved by defining the *wildcard* user:
|
||||
|
||||
```sh
|
||||
galenectl create-user -group city-watch -wildcard
|
||||
galenectl set-password -group city-watch -wildcard
|
||||
```
|
||||
|
||||
For open groups, where any user can login with any password, the wildcard
|
||||
user's password is set to the password of type `wildcard`:
|
||||
|
||||
```sh
|
||||
galenectl set-password -group city-watch -wildcard -type wildcard
|
||||
```
|
||||
|
||||
See the section "Client authorisation" below for more information about
|
||||
password types.
|
||||
|
||||
#### Automatic subgroups
|
||||
|
||||
It is sometimes necessary to create large numbers of identical groups.
|
||||
For example, the author has been using Galene to supervise computer
|
||||
science practicals, where up to 40 students are working in groups of two.
|
||||
|
||||
While it is possible to automate the creation of groups, by accessing
|
||||
Galene's API, by scripting calls to galenectl, or by directly generating
|
||||
files under `groups/`, Galene provides a facility known as *automatic
|
||||
subgroups* that can be used to generate groups on demand.
|
||||
|
||||
Automatic subgroups are enabled by setting the `"auto-subgroups"`
|
||||
field in the group description:
|
||||
|
||||
```sh
|
||||
galenectl create-group unseen-university -auto-subgroups
|
||||
```
|
||||
|
||||
Whenever a user attempts to access a subgroup of `unseen-university`, for
|
||||
example `unseen-university/hex`, the group is created in memory and
|
||||
persists until it is empty and its chat history has expired. The main
|
||||
group's operator can view the list of populated subgroups with the command
|
||||
`/subgroups`.
|
||||
|
||||
#### Managing tokens
|
||||
|
||||
Tokens are normally managed using the `/invite`, `/reinvite` and `/expire`
|
||||
commands in Galene's user interface, but they may also be managed using
|
||||
the `galenectl` utility's `create-token`, `revoke-token`, `delete-token`
|
||||
and `list-tokens` commands:
|
||||
|
||||
```sh
|
||||
galenectl create-token -group city-watch
|
||||
galenectl list-tokens -l -group city-watch
|
||||
```
|
||||
|
||||
A token that is generated with the `-include-subgroups` flag applies to
|
||||
the whole hierarchy rooted at the given group, including both ordinary
|
||||
groups and automatically generated subgroups.
|
||||
|
||||
```sh
|
||||
galenectl create-token -group city-watch -include-subgroups
|
||||
```
|
||||
|
||||
Such a token can be attached to the root of the group hierarchy, and
|
||||
therefore be valid for any group on the server:
|
||||
|
||||
```sh
|
||||
galenectl create-token -group '' -include-subgroups
|
||||
```
|
||||
|
||||
### Group description reference
|
||||
|
||||
The definition for the group called *groupname* is in the file
|
||||
`groups/groupname.json`; it 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
|
||||
*teaching/networking*.
|
||||
|
||||
Every group definition file contains a single JSON dictionary. All fields
|
||||
are optional. The following fields are allowed:
|
||||
|
||||
- `users`: is a dictionary that maps user names to user descriptions (see
|
||||
below);
|
||||
|
||||
- `wildcard-user` is a user description that will be used for usernames
|
||||
with no matching entry in the `users` dictionary;
|
||||
|
||||
- `authKeys`, `authServer` and `authPortal`: see *Authorisation* below;
|
||||
|
||||
- `public`: if true, then the group is listed on the landing page;
|
||||
|
||||
- `displayName`: a human-friendly version of the group name; this is
|
||||
displayed at the top of the group page;
|
||||
|
||||
- `description`: a human-readable description of the group; this is
|
||||
displayed on the landing page for public groups;
|
||||
|
||||
- `contact`: a human-readable contact for this group, such as an e-mail
|
||||
address, ignored by the server;
|
||||
|
||||
- `comment`: a human-readable string, ignored by the server;
|
||||
|
||||
- `max-clients`: the maximum number of clients that may join the group at
|
||||
a time;
|
||||
|
||||
- `max-history-age`: the time, in seconds, during which chat history is
|
||||
kept (default 14400, i.e. 4 hours);
|
||||
|
||||
- `not-before` and `expires`: the times (in ISO 8601 or RFC 3339 format)
|
||||
between which joining the group is allowed;
|
||||
|
||||
- `allow-recording`: if true, then recording is allowed in this group;
|
||||
|
||||
- `unrestricted-tokens`: if true, then ordinary users (without the "op"
|
||||
privilege) are allowed to create tokens;
|
||||
|
||||
- `allow-anonymous`: if true, then users may connect with an empty username;
|
||||
|
||||
- `auto-subgroups`: if true, then subgroups of the form `group/subgroup`
|
||||
are automatically created when first accessed;
|
||||
|
||||
- `autolock`: if true, the group will start locked and become locked
|
||||
whenever there are no clients with operator privileges;
|
||||
|
||||
- `autokick`: if true, all clients will be kicked out whenever there are
|
||||
no clients with operator privileges; this is not recommended, prefer
|
||||
the `autolock` option instead;
|
||||
|
||||
- `redirect`: if set, then attempts to join the group will be redirected
|
||||
to the given URL; most other fields are ignored in this case;
|
||||
|
||||
- `codecs`: this is a list of codecs allowed in this group, see below for
|
||||
possible values. The default is `["vp8", "opus"]`.
|
||||
|
||||
A user definition is a dictionary with entries `password` and
|
||||
`permission`. The value of the `password` field is either a plaintext
|
||||
password, or a hashed password generated for example by the `galenectl
|
||||
hash-password` command. The value of the `permissions` field can either
|
||||
be an array of individual permissions (not recommended), or one of the
|
||||
following strings:
|
||||
|
||||
- `op`, a group operator, with all rights except administering the group;
|
||||
- `present`, an ordinary user with the right to publish audio and video
|
||||
streams and send chat messages;
|
||||
- `message`, a user with the right to send chat messages;
|
||||
- `observe`, a user that receives media streams and chat messages, but
|
||||
is not allowed to send them;
|
||||
- `caption`, a user with the right to display captions (only);
|
||||
- `admin`, a user with the right to administer the group (only).
|
||||
|
||||
The value of the `codecs` field is an array of codecs allowed in the
|
||||
group. Supported video codecs include:
|
||||
|
||||
- `"vp8"` (compatible with all supported browsers, full functionality);
|
||||
- `"vp9"` (better video quality, but incompatible with Safari; somewhat
|
||||
buggy in Firefox; full functionality);
|
||||
- `"av1"` (even better video quality, only supported by some browsers,
|
||||
limited functionality: no recording, no SVC);
|
||||
- `"h264"` (well supported by Apple devices, but incompatible with Debian
|
||||
Linux and with some older Android devices, SVC is not supported; might
|
||||
be covered by patents in some countries).
|
||||
|
||||
Supported audio codecs include `"opus"`, `"g722"`, `"pcmu"` and `"pcma"`.
|
||||
Only Opus can be recorded to disk. There is no good reason to use
|
||||
anything except Opus.
|
||||
|
||||
## Client Authorisation
|
||||
|
||||
Galene implements three authorisation methods: a username/password
|
||||
authorisation scheme, a scheme using stateful tokens, and a mechanism
|
||||
based on cryptographic tokens. The former two mechanism are intended to
|
||||
be used in standalone installations, while the cryptographic mechanism is
|
||||
designed to allow easy integration with an existing authorisation
|
||||
infrastructure (such as LDAP, OAuth2, or even Unix passwords).
|
||||
|
||||
### Password authorisation
|
||||
|
||||
When password authorisation is used, authorised usernames and password are
|
||||
defined directly in the group configuration file, in the `users` and
|
||||
`wildcard-user` entries. The `users` entry is a dictionary that maps user
|
||||
names to user descriptions; the `wildcard-user` is a user description
|
||||
that is used with usernames that don't appear in `users`. These two
|
||||
entries are usually managed by the `galenectl` utility.
|
||||
|
||||
Every user description is a dictionary with fields `password` and
|
||||
`permissions`. The `password` field may be a literal password string, or
|
||||
a dictionary describing a hashed password or a wildcard. The
|
||||
`permissions` field should be one of `op`, `present`, `message` or
|
||||
`observe`. (An array of Galene's internal permissions is also allowed,
|
||||
but this is not recommended, since internal permissions may vary from
|
||||
version to version).
|
||||
|
||||
For example, the entry
|
||||
|
||||
```json
|
||||
{
|
||||
"users": {"vimes": {"password": "sybil", "permissions": "op"}}
|
||||
}
|
||||
```
|
||||
|
||||
specifies that user "vimes" may login as operator with password "sybil", while
|
||||
|
||||
```json
|
||||
{
|
||||
"wildcard-user": {"password": "1234", "permissions": "present"}
|
||||
}
|
||||
````
|
||||
|
||||
allows any username with password *1234*. Finally,
|
||||
|
||||
```json
|
||||
{
|
||||
"wildcard-user":
|
||||
{"password": {"type": "wildcard"}, "permissions": "present"}
|
||||
}
|
||||
```
|
||||
|
||||
allows any username with any password.
|
||||
|
||||
### Hashed passwords
|
||||
|
||||
For security reasons, passwords are usually hashed before being stored in
|
||||
group descriptions (in fact, the `galenectl` utility does not even support
|
||||
storing plaintext passwords). A hased password is represented as a JSON
|
||||
dicttionary with a field `type` and a number of type-specific fields.
|
||||
|
||||
A user entry with a hashed password looks like this:
|
||||
|
||||
```json
|
||||
"users": {
|
||||
"vimes": {
|
||||
"password": {
|
||||
"type": "pbkdf2",
|
||||
"hash": "sha-256",
|
||||
"key": "f591c35604e6aef572851d9c3543c812566b032b6dc083c81edd15cc24449913",
|
||||
"salt": "92bff2ace56fe38f",
|
||||
"iterations": 4096
|
||||
},
|
||||
"permissions": "op"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Hashed passwords are normally generated transparently to the user by the
|
||||
`galenectl set-password` command. When edition group description files
|
||||
manually, hashed passwords can be generated with the `galenectl hash-password`
|
||||
utility.
|
||||
|
||||
### Stateful tokens
|
||||
|
||||
Stateful tokens are created by the `/invite` command in the Galene user
|
||||
interface or by the `galenectl create-token` command; see the section
|
||||
*Managing tokens* above. They are stored in the file
|
||||
`data/var/tokens.jsonl`, which, on most filesystems, can be safely backed
|
||||
up without stopping the server.
|
||||
|
||||
### Cryptographic tokens
|
||||
|
||||
In many cases, it is useful to delegate authorisation decisions to a third
|
||||
party, such as an LDAP or OAuth2 client. Galene implements delegation of
|
||||
authorisation decisions using cryptographic tokens generated by a third
|
||||
party known as an *authorisaton server*. Two authorisation servers are
|
||||
available: an [LDAP client][2], and a [sample server written in Python][3].
|
||||
|
||||
When an authorisation server is used, the `"authKeys"` entry of the group
|
||||
configuration file specifies one or more public keys in JWK format (with
|
||||
the restriction that the "alg" key must be specified explicitly):
|
||||
|
||||
```json
|
||||
{
|
||||
"authKeys": [{
|
||||
"kty": "oct",
|
||||
"alg": "HS256",
|
||||
"k": "MYz3IfCq4Yq-UmPdNqWEOdPl4C_m9imHHs9uveDUJGQ",
|
||||
}, {
|
||||
"kty": "EC",
|
||||
"alg": "ES256",
|
||||
"crv": "P-256",
|
||||
"x": "dElK9qBNyCpRXdvJsn4GdjrFzScSzpkz_I0JhKbYC88",
|
||||
"y": "pBhVb37haKvwEoleoW3qxnT4y5bK35_RTP7_RmFKR6Q",
|
||||
}]
|
||||
}
|
||||
```
|
||||
|
||||
If multiple keys are provided, then they will all be tried in turn, unless
|
||||
the token includes the "kid" header field, in which case only the
|
||||
specified key will be used.
|
||||
|
||||
The group file should also specify either an authorisation server or an
|
||||
authorisation portal. An authorisation server is specified using the
|
||||
`"authServer"` key:
|
||||
|
||||
```json
|
||||
{
|
||||
"authServer": "https://auth.example.org",
|
||||
}
|
||||
```
|
||||
|
||||
If an authorisation server is specified, then the client, after it prompts
|
||||
for a password, will request a token from the authorisation server and
|
||||
join the group using token authentication. The password is never
|
||||
communicated to the server.
|
||||
|
||||
Alternatively, the group file may specify an authorisation portal using
|
||||
the `"authPortal"` key
|
||||
|
||||
If an authorisation portal is specified, then the default client will
|
||||
redirect initial client connections to the authorisation portal. The
|
||||
authorisation portal is expected to authorise the client and then redirect
|
||||
it to Galene with the `username` and `token` query parameters set.
|
||||
|
||||
[1]: <galene-install.md>
|
||||
[2]: <https://github.com/jech/galene-imap/>
|
||||
[3]: <https://github.com/jech/galene-sample-auth-server/>
|
||||
Reference in New Issue
Block a user