From c9a4f7768cfd77b03ec26136c3fd25edb63909e0 Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Mon, 18 Nov 2024 18:22:08 +0100 Subject: [PATCH 1/2] Added documentation for `DoSHandler`. Signed-off-by: Simone Bordet --- .../server/http/HTTPServerDocs.java | 39 +++++++++++++++++++ .../pages/modules/standard.adoc | 13 +++++++ .../programming-guide/pages/server/http.adoc | 32 ++++++++++++++- .../src/main/config/etc/jetty-dos.xml | 23 ++++++++--- .../src/main/config/modules/dos.mod | 30 +++++++++----- .../jetty/server/handler/DoSHandler.java | 8 ++-- 6 files changed, 124 insertions(+), 21 deletions(-) diff --git a/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java b/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java index 374fa089af8..aa54c6ac0e8 100644 --- a/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java +++ b/documentation/jetty/modules/code/examples/src/main/java/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java @@ -83,6 +83,7 @@ import org.eclipse.jetty.server.handler.ContextHandlerCollection; import org.eclipse.jetty.server.handler.CrossOriginHandler; import org.eclipse.jetty.server.handler.DefaultHandler; +import org.eclipse.jetty.server.handler.DoSHandler; import org.eclipse.jetty.server.handler.EventsHandler; import org.eclipse.jetty.server.handler.QoSHandler; import org.eclipse.jetty.server.handler.ResourceHandler; @@ -1747,4 +1748,42 @@ public void requestCustomizer() throws Exception server.start(); // end::requestCustomizer[] } + + public void dosHandler() throws Exception + { + // tag::dosHandler[] + class CatalogHandler extends Handler.Abstract + { + @Override + public boolean handle(Request request, Response response, Callback callback) + { + // Implement the catalog application. + callback.succeeded(); + return true; + } + } + + Server server = new Server(); + ServerConnector connector = new ServerConnector(server); + server.addConnector(connector); + + // Create and configure DoSHandler. + DoSHandler dosHandler = new DoSHandler( + // Identify remote clients by IP address. + DoSHandler.ID_FROM_REMOTE_ADDRESS, + // Allow 50 requests/s per remote client. + new DoSHandler.LeakingBucketTrackerFactory(50), + // When the request rate is exceeded, delay for 10s and then respond with 429. + new DoSHandler.DelayedRejectHandler(10000, -1, new DoSHandler.StatusRejectHandler()), + // Limit the number of remote clients. + 5000 + ); + server.setHandler(dosHandler); + + // Protect the catalog application by wrapping CatalogHandler with DoSHandler. + dosHandler.setHandler(new CatalogHandler()); + + server.start(); + // end::dosHandler[] + } } diff --git a/documentation/jetty/modules/operations-guide/pages/modules/standard.adoc b/documentation/jetty/modules/operations-guide/pages/modules/standard.adoc index 6fd4234db76..48522e97319 100644 --- a/documentation/jetty/modules/operations-guide/pages/modules/standard.adoc +++ b/documentation/jetty/modules/operations-guide/pages/modules/standard.adoc @@ -129,6 +129,19 @@ The module properties are: include::{jetty-home}/modules/debuglog.mod[tags=documentation] ---- +[[dos]] +== Module `dos` + +The `dos` module installs the `org.eclipse.jetty.server.handler.DoSHandler` at the root of the `Handler` tree. + +The `DoSHandler` limits the rate of requests per remote client, as explained in xref:programming-guide:server/http.adoc#handler-use-dos[this section]. + +The module properties are: + +---- +include::{jetty-home}/modules/dos.mod[tags=documentation] +---- + [[eager-content]] == Module `eager-content` diff --git a/documentation/jetty/modules/programming-guide/pages/server/http.adoc b/documentation/jetty/modules/programming-guide/pages/server/http.adoc index e2dcc95efb9..fc0a7aa2e3f 100644 --- a/documentation/jetty/modules/programming-guide/pages/server/http.adoc +++ b/documentation/jetty/modules/programming-guide/pages/server/http.adoc @@ -1111,6 +1111,8 @@ This is an example of a `QoSHandler` subclass where you can implement a custom p include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java[tags=advancedQoSHandler] ---- +Note that `QoSHandler` limits the number of _active_ concurrent requests for the whole server; if you want to apply limits for each remote client, see <> and <>. + [[handler-use-secured]] ==== SecuredRedirectHandler -- Redirect from HTTP to HTTPS @@ -1288,10 +1290,36 @@ This allows web applications that use blocking API calls such as `HttpServletReq The remote IP address can be derived from the network, or from the `Forwarded` (or the now obsolete `X-Forwarded-For`) HTTP header. The `Forwarded` header is typically present in requests that have been forwarded to Jetty by a reverse proxy such as HAProxy, Nginx, Apache, etc. -// TODO: mention the DoSHandler in Jetty 12.1.x. - Note that `ThreadLimitHandler` is different from xref:handler-use-qos[`QoSHandler`] in that it limits the number of concurrent requests per remote IP address, while `QoSHandler` limits the total number of concurrent requests. +Note also that `ThreadLimitHandler` is different from xref:handler-use-dos[`DoSHandler`] in that it limits the number of concurrent requests per remote IP address, while `DoSHandler` limits the request rate per remote IP address. + +[[handler-use-dos]] +==== DoSHandler + +`DoSHandler` tracks remote clients (in a pluggable way, by default using the remote IP address), and limits the rate of requests for each remote client, to protect against denial-of-service attacks. + +`DoSHandler` extends xref:handler-use-conditional[`ConditionalHandler`], so you may be able to restrict what `DoSHandler` does to only requests that match the conditions (for example, only to `POST` requests, or only for certain request URIs, etc.) + +`DoSHandler` allows you to specify how to identify a remote client (by default using the remote IP address, but this is configurable), and the algorithm to use to calculate the rate of requests for each remote client (by default the link:https://en.wikipedia.org/wiki/Leaky_bucket[Leaky Bucket Algorithm]). + +If a remote client exceeds the configured request rate, `DoSHandler` forwards the request handling to a configurable `Request.Handler` that allows you to decide how to handle the request. +`DosHandler` provides two such ``Request.Handler``s out of the box: + +* `StatusRejectHandler` (the default) that responds with a configurable HTTP status code (by default `429 Too Many Requests`) +* `DelayedRejectHandler` that delays the request handling by a configurable timeout, and then forwards to request handling to another `Request.Handler` (by default `StatusRejectHandler`). +Delaying the request handling is of little cost for the server (since the request is delayed asynchronously), but the attacker would not know whether the request it sent was processed or rejected until the timeout elapses. +Hopefully, the delay forces the attacker to use more resources, and/or reduce the request rate, therefore reducing the attack impact on the server. + +Here is a simple example that shows how to use `DoSHandler`: + +[,java,indent=0,options=nowrap] +---- +include::code:example$src/main/java/org/eclipse/jetty/docs/programming/server/http/HTTPServerDocs.java[tags=dosHandler] +---- + +For other denial-of-service protections, see also <> and <>. + [[handler-use-servlet]] === Servlet API Handlers diff --git a/jetty-core/jetty-server/src/main/config/etc/jetty-dos.xml b/jetty-core/jetty-server/src/main/config/etc/jetty-dos.xml index 68b4b9de008..91f654a5a8b 100644 --- a/jetty-core/jetty-server/src/main/config/etc/jetty-dos.xml +++ b/jetty-core/jetty-server/src/main/config/etc/jetty-dos.xml @@ -40,18 +40,17 @@ - - + - + - + - + @@ -69,6 +68,20 @@ + + + + + + + + + + + + + + diff --git a/jetty-core/jetty-server/src/main/config/modules/dos.mod b/jetty-core/jetty-server/src/main/config/modules/dos.mod index 45d5a9f5da4..7ae61e49daa 100644 --- a/jetty-core/jetty-server/src/main/config/modules/dos.mod +++ b/jetty-core/jetty-server/src/main/config/modules/dos.mod @@ -6,14 +6,18 @@ Enables the DosHandler for the server. [tags] connector -[depend] +[before] +compression +gzip + +[depends] server [xml] etc/jetty-dos.xml [ini-template] - +#tag::documentation[] ## The algorithm to use for obtaining an remote client identifier from a Request: ID_FROM_REMOTE_ADDRESS, ID_FROM_REMOTE_PORT, ID_FROM_REMOTE_ADDRESS_PORT, ID_FROM_CONNECTION #jetty.dos.id.type=ID_FROM_REMOTE_ADDRESS #jetty.dos.id.class=org.eclipse.jetty.server.handler.DosHandler @@ -48,15 +52,21 @@ etc/jetty-dos.xml ## The status code used to reject requests; or 0 to abort the request; or -1 for a default #jetty.dos.rejectStatus=429 -## List of InetAddress patterns to include -#jetty.dos.include.inet=10.10.10-14.0-128 +## A comma-separated list of HTTP methods to include when matching a request. +# jetty.dos.include.method= + +## A comma-separated list of HTTP methods to exclude when matching a request. +# jetty.dos.exclude.method= -## List of InetAddressPatterns to exclude -#jetty.dos.exclude.inet=10.10.10-14.0-128 +## A comma-separated list of URI path patterns to include when matching a request. +# jetty.dos.include.path= -## List of path patterns to include -#jetty.dos.include.path=/context/* +## A comma-separated list of URI path patterns to exclude when matching a request. +# jetty.dos.exclude.path= -## List of path to exclude -#jetty.dos.exclude.path=/context/* +## A comma-separated list of remote addresses patterns to include when matching a request. +# jetty.dos.include.inet= +## A comma-separated list of remote addresses patterns to exclude when matching a request. +# jetty.dos.exclude.inet= +#end::documentation[] diff --git a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DoSHandler.java b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DoSHandler.java index 5aaa5d2be2a..98f0d39a983 100644 --- a/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DoSHandler.java +++ b/jetty-core/jetty-server/src/main/java/org/eclipse/jetty/server/handler/DoSHandler.java @@ -185,7 +185,7 @@ protected boolean onConditionsMet(Request request, Response response, Callback c // Calculate an id for the request (which may be global empty string). String id = _clientIdFn.apply(request); - // Reject or handle untracked request + // Reject or handle untracked request. if (id == null) id = ""; @@ -193,18 +193,18 @@ protected boolean onConditionsMet(Request request, Response response, Callback c // Trackers are removed if CyclicTimeouts#onExpired returns true. Tracker tracker = _trackers.computeIfAbsent(id, this::newTracker); - // If we have too many trackers, then we will have a null tracker + // If we have too many trackers, then we will have a null tracker. if (tracker == null) return _rejectUntracked ? _rejectHandler.handle(request, response, callback) : nextHandler(request, response, callback); - // IS the request allowed by the tracker? + // Is the request allowed by the tracker? boolean allowed = tracker.onRequest(NanoTime.now()); if (LOG.isDebugEnabled()) LOG.debug("allowed={} {}", allowed, tracker); if (allowed) return nextHandler(request, response, callback); - // Otherwise reject the request as it is over rate + // Otherwise reject the request as it is over the rate. return _rejectHandler.handle(request, response, callback); } From 2e6b7d707811f9591bff4d86386a7fd7ee13ba3f Mon Sep 17 00:00:00 2001 From: Simone Bordet Date: Tue, 19 Nov 2024 09:56:30 +0100 Subject: [PATCH 2/2] Added caution paragraph about dos protection. Signed-off-by: Simone Bordet --- .../jetty/modules/programming-guide/pages/server/http.adoc | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/documentation/jetty/modules/programming-guide/pages/server/http.adoc b/documentation/jetty/modules/programming-guide/pages/server/http.adoc index fc0a7aa2e3f..f653eaca5ee 100644 --- a/documentation/jetty/modules/programming-guide/pages/server/http.adoc +++ b/documentation/jetty/modules/programming-guide/pages/server/http.adoc @@ -1311,6 +1311,11 @@ If a remote client exceeds the configured request rate, `DoSHandler` forwards th Delaying the request handling is of little cost for the server (since the request is delayed asynchronously), but the attacker would not know whether the request it sent was processed or rejected until the timeout elapses. Hopefully, the delay forces the attacker to use more resources, and/or reduce the request rate, therefore reducing the attack impact on the server. +[CAUTION] +==== +While `DoSHandler` may be a good start for simple setups, denial-of-service protection is typically best performed _outside_ the server itself, for example in load balancers or other data center network infrastructure components. +==== + Here is a simple example that shows how to use `DoSHandler`: [,java,indent=0,options=nowrap]