diff --git a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java index d8596910..84409262 100644 --- a/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java +++ b/bukkit/src/main/java/com/github/games647/fastlogin/bukkit/listener/protocollib/VerifyResponseTask.java @@ -36,15 +36,18 @@ import com.comphenix.protocol.wrappers.WrappedChatComponent; import com.comphenix.protocol.wrappers.WrappedGameProfile; import com.github.games647.craftapi.model.auth.Verification; import com.github.games647.craftapi.model.skin.SkinProperty; +import com.github.games647.craftapi.resolver.AbstractResolver; import com.github.games647.craftapi.resolver.MojangResolver; import com.github.games647.fastlogin.bukkit.BukkitLoginSession; import com.github.games647.fastlogin.bukkit.FastLoginBukkit; import java.io.IOException; +import java.io.InputStream; +import java.io.Reader; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; -import java.net.InetAddress; -import java.net.InetSocketAddress; +import java.net.*; +import java.nio.charset.StandardCharsets; import java.security.GeneralSecurityException; import java.security.Key; import java.security.KeyPair; @@ -161,9 +164,7 @@ public class VerifyResponseTask implements Runnable { receiveFakeStartPacket(realUsername); } else { //user tried to fake an authentication - disconnect("invalid-session", - "GameProfile {0} ({1}) tried to log in with an invalid session ServerId: {2}", - session.getRequestUsername(), socketAddress, serverId); + disconnect("invalid-session", "GameProfile {} ({}) tried to log in with an invalid session. ServerId: {}", session.getRequestUsername(), socketAddress, serverId); } } catch (IOException ioEx) { disconnect("error-kick", "Failed to connect to session server", ioEx); diff --git a/core/src/main/java/com/github/games647/fastlogin/core/mojang/ProxyAgnosticMojangResolver.java b/core/src/main/java/com/github/games647/fastlogin/core/mojang/ProxyAgnosticMojangResolver.java new file mode 100644 index 00000000..aabba993 --- /dev/null +++ b/core/src/main/java/com/github/games647/fastlogin/core/mojang/ProxyAgnosticMojangResolver.java @@ -0,0 +1,77 @@ +/* + * SPDX-License-Identifier: MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2015-2022 games647 and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ +package com.github.games647.fastlogin.core.mojang; + +import com.github.games647.craftapi.model.auth.Verification; +import com.github.games647.craftapi.resolver.MojangResolver; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.InetAddress; +import java.util.Optional; + +/** + * An extension to {@link MojangResolver} which allows connection using transparent reverse proxies. + * The significant difference is that unlike MojangResolver from the CraftAPI implementation, which sends the "ip" parameter + * when the hostIp parameter is an IPv4 address, but skips it for IPv6, this implementation leaves out the "ip" parameter + * also for IPv4, effectively enabling transparent proxies to work. + * @author games647, Enginecrafter77 + */ +public class ProxyAgnosticMojangResolver extends MojangResolver { + /** + * A formatting string containing an URL used to call the {@code hasJoined} method on mojang session servers. + * + * Formatting parameters: + * 1. The username of the player in question + * 2. The serverId of this server + */ + public static final String MOJANG_SESSIONSERVER_HASJOINED_CALL_URLFMT = "https://sessionserver.mojang.com/session/minecraft/hasJoined?username=%s&serverId=%s"; + + @Override + public Optional hasJoined(String username, String serverHash, InetAddress hostIp) throws IOException + { + String url = String.format(MOJANG_SESSIONSERVER_HASJOINED_CALL_URLFMT, username, serverHash); + + HttpURLConnection conn = this.getConnection(url); + int responseCode = conn.getResponseCode(); + + Verification verification = null; + + // Mojang session servers send HTTP 204 (NO CONTENT) when the authentication seems invalid + // If that's not our case, the authentication is valid, and so we can parse the response. + if(responseCode != HttpURLConnection.HTTP_NO_CONTENT) + verification = this.parseRequest(conn, this::parseVerification); + + return Optional.ofNullable(verification); + } + + // Functional implementation of InputStreamAction, used in hasJoined method in parseRequest call + protected Verification parseVerification(InputStream input) throws IOException + { + return this.readJson(input, Verification.class); + } +} diff --git a/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java b/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java index c016087b..a5723bf4 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/shared/FastLoginCore.java @@ -33,6 +33,7 @@ import com.github.games647.fastlogin.core.TickingRateLimiter; import com.github.games647.fastlogin.core.hooks.AuthPlugin; import com.github.games647.fastlogin.core.hooks.DefaultPasswordGenerator; import com.github.games647.fastlogin.core.hooks.PasswordGenerator; +import com.github.games647.fastlogin.core.mojang.ProxyAgnosticMojangResolver; import com.github.games647.fastlogin.core.storage.MySQLStorage; import com.github.games647.fastlogin.core.storage.SQLStorage; import com.github.games647.fastlogin.core.storage.SQLiteStorage; @@ -83,7 +84,7 @@ public class FastLoginCore

> { private final Collection pendingConfirms = new HashSet<>(); private final T plugin; - private final MojangResolver resolver = new MojangResolver(); + private MojangResolver resolver; private Configuration config; private SQLStorage storage; @@ -118,6 +119,9 @@ public class FastLoginCore

> { return; } + // Initialize the resolver based on the config parameter + this.resolver = this.config.getBoolean("useProxyAgnosticResolver", false) ? new ProxyAgnosticMojangResolver() : new MojangResolver(); + rateLimiter = createRateLimiter(config.getSection("anti-bot")); Set proxies = config.getStringList("proxies") .stream() diff --git a/core/src/main/resources/config.yml b/core/src/main/resources/config.yml index e3a01799..5bec5b88 100644 --- a/core/src/main/resources/config.yml +++ b/core/src/main/resources/config.yml @@ -152,6 +152,24 @@ forwardSkin: true # If they still want to invoke the command, they have to invoke /premium again premium-warning: true +# ======[[ Spigot+ProtocolLib users only ]]====== +# When set to true, enables the use of alternative session resolver which does not send the server IP +# to mojang session servers. This setting might be useful when you are trying to run the server via a +# transparent reverse proxy or some other form of DNAT. As far as security goes, this setting has +# negligible to no security impact. +# +# This setting works on a similar principle as 'prevent-proxy' setting in server.properties. +# When set to false, the server behaves like prevent-proxy was set to true and vice-versa. +# Normally, when you use the prevent-proxy=true, you would want this disabled. +# +# Please note that this setting has no effect when used outside of Spigot+ProtocolLib context. +# +# !!! [WARNING] !!! +# This option is considered highly experimental. While it is highly unlikely this will break your server, +# more tests need to be conducted in order to verify its effectiveness. Brief tests seemed promising, but +# every environment is different, and so it might not work for you as it did for me. +useProxyAgnosticResolver: false + # If you have autoRegister or nameChangeCheck enabled, you could be rate-limited by Mojang. # The requests of the both options will be only made by FastLogin if the username is unknown to the server # You are allowed to make 600 requests per 10-minutes (60 per minute)