Script kitties are relentless. Your servers are under constant attack and for me, I find it fun to watch the attack attempts in my logging server with a glass of Malbec at sunset. But really, these are super annoying and they need to be stopped.
Traefik offers a way to provide a list of IPs that are allowed to access an endpoint while blocking all other requests. However, they don’t have a way to block specific IPs from accessing a publicly accessible endpoint.
TL;DR – Skip here to see the solution in docker compose
Git repo with related files: » HERE
Traefik ForwardAuth Middleware
As a workaround for implementing an IP block list in Traefik, we will use the ForwardAuth middleware.
The ForwardAuth middleware delegate the authentication to an external service. If the service response code is 2XX, access is granted and the original request is performed. Otherwise, the response from the authentication server is returned.
Read more here: https://docs.traefik.io/middlewares/forwardauth
Setup Nginx as our IP blocklist server
Nginx will be used here with an extremely simple configuration. In this case simplicity is key. Because this is a middleware service, the server MUST have the following features:
- Requests return in 5ms or less (±2ms)
- Low resource usage
- Reliable
You can use any webserver you like, but nginx seemed like a solid tried and true choice for this job.
Our docker container will run vanilla nginx with the following two files mounted to the /etc/nginx/conf.d/
directory.
- blocklist.conf – This file is where the blocked IPs will be entered
- nginx_default.conf – this is where we’ll define the route that will produce the response we need.
Return 2xx
“OK” if the IP is not in the list of blocked IPs
Return 403
if the IP is in the blocked IP list.
Nginx Config files
These files are mounted to the nginx docker container. Minimal setup.
# blocklist.conf
geo $blocked_ip {
default 0;
192.25.0.56 1;
192.168.1.0/24 1;
10.1.0.0/16 1;
::1 2;
2001:0db8::/32 1;
}
# nginx_default.conf
server {
listen 8080;
location /traefik {
add_header Content-Type "default_type text/plain";
if ($blocked_ip) {
return 403;
}
return 200 "OK";
}
}
Docker Compose – See it in action
version: '3.1'
services:
todo:
image: prologic/todo
restart: always
labels:
- "traefik.enable=true"
- "traefik.http.middlewares.blocklist.forwardauth.address=http://ipfilter:8080/traefik"
- "traefik.http.routers.todo_app.entrypoints=web"
- "traefik.http.routers.todo_app.middlewares=blocklist"
- "traefik.http.routers.todo_app.rule=Host(`localhost`)"
traefik:
image: "traefik:v2.2.1"
container_name: "traefik"
command:
# - "--log.level=DEBUG"
- "--accesslog=true"
- "--api.insecure=true"
- "--api.dashboard=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:8080"
- "--entrypoints.traefik.address=:8888"
labels:
traefik.enable: true
traefik.http.routers.traefik.service: api@internal
traefik.http.routers.traefik.entrypoints: traefik
ports:
- "8080:8080"
- "8888:8888"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
ipfilter:
image: nginx
container_name: ipfilter
volumes:
- "./nginx/nginx_default.conf:/etc/nginx/conf.d/default.conf"
- "./nginx/blocklist.conf:/etc/nginx/conf.d/ip_filter.conf"
ports:
- "9999:8080"
Test app here is a simple todo app. You can test out denying yourself access by adding 0.0.0.0.
See below for sample request logs for allowed and blocked requests
# Allowed request - IP 172.26.0.1 is not in the blocklist
ipfilter | 172.26.0.1 - - [21/Jun/2020:07:42:10 +0000] "GET /traefik HTTP/1.1" 200 2 "http://localhost:8080/" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36" "172.26.0.1"
todo_1 | [todo] 2020/06/21 07:42:10 (172.26.0.1) "GET /icons/favicon.ico HTTP/1.1" 200 2227 680.4µs
traefik | 172.26.0.1 - - [21/Jun/2020:07:42:10 +0000] "GET /icons/favicon.ico HTTP/1.1" 200 2227 "-" "-" 14 "todo_app@docker" "http://172.26.0.2:8000" 2ms
# Blocked request - IP 172.26.0.1 matches an address in the blocked list
ipfilter | 172.26.0.1 - - [21/Jun/2020:07:45:04 +0000] "GET /traefik HTTP/1.1" 403 556 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/83.0.4103.97 Safari/537.36" "172.26.0.1"
traefik | 172.26.0.1 - - [21/Jun/2020:07:45:04 +0000] "GET / HTTP/1.1" 403 556 "-" "-" 3 "todo_app@docker" "-" 1ms
I hope the traefik devs end up adding this as a native feature, but until then, this would have to do.
Git repo with related files: » HERE