13
0
geforkt von Mirrors/Velocity

Add support for HMACed player forwarding data.

This provides a small degree of security but also makes Velocity "secure
by default", especially on shared hosts.
Dieser Commit ist enthalten in:
Andrew Steinborn 2018-08-09 05:24:47 -04:00
Ursprung 254508a5cf
Commit 1f0a4a8228
5 geänderte Dateien mit 92 neuen und 41 gelöschten Zeilen

Datei anzeigen

@ -1,6 +1,6 @@
package com.velocitypowered.proxy.config;
public enum IPForwardingMode {
public enum PlayerInfoForwarding {
NONE,
LEGACY,
MODERN

Datei anzeigen

@ -5,6 +5,7 @@ import com.moandjiezana.toml.Toml;
import com.velocitypowered.api.server.Favicon;
import com.velocitypowered.proxy.util.AddressUtil;
import com.velocitypowered.api.util.LegacyChatColorUtils;
import io.netty.buffer.ByteBufUtil;
import net.kyori.text.Component;
import net.kyori.text.serializer.ComponentSerializers;
import org.apache.logging.log4j.LogManager;
@ -28,7 +29,7 @@ public class VelocityConfiguration {
private final String motd;
private final int showMaxPlayers;
private final boolean onlineMode;
private final IPForwardingMode ipForwardingMode;
private final PlayerInfoForwarding playerInfoForwardingMode;
private final Map<String, String> servers;
private final List<String> attemptConnectionOrder;
private final int compressionThreshold;
@ -40,21 +41,25 @@ public class VelocityConfiguration {
private Component motdAsComponent;
private Favicon favicon;
private final byte[] forwardingSecret;
private VelocityConfiguration(String bind, String motd, int showMaxPlayers, boolean onlineMode,
IPForwardingMode ipForwardingMode, Map<String, String> servers,
PlayerInfoForwarding playerInfoForwardingMode, Map<String, String> servers,
List<String> attemptConnectionOrder, int compressionThreshold,
int compressionLevel, boolean queryEnabled, int queryPort) {
int compressionLevel, boolean queryEnabled, int queryPort,
byte[] forwardingSecret) {
this.bind = bind;
this.motd = motd;
this.showMaxPlayers = showMaxPlayers;
this.onlineMode = onlineMode;
this.ipForwardingMode = ipForwardingMode;
this.playerInfoForwardingMode = playerInfoForwardingMode;
this.servers = servers;
this.attemptConnectionOrder = attemptConnectionOrder;
this.compressionThreshold = compressionThreshold;
this.compressionLevel = compressionLevel;
this.queryEnabled = queryEnabled;
this.queryPort = queryPort;
this.forwardingSecret = forwardingSecret;
}
public boolean validate() {
@ -76,9 +81,15 @@ public class VelocityConfiguration {
logger.info("Proxy is running in offline mode!");
}
switch (ipForwardingMode) {
switch (playerInfoForwardingMode) {
case NONE:
logger.info("IP forwarding is disabled! All players will appear to be connecting from the proxy and will have offline-mode UUIDs.");
logger.info("Player info forwarding is disabled! All players will appear to be connecting from the proxy and will have offline-mode UUIDs.");
break;
case MODERN:
if (forwardingSecret.length == 0) {
logger.error("You don't have a forwarding secret set.");
valid = false;
}
break;
}
@ -178,8 +189,8 @@ public class VelocityConfiguration {
return onlineMode;
}
public IPForwardingMode getIpForwardingMode() {
return ipForwardingMode;
public PlayerInfoForwarding getPlayerInfoForwardingMode() {
return playerInfoForwardingMode;
}
public Map<String, String> getServers() {
@ -202,6 +213,10 @@ public class VelocityConfiguration {
return favicon;
}
public byte[] getForwardingSecret() {
return forwardingSecret;
}
@Override
public String toString() {
return "VelocityConfiguration{" +
@ -209,7 +224,7 @@ public class VelocityConfiguration {
", motd='" + motd + '\'' +
", showMaxPlayers=" + showMaxPlayers +
", onlineMode=" + onlineMode +
", ipForwardingMode=" + ipForwardingMode +
", playerInfoForwardingMode=" + playerInfoForwardingMode +
", servers=" + servers +
", attemptConnectionOrder=" + attemptConnectionOrder +
", compressionThreshold=" + compressionThreshold +
@ -218,6 +233,7 @@ public class VelocityConfiguration {
", queryPort=" + queryPort +
", motdAsComponent=" + motdAsComponent +
", favicon=" + favicon +
", forwardingSecret=" + ByteBufUtil.hexDump(forwardingSecret) +
'}';
}
@ -236,18 +252,22 @@ public class VelocityConfiguration {
}
}
byte[] forwardingSecret = toml.getString("player-info-forwarding-secret", "5up3r53cr3t")
.getBytes(StandardCharsets.UTF_8);
return new VelocityConfiguration(
toml.getString("bind"),
toml.getString("motd"),
toml.getLong("show-max-players").intValue(),
toml.getBoolean("online-mode"),
IPForwardingMode.valueOf(toml.getString("ip-forwarding").toUpperCase()),
toml.getString("bind", "0.0.0.0:25577"),
toml.getString("motd", "&3A Velocity Server"),
toml.getLong("show-max-players", 500L).intValue(),
toml.getBoolean("online-mode", true),
PlayerInfoForwarding.valueOf(toml.getString("player-info-forwarding", "MODERN").toUpperCase()),
ImmutableMap.copyOf(servers),
toml.getTable("servers").getList("try"),
toml.getTable("advanced").getLong("compression-threshold", 1024L).intValue(),
toml.getTable("advanced").getLong("compression-level", -1L).intValue(),
toml.getTable("query").getBoolean("enabled"),
toml.getTable("query").getLong("port", 25577L).intValue());
toml.getTable("query").getBoolean("enabled", false),
toml.getTable("query").getLong("port", 25577L).intValue(),
forwardingSecret);
}
}
}

Datei anzeigen

@ -2,7 +2,8 @@ package com.velocitypowered.proxy.connection.backend;
import com.velocitypowered.api.proxy.ConnectionRequestBuilder;
import com.velocitypowered.proxy.VelocityServer;
import com.velocitypowered.proxy.config.IPForwardingMode;
import com.velocitypowered.proxy.config.PlayerInfoForwarding;
import com.velocitypowered.proxy.config.VelocityConfiguration;
import com.velocitypowered.proxy.connection.VelocityConstants;
import com.velocitypowered.proxy.connection.client.ClientPlaySessionHandler;
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults;
@ -17,6 +18,11 @@ import io.netty.buffer.Unpooled;
import io.netty.channel.ChannelPipeline;
import net.kyori.text.TextComponent;
import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
@ -30,7 +36,7 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
@Override
public void activated() {
if (VelocityServer.getServer().getConfiguration().getIpForwardingMode() == IPForwardingMode.MODERN) {
if (VelocityServer.getServer().getConfiguration().getPlayerInfoForwardingMode() == PlayerInfoForwarding.MODERN) {
forwardingCheckTask = connection.getMinecraftConnection().getChannel().eventLoop().schedule(() -> {
connection.getProxyPlayer().handleConnectionException(connection.getServerInfo(),
TextComponent.of("Your server did not send the forwarding request in time. Is it set up correctly?"));
@ -44,12 +50,14 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
throw new IllegalStateException("Backend server is online-mode!");
} else if (packet instanceof LoginPluginMessage) {
LoginPluginMessage message = (LoginPluginMessage) packet;
if (VelocityServer.getServer().getConfiguration().getIpForwardingMode() == IPForwardingMode.MODERN &&
VelocityConfiguration configuration = VelocityServer.getServer().getConfiguration();
if (configuration.getPlayerInfoForwardingMode() == PlayerInfoForwarding.MODERN &&
message.getChannel().equals(VelocityConstants.VELOCITY_IP_FORWARDING_CHANNEL)) {
LoginPluginResponse response = new LoginPluginResponse();
response.setSuccess(true);
response.setId(message.getId());
response.setData(createForwardingData(connection.getProxyPlayer().getRemoteAddress().getHostString(),
response.setData(createForwardingData(configuration.getForwardingSecret(),
connection.getProxyPlayer().getRemoteAddress().getHostString(),
connection.getProxyPlayer().getProfile()));
connection.getMinecraftConnection().write(response);
cancelForwardingCheck();
@ -122,23 +130,43 @@ public class LoginSessionHandler implements MinecraftSessionHandler {
}
}
private static ByteBuf createForwardingData(String address, GameProfile profile) {
ByteBuf buf = Unpooled.buffer();
ProtocolUtils.writeString(buf, address);
ProtocolUtils.writeUuid(buf, profile.idAsUuid());
ProtocolUtils.writeString(buf, profile.getName());
ProtocolUtils.writeVarInt(buf, profile.getProperties().size());
static ByteBuf createForwardingData(byte[] hmacSecret, String address, GameProfile profile) {
ByteBuf dataToForward = Unpooled.buffer();
ByteBuf finalData = Unpooled.buffer();
try {
ProtocolUtils.writeString(dataToForward, address);
ProtocolUtils.writeUuid(dataToForward, profile.idAsUuid());
ProtocolUtils.writeString(dataToForward, profile.getName());
ProtocolUtils.writeVarInt(dataToForward, profile.getProperties().size());
for (GameProfile.Property property : profile.getProperties()) {
ProtocolUtils.writeString(buf, property.getName());
ProtocolUtils.writeString(buf, property.getValue());
ProtocolUtils.writeString(dataToForward, property.getName());
ProtocolUtils.writeString(dataToForward, property.getValue());
String signature = property.getSignature();
if (signature != null) {
buf.writeBoolean(true);
ProtocolUtils.writeString(buf, signature);
dataToForward.writeBoolean(true);
ProtocolUtils.writeString(dataToForward, signature);
} else {
buf.writeBoolean(false);
dataToForward.writeBoolean(false);
}
}
return buf;
SecretKey key = new SecretKeySpec(hmacSecret, "HmacSHA256");
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(key);
mac.update(dataToForward.array(), dataToForward.arrayOffset(), dataToForward.readableBytes());
byte[] sig = mac.doFinal();
finalData.writeBytes(sig);
finalData.writeBytes(dataToForward);
return finalData;
} catch (InvalidKeyException e) {
finalData.release();
throw new RuntimeException("Unable to authenticate data", e);
} catch (NoSuchAlgorithmException e) {
// Should never happen
finalData.release();
throw new AssertionError(e);
} finally {
dataToForward.release();
}
}
}

Datei anzeigen

@ -1,7 +1,7 @@
package com.velocitypowered.proxy.connection.backend;
import com.velocitypowered.api.proxy.ConnectionRequestBuilder;
import com.velocitypowered.proxy.config.IPForwardingMode;
import com.velocitypowered.proxy.config.PlayerInfoForwarding;
import com.velocitypowered.proxy.connection.MinecraftConnectionAssociation;
import com.velocitypowered.proxy.connection.util.ConnectionRequestResults;
import com.velocitypowered.proxy.protocol.ProtocolConstants;
@ -97,7 +97,7 @@ public class ServerConnection implements MinecraftConnectionAssociation {
Handshake handshake = new Handshake();
handshake.setNextStatus(StateRegistry.LOGIN_ID);
handshake.setProtocolVersion(proxyPlayer.getConnection().getProtocolVersion());
if (VelocityServer.getServer().getConfiguration().getIpForwardingMode() == IPForwardingMode.LEGACY) {
if (VelocityServer.getServer().getConfiguration().getPlayerInfoForwardingMode() == PlayerInfoForwarding.LEGACY) {
handshake.setServerAddress(createBungeeForwardingAddress());
} else {
handshake.setServerAddress(serverInfo.getAddress().getHostString());
@ -111,7 +111,7 @@ public class ServerConnection implements MinecraftConnectionAssociation {
// Send the server login packet for <=1.12.2 and for 1.13+ servers not using "modern" forwarding.
if (protocolVersion <= ProtocolConstants.MINECRAFT_1_12_2 ||
VelocityServer.getServer().getConfiguration().getIpForwardingMode() != IPForwardingMode.MODERN) {
VelocityServer.getServer().getConfiguration().getPlayerInfoForwardingMode() != PlayerInfoForwarding.MODERN) {
ServerLogin login = new ServerLogin();
login.setUsername(proxyPlayer.getUsername());
minecraftConnection.write(login);

Datei anzeigen

@ -19,7 +19,10 @@ online-mode = true
# servers using Minecraft 1.12 or lower.
# - "modern": Forward player IPs and UUIDs as part of the login process using Velocity's native
# forwarding. Only applicable for Minecraft 1.13 or higher.
ip-forwarding = "modern"
player-info-forwarding = "modern"
# If you are using modern IP forwarding, configure an unique secret here.
player-info-forwarding-secret = "5up3r53cr3t"
[servers]
# Configure your servers here.