drmaxnix/banjo
Docker Web Application Firewall (WAF) to protect against Brute Force Attacks
25
Source Code: git.tjdev.de/DrMaxNix/banjo
Docker Web Application Firewall (WAF) to protect against Brute Force Attacks
429 Too Many Requests
page to this client instead of forwarding calls to the application containerNoteIf your first thought after reading this is, that adding another proxy to the stack will increase latency, you are totally correct! I have measured the RTT of a stack with and without Banjo: Banjo adds about 0.2ms (that's 200µs) of latency. For most use cases, this should be completely fine an not really noticeable.
CautionAs the Banjo container and your application don't know the client's IP address (they only see the address of the container before them), the first link in the chain (eg. your reverse proxy) must forward the client's IP address to following containers using the `X-Forwarded-For` HTTP header. Banjo expects this header to be set; a missing or wrong `X-Forwarded-For` header value will lead to unexpected behavior!
1. Set up Logproxy in Application Container
As described above, the application container's log must be proxied into the Banjo container. For this purpose, there is a banjo_logproxy
script, which will replace the application container's entrypoint ("init script"). The banjo_logproxy
script will then call the real init script (the original entrypoint) and forward all of its output not only to the docker log, but also into a named fifo pipe, which will then be mounted into the Banjo container.
Determine the original entrypoint of your application container (replace exampleorg/exampleimage
with your application's image name):
$ docker inspect -f '{{ .Config.Entrypoint }}' exampleorg/exampleimage
You will see something like this, where /usr/bin/entrypoint
is the original entrypoint:
[/usr/bin/entrypoint]
Now mount the banjo_logproxy
script into your application's container and set it as the new entry point. Make sure to replace /usr/bin/entrypoint
with the output from above!
services:
# ...
app:
# ...
volumes:
# ...
- ./banjo_logproxy:/banjo_logproxy:ro
- banjo_logproxy_data:/banjo_logproxy_data
entrypoint: /banjo_logproxy /usr/bin/entrypoint
volumes:
banjo_logproxy_data:
TipThis assumes the `banjo_logproxy` file is placed in the same directory as the `docker-compose.yml` file. Make sure to change this accordingly. The `banjo_logproxy` script can be found in the root of this repo.
2. Set up Banjo
Add the Banjo service to your docker-compose.yml
, configure the regex pattern and point it to your application container:
services:
banjo:
image: drmaxnix/banjo
restart: unless-stopped
environment:
LOGMATCH_REGEX: '/Login failed: (?<ip>[0-9a-f\.\:]+)/'
APPLICATION_HOST: "app"
volumes:
- ./data/banjo:/opt/banjo-data
- banjo_logproxy_data:/banjo_logproxy_data
ports:
- 127.0.0.1:80:80
# ...
volumes:
banjo_logproxy_data:
TipAbove example will store _iplist_ files in the `./data/banjo` directory. These files help Banjo restore its ban-list after a container restart. Feel free to change this to your needs.
3. Configure your reverse proxy
Now point your reverse proxy at port 80
of the Banjo container.
ImportantMake sure, you don't point your reverse proxy directly at the application container and check that your application container has no publicly accessible http/https ports (eg. remove `ports` entries from `docker-compose.yml`)
4. Final docker-compose.yml
Example
services:
banjo:
image: drmaxnix/banjo
restart: unless-stopped
environment:
LOGMATCH_REGEX: '/Login failed: (?<ip>[0-9a-f\.\:]+)/'
APPLICATION_HOST: "app"
volumes:
- ./data/banjo:/opt/banjo-data
- banjo_logproxy_data:/banjo_logproxy_data
ports:
- 127.0.0.1:80:80
app:
# ...
volumes:
# ...
- ./banjo_logproxy:/banjo_logproxy:ro
- banjo_logproxy_data:/banjo_logproxy_data
entrypoint: /banjo_logproxy /usr/bin/entrypoint
volumes:
banjo_logproxy_data:
Following configuration options are available by setting environment variables:
Option | Type | Default | Description |
---|---|---|---|
DEBUG | bool | false | Whether debug logging should be enabled (not recommended for production usage) |
LOGMATCH_ENABLED | bool | true | Whether the logmatch module should be enabled |
LOGMATCH_FILE | string | /banjo_logproxy_data/log | Path to named pipe where log can be streamed from |
LOGMATCH_REGEX🔸 | regex | Regex pattern to use for finding failed login attempts in the log; the IP address to be banned must be matched either as the first capturing group, or a group named ip | |
LOGMATCH_REGEX_* | regex | empty | Additional patterns (eg. LOGMATCH_REGEX_FOO ) |
LOGMATCH_FINDTIME | duration | 12h | Interval hits are counted within |
LOGMATCH_THRESHOLD | int | 10 | Hit count threshold after which address is being banned |
LOGMATCH_BANTIME | duration | 6h | Duration of a ban |
APPLICATION_HOST🔸 | host/ip | Hostname or ip address of application to forward requests to | |
APPLICATION_PORT | int | 80 | Port number of application to forward requests to |
Options marked with
🔸
are required
Explanation of types:
Type | Description | Example(s) |
---|---|---|
bool | a boolean value, either true or false | true |
string | one or more character(s) | TestTest 123 %$&/(&%$) |
regex | string of the form /pattern/ , where pattern is a valid regex pattern | /[a-z0-9]+/ |
duration | either a number of seconds, or a number followed by a unit, which can be one of [smhd] for seconds, minutes, hours and days respectively | 15m = 15 minutes = 900 seconds |
int | a number contained in ℤ | 1337 |
host/ip | a hostname, domain name, or IPv4/IPv6 address | app app.example.com 198.51.100.49 2001:db8::a17d:661e |
Additional value restrictions may be applied on a semantic level (eg. port number must be in range 1-65535)
docker pull drmaxnix/banjo