I have two docker compose projects:
- traefik: contains traefik 3.6.1 (with v2 ruleset), waf (an instance of modsecurity-crs) and dummy (an instance of whoami used by modsecurity). Also contains traefiknet with bridge driver. Traefik uses the docker provider.
- app: contains a web application and database. The web application is in external network traefik_traefiknet. This contains labels for traefik, causing it to expose the web application, generate a letsencrypt certificate and supply a http basic auth.
This setup has been working flawlessly for years. However, as soon as I add another middleware to the app pointing to waf, traefik will respond with 404 when attempting to connect to the web app. The reason is: error="invalid middleware "waf@docker" configuration: invalid middleware type or middleware does not exist".
I’ve managed to boil this down to what is likely a race condition. It occurs when:
- Have traefik and the waf (and whoami) running already
- Recreate the docker container of the web app
The issue sometimes also occurs on machine restart, which causes all docker containers to be started automatically (restart: always).
The issue can be fixed by restarting traefik, which causes it to see both the waf and web application.
This may be related to Middleware is sometimes not found when defined in the traefik service's docker labels - #12 by kevinpollet but I’m seeing a slightly different reproduction pattern and I don’t have healthchecks.
I really like the docker provider and switching the stack to a file provider would be a lot of work, as I have many such setups running.
Is there a way to make this more solid, i.e. have traefik automatically retry detecting the waf middleware, or defining a dependency order?
Best,
Kalsan
EDIT: as requested, here are the relevant config files:
The app:
services:
web:
image: hub.me.com/myapp:0.2.14
restart: always
env_file:
- .env
labels:
- "traefik.enable=true"
- "traefik.http.routers.myinstance.rule=Host(`myapp.com`) || Host(`www.myapp.com`)"
- "traefik.http.routers.myinstance.entrypoints=https"
- "traefik.http.routers.myinstance.tls.certresolver=letsencryptresolver"
- "traefik.http.middlewares.waf.plugin.traefik-modsecurity-plugin.modSecurityUrl=http://waf:8080"
- "traefik.http.middlewares.waf.plugin.traefik-modsecurity-plugin.maxBodySize=10485760"
- "traefik.http.routers.myinstance.middlewares=waf@docker"
depends_on:
- db
networks:
- internal
- traefik_traefiknet
volumes:
- ./web-storage:/app/storage
db:
image: postgres:18.1
restart: always
env_file:
- .env
networks:
- internal
volumes:
- ./db-data:/var/lib/postgresql/18/docker
networks:
internal:
driver: bridge
traefik_traefiknet:
external: true
Traefik:
services:
traefik:
image: "traefik:v3.6.1"
command:
- "--log.level=INFO"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.network=traefik_traefiknet"
- "--entrypoints.http.address=:80"
- "--entrypoints.https.address=:443"
- "--entrypoints.http.http.redirections.entrypoint.to=https"
- "--entrypoints.http.http.redirections.entrypoint.scheme=https"
- "--entrypoints.https.proxyprotocol.trustedips=192.168.1.1"
- "--certificatesresolvers.letsencryptresolver.acme.httpchallenge=true"
- "--certificatesresolvers.letsencryptresolver.acme.httpchallenge.entrypoint=http"
- "--certificatesresolvers.letsencryptresolver.acme.email=me@me.com"
- "--certificatesresolvers.letsencryptresolver.acme.storage=/letsencrypt/acme.json"
- "--experimental.plugins.traefik-modsecurity-plugin.modulename=github.com/acouvreur/traefik-modsecurity-plugin"
- "--experimental.plugins.traefik-modsecurity-plugin.version=v1.3.0"
ports:
- "80:80"
- "443:443"
volumes:
- "./letsencrypt:/letsencrypt"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
restart: always
networks:
- traefiknet
- wafnet
waf:
image: owasp/modsecurity-crs:3.3.9-apache-202604040104
environment:
- PARANOIA=1
- ANOMALY_INBOUND=10
- ANOMALY_OUTBOUND=5
- BACKEND=http://dummy
volumes:
- './RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf:/etc/modsecurity.d/owasp-crs/rules/RESPONSE-999-EXCLUSION-RULES-AFTER-CRS.conf'
restart: always
networks:
- wafnet
dummy:
image: containous/whoami
restart: always
networks:
- traefiknet
- wafnet
networks:
traefiknet:
driver: bridge
wafnet:
driver: bridge