diff --git a/INSTALL b/INSTALL deleted file mode 100644 index 80e8927..0000000 --- a/INSTALL +++ /dev/null @@ -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. diff --git a/README b/README deleted file mode 100644 index 9e0d1d6..0000000 --- a/README +++ /dev/null @@ -1,408 +0,0 @@ -Galene is a videoconferencing server that is easy to deploy and requires -moderate server resources. It is described at . - - -# 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 , 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 . - -Answers to common questions and issues are at . - - --- Juliusz Chroboczek diff --git a/README.md b/README.md new file mode 100644 index 0000000..1546f8f --- /dev/null +++ b/README.md @@ -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 +. + +## 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 , 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 . + +Answers to common questions and issues are at . + + +-- Juliusz Chroboczek + +[1]: +[2]: +[3]: +[4]: diff --git a/README.API b/galene-api.md similarity index 100% rename from README.API rename to galene-api.md diff --git a/README.FRONTEND b/galene-client.md similarity index 100% rename from README.FRONTEND rename to galene-client.md diff --git a/galene-install.md b/galene-install.md new file mode 100644 index 0000000..8e1e289 --- /dev/null +++ b/galene-install.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 . + +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. diff --git a/README.PROTOCOL b/galene-protocol.md similarity index 100% rename from README.PROTOCOL rename to galene-protocol.md diff --git a/galene.md b/galene.md new file mode 100644 index 0000000..ffd8eda --- /dev/null +++ b/galene.md @@ -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]: +[2]: +[3]: