From d752edc92d85e16a6ca143dd400936ff681bac15 Mon Sep 17 00:00:00 2001 From: MatrixTunnel Date: Fri, 10 Aug 2018 11:08:55 -0700 Subject: [PATCH 1/2] Add login ratelimit configuration --- .../velocitypowered/proxy/VelocityServer.java | 4 +++- .../proxy/config/VelocityConfiguration.java | 17 +++++++++++++++-- .../proxy/util/Ratelimiter.java | 18 ++++++++++++------ proxy/src/main/resources/velocity.toml | 4 ++++ 4 files changed, 34 insertions(+), 9 deletions(-) diff --git a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java index d202b9b13..aea8c08ba 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/VelocityServer.java @@ -72,7 +72,7 @@ public class VelocityServer implements ProxyServer { return true; } }; - private final Ratelimiter ipAttemptLimiter = new Ratelimiter(3000); // TODO: Configurable. + private Ratelimiter ipAttemptLimiter; private VelocityServer() { commandManager.registerCommand("velocity", new VelocityCommand()); @@ -127,6 +127,8 @@ public class VelocityServer implements ProxyServer { serverKeyPair = EncryptionUtils.createRsaKeyPair(1024); + ipAttemptLimiter = new Ratelimiter(configuration.getLoginRatelimit()); + httpClient = new NettyHttpClient(this); this.cm.bind(configuration.getBind()); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java index 13c97eff5..f18dee55c 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/config/VelocityConfiguration.java @@ -34,6 +34,7 @@ public class VelocityConfiguration { private final List attemptConnectionOrder; private final int compressionThreshold; private final int compressionLevel; + private final int loginRatelimit; private final boolean queryEnabled; private final int queryPort; @@ -46,8 +47,8 @@ public class VelocityConfiguration { private VelocityConfiguration(String bind, String motd, int showMaxPlayers, boolean onlineMode, PlayerInfoForwarding playerInfoForwardingMode, Map servers, List attemptConnectionOrder, int compressionThreshold, - int compressionLevel, boolean queryEnabled, int queryPort, - byte[] forwardingSecret) { + int compressionLevel, int loginRatelimit, boolean queryEnabled, + int queryPort, byte[] forwardingSecret) { this.bind = bind; this.motd = motd; this.showMaxPlayers = showMaxPlayers; @@ -57,6 +58,7 @@ public class VelocityConfiguration { this.attemptConnectionOrder = attemptConnectionOrder; this.compressionThreshold = compressionThreshold; this.compressionLevel = compressionLevel; + this.loginRatelimit = loginRatelimit; this.queryEnabled = queryEnabled; this.queryPort = queryPort; this.forwardingSecret = forwardingSecret; @@ -138,6 +140,11 @@ public class VelocityConfiguration { logger.warn("ALL packets going through the proxy are going to be compressed. This may hurt performance."); } + if (loginRatelimit < 0) { + logger.error("Invalid login ratelimit {}", loginRatelimit); + valid = false; + } + loadFavicon(); return valid; @@ -209,6 +216,10 @@ public class VelocityConfiguration { return compressionLevel; } + public int getLoginRatelimit() { + return loginRatelimit; + } + public Favicon getFavicon() { return favicon; } @@ -229,6 +240,7 @@ public class VelocityConfiguration { ", attemptConnectionOrder=" + attemptConnectionOrder + ", compressionThreshold=" + compressionThreshold + ", compressionLevel=" + compressionLevel + + ", loginRatelimit=" + loginRatelimit + ", queryEnabled=" + queryEnabled + ", queryPort=" + queryPort + ", motdAsComponent=" + motdAsComponent + @@ -265,6 +277,7 @@ public class VelocityConfiguration { toml.getTable("servers").getList("try"), toml.getTable("advanced").getLong("compression-threshold", 1024L).intValue(), toml.getTable("advanced").getLong("compression-level", -1L).intValue(), + toml.getTable("advanced").getLong("login-ratelimit", 3000L).intValue(), toml.getTable("query").getBoolean("enabled", false), toml.getTable("query").getLong("port", 25577L).intValue(), forwardingSecret); diff --git a/proxy/src/main/java/com/velocitypowered/proxy/util/Ratelimiter.java b/proxy/src/main/java/com/velocitypowered/proxy/util/Ratelimiter.java index 2f49c6e6b..6095a56cd 100644 --- a/proxy/src/main/java/com/velocitypowered/proxy/util/Ratelimiter.java +++ b/proxy/src/main/java/com/velocitypowered/proxy/util/Ratelimiter.java @@ -19,15 +19,21 @@ public class Ratelimiter { @VisibleForTesting Ratelimiter(long timeoutMs, Ticker ticker) { - this.timeoutNanos = TimeUnit.MILLISECONDS.toNanos(timeoutMs); - this.expiringCache = CacheBuilder.newBuilder() - .ticker(ticker) - .concurrencyLevel(Runtime.getRuntime().availableProcessors()) - .expireAfterWrite(timeoutMs, TimeUnit.MILLISECONDS) - .build(); + if (timeoutMs == 0) { + this.timeoutNanos = timeoutMs; + this.expiringCache = null; + } else { + this.timeoutNanos = TimeUnit.MILLISECONDS.toNanos(timeoutMs); + this.expiringCache = CacheBuilder.newBuilder() + .ticker(ticker) + .concurrencyLevel(Runtime.getRuntime().availableProcessors()) + .expireAfterWrite(timeoutMs, TimeUnit.MILLISECONDS) + .build(); + } } public boolean attempt(InetAddress address) { + if (timeoutNanos == 0) return true; long expectedNewValue = System.nanoTime() + timeoutNanos; long last; try { diff --git a/proxy/src/main/resources/velocity.toml b/proxy/src/main/resources/velocity.toml index 3bdd006e1..24a7921bd 100644 --- a/proxy/src/main/resources/velocity.toml +++ b/proxy/src/main/resources/velocity.toml @@ -43,6 +43,10 @@ compression-threshold = 1024 # How much compression should be done (from 0-9). The default is -1, which uses zlib's default level of 6. compression-level = -1 +# How fast (in miliseconds) are clients allowed to connect after the last connection? Default: 3000 +# Disable by setting to 0 +login-ratelimit = 3000 + [query] # Whether to enable responding to GameSpy 4 query responses or not enabled = false From 6e4f90dfae1ba51d723867871881b49c75d1202e Mon Sep 17 00:00:00 2001 From: MatrixTunnel Date: Fri, 10 Aug 2018 18:57:10 -0700 Subject: [PATCH 2/2] Add ratelimit tests --- .../velocitypowered/proxy/util/RatelimiterTest.java | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/proxy/src/test/java/com/velocitypowered/proxy/util/RatelimiterTest.java b/proxy/src/test/java/com/velocitypowered/proxy/util/RatelimiterTest.java index 7df52cb96..b9f9ff204 100644 --- a/proxy/src/test/java/com/velocitypowered/proxy/util/RatelimiterTest.java +++ b/proxy/src/test/java/com/velocitypowered/proxy/util/RatelimiterTest.java @@ -12,7 +12,14 @@ import static org.junit.jupiter.api.Assertions.*; class RatelimiterTest { @Test - void attempt() { + void attemptZero() { + Ratelimiter noRatelimiter = new Ratelimiter(0); + assertTrue(noRatelimiter.attempt(InetAddress.getLoopbackAddress())); + assertTrue(noRatelimiter.attempt(InetAddress.getLoopbackAddress())); + } + + @Test + void attemptOne() { long base = System.nanoTime(); AtomicLong extra = new AtomicLong(); Ticker testTicker = new Ticker() { @@ -27,4 +34,5 @@ class RatelimiterTest { extra.addAndGet(TimeUnit.SECONDS.toNanos(2)); assertTrue(ratelimiter.attempt(InetAddress.getLoopbackAddress())); } -} \ No newline at end of file + +}