From 0dc6568dcc28e6abd6b3cede119295570963be0c Mon Sep 17 00:00:00 2001 From: Julien Viet Date: Wed, 11 Oct 2023 09:48:07 +0200 Subject: [PATCH] Netty 4.1.100.Final added an HTTP/2 RST frame flood protection, this makes it configurable. --- .../io/vertx/core/http/HttpServerOptions.java | 85 +++++++++++++++++++ .../core/http/impl/HttpServerWorker.java | 3 + .../VertxHttp2ConnectionHandlerBuilder.java | 5 ++ 3 files changed, 93 insertions(+) diff --git a/src/main/java/io/vertx/core/http/HttpServerOptions.java b/src/main/java/io/vertx/core/http/HttpServerOptions.java index c13b8d80ff4..da4869c07a8 100755 --- a/src/main/java/io/vertx/core/http/HttpServerOptions.java +++ b/src/main/java/io/vertx/core/http/HttpServerOptions.java @@ -169,6 +169,21 @@ public class HttpServerOptions extends NetServerOptions { */ public static final boolean DEFAULT_REGISTER_WEBSOCKET_WRITE_HANDLERS = false; + /** + * HTTP/2 RST floods DDOS protection, max number of RST frame per time window allowed = 200. + */ + public static final int DEFAULT_HTTP2_RST_FLOOD_MAX_RST_FRAME_PER_WINDOW = 200; + + /** + * HTTP/2 RST floods DDOS protection, time window duration = 30. + */ + public static final int DEFAULT_HTTP2_RST_FLOOD_WINDOW_DURATION = 30; + + /** + * HTTP/2 RST floods DDOS protection, time window duration unit = {@link TimeUnit#SECONDS}. + */ + public static final TimeUnit DEFAULT_HTTP2_RST_FLOOD_WINDOW_DURATION_TIME_UNIT = TimeUnit.SECONDS; + private boolean compressionSupported; private int compressionLevel; private List compressors; @@ -194,6 +209,9 @@ public class HttpServerOptions extends NetServerOptions { private int webSocketClosingTimeout; private TracingPolicy tracingPolicy; private boolean registerWebSocketWriteHandlers; + private int http2RstFloodMaxRstFramePerWindow; + private int http2RstFloodWindowDuration; + private TimeUnit http2RstFloodWindowDurationTimeUnit; /** * Default constructor @@ -236,6 +254,9 @@ public HttpServerOptions(HttpServerOptions other) { this.webSocketClosingTimeout = other.webSocketClosingTimeout; this.tracingPolicy = other.tracingPolicy; this.registerWebSocketWriteHandlers = other.registerWebSocketWriteHandlers; + this.http2RstFloodMaxRstFramePerWindow = other.http2RstFloodMaxRstFramePerWindow; + this.http2RstFloodWindowDuration = other.http2RstFloodWindowDuration; + this.http2RstFloodWindowDurationTimeUnit = other.http2RstFloodWindowDurationTimeUnit; } /** @@ -285,6 +306,9 @@ private void init() { webSocketClosingTimeout = DEFAULT_WEBSOCKET_CLOSING_TIMEOUT; tracingPolicy = DEFAULT_TRACING_POLICY; registerWebSocketWriteHandlers = DEFAULT_REGISTER_WEBSOCKET_WRITE_HANDLERS; + http2RstFloodMaxRstFramePerWindow = DEFAULT_HTTP2_RST_FLOOD_MAX_RST_FRAME_PER_WINDOW; + http2RstFloodWindowDuration = DEFAULT_HTTP2_RST_FLOOD_WINDOW_DURATION; + http2RstFloodWindowDurationTimeUnit = DEFAULT_HTTP2_RST_FLOOD_WINDOW_DURATION_TIME_UNIT; } @Override @@ -1096,4 +1120,65 @@ public HttpServerOptions setRegisterWebSocketWriteHandlers(boolean registerWebSo this.registerWebSocketWriteHandlers = registerWebSocketWriteHandlers; return this; } + + /** + * @return the max number of RST frame allowed per time window + */ + public int getHttp2RstFloodMaxRstFramePerWindow() { + return http2RstFloodMaxRstFramePerWindow; + } + + /** + * Set the max number of RST frame allowed per time window, this is used to prevent HTTP/2 RST frame flood DDOS + * attacks. The default value is {@link #DEFAULT_HTTP2_RST_FLOOD_MAX_RST_FRAME_PER_WINDOW}, setting zero or a negative value, disables flood protection. + * + * @param http2RstFloodMaxRstFramePerWindow the new maximum + * @return a reference to this, so the API can be used fluently + */ + public HttpServerOptions setHttp2RstFloodMaxRstFramePerWindow(int http2RstFloodMaxRstFramePerWindow) { + this.http2RstFloodMaxRstFramePerWindow = http2RstFloodMaxRstFramePerWindow; + return this; + } + + /** + * @return the duration of the time window when checking the max number of RST frames. + */ + public int getHttp2RstFloodWindowDuration() { + return http2RstFloodWindowDuration; + } + + /** + * Set the duration of the time window when checking the max number of RST frames, this is used to prevent HTTP/2 RST frame flood DDOS + * attacks. The default value is {@link #DEFAULT_HTTP2_RST_FLOOD_WINDOW_DURATION}, setting zero or a negative value, disables flood protection. + * + * @param http2RstFloodWindowDuration the new duration + * @return a reference to this, so the API can be used fluently + */ + public HttpServerOptions setHttp2RstFloodWindowDuration(int http2RstFloodWindowDuration) { + this.http2RstFloodWindowDuration = http2RstFloodWindowDuration; + return this; + } + + /** + * @return the time unit of the duration of the time window when checking the max number of RST frames. + */ + public TimeUnit getHttp2RstFloodWindowDurationTimeUnit() { + return http2RstFloodWindowDurationTimeUnit; + } + + /** + * Set the time unit of the duration of the time window when checking the max number of RST frames, this is used to + * prevent HTTP/2 RST frame flood DDOS attacks. The default value is {@link #DEFAULT_HTTP2_RST_FLOOD_WINDOW_DURATION_TIME_UNIT}, + * setting zero or a negative value, disables the flood protection. + * + * @param http2RstFloodWindowDurationTimeUnit the new duration + * @return a reference to this, so the API can be used fluently + */ + public HttpServerOptions setHttp2RstFloodWindowDurationTimeUnit(TimeUnit http2RstFloodWindowDurationTimeUnit) { + if (http2RstFloodWindowDurationTimeUnit == null) { + throw new NullPointerException(); + } + this.http2RstFloodWindowDurationTimeUnit = http2RstFloodWindowDurationTimeUnit; + return this; + } } diff --git a/src/main/java/io/vertx/core/http/impl/HttpServerWorker.java b/src/main/java/io/vertx/core/http/impl/HttpServerWorker.java index 2f72d3a3616..cf37c4e8bbc 100644 --- a/src/main/java/io/vertx/core/http/impl/HttpServerWorker.java +++ b/src/main/java/io/vertx/core/http/impl/HttpServerWorker.java @@ -239,9 +239,12 @@ void configureHttp2(ChannelPipeline pipeline) { VertxHttp2ConnectionHandler buildHttp2ConnectionHandler(EventLoopContext ctx, Handler handler_) { HttpServerMetrics metrics = (HttpServerMetrics) server.getMetrics(); + int maxRstFramesPerWindow = options.getHttp2RstFloodMaxRstFramePerWindow(); + int secondsPerWindow = (int)options.getHttp2RstFloodWindowDurationTimeUnit().toSeconds(options.getHttp2RstFloodWindowDuration()); VertxHttp2ConnectionHandler handler = new VertxHttp2ConnectionHandlerBuilder() .server(true) .useCompression(compressionOptions) + .decoderEnforceMaxRstFramesPerWindow(maxRstFramesPerWindow, secondsPerWindow) .useDecompression(options.isDecompressionSupported()) .initialSettings(options.getInitialSettings()) .connectionFactory(connHandler -> { diff --git a/src/main/java/io/vertx/core/http/impl/VertxHttp2ConnectionHandlerBuilder.java b/src/main/java/io/vertx/core/http/impl/VertxHttp2ConnectionHandlerBuilder.java index 98239ee14a5..6a899c27373 100644 --- a/src/main/java/io/vertx/core/http/impl/VertxHttp2ConnectionHandlerBuilder.java +++ b/src/main/java/io/vertx/core/http/impl/VertxHttp2ConnectionHandlerBuilder.java @@ -55,6 +55,11 @@ VertxHttp2ConnectionHandlerBuilder useCompression(CompressionOptions[] compre return this; } + @Override + protected VertxHttp2ConnectionHandlerBuilder decoderEnforceMaxRstFramesPerWindow(int maxRstFramesPerWindow, int secondsPerWindow) { + return super.decoderEnforceMaxRstFramesPerWindow(maxRstFramesPerWindow, secondsPerWindow); + } + @Override protected VertxHttp2ConnectionHandlerBuilder gracefulShutdownTimeoutMillis(long gracefulShutdownTimeoutMillis) { return super.gracefulShutdownTimeoutMillis(gracefulShutdownTimeoutMillis);