Limiting request rate to your services inside Docker containers

In our research group AKSW we stumbled upon a task to limit the request rate to our services. And we found a simple solution using OpenRepose and Docker.

The complete example is located on my Github.

The basic idea behind the solution was not to modify any of the existing services, but to introduce a middleware, which will block users from making excessive amount of requests. As we are running most of our services in Docker containers, the solution should be able to run in the Docker. It is indeed possible with OpenRepose and the simplest setup inside docker-compose.yml looks like this:

version: “2.1”
image: rackerlabs/repose:
— ./etc/repose:/etc/repose
— 80:80
image: nginx

Our actual service nginx is not exposed to the outside world and thus is hidden behind repose-proxy, which controls the flow of requests. The configuration of repose includes several things.

Main configuration file system-model.cfg.xml (located in ./etc/repose/system-model.cfg.xml) defines which port the Repose will listen to (port 80, which is bound to port 80 on outside host in docker-compose.yml, the binding in docker compose is always HOST:CONTAINER):

<node id=”repose_node1" hostname=”localhost” http-port=”80"/>

Defines two active filters (rate-limiting filter requires ip-user filter to inject X-PP-User HTTP header based on user IP address):

<filter name=”ip-user”/>
<filter name=”rate-limiting”/>

And the default proxy destination — nginx service (the hostname nginx should match the service name in docker-compose.yml definition and be in the same docker network as repose service):

<endpoint id=”service” protocol=”http” hostname=”nginx” root-path=”/” port=”80" default=”true”/>

The next thing is to configure IP ranges and assign them various user groups. In this example, we only define one user group match-all. The IP ranges configuration is ip-user.cfg.xml (located in ./etc/repose/ip-user.cfg.xml). We define match-all to match all IPv4 and IPv6:

<group name=”match-all”>

Now we can define the rate limit for the match-all group. The rate limit configuration is rate-limiting.cfg.xml (located in ./etc/repose/rate-limiting.cfg.xml). It uses the groups of users you defined in the IP ranges configuration previously (e.g. match-all group). Also, you can define the global limit for all the users:

<limit id=”global” uri=”*” uri-regex=”.*” value=”1000" unit=”MINUTE”/>

The global limit affects the whole amount of requests to 1000 per minute. If this amount of requests is exceeded, no more requests will be possible. To limit requests for a particular group of users (i.e. match-all group) we define a limit group:

<limit-group id=”limited” groups=”match-all” default=”true”>
<limit id=”all” uri=”*” uri-regex=”/.*” http-methods=”POST PUT GET DELETE” unit=”MINUTE” value=”10"/>

This will limit requests for any given IP address within previously specified IP range (any IP addresses in this case) to 10 per one minute.

For additional information on OpenRepose refer to the official wiki and see examples (located in ./etc/repose/examples) folder for available filter configurations.

Thank you for reading, Ivan Ermilov :)