mirror of
https://github.com/Minestom/Minestom.git
synced 2025-01-06 16:37:38 +01:00
add BungeeGuard authentication (#1502)
This commit is contained in:
parent
84f4ae040e
commit
e5d0a43ba4
@ -1,12 +1,22 @@
|
|||||||
package net.minestom.server.extras.bungee;
|
package net.minestom.server.extras.bungee;
|
||||||
|
|
||||||
|
import net.kyori.adventure.text.Component;
|
||||||
|
import net.kyori.adventure.text.format.NamedTextColor;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.jetbrains.annotations.Nullable;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* BungeeCord forwarding support. This does not count as a security feature, and you will still be required to manage your firewall.
|
* BungeeCord forwarding support. Enabling BungeeGuard support with {@link #setBungeeGuardTokens(Set)} helps to secure the server,
|
||||||
|
* but managing your firewall is still recommended.
|
||||||
* <p>
|
* <p>
|
||||||
* Please consider using {@link net.minestom.server.extras.velocity.VelocityProxy} instead.
|
* Please consider using {@link net.minestom.server.extras.velocity.VelocityProxy} instead.
|
||||||
*/
|
*/
|
||||||
public final class BungeeCordProxy {
|
public final class BungeeCordProxy {
|
||||||
|
|
||||||
|
private static Set<String> bungeeGuardTokens = null;
|
||||||
|
|
||||||
private static volatile boolean enabled;
|
private static volatile boolean enabled;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -24,4 +34,35 @@ public final class BungeeCordProxy {
|
|||||||
public static boolean isEnabled() {
|
public static boolean isEnabled() {
|
||||||
return enabled;
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the tokens used by BungeeGuard authentication.
|
||||||
|
* Setting the tokens to a not-null value enables BungeeGuard authentication,
|
||||||
|
* and setting it to a null value disables BungeeGuard authentication.
|
||||||
|
*
|
||||||
|
* @param tokens The new BungeeGuard authentication tokens
|
||||||
|
*/
|
||||||
|
public static void setBungeeGuardTokens(@Nullable Set<String> tokens) {
|
||||||
|
bungeeGuardTokens = tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether BungeeGuard authentication is enabled.
|
||||||
|
*
|
||||||
|
* @return Whether BungeeGuard authentication is enabled
|
||||||
|
*/
|
||||||
|
public static boolean isBungeeGuardEnabled() {
|
||||||
|
return bungeeGuardTokens != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether a token is one of the valid BungeeGuard tokens
|
||||||
|
*
|
||||||
|
* @param token The token to test
|
||||||
|
* @return Whether the token is a valid BungeeGuard token
|
||||||
|
*/
|
||||||
|
public static boolean isValidBungeeGuardToken(@NotNull String token) {
|
||||||
|
return isBungeeGuardEnabled() && bungeeGuardTokens.contains(token);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -16,6 +16,8 @@ import net.minestom.server.network.player.GameProfile;
|
|||||||
import net.minestom.server.network.player.PlayerConnection;
|
import net.minestom.server.network.player.PlayerConnection;
|
||||||
import net.minestom.server.network.player.PlayerSocketConnection;
|
import net.minestom.server.network.player.PlayerSocketConnection;
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.net.SocketAddress;
|
import java.net.SocketAddress;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@ -27,14 +29,20 @@ import static net.minestom.server.network.NetworkBuffer.*;
|
|||||||
public record HandshakePacket(int protocolVersion, @NotNull String serverAddress,
|
public record HandshakePacket(int protocolVersion, @NotNull String serverAddress,
|
||||||
int serverPort, int nextState) implements ClientPreplayPacket {
|
int serverPort, int nextState) implements ClientPreplayPacket {
|
||||||
|
|
||||||
|
private final static Logger LOGGER = LoggerFactory.getLogger(HandshakePacket.class);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Text sent if a player tries to connect with an invalid version of the client
|
* Text sent if a player tries to connect with an invalid version of the client
|
||||||
*/
|
*/
|
||||||
private static final Component INVALID_VERSION_TEXT = Component.text("Invalid Version, please use " + MinecraftServer.VERSION_NAME, NamedTextColor.RED);
|
private static final Component INVALID_VERSION_TEXT = Component.text("Invalid Version, please use " + MinecraftServer.VERSION_NAME, NamedTextColor.RED);
|
||||||
private static final Component INVALID_BUNGEE_FORWARDING = Component.text("If you wish to use IP forwarding, please enable it in your BungeeCord config as well!", NamedTextColor.RED);
|
|
||||||
|
/**
|
||||||
|
* Indicates that a BungeeGuard authentication was invalid due to missing, multiple, or invalid tokens.
|
||||||
|
*/
|
||||||
|
private static final Component INVALID_BUNGEE_FORWARDING = Component.text("Invalid connection, please connect through the BungeeCord proxy. If you believe this is an error, contact a server administrator.", NamedTextColor.RED);
|
||||||
|
|
||||||
public HandshakePacket {
|
public HandshakePacket {
|
||||||
if (serverAddress.length() > (BungeeCordProxy.isEnabled() ? Short.MAX_VALUE : 255)) {
|
if (serverAddress.length() > getMaxHandshakeLength()) {
|
||||||
throw new IllegalArgumentException("Server address too long: " + serverAddress.length());
|
throw new IllegalArgumentException("Server address too long: " + serverAddress.length());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -47,7 +55,7 @@ public record HandshakePacket(int protocolVersion, @NotNull String serverAddress
|
|||||||
@Override
|
@Override
|
||||||
public void write(@NotNull NetworkBuffer writer) {
|
public void write(@NotNull NetworkBuffer writer) {
|
||||||
writer.write(VAR_INT, protocolVersion);
|
writer.write(VAR_INT, protocolVersion);
|
||||||
int maxLength = BungeeCordProxy.isEnabled() ? Short.MAX_VALUE : 255;
|
int maxLength = getMaxHandshakeLength();
|
||||||
if (serverAddress.length() > maxLength) {
|
if (serverAddress.length() > maxLength) {
|
||||||
throw new IllegalArgumentException("serverAddress is " + serverAddress.length() + " characters long, maximum allowed is " + maxLength);
|
throw new IllegalArgumentException("serverAddress is " + serverAddress.length() + " characters long, maximum allowed is " + maxLength);
|
||||||
}
|
}
|
||||||
@ -64,6 +72,12 @@ public record HandshakePacket(int protocolVersion, @NotNull String serverAddress
|
|||||||
final String[] split = address.split("\00");
|
final String[] split = address.split("\00");
|
||||||
|
|
||||||
if (split.length == 3 || split.length == 4) {
|
if (split.length == 3 || split.length == 4) {
|
||||||
|
boolean hasProperties = split.length == 4;
|
||||||
|
if (BungeeCordProxy.isBungeeGuardEnabled() && !hasProperties) {
|
||||||
|
bungeeDisconnect(socketConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
address = split[0];
|
address = split[0];
|
||||||
|
|
||||||
final SocketAddress socketAddress = new java.net.InetSocketAddress(split[1],
|
final SocketAddress socketAddress = new java.net.InetSocketAddress(split[1],
|
||||||
@ -78,7 +92,8 @@ public record HandshakePacket(int protocolVersion, @NotNull String serverAddress
|
|||||||
);
|
);
|
||||||
|
|
||||||
List<GameProfile.Property> properties = new ArrayList<>();
|
List<GameProfile.Property> properties = new ArrayList<>();
|
||||||
if (split.length == 4) {
|
if (hasProperties) {
|
||||||
|
boolean foundBungeeGuardToken = false;
|
||||||
final String rawPropertyJson = split[3];
|
final String rawPropertyJson = split[3];
|
||||||
final JsonArray propertyJson = JsonParser.parseString(rawPropertyJson).getAsJsonArray();
|
final JsonArray propertyJson = JsonParser.parseString(rawPropertyJson).getAsJsonArray();
|
||||||
for (JsonElement element : propertyJson) {
|
for (JsonElement element : propertyJson) {
|
||||||
@ -92,15 +107,28 @@ public record HandshakePacket(int protocolVersion, @NotNull String serverAddress
|
|||||||
final String valueString = value.getAsString();
|
final String valueString = value.getAsString();
|
||||||
final String signatureString = signature == null ? null : signature.getAsString();
|
final String signatureString = signature == null ? null : signature.getAsString();
|
||||||
|
|
||||||
|
if (BungeeCordProxy.isBungeeGuardEnabled() && nameString.equals("bungeeguard-token")) {
|
||||||
|
if (foundBungeeGuardToken || !BungeeCordProxy.isValidBungeeGuardToken(valueString)) {
|
||||||
|
bungeeDisconnect(socketConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foundBungeeGuardToken = true;
|
||||||
|
}
|
||||||
|
|
||||||
properties.add(new GameProfile.Property(nameString, valueString, signatureString));
|
properties.add(new GameProfile.Property(nameString, valueString, signatureString));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (BungeeCordProxy.isBungeeGuardEnabled() && !foundBungeeGuardToken) {
|
||||||
|
bungeeDisconnect(socketConnection);
|
||||||
|
return;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final GameProfile gameProfile = new GameProfile(playerUuid, "test", properties);
|
final GameProfile gameProfile = new GameProfile(playerUuid, "test", properties);
|
||||||
socketConnection.UNSAFE_setProfile(gameProfile);
|
socketConnection.UNSAFE_setProfile(gameProfile);
|
||||||
} else {
|
} else {
|
||||||
socketConnection.sendPacket(new LoginDisconnectPacket(INVALID_BUNGEE_FORWARDING));
|
bungeeDisconnect(socketConnection);
|
||||||
socketConnection.disconnect();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,8 +145,7 @@ public record HandshakePacket(int protocolVersion, @NotNull String serverAddress
|
|||||||
connection.setConnectionState(ConnectionState.LOGIN);
|
connection.setConnectionState(ConnectionState.LOGIN);
|
||||||
} else {
|
} else {
|
||||||
// Incorrect client version
|
// Incorrect client version
|
||||||
connection.sendPacket(new LoginDisconnectPacket(INVALID_VERSION_TEXT));
|
disconnect(connection, INVALID_VERSION_TEXT);
|
||||||
connection.disconnect();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
default -> {
|
default -> {
|
||||||
@ -126,4 +153,20 @@ public record HandshakePacket(int protocolVersion, @NotNull String serverAddress
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int getMaxHandshakeLength() {
|
||||||
|
// BungeeGuard limits handshake length to 2500 characters, while vanilla limits it to 255
|
||||||
|
return BungeeCordProxy.isEnabled() ? (BungeeCordProxy.isBungeeGuardEnabled() ? 2500 : Short.MAX_VALUE) : 255;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disconnect(@NotNull PlayerConnection connection, @NotNull Component reason) {
|
||||||
|
connection.sendPacket(new LoginDisconnectPacket(reason));
|
||||||
|
connection.disconnect();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void bungeeDisconnect(@NotNull PlayerConnection connection) {
|
||||||
|
LOGGER.warn("{} tried to log in without valid BungeeGuard forwarding information.", connection.getIdentifier());
|
||||||
|
disconnect(connection, INVALID_BUNGEE_FORWARDING);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user