a3d5f64ef5
Corrected real errors: several 1.7.x release dates, resource_pack_send version, config packet ordering, structured-component count (56), PLAYER_LOADED (1.21.4), entity_sound_effect field order. Confirmed+cited the rest; remaining ~19 items re-marked UNCONFIRMED (third-party/ViaLegacy/26.2 internals unreachable from refs). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
70 lines
5.6 KiB
Markdown
70 lines
5.6 KiB
Markdown
# BungeeGuard
|
|
|
|
A **hardening of [legacy forwarding](bungeecord-legacy.md)**, not a new transport. Legacy's fatal flaw is that anyone who can reach the backend port can forge the `\0`-delimited identity string and connect as anyone. BungeeGuard fixes that by smuggling a **shared secret token** into the forwarded data; the backend plugin refuses the login unless the token matches. It bridges backends that **only support legacy forwarding** up to "can't be trivially forged," without requiring [modern/Velocity](velocity-modern.md) support.
|
|
|
|
It is **third-party** — not part of Spigot/Paper core or the Mojang protocol. The original is the [BungeeGuard plugin](https://github.com/lucko/BungeeGuard) (lucko), installed on both the proxy and every backend.
|
|
|
|
## How it works
|
|
|
|
BungeeGuard reuses the exact legacy wire format — the four `\0`-separated handshake-address segments (`realHost\0clientIP\0uuidNoDashes\0propertiesJSON`). The trick is in the **properties JSON**: it appends one **synthetic game-profile property** named **`bungeeguard-token`** whose value is the shared secret.
|
|
|
|
A game-profile property is a `{name, value, signature?}` object — the same shape as the Mojang `textures` property. BungeeGuard injects an extra one:
|
|
|
|
```json
|
|
[
|
|
{ "name": "textures", "value": "...", "signature": "..." },
|
|
{ "name": "bungeeguard-token","value": "<the shared secret>" }
|
|
]
|
|
```
|
|
|
|
On the backend, the BungeeGuard plugin (replacing the stock legacy parser) reads the player's properties, finds the `bungeeguard-token` entry, and compares its value against the secret it was configured with. Match → accept and strip the token; mismatch or absent → reject the connection. The real IP/UUID/skin are still taken from the same legacy segments.
|
|
|
|
Velocity has this mode built in (`player-info-forwarding-mode = "legacy"` is bare legacy; `"bungeeguard"` adds the token). Its source builds the address identically to legacy and just adds the token property:
|
|
|
|
> ```java
|
|
> private static final String BUNGEE_GUARD_TOKEN_PROPERTY_NAME = "bungeeguard-token";
|
|
> ...
|
|
> final GameProfile.Property property = new GameProfile.Property(
|
|
> BUNGEE_GUARD_TOKEN_PROPERTY_NAME,
|
|
> new String(forwardingSecret, StandardCharsets.UTF_8),
|
|
> "");
|
|
> return createLegacyForwardingAddress(serverAddress, playerAddress, profile,
|
|
> properties -> ImmutableList.<GameProfile.Property>builder()
|
|
> .addAll(properties).add(property).build());
|
|
> ```
|
|
>
|
|
> — `Velocity/proxy/src/main/java/com/velocitypowered/proxy/connection/PlayerDataForwarding.java:52` (name) + `:175-196` (`createBungeeGuardForwardingAddress`)
|
|
|
|
So a BungeeGuard handshake address is exactly a legacy one with one extra property in the JSON array.
|
|
|
|
## Security: better than legacy, weaker than modern
|
|
|
|
| | bare legacy | BungeeGuard | modern (Velocity) |
|
|
|---|---|---|---|
|
|
| Forge-able by anyone reaching the port | **Yes** | No (needs the token) | No (needs the secret) |
|
|
| Secret/token crosses the wire | n/a | **Yes — token sent in cleartext** inside the connection | No (only an HMAC crosses) |
|
|
| Tamper-proofs IP/UUID/props | No | **No** — only gate-keeps; the identity fields are still unsigned plaintext | **Yes** — whole payload is HMAC'd |
|
|
| Backend requirement | core `bungeecord:true` | BungeeGuard plugin | Paper/Fabric modern support |
|
|
|
|
The crucial limitation: BungeeGuard is **authentication of the connection, not authentication of the payload**. It proves "whoever sent this knows the token," but the IP/UUID/properties themselves are not signed — once the token check passes, those fields are trusted as-is, exactly like legacy. And the token is transmitted **in cleartext** in the handshake. So:
|
|
|
|
- If the connection between proxy and backend is **not** encrypted/private, a network observer can lift the token and then forge freely.
|
|
- The token is a single shared value across all backends; leaking it from any one host compromises all.
|
|
|
|
It is a real improvement over bare legacy (you no longer rely solely on a firewall) and the right choice for a backend that **cannot** do modern forwarding. But when every backend supports it, **[modern/Velocity forwarding](velocity-modern.md) is strictly stronger** — it signs the payload with an HMAC and never puts the secret on the wire.
|
|
|
|
## When to use it
|
|
|
|
- A backend (old Spigot, a plugin platform, a Fabric server without modern-forward support) only speaks **legacy**, and you can't firewall it tightly enough to trust bare legacy.
|
|
- You're migrating a BungeeCord/Waterfall network and want hardening without switching every backend to Velocity-native forwarding.
|
|
|
|
Otherwise prefer modern. See the [README comparison table](README.md#comparison).
|
|
|
|
---
|
|
|
|
**Sources**
|
|
|
|
- `Velocity/proxy/src/main/java/com/velocitypowered/proxy/connection/PlayerDataForwarding.java:52` (`bungeeguard-token` property name), `:175-196` (`createBungeeGuardForwardingAddress` — legacy address + token property), `:154-173` (shared `createLegacyForwardingAddress` it builds on).
|
|
- [lucko/BungeeGuard](https://github.com/lucko/BungeeGuard) — the original third-party plugin (token property + backend check). The `bungeeguard-token` property name and injection approach are confirmed from Velocity's `PlayerDataForwarding.java:52,175-196`. The upstream README/INSTALLATION.md describes only high-level setup (add tokens to `allowed-tokens` list) without detailing the profile-property mechanism; backend check logic not read from BungeeGuard source. <!-- UNCONFIRMED: backend plugin's property-inspection code verified only via Velocity's matching injection, not from BungeeGuard plugin source -->
|
|
- See [bungeecord-legacy.md](bungeecord-legacy.md) for the underlying `\0`-delimited wire format BungeeGuard extends.
|