Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mumble #131

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,7 @@ configured, you may need to open these ports in your firewall:
| TLS | TCP | 8883 | Traefik MQTT (TLS) entrypoint |
| WebRTC | UDP | 10000 | Jitsi Meet video bridge (direct-map) |
| VPN | UDP | 51820 | Wireguard (Traefik VPN) (direct-map) |
| TCP socket | TCP | 64738 | Traefik Mumble (VoIP) entrypoint |

The ports that are listed as `(direct-map)` are not connected to
Traefik, but are directly exposed (public) to the docker host network.
Expand Down
25 changes: 25 additions & 0 deletions mumble/.env-dist
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# The domain name for the mumble service:
MUMBLE_TRAEFIK_HOST=mumble.example.com
MUMBLE_VERSION=v1.4.230-6

# The name of this instance. If there is only one instance, use 'default'.
MUMBLE_INSTANCE=

# Filter access by IP address source range (CIDR):
##Disallow all access: 0.0.0.0/32
##Allow all access: 0.0.0.0/0
MUMBLE_IP_SOURCERANGE=0.0.0.0/0

MUMBLE_SUPERUSER_PASSWORD=

MUMBLE_VERBOSE=false

## Mumble config
## All vars start with MUMBLE_CONFIG_ prefix
## See all config vars @ https://wiki.mumble.info/wiki/Murmur.ini
MUMBLE_CONFIG_WELCOME_TEXT="Yo welcome to mumble powered by d.rymcg.tech"

## All users are required to use client certificates:
MUMBLE_CONFIG_CERT_REQUIRED=true
## All users are placed in the Root channel (id=0) by default:
MUMBLE_CONFIG_DEFAULT_CHANNEL=0
53 changes: 53 additions & 0 deletions mumble/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
ROOT_DIR = ..
include ${ROOT_DIR}/_scripts/Makefile.projects-no-open
include ${ROOT_DIR}/_scripts/Makefile.instance

.PHONY: config-hook
config-hook:
#### This interactive configuration wizard creates the .env_{DOCKER_CONTEXT}_{INSTANCE} config file using .env-dist as the template:
#### reconfigure_ask asks the user a question to set the variable into the .env file, and with a provided default value.
#### reconfigure sets the value of a variable in the .env file without asking.
#### reconfigure_htpasswd will configure the HTTP Basic Authentication setting the var name and with a provided default value.
@${BIN}/reconfigure_ask ${ENV_FILE} MUMBLE_TRAEFIK_HOST "Enter the mumble domain name" mumble${INSTANCE_URL_SUFFIX}.${ROOT_DOMAIN}
@${BIN}/reconfigure ${ENV_FILE} MUMBLE_INSTANCE=$${instance:-default}
@${BIN}/reconfigure_password ${ENV_FILE} MUMBLE_SUPERUSER_PASSWORD
@echo ""

.PHONY: override-hook
override-hook:
#### This sets the override template variables for docker-compose.instance.yaml:
#### The template dynamically renders to docker-compose.override_{DOCKER_CONTEXT}_{INSTANCE}.yaml
#### These settings are used to automatically generate the service container labels, and traefik config, inside the template.
#### The variable arguments have three forms: `=` `=:` `=@`
#### name=VARIABLE_NAME # sets the template 'name' field to the value of VARIABLE_NAME found in the .env file
#### # (this hardcodes the value into docker-compose.override.yaml)
#### name=:VARIABLE_NAME # sets the template 'name' field to the literal string 'VARIABLE_NAME'
#### # (this hardcodes the string into docker-compose.override.yaml)
#### name=@VARIABLE_NAME # sets the template 'name' field to the literal string '${VARIABLE_NAME}'
#### # (used for regular docker-compose expansion of env vars by name.)
@${BIN}/docker_compose_override ${ENV_FILE} project=:mumble instance=@MUMBLE_INSTANCE traefik_host=@MUMBLE_TRAEFIK_HOST ip_sourcerange=@MUMBLE_IP_SOURCERANGE


.PHONY: shell
shell:
@make --no-print-directory docker-compose-shell SERVICE=mumble COMMAND=/bin/bash

