/hopper-rs

Hopper - Fast, configurable, lightweight Reverse Proxy for Minecraft

Primary LanguageRustMIT LicenseMIT

Hopper License

Hopper is a lightweight reverse proxy for minecraft. It allows you to connect multiple servers under the same IP and port, with additional functionalities, just like Nginx. It is built with Rust 🦀 to ensure maximum performance and efficiency.

Hopper works starting from version 1.7 up to the latest version of Minecraft.

NOTE: this proxy is still heavily under development, and a lot of new features are coming really soon!

FEATURES:

Index

Configuration

To quickly get started checkout this example configuration:

Example Config.toml:
# the address hopper will listen on
listen = "0.0.0.0:25565"

# metrics configuration
# [metrics]
# type = "influx"
# ...
#
# follow the section below for more information about
# gathering metrics with hopper

# general routing configuration
[routing]
default = { ip = "127.0.0.1:12345" } # optional
# default = { ip = ["127.0.0.1:12001", "127.0.0.1:12002"] } # load balanced

# list of servers fronted by hopper
[routing.routes]
# simple reverse proxy
"mc.gaming.tk" = { ip = "docker_hostname:25008" } # hostnames are supported too!

# bungeecord's ip forwarding feature enabled
"mc.server.com" = { ip-forwarding = "bungeecord", ip = "127.0.0.1:25123" }

# RealIP ip forwarding feature enabled
"mc.withrealip.com" = { ip-forwarding = "realip", ip = "127.0.0.1:26161" }

# this will load balance between the two servers
"other.gaming.tk" = { ip = ["127.0.0.1:25009", "10.1.0.1:25123"] }

Load balancing

Hopper's load balancer is a hash distributor based on the player's source IP and port.

You can load balance players between two backend servers by specifying a list of ip addresses instead of a single address.

[routing]
default = { ip = ["1.1.1.1:25565", "2.2.2.2:25577"] } # works on non-default routes too

IP Forwarding

Without IP Forwarding, when servers receive connections from this reverse proxy they won't see the original client's ip address. This may lead to problems with sessions with plugins such as AuthMe. Hopper implements both the legacy BungeeCord protocol and the more versatile RealIP one.

Bungeecord

You must enable bungeecord ip-forwarding inside of spigot.yml just like you would using bungeecord. Click here to learn more.

You can enable ip forwarding per-server on hopper with the "ip-forwarding" directive:

# You can either do it this way
[routing.routes]
"your.hostname.com" = { ip-forwarding = "bungeecord", ip = "<your server ip>" }

# or this way
[routing.routes."your.hostname.com"]
ip-forwarding = "bungeecord" # available options are: bungeecord, none. Defaults to none
ip = "<your server ip>"

RealIP

Hopper supports up to RealIP v2.4 (private/public key authentication has been implemented for versions after that, which only works with TCPShield).

⚠️ Note: RealIP v2.4 was built using older dependencies, hence support for newer minecraft versions may be lacking.

You must whitelist Hopper's ip address (or network) by adding a line inside of plugins/TCPShield/ip-whitelist/tcpshield-ips.list.

Finally, you must enable RealIP support in your Config.toml:

[routing.routes]
"your.hostname.com" = { ip-forwarding = "realip", ip = "<your server ip>" }

PROXY Protocol a.k.a HAProxy V2

Support for PROXY Protocol is available by setting ip-forwarding to proxy_protocol. Only the 2nd version of the protocol (the one supported by Bungeecord out of the box) is implemented in Hopper.

Example configuration:

[routing.routes]
"my.bungee.hostname.com" = { ip-forwarding = "proxy_protocol", ip = "<your server ip>" }

Logging metrics with InfluxDB

Hopper supports cheap (resource-wise), easily configurable data gathering through the help of an external database like InfluxDB (although other databases will be supported in the future, I still recommend InfluxDB whose query language is very easy and versatile).

You must first configure an InfluxDB instance and get a token with writing privilege before moving along with this section.

Add and modify this configuration section in Config.toml according to your setup:

[metrics] # top-level section
type = "influxdb"
# hostname = "my-hostname" # OPTIONAL, defaults to system hostname
url = "<http/https>://<influxdb-host-or-ip>:<port>/"
organization = "<Your organization>"
bucket = "<Your data bucket>"
token = "<Your access token>"

Hopper will start logging every 5 seconds according to this data format:

Measurement "traffic":

Field Type Description
host Tag system (or custom if specified) hostname generating this metric
destination_hostname Tag the hostname clients connected corresponding to these metrics
clientbound_traffic Value (int) the traffic this host generated server=>client
serverbound_traffic Value (int) same as above, but client=>server
open_connections Value(int) connections opened in the moment of the measurement
total_game Value(int) people who attemped or succeded joining this server
total_ping Value(int) people who pinged this server

NOTE: Since counters reset through restarts, data manipulation using the influx query language allows you to aggregate rows and get persistent results.

How to run

There are two ways to run hopper:

Docker GitHub Workflow Status

  • Pull the latest image from the GitHub registry:
docker pull ghcr.io/bra1l0r/hopper-rs
  • Create a Config.toml (NOTE: the port you will specify must match the exposed port below)
  • Run it using docker:
docker run -d -p 25565:25565 -v /home/user/path-to/Config.toml:/Config.toml ghcr.io/bra1l0r/hopper-rs

Using docker-compose (recommended):

# new versions of compose don't require this anymore
version: "3"

services:
  hopper:
    image: ghcr.io/bra1l0r/hopper-rs
    ports:
      - 25565:25565
    volumes:
      - ./Config.toml:/Config.toml

Binary GitHub Workflow Status

You can either download the latest release (recommended) or follow the steps below to build your own binary:

  • Download and install the latest version of the rustc toolchain
  • Clone and build the repo:
# Clone the repo into hopper-rs and enter the directory
git clone https://github.com/BRA1L0R/hopper-rs
cd hopper-rs/

# Build the project with the release profile
cargo build --release
  • The runnable binary will now be available at target/release/hopper

Systemd configuration

Assuming both the hopper binary and Config.toml configuration file are located inside /opt/hopper, you can extend this systemd configuration that supports both the stop and reload commands.

It is strongly advised not to use the root user as a best practice, however this configuration does use it and is to be intended as a template.

[Unit]
Description=Hopper reverse proxy
After=network.target

[Service]
Type=simple
ExecStart=/opt/hopper/hopper
ExecReload=/bin/kill -HUP $MAINPID
ExecStop=/bin/kill -SIGINT $MAINPID
WorkingDirectory=/opt/hopper

[Install]
WantedBy=multi-user.target

Changing the verbosity level

If you think something is off with your instance and want to enable debug logging, or you just want to reduce the default talkativeness of hopper you must choose your desired level of verbosity through the RUST_LOG environment variable.

Level Description
off No console output at all
error Only output important errors such as an unreachable backend server
info Informative data such as incoming connections and the current listening address
debug More descriptive errors (includes failed handshakes and bad packet data)

Default: info

Example:

RUST_LOG="debug" ./hopper

Hot reload

Hot reloading of Config.toml is supported only on linux as it provides the best way to signal the process to perform a hot reload.

If you think you know how something similar might be implemented in Windows please open an issue explaining your idea.

Hopper supports hot reloading through the SIGHUP process signal. Just like nginx. A rudimentary example of doing this would be through the kill command:

kill -s sighup <process PID>

If the new configuration contains the reload is aborted and the server continues running with the previous configuration.

You can automate this with systemd, allowing for the handy systemctl reload <service> shortcut, as you can see explained here.