Server:Server Status

Start to finish guide for creating a mumble server and hooking it into Bukkit/Spigot/Paper for interconnected chat, with a web interface.

This was originally posted to reddit Here but I am archiving the post here for posterity and self-reliance.

A long time ago we had IRC integrated into our server so offline people could chat, but with IRC becoming less used, we wanted to switch to something Discord-like which could enable voice chat. My preference is for self-hosted, open-source software where I control the data and service, so my target platform was Mumble. The result is pretty great; I have a easy mumble web client users can hang out in and talk with the server when they're offline, I have a phone app which receives notifications and does text to speech so with my headset I can be notified if a new user joins or if someone dies (this is all configurable). Users can choose to voice chat as well to collab on projects.

This wasn't as straightforward as I'd have thought, and ended up interconnecting MANY projects:

Mumble-Server/Murmur

Mumble-web-proxy

Mumble-web

Apache Web Server

Matterbridge

MatterBukkit

Deep in this guide, when we get to the part of this writeup about matterbridge, that's the meat of this. As much as I'm using this with Mumble, with that, you could integrate this with XMPP, Discord, Slack, Matrix...a bunch of things. That is our link between all the different forms of communication.

I am running this on the server that my Minecraft server is on which is Ubuntu 20.04, so everything internally is localhost, though some things want to be referred to by the fqdn. I will refer to my fqdn as mc.domain.com for this post. Additionally, I have actually paid for a verified SSL cert for this domain (I host a bunch of sites like dynmap as well) but this could be done with letsencrypt, either way you want SSL for this. I recommend paying the $20/year for a real ssl, but however you get the cert/key/chain is up to you.

This project required 4 ports, 3 of which will be public. I used non-standard ports listed below due to my hosting config (stuff from the same site uses a lot of default ports), but you can use whatever you want including the defaults:

Mumble: 50730

Mumble web proxy: 50731

Apache2 for mumble web: 50732

Internal port for MatterBridge: 4242

So, if a user is connecting with a proper mumble client, they connect to "mc.domain.com" port 50730, but if they just wanna use a web client, they connect with "https://mc.domain.com:50732". Since it's proper SSL, I can embed that in an iframe on my server's site which is nice, because I get a bit of ad revenue when people sit on there all day, we do the same thing with dynmap as well...they're unobtrusive banner ads from our hosting provider and not popups or overlays.

My ssl certs are located here:

  • /etc/apache2/ssl/mc.domain.com.pem
  • /etc/apache2/ssl/mc.domain.com.key
  • /etc/apache2/ssl/mc.domain.com.intermediate.crt

I will post my configs, with secrets obfuscated and comment lines removed which should be enough to get someone going.

First, I installed mumble-server per the guide listed above. The nice thing is it installs a user account, and I used that user account for most of the services we'll be setting up today (except for apache, which uses www-user by default). Once you've gone through the mumble-server configuration, this is what I have in /etc/mumble-server.ini:

database=/var/lib/mumble-server/mumble-server.sqlite
icesecretwrite=
logfile=/var/log/mumble-server/mumble-server.log
pidfile=/run/mumble-server/mumble-server.pid
welcometext="
Welcome to the DOMAIN Minecraft Mumble Server!
" port=50730 host= serverpassword= bandwidth=72000 users=100 messageburst=5 messagelimit=1 allowping=true defaultchannel=1 rememberchannel=false registerName=DOMAIN Minecraft Server sslCert=/etc/apache2/ssl/mc.domain.com.pem sslKey=/etc/apache2/ssl/mc.domain.com.key sslCA=/etc/apache2/ssl/mc.domain.com.intermediate.crt uname=mumble-server autobanAttempts=10 autobanTimeframe=120 autobanTime=300 [Ice] Ice.Warn.UnknownProperties=1 Ice.MessageSizeMax=65536

My mumble server has a "general chat" channel which is ID 1, and that's what I have set as the default...otherwise it'll be "root".

Once mumble is up and running, you should log in with the actual mumble client and do some config like make rooms, make your real account, get registered, make yourself an admin, etc. One thing to do is go to "Server->Access Tokens" and make an access token for later use, it's just a big secret key that is freetext but authorizes apps to connect.

The next thing is to get mumble web proxy working. This was one of the more complex parts because there's a bug where if you don't have ipv6 enabled it never falls back to ipv4, and to make that patch you have to use an older rustup to compile. It ended up being pretty simple, so I will reference my code patch, the build chain configuration needed, and the general build instructions. If it looks intimidating it isn't, once it compiles it makes a single binary executable. I made a folder "/etc/mumble-web-proxy" and put this binary there, then changed permissions on that file and folder to be owned by "mumble-server". I then made the following systemd unit file located in /etc/systemd/system/mumble-web-proxy.service :

[Unit]
Description=Mumble Web Proxy
After=network.target
After=mumble-server.service

[Service]
Type=simple
WorkingDirectory=/etc/mumble-web-proxy
PrivateUsers=true 
User=mumble-server
Group=mumble-server
ProtectSystem=full 
ProtectHome=true 
ProtectKernelTunables=true 
ProtectKernelModules=true 
ProtectControlGroups=true 