.PHONY: open
open:
@echo "##"
@echo "## Download the mumble client from your package manager"
@echo "## or from https://www.mumble.info/"
@echo "##"
@echo "## Use your mumble client to connect to your server:"
@echo "## address: $$(${BIN}/dotenv -f ${ENV_FILE} get MUMBLE_TRAEFIK_HOST)"
@echo "## port: $$(${BIN}/dotenv -f ../traefik/.env_${DOCKER_CONTEXT}_default get TRAEFIK_MUMBLE_ENTRYPOINT_PORT)"
@echo "## username: SuperUser"
@echo "## password: $$(${BIN}/dotenv -f ${ENV_FILE} get MUMBLE_SUPERUSER_PASSWORD)"
@echo "##"
@echo "## Open access and user registration is enabled by default!"
@echo "## Use the SuperUser account to edit the global ACLs to change these defaults."
@echo "##"

.PHONY: client
client:
@./client.sh
212 changes: 212 additions & 0 deletions mumble/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,212 @@
# mumble

[Mumble](https://www.mumble.info/) is a low latency group voice chat system.

## Setup

```
make config
```

## Install

```
make install
```

This takes almost an eternity to build, sorry about that. Just be
patient.

## Background info on Mumble

Here is some knowledge dump about Mumble that you may want to know:

* Mumble has Channels, and Users, that form a hierachical tree/list
structure.
* Channels are audio rooms where users in the same room can speak
(VoIP audio) and/or type text to one another.
* The `Root` channel holds *all* the channels and users. The `Root`
is at the top, and everything else is below it.
* Channels can be nested, to support sub-channels, and sub-channels
can have sub-sub-channels, and so-on. Channels may be moved
(dragged) around in the hierarchy as desired. The `Root` channel is
the only channel that cannot be moved, nor renamed (apparently).\
* Users are free (by default) to traverse this entire tree and to
visit any channel in the structure. While users can only be "in" a
single room at a time, they may "listen" to more than one one at
the same time. Users can only speak into the room that they are
currently "in" (but an admin may also link a channel audio
together.)
* Channels may be removed, and if they have sub-channels, they will
be removed as well. If there are users currently in any of these
channels, they will be moved into the parent channel (or the
closest grand-parent that isn't being removed for deeper
sub-trees).
* By default, anyone may freely join the server, and register their
own username. Clients automatically generate a certificate which
identities the registered user.
* The `SuperUser` can create groups and organize users and apply
Access Control Lists (ACLs) to limit/grant what these groups are
allowed to do, and where they can go, on the server.
* ACLs work top-to-bottom, where the top is applied first, and then
the bottom rows overwrite the rules above it. Another way to think
about it, is that the rule on the *bottom* has the *highest*
priority.
* The `SuperUser` can create new channels, and these will stay
permanently in the list (unless deleted).
* By default, all users can can freely create Temporary channels,
which are created ephemerally, and automatically removed once the
last user leaves the channel.
* The user that creates a channel, becomes the administrator of that
channel, and this gives that user broad control to edit the
permissions of that channel, and its sub-channels. However, no
sub-channel may override the global permissions set by the
`SuperUser`.
* There are actually two different types of users:
* Unregistered users
* Registered users
* Unregistered users may not create any channels, however, by
default, unregistered users are allowed to register themselves (by
right clicking their name and selecting `Register`).
* By default, any registered user is allowed to create Temporary
channels.

## Manual Server Configuration

There is not much in the way of customization from the `.env` file, as
most of the mumble configuration is designed to be done at runtime
with the GUI client, and using the `SuperUser` account to do it. This
could probably be automated somehow so that configuration from the
`.env` file would make an API call to the mumble RPC service to make
the configured changes automatically... but I haven't figured out how
to do that yet. (Contrary to how most d.rymcg.tech apps are
configured, this ad-hoc configuration, and delegation, from the client
itself could be a powerful and flexible asset, so lets roll with it,
and just document the process.)

For now, these are the instructions for *manually* configuring a
secure mumble service:

* Get the `SuperUser` credentials, run: `make open`.
* In your mumble client create a new server configuration:
* Enter the address, port, username, and password printed from the
output of `make open`.
* Don't use the default label (Which only includes the server name
by default), instead give it a good name like
`SuperUser@my_server`, so that you can tell this config apart
from other configs you will create later.
* Connect to the `SuperUser@my_server` account.

