add BungeeGuard authentication (#1502)

This commit is contained in:
tahmid-23 2022-11-28 00:13:09 -05:00 committed by GitHub
parent 84f4ae040e
commit e5d0a43ba4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 93 additions and 9 deletions

View File

@ -1,12 +1,22 @@
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>
* Please consider using {@link net.minestom.server.extras.velocity.VelocityProxy} instead.
*/
public final class BungeeCordProxy {
private static Set<String> bungeeGuardTokens = null;
private static volatile boolean enabled;
/**
@ -24,4 +34,35 @@ public final class BungeeCordProxy {
public static boolean isEnabled() {
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);
}
}

View File

@ -16,6 +16,8 @@ import net.minestom.server.network.player.GameProfile;
import net.minestom.server.network.player.PlayerConnection;
import net.minestom.server.network.player.PlayerSocketConnection;
import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.net.SocketAddress;
import java.util.ArrayList;
@ -27,14 +29,20 @@ import static net.minestom.server.network.NetworkBuffer.*;
public record HandshakePacket(int protocolVersion, @NotNull String serverAddress,
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
*/
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 {
if (serverAddress.length() > (BungeeCordProxy.isEnabled() ? Short.MAX_VALUE : 255)) {
if (serverAddress.length() > getMaxHandshakeLength()) {
throw new IllegalArgumentException("Server address too long: " + serverAddress.length());
}
}
@ -47,7 +55,7 @@ public record HandshakePacket(int protocolVersion, @NotNull String serverAddress
@Override
public void write(@NotNull NetworkBuffer writer) {
writer.write(VAR_INT, protocolVersion);
int maxLength = BungeeCordProxy.isEnabled() ? Short.MAX_VALUE : 255;
int maxLength = getMaxHandshakeLength();
if (serverAddress.length() > 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");
if (split.length == 3 || split.length == 4) {
boolean hasProperties = split.length == 4;
if (BungeeCordProxy.isBungeeGuardEnabled() && !hasProperties) {
bungeeDisconnect(socketConnection);
return;
}
address = split[0];
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<>();
if (split.length == 4) {
if (hasProperties) {
boolean foundBungeeGuardToken = false;
final String rawPropertyJson = split[3];
final JsonArray propertyJson = JsonParser.parseString(rawPropertyJson).getAsJsonArray();
for (JsonElement element : propertyJson) {
@ -92,15 +107,28 @@ public record HandshakePacket(int protocolVersion, @NotNull String serverAddress
final String valueString = value.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));
}
if (BungeeCordProxy.isBungeeGuardEnabled() && !foundBungeeGuardToken) {
bungeeDisconnect(socketConnection);
return;
}
}
final GameProfile gameProfile = new GameProfile(playerUuid, "test", properties);
socketConnection.UNSAFE_setProfile(gameProfile);
} else {
socketConnection.sendPacket(new LoginDisconnectPacket(INVALID_BUNGEE_FORWARDING));
socketConnection.disconnect();
bungeeDisconnect(socketConnection);
return;
}
}
@ -117,8 +145,7 @@ public record HandshakePacket(int protocolVersion, @NotNull String serverAddress
connection.setConnectionState(ConnectionState.LOGIN);
} else {
// Incorrect client version
connection.sendPacket(new LoginDisconnectPacket(INVALID_VERSION_TEXT));
connection.disconnect();
disconnect(connection, INVALID_VERSION_TEXT);
}
}
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);
}
}