ExecStart=/etc/mumble-web-proxy/mumble-web-proxy --listen-ws 50731 --server mc.domain.com:50730 --ice-port-min 20000 --ice-port-max 21000

Restart=on-failure
RestartSec=60s

[Install]
WantedBy=multi-user.target

Pay attention to the ports, AND you need to refer to the server by the ssl name and NOT localhost/127.0.0.1. Once you do a "systemctl daemon-reload; systemctl enable mumble-web-proxy.service; systemctl start mumble-web-proxy.service" it should be running and visible with "netstat -tulpn".

Next is Apache, for mumble-web. There are millions of guides for installing apache on ubuntu so just find one, but in my case since I'm using a weird port, I had to edit /etc/apache2/ports.conf and make sure ssl is listening on my 50732 port. In the link above to mumble-web, follow the instructions to build this site with npm. I made a directory "/var/www/mumble" and put the contents of the resulting dist directory there. In that folder is a file called config.js, this file is a bit of a mess but your configuration goes at the bottom. Here is the relevant section of mine:

window.mumbleWebConfig = {
  // Which fields to show on the Connect to Server dialog
  'connectDialog': {
    'address': true,
    'port': true,
    'token': false,
    'username': true,
    'password': false,
    'channelName': false
  },
  'settings': {
    'voiceMode': 'vad',
    'pttKey': 'ctrl + shift',
    'vadLevel': 0.3,
    'toolbarVertical': false,
    'showAvatars': 'always',
    'userCountInChannelName': false,
    'audioBitrate': 40000,
    'samplesPerPacket': 960
  },
  'defaults': {
    'address': 'mc.domain.com',
    'port': '50732/_voice',
    'token': 'MumbleWebAccessToken',
    'username': 'web-',
    'password': '',
    'webrtc': 'auto',
    'joinDialog': false,
    'matrix': false,
    'avatarurl': '',
    'theme': 'MetroMumbleDark',
    'startMute': true,
    'startDeaf': false
  }
};

Obviously change what needs to be changed. You need to add the Access Token you created from the mumble client to this so it's authorized to chat on your mumble server. This will present the server name and port, but mainly the username with a "web-" prefix for them to append their name to. Pay attention to port 50732 there, that is our apapche ssl port and it will be used in the next step, if you use a different port, put that there.

First of all, you need the following modules enabled in apache:

Loaded Modules:
 core_module (static)
 so_module (static)
 watchdog_module (static)
 http_module (static)
 log_config_module (static)
 logio_module (static)
 version_module (static)
 unixd_module (static)
 access_compat_module (shared)
 alias_module (shared)
 auth_basic_module (shared)
 authn_core_module (shared)
 authn_file_module (shared)
 authz_core_module (shared)
 authz_host_module (shared)
 authz_user_module (shared)
 autoindex_module (shared)
 deflate_module (shared)
 dir_module (shared)
 env_module (shared)
 filter_module (shared)
 headers_module (shared)
 mime_module (shared)
 mpm_event_module (shared)
 negotiation_module (shared)
 proxy_module (shared)
 proxy_http_module (shared)
 proxy_wstunnel_module (shared)
 reqtimeout_module (shared)
 rewrite_module (shared)
 setenvif_module (shared)
 socache_shmcb_module (shared)
 ssl_module (shared)
 status_module (shared)

Then, you should create a file in /etc/apache2/sites-available called mumble.conf and put the following in it:


        ServerName mc.domain.com
        ServerAdmin username@domain.com

        DocumentRoot /var/www/mumble

        
                ProxyPass ws://127.0.0.1:50731/
                ProxyPassReverse ws://127.0.0.1:50731/
        

        ErrorLog ${APACHE_LOG_DIR}/error.log
        LogLevel warn
        CustomLog ${APACHE_LOG_DIR}/ssl_access.log combined
        SSLEngine on
        SSLCertificateFile /etc/apache2/ssl/mc.domain.com.pem
        SSLCertificateKeyFile /etc/apache2/ssl/mc.domain.com.key
        SSLCACertificateFile /etc/apache2/ssl/mc.domain.com.intermediate.crt

Remember, I'm using the nonstandard port of 50732 for https/wss for this app, and mumble-web-proxy is listening on 50731. Update as you see fit. This not only hosts the web app, but uses apache as your websocket reverse proxy.

Load this site with "a2ensite mumble.conf" and restart apache.

Next, make a folder /etc/matterbridge and get the latest release executable (linux 64-bit most likely) from here and put it in that folder. Make it executable and change the ownership to mumble-server. You will want to create a client certificate and key with the following command: openssl req -x509 -newkey rsa:2048 -nodes -days 10000 -keyout mumble.key -out mumble.crt then make sure to put them in that folder as well, once again owned by mumble-server. Lastly, we need to make the file /etc/matterbridge/matterbridge.toml but this is the file that will have the most deviation. You need to create a gateway for each chat platform you want to link, then link those gateways at the bottom. Here is what mine looks like:

[general]