You should now find yourself logged in, and see yourself as the
`SuperUser` (in bold text, to indicate yourself, I think) and you are
placed, alone, in a list directly beneath a `Root` node:

![Connected as the SuperUser](doc/01-connected-superuser.jpg)


### Locking down privileges

For this example, lets lock down the server a bit, to limit what newly
created users are allowed to do.

* Right click on the `Root` node to open the context menu, and then
select `Edit...`. This will open the `Root` channel edit window.
* Click on the `ACL` tab.
* The list of `Active ACLs` shows all the rules that govern the
`Root` channel, and all the channels that will be created below it.
This includes a default configuration, but for this demonstration
we will remove it all and start from scratch.
* The very top of the `Active ACLs` list has the `@all` item in
italics, this is the default ACL and it cannot be removed or
deleted. It is always at the top, and therefore always has the
*lowest* priority.
* Go ahead and delete all the other ACLs below it. (In my case I see
`@admin`, `@auth`, `@all` [a second one]). Click each one, and
press the `Remove` button, in turn. At the end you should see just
the single `@all` item (in italics) which is the one you can't
remove.
* To override the default, you will now create a new ACL below it
(below means *higher* priority): Make sure the `Inherit ACLs` is
left unchecked, and then click the `Add` button, and a new `@all`
item will appear beneath it.
* With the new `@all` ACL selected, you can choose new Permissions in
the list on the right side:
* Click on the `Deny` column for everything in the permissions
list, *except* for the following:
* Allow `Traverse`.
* Allow `Register Self`.
* Click `OK` to close the window.

To recap: at this point you have created an ACL for the `Root` node
that denies all users from doing practically everything:

* Anyone may still join the server, using any username they want.
* However, they can only join the Root channel, and they won't be
able to speak, nor listen, nor text, nor even leave this channel.
They will be able to see the other users and channels in the list,
and that's about it. All users (except the `SuperUser`) are now
effectively stuck in the Root channel and they can't do anything or
move.
* Create a new connection profile in your mumble client to verify this:
* Use the same hostname, and port you used before.
* Make up a brand new username.
* *Do not enter a password!* (A client certificate will be
automatically created and used instead; only the `SuperUser`
account requires a password.)
* Give it a good label, like `username@my_server`.
* Try conecting with the new user, and verify that the user cannot
do very much, or move around.
* One thign the user *can* do, is register. In the top bar of the
client click on `Self` and then click `Register...`.
* Register your new user, and your user will own the username you
chose from that point onward.

