Minecraft Game Guard (minecraft)
The Minecraft Game Guard plugin inspects Minecraft Java Edition protocol handshakes and Server List Ping (SLP) frames. It mitigates SLP ping floods, validates protocol version identifiers, drops malformed handshake packets before they hit Netty network threads, and enforces strict rate limits to protect server tick rates (TPS).
Capabilities & Threat Defense
| Threat / Attack Vector | Defense Mechanism | Action Taken |
|---|---|---|
| Server List Ping (SLP) Floods | Inspects SLP request intent (status vs login) | Excess status pings throttled per client IP |
| Malformed Handshake Exploits | Validates VarInt framing and packet boundaries | Malformed packets dropped at the proxy layer |
| Connection Table Exhaustion | Enforces rate limits per minute and burst caps | Spammed connections dropped before reaching Paper/Spigot |
| TCP Fragmentation Bypass | Stream accumulation loop ensures complete handshake is buffered before inspection (since v3.4.0) | Fragmented multi-segment handshakes are fully reassembled before blocked_protocol_versions is evaluated |
| Non-Handshake Packet Spoofing | Packet ID != 0 detected and rejected immediately (since v3.4.0) | Non-handshake packets dropped before upstream forwarding |
Installation
Install the plugin via CLI or declaratively:
# Install using short nametcp-warden plugins install minecraft# Or install via Git repository URLtcp-warden plugins install https://github.com/routewarden/plugins/minecraftConfiguration Reference
Add a Minecraft guard service to tcp-warden.yaml:
services: mc_guard: listen: ":25565" upstream: "127.0.0.1:255650" protocol: "minecraft" rate_limit: connections_per_minute: 30 burst: 5 plugin_config: max_connections_per_sec: 10Configuration Options
| Field | Type | Default | Description |
|---|---|---|---|
listen | string | ":25565" | Local proxy listen address and port. |
upstream | string | "127.0.0.1:255650" | Target Minecraft game server address and port. |
protocol | string | "minecraft" | Must be set to "minecraft" or "mc". |
plugin_config.max_connections_per_sec | int | 10 | Maximum new connections per second permitted per client IP. |
plugin_config.blocked_protocol_versions | []int | [] | List of Minecraft Java Edition protocol version integers to block (e.g. [47] for 1.8, [340] for 1.12.2, [760] for 1.19.2). Connections from clients advertising a blocked version are rejected at the handshake layer. Version inspection is robust against TCP segment fragmentation (since v3.4.0). |
Network & Deployment Architecture
Because Minecraft Java Edition operates on an unprivileged port (25565), firewall redirects (nftables/iptables) are not required. Deploy RouteWarden using either direct reverse proxying or container isolation:
Option A: Direct Port Swapping (Bare-Metal / VM)
Keep Paper/Purpur listening on loopback on an alternate port, and expose RouteWarden on standard port :25565:
# tcp-warden.yaml — RouteWarden binds to standard Minecraft port 25565services: mc_guard: listen: ":25565" upstream: "127.0.0.1:255650" # Or private host "10.0.0.60:25565" protocol: "minecraft"Option B: Docker Compose Bridge Network
In container stacks, publish port 25565 on RouteWarden while keeping the game server container private:
services: routewarden: image: ghcr.io/routewarden/tcp-warden:latest ports: - "25565:25565" - "9091:9091" volumes: - ./tcp-warden.yaml:/etc/routewarden/tcp-warden.yaml:ro depends_on: - paper paper: image: itzg/minecraft-server:latest environment: EULA: "TRUE" TYPE: "PAPER" # Do NOT publish host port 25565 directlyTesting & Verification
Query server latency and status using mcstatus:
# Query server status and ping through RouteWardenmcstatus 127.0.0.1:25565 status