[mumble.domain]
        Server = "mc.domain.com:50730"
        Nick = "MC"
        TLSClientCertificate="mumble.crt"
        TLSClientKey="mumble.key"
        SkipTLSVerify=true
        RemoteNickFormat="[{NICK}] "

[api.myapi]
        BindAddress="127.0.0.1:4242"
        Buffer=1000
        RemoteNickFormat="{NICK}"
        PrefixMessagesWithNick=true
        Token="domainMatterBridgeAPIToken"

[[gateway]]
        name="domainMumbleGateway"
        enable=true

        [[gateway.inout]]
                account="mumble.domain"
                channel="1"

        [[gateway.inout]]
                account="api.myapi"
                channel="api"

Update your domain name wherever you see it. You're pointing directly at your mumble server and not the web proxy or anything, so pay attention to the port. The matterbridge API is only exposed to localhost, so the default port is fine. You should make an API token for the bukkit plugin to validate with. I used the mumble nic "MC" with the prefix "[{NICK}] " so in mumble, messages show up like this: "MC: [playername] hi"

To run this, I made another systemd unit file at /etc/systemd/system/matterbridge.service:

[Unit]
Description=Matterbridge Chat Connector
After=network.target
After=mumble-server.service
After=mumble-web-proxy.service

[Service]
Type=simple
WorkingDirectory=/etc/matterbridge
PrivateUsers=true 
User=mumble-server
Group=mumble-server
ProtectSystem=full 
ProtectHome=true 
ProtectKernelTunables=true 
ProtectKernelModules=true 
ProtectControlGroups=true 

ExecStart=/etc/matterbridge/matterbridge -conf /etc/matterbridge/matterbridge.toml

Restart=on-failure
RestartSec=60s

[Install]
WantedBy=multi-user.target

Another "systemctl daemon-reload; systemctl enable matterbridge.service; systemctl start matterbridge.service" and it'll be running, logging to /var/log/syslog. You can see this with "tail -f /var/log/syslog | grep matterbridge". You will also see the "MC" user in your mumble server, you should go into your mumble client with the admin account, right click "MC" and register them so no one else can use that name.

If you run "netstat -tulpn" you'll now see listeners on 50730, 50731, 50732, and 4242 (or whatever ports you're using). That's perfect, now we're ready to install the bukkit plugin.

Get it from the link above, run it, it'll create a config directory with a file. Edit that file, then restart your server. Here's the contents of plugins/MatterBukkit/config.yml:

bridge:
  # URL to use to connect to MatterBridge
  url: http://localhost:4242

  # Name of the gateway to which MatterBukkit should connect to
  gateway: domainMumbleGateway

  # Token to use for MatterBridge authentication (optional)
  token: domainMatterBridgeAPIToken

incoming:
  # Print chat messages received from MatterBridge to all players
  enable: true

  # Format for incoming chat messages
  # Placeholders: %username% (name of the user who sent the message), %text% (content of the message)
  format: "%username% %text%"

outgoing:
  # URL to use for avatars in outgoing messages (requires a protocol on the other side of MatterBridge to support avatars)
  # Placeholders: %playername% (name of the player), %uuid% (UUID of the player)
  avatar-url: "https://crafatar.com/avatars/%uuid%"

  # URL to use for avatars in outgoing system messages (i.e. death messages, player joins/quits, advancements and level up)
  system-avatar-url: null

  # Username to use for outgoing system messages
  system-username: "System"

  chat:
    # Send player chat messages to MatterBridge
    enable: true

  death:
    # Send death messages to MatterBridge
    enable: true

    # Format for death messages sent to MatterBridge
    # Placeholders: %playername% (name of the player), %death-message% (the death message as shown to the player, i.e. "Playername was blown up by Creeper")
    format: "%death-message%"

  advancement:
    # Send advancement messages to MatterBridge
    enable: true

    # Format for advancement messages sent to MatterBridge
    # Placeholders: %playername% (name of the player), %advancement% (name of the advancement as defined in advancements.yml)
    format: "%playername% has made the advancement [%advancement%]"

  level-up:
    # Send level up messages to MatterBridge
    enable: true

    # Minimum level to reach before sending a level up message
    minimum-level: 0

    # Only send level up messages every n levels (i.e. "5" would send a message once the player reached level 5, 10, 15, etc.)
    modulus: 25

    # Format for level up messages sent to MatterBridge
    # Placeholders: %playername% (name of the player), %old-level% (the level the player was at before reaching the new level), %new-level% (the reached level)
    format: "%playername% reached level %new-level%"

  join:
    # Send join messages to MatterBridge (i.e. "Playername joined the server")
    enable: true

  quit:
    # Send quit messages to MatterBridge (i.e. "Playername left the server")
    enable: true

You can see a lot of the config here, adjust it to your needs.

And that's it! Took me a few days to get set up, but now that it's done it's very very useful and I'm super happy with it, and hope that this can provide some help to anyone else looking to set things up. There's many easier ways to do this with things like Discord plugins, but if you just want something self-hosted and open source, this is a great setup.

Leave a Reply