It will be useful at this point to have two clients, one to login with
the `SuperUser` and the other to login with your test user. If you
also need to test more than one test user, you will need to use
another supported device (eg.
[Mumla](https://f-droid.org/en/packages/se.lublin.mumla/) for
Android).

### Build new privilege ACLs from scratch

Log back in as the `SuperUser@my_server`, and you can start to build a
new set of ACLs to allow your users to start doing some stuff:

* Right click the `Root` channel, click on `Edit...`
* Open the `Group` tab. In the `Group` dropdown, you type in it to
create a new group. Create a new group called `users` to represent
a basic group of authenticated users.
* Underneath the `Members` list, there is another dropdown next to
the `Add` button. Type the names of all the users you wish to add
to the `users` group (these users need to be registered first), and
click `Add` for each one. If the username appears in italics, then
this username hasn't been registered, and you won't be able to save
the usernames that aren't registered. Add the test user that you
created before to the `users` group.

Now we will create a new ACL for the `users group`:

* Click on the `ACL` tab in the `Root` channel `Edit...` window.
* Click the `Add` button and a new item will appear in the `Active
ACLs` list. (Initially the new item is called `@all`, similar to
the one above it.)
* In the `User/Group` menu, select the `Group` for the new ACL:
select `users`, and you will see that the ACL has also been renamed
to `@users`.
* Make sure both of the checkboxes are checked for this ACL:
* `Applies to sub-channels` should be checked.
* `Applies to this channel` should be checked.
* Select these Permissions for the `@users` ACL:
* Allow `Enter` (this allows users to enter this channel)
* Allow `Speak` (this allows users to speak in channels (audio))
* Allow `Whisper` (this allows users to speak directly to other channel users)
* Allow `Text Message` (this allows users to type text to the channel)
* Allow `Make temporary` (this allows users to create temporary
sub-channels inside this one)
* Allow `Listen` (this allows users to listen to this channel)
* Click `OK` to close the edit window.
* Now you should find that the test user that is added to the `users`
group can do all of those things that the ACL grants them.


35 changes: 35 additions & 0 deletions mumble/client.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
#!/bin/bash

set -e
BIN=../_scripts
source ${BIN}/funcs.sh

ACCOUNT=$1;
check_var ACCOUNT

## Create a new config and database directories:
NEW_CONFIG="${HOME}/.config/Mumble/${ACCOUNT}"
NEW_DATA="${HOME}/.local/share/Mumble/${ACCOUNT}"
mkdir -p "${NEW_CONFIG}"
mkdir -p "${NEW_DATA}"

if [[ ! -f "${NEW_CONFIG}/mumble_settings.json" ]]; then
## Create a new config with the correct new database location:
echo "{}" | jq ".misc.database_location = \"${NEW_DATA}/mumble.sqlite\"" \
| jq ".settings_version = 1" \
| jq ".ui.theme_style = \"Dark\"" \
| jq ".mumble_has_quit_normally = true" \
| jq ".misc.audio_wizard_has_been_shown = true" \
| jq ".misc.viewed_server_ping_consent_message = true" \
| jq ".ui.disable_public_server_list = true" \
> "${NEW_CONFIG}/mumble_settings.json"
fi

touch "${NEW_DATA}/mumble.sqlite"

cat "${NEW_CONFIG}/mumble_settings.json" | jq ".certificate" | md5sum

## Launch mumble client using the new config:
mumble -c "${NEW_CONFIG}/mumble_settings.json"

cat "${NEW_CONFIG}/mumble_settings.json" | jq ".certificate" | md5sum
Binary file added mumble/doc/01-connected-superuser.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
41 changes: 41 additions & 0 deletions mumble/docker-compose.instance.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
#! This is a ytt template file for docker-compose.override.yaml
#! References:
#! https://carvel.dev/ytt
#! https://docs.docker.com/compose/extends/#adding-and-overriding-configuration
#! https://github.com/enigmacurry/d.rymcg.tech#overriding-docker-composeyaml-per-instance

#! ### Standard project vars:
#@ load("@ytt:data", "data")
#@ project = data.values.project
#@ instance = data.values.instance
#@ context = data.values.context
#@ traefik_host = data.values.traefik_host
#@ ip_sourcerange = data.values.ip_sourcerange
#@ enabled_middlewares = []

#@yaml/text-templated-strings
services:
mumble:
#@ service = "mumble"
labels:
#! Services must opt-in to be proxied by Traefik:
- "traefik.enable=true"

#! 'router' is the fully qualified key in traefik for this router/service: project + instance + service
#@ router = "{}-{}-{}".format(project,instance,service)

#! Mumble TCP:
- "traefik.tcp.routers.(@= router @).rule=HostSNI(`*`)"
- "traefik.tcp.routers.(@= router @).entrypoints=mumble"
#@ enabled_middlewares.append("{}-ipallowlist".format(router))
- "traefik.tcp.middlewares.(@= router @)-ipallowlist.ipallowlist.sourcerange=(@= ip_sourcerange @)"

#! Mumble UDP:
#!- "traefik.udp.routers.(@= router @).rule=Host(`*`)"
#!- "traefik.udp.routers.(@= router @).entrypoints=mumble"
#!#@ enabled_middlewares.append("{}-ipallowlist".format(router))
#!- "traefik.udp.middlewares.(@= router @)-ipallowlist.ipallowlist.sourcerange=(@= ip_sourcerange @)"

#! Apply all middlewares (do this at the end!)
- "traefik.tcp.routers.(@= router @).middlewares=(@= ','.join(enabled_middlewares) @)"
#! - "traefik.udp.routers.(@= router @).middlewares=(@= ','.join(enabled_middlewares) @)"
Loading