Traefik intermittent 404 on docker-compose - docker-compose

I'm running a bunch of services behind a traefik reverse proxy. I have tested those services to death and they work great. The problem is that when traefik is involved I get intermittent 404 errors whhenever I interact with them.
One of those services exposes a nice and simple REST api. Consuming code has to retry all requests. This is managable.
One of thse services exposes a frontend: If I want to use the frontend I have to constantly refresh the page. This is a truely aweful user experience.
Here are some samples from my compose file:
version: "2.1"
services:
reverse-proxy:
image: traefik:v2.2
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "8081:80"
- "8082:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
api_service:
image: its_just_a_flask_app
labels:
- "traefik.enable=true"
- "traefik.http.routers.configrouter1.rule=PathPrefix(`/config_backend/`) && (Method(`GET`) || Method(`POST`))"
- "traefik.http.routers.configrouter1.middlewares=config-backend-auth#docker"
- "traefik.http.middlewares.config-backend-auth.basicauth.usersfile=/config/usersfile"
webserver:
image: puckel/docker-airflow:with_a_few_lil_tweaks
restart: always
depends_on:
- postgres # these exist and work fine
- redis
environment:
- LOAD_EX=n
- FERNET_KEY=stuff=
- EXECUTOR=Celery
- AIRFLOW__WEBSERVER__BASE_URL=http://webserver/airflow
volumes:
- ../orchistrator/dags/:/usr/local/airflow/dags
- ./requirements.txt:/requirements.txt
command: webserver
healthcheck:
test: ["CMD-SHELL", "[ -f /usr/local/airflow/airflow-webserver.pid ]"]
interval: 30s
timeout: 30s
retries: 3
labels:
- "traefik.enable=true"
- "traefik.http.routers.aurflowrouter.rule=PathPrefix(`/airflow`)"
- "traefik.http.routers.aurflowrouter.middlewares=airflow-basic-auth#docker"
- "traefik.http.middlewares.airflow-basic-auth.basicauth.usersfile=/config/usersfile"
- traefik.http.services.my-service.loadbalancer.server.port=8080
As you can see both of these use basic auth. removing the auth has no effect
removing the webserver's healthcheck has no effect
when making api calls to the api_service, the first call often fails, the second call always succeeds
when accessing the airflow frontend: the first page load fails, after that it succeeds.
theres a button on the airflow fronend that triggers a POST. The POST returns a 302 Found, then the redirect always gives me a 404 at first
sometimes the web frontend oads, but the static resources that it relies on do not load, resulting in an ugly and unusable site. So I find myself refreshing the page a lot
I'm at a loss here. Any help would be very apprectiated.
Traefik tags I've tried:
Sinve the only advice I've recieved or found so far is about using old tags or new tags, here's what I've found
image: traefik:v2.2
image: traefik:v2.2.1
image: traefik:v2.2.5
image: traefik:latest

I got similar issues after pulling traefik:latest yesterday.
Just noticed the image was updated today and a new pull fixed my issues.

Turned out it was a bug on v2.2.2. See here for more. Use, for example, v2.2.5 to get rid of this problem

Related

Attempt to rewrite minimal Traefik example to use TLS does not work

The minimal example from https://doc.traefik.io/traefik/user-guides/docker-compose/basic-example/ works on my local machine. However, when I try to adapt this to use TLS I run into an issue. I'm a Traefik newbie, so I might be doing a stupid mistake.
This is my attempt:
version: "3.3"
services:
traefik:
image: "traefik:v2.8"
container_name: "traefik"
command:
- "--log.level=DEBUG"
- "--accesslog=true"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
ports:
- "443:443"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
whoami:
image: "traefik/whoami"
container_name: "simple-service"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`127.0.0.1`)"
- "traefik.http.routers.whoami.entrypoints=websecure"
So the major modification is to use "traefik.http.routers.whoami.entrypoints=websecure" instead of "traefik.http.routers.whoami.entrypoints=web"
Running
$ curl -k https://127.0.0.1
I get
404 page not found
The traefik log shows no routing related issues and the internal traefik setup for routing etc shown using curl https://127.0.0.1:8080/api/rawdata | jq . looks the same as the one of the working example, except the changed port.
So I opted for new answer instead of just editing the old answer. (Reason being even incorrect answers teach something).
My reference is this great post by Marc Mogdanz (link: https://marcmogdanz.de/posts/infrastructure-with-traefik-and-cloudflare/).
The direct answer to your query is:
Expose port 8080 but do not publish it
Add a host name rule. This will allow Traefik to route a URL request to its own port 8080.
The affected part of the compose file would be as follows (assuming that the URL https://dashboard.example.com is the desired URL to reach the dashboard):
expose:
- 8080
...
labels:
- "traefik.enable=true"
- "traefik.http.routers.traefik.rule=Host(`dashboard.example.com`)"
- "traefik.http.routers.traefik.tls=true"
- "traefik.http.services.traefik.loadbalancer.server.port=8080"
Finally, I noticed you are testing on localhost. If you are testing on a local machine, use localhost for the dashboard and keep 127.0.0.1 for whoami.
Or, alternately, add a static entry for a subdomain (see https://stackoverflow.com/a/19016600).
Either way, Traefik is looking at the SNI requested - not necessarily the IP address - when matching the Host rule.
Request ----> Docker:443 ---> {Traefik}-"SNI?"---"127.0.0.1"---> {whoami}
| \
| \
8080<---"dashboard.localhost"
Add the following entry to your Traefik:
"--entrypoints.websecure.address=:8080"
Normally it would be 8080 for http and 8443 for https alternative ports, but since your example specifically states https://~:8080, I have adapted it accordingly.

Basic auth not working in my second docker-compose file for my web app

So I have 2 configurations, the first docker-compose configuration sets up traefik and it has basic auth middleware set up properly and working
But the second I set up basic auth properly and the website runs very well on https and all but the basic authentication doesn't work
Below is the docker-compose file for my website
version: '3.7'
networks:
traefik-proxy:
external: true
internal:
external: false
services:
web:
container_name: web
build: .
image: ibl-docs
command: yarn run serve --build --port ${PORT} --host 0.0.0.0
volumes:
- .:/code
- '/code/node_modules'
environment:
- CHOKIDAR_USEPOLLING=true
expose:
- ${PORT}
networks:
- traefik-proxy
- internal
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik-proxy"
## HTTP
- "traefik.http.routers.${TRAEFIK_MANAGER_ID}.entrypoints=web"
- "traefik.http.routers.${TRAEFIK_MANAGER_ID}.rule=Host(`${HOST}`)"
- "traefik.http.routers.${TRAEFIK_MANAGER_ID}.service=${TRAEFIK_MANAGER_ID}-secure"
- "traefik.http.services.${TRAEFIK_MANAGER_ID}.loadbalancer.server.port=${PORT}"
# Redirect
- "traefik.http.middlewares.https-redirect.redirectscheme.scheme=https"
- "traefik.http.routers.${TRAEFIK_MANAGER_ID}.middlewares=https-redirect"
## HTTPS
- "traefik.http.routers.${TRAEFIK_MANAGER_ID}-secure.entrypoints=websecure"
- "traefik.http.routers.${TRAEFIK_MANAGER_ID}-secure.rule=Host(`${HOST}`)"
- "traefik.http.routers.${TRAEFIK_MANAGER_ID}-secure.tls=${HTTPS_ROUTER_TLS_MODE}"
- "traefik.http.routers.${TRAEFIK_MANAGER_ID}-secure.tls.certresolver=default"
- "traefik.http.routers.${TRAEFIK_MANAGER_ID}-secure.service=${TRAEFIK_MANAGER_ID}-secure"
- "traefik.http.services.${TRAEFIK_MANAGER_ID}-secure.loadbalancer.server.port=${PORT}"
## Admin
- "traefik.http.routers.${TRAEFIK_MANAGER_ID}-admin.rule=Host(`${HOST}`) && PathPrefix(`/admin`)"
- "traefik.http.routers.${TRAEFIK_MANAGER_ID}-admin.entrypoints=websecure"
- "traefik.http.routers.${TRAEFIK_MANAGER_ID}-admin.middlewares=${TRAEFIK_MANAGER_ID}-auth"
- "traefik.http.routers.${TRAEFIK_MANAGER_ID}-admin.tls=${HTTPS_ROUTER_TLS_MODE}"
- "traefik.http.routers.${TRAEFIK_MANAGER_ID}-admin.tls.certresolver=default"
- "traefik.http.routers.${TRAEFIK_MANAGER_ID}-admin.service=${TRAEFIK_MANAGER_ID}-admin"
- "traefik.http.services.${TRAEFIK_MANAGER_ID}-admin.loadbalancer.server.port=${PORT}"
- "traefik.http.middlewares.${TRAEFIK_MANAGER_ID}-auth.basicauth.users=${BASIC_AUTH_USERS}"
the variable BASIC_AUTH_USERS is set in the .env file as username:sdksjdlakjsdlaslkda
and the credentials are being generated from .httpaccess
The docker-compose logs for traefik point to no error at all and I have tried ass the credentials within the docker-compose file of my website without the environment variable and escaping the $ twice as suggested by many, but the http auth just doesn't show up.
Any help and suggestions will be much appreciated at this point, thanks.
For me it works like this:
docker-compose.yml
labels:
[...]
- "traefik.http.middlewares.auth.basicauth.users=${TRAEFIK_USER}:${TRAEFIK_PASSWORD}"
[...]
.env
[...]
TRAEFIK_USER=username
TRAEFIK_PASSWORD='$2y$05$xahM2aRLsfQtudl1rimk5OZom7ekdizT911qCAk92tPvathYlZ8B7'
[...]
So replacing the single $ from the hash is not necessary when the variable is substituted.

Keycloak Gatekeeper (forwarding proxy) does nothing

I have a docker-compose setup with service 1 (grafana) sending requests to service 2 (prometheus).
I've put a Keycloak Gatekeeper in front of service 2, asking for authentication.
That works perfectly fine.
Now my idea was to also place a second Keycloak Gatekeeper in front of service 1, injecting said authentication.
Unfortunately, that doesnt work.
Observing my traffic via wireshark, it seems my second Keycloak Gatekeeper is not even involved in any communication.
My docker compose file for service 1 and the forwarding proxy looks roughly like this:
keycloak-forwarder:
image: quay.io/keycloak/keycloak-gatekeeper
command:
- --enable-forwarding=true
- --forwarding-username=<my_username>
- --forwarding-password=<my_password>
- --forwarding-domains=${BASE_DOMAIN}/grafana
- --listen=:3001
- --client-id=<my_keycloak_client_id>
- --client-secret=<my_keycloak_client_secret>
- --discovery-url=${DOMAIN_PROTOCOL}://${KEYCLOAK_DOMAIN}/auth/realms/<my_keycloak_realm>
networks:
- webgateway
grafana:
image: grafana/grafana
networks:
- webgateway
labels:
traefik.enable: true
traefik.backend: grafana
traefik.frontend.rule: Host:${BASE_DOMAIN};PathPrefix:/grafana;PathPrefixStrip:/grafana
traefik.port: 3000
traefik.docker.network: webgateway
Ok, I've found the solution.
There are several things, that I did wrong.
The forwarding-domains flag describes the domains the request goes to,
not comes from. So if grafana talks to prometheus, the prometheus
domain needs to be entered here.
The Keycloak Gatekeeper doesn't automatically intercept communication. So grafana
needs to use it explicitly as proxy.
So the fixed docker-compose looks the following:
keycloak-forwarder:
image: quay.io/keycloak/keycloak-gatekeeper
command:
- --enable-forwarding=true
- --forwarding-username=<my_username>
- --forwarding-password=<my_password>
- --forwarding-domains=${BASE_DOMAIN}/prometheus
- --listen=:3001
- --client-id=<my_keycloak_client_id>
- --client-secret=<my_keycloak_client_secret>
- --discovery-url=${DOMAIN_PROTOCOL}://${KEYCLOAK_DOMAIN}/auth/realms/<my_keycloak_realm>
networks:
- webgateway
grafana:
image: grafana/grafana
networks:
- webgateway
environment:
- HTTP_PROXY=http://keycloak-forwarder:3001
- HTTPS_PROXY=http://keycloak-forwarder:3001
labels:
traefik.enable: true
traefik.backend: grafana
traefik.frontend.rule: Host:${BASE_DOMAIN};PathPrefix:/grafana;PathPrefixStrip:/grafana
traefik.port: 3000
traefik.docker.network: webgateway

Traefik docker-compose only creating one frontend route instead of 3

I want traefik to routh specific paths to my nginx service.
The setup below works on my development environment, but not on a live
environment.
The issue is, for the nginx service it only ever creates one of the frontend
routes, usually just static, or just media. It's as if it finds one, creates it
and then just doesn't bother with the rest.
If I restart my containers, then traefik will randomly do only one of the routes
again, but never all 3 as it does on my dev machine.
docker-compose.yml
version: '3'
services:
db:
image: postgres:latest
webapp:
build:
context: ./src/webapp/
env_file:
- .env
volumes:
- "./resources:/mnt/resources/"
depends_on:
- db
links:
- "db:dbhost"
labels:
- "traefik.backend=webapp"
- "traefik.port=8000"
- "traefik.webapp.entryPoints=https"
- "traefik.webapp.frontend.rule=Host:my.domain.example"
nginx:
build:
context: ./src/nginx/
volumes:
- ./resources:/mnt/resources
labels:
- "traefik.backend=nginx"
- "traefik.port=443"
- "traefik.frontend.entryPoints=https"
- "traefik.static.frontend.rule=Host:my.domain.example;PathPrefix:/static"
- "traefik.media.frontend.rule=Host:my.domain.example;PathPrefix:/media"
- "traefik.sw.frontend.rule=Host:my.domain.example;Path:/service-worker.js"
traefik:
image: traefik:1.6.3
restart: always
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- ./src/traefik/traefik.toml:/traefik.toml
- ./src/traefik/acme.json:/acme.json
A separate question but still traefik related:
Traefik is supposed to fetch letsencrypt certs for the main.domain, and does so
perfectly well for that domain.
But it's also trying to fetch certs for the db and traefik service.
How do I tell traefik not to fetch certs for these services?
Finally figured it out.
For the frontends that weren't generated, I basically had to specify a backend for each frontend, and then they worked as expected.
so this:
- "traefik.static.frontend.rule=Host:my.domain.example;PathPrefix:/static"
- "traefik.media.frontend.rule=Host:my.domain.example;PathPrefix:/media"
- "traefik.sw.frontend.rule=Host:my.domain.example;Path:/service-worker.js"
... became this:
- "traefik.static.backend=nginx-static"
- "traefik.static.frontend.rule=Host:my.domain.example;PathPrefix:/static"
- "traefik.media.backend=nginx-media"
- "traefik.media.frontend.rule=Host:my.domain.example;PathPrefix:/media"
- "traefik.sw.backend=nginx-sw"
- "traefik.sw.frontend.rule=Host:my.domain.example;Path:/service-worker.js"
After that everything worked correctly.

Using Traefik to reverse proxy Grafana at a suburl; 404 responses

I have tried my best to follow the documentation for both Grafana and Traefik to reverse proxy incoming requests to https://metrics.mydomain/grafana through Traefik to a Grafana instance running as a container, but I get 404 responses from Grafana and I can't figure out why.
Here's what I've done after reading docs and trying various things in my docker-compose.yml file:
version: '3'
services:
traefik:
image: traefik
restart: always
ports:
- 80:80
- 443:443
- 8080:8080
volumes:
- /var/run/docker.sock:/var/run/docker.sock
- /home/ben/traefik.toml:/etc/traefik/traefik.toml
- /etc/letsencrypt/live/mydomain/fullchain.pem:/mydomain_fullchain.pem
- /etc/letsencrypt/live/mydomain/privkey.pem:/mydomain_privkey.pem
grafana:
image: grafana/grafana
restart: always
ports:
- 3000:3000
volumes:
- /srv/grafana:/var/lib/grafana
environment:
- GF_SERVER_ROOT_URL=%(protocol)s://%(domain)s:%(http_port)s/grafana
labels:
- traefik.enable=true
- 'traefik.frontend.rule=Host:metrics.mydomain;PathPrefix:/grafana;'
- 'traefik.frontend.redirect.entryPoint=https'
Traefik does pick up on this. Here's what it shows on the dashboard:
However, when I actually request https://metrics.mydomain/grafana, what I get back is 404 and an error document, and then subsequent 404s for the resources requested by the custom error document:
Finally, and most confusingly, it appears that the requests as received by Grafana itself are correct, but still result in 404s:
method=GET path=/grafana status=404 remote_addr=192.168.0.1
method=GET path=/grafana/public/build/grafana.dark.css status=404 remote_addr=192.168.0.1
...etc
I don't understand why this is so fussy, and there doesn't appear (to me) to be any evidence that would suggest what the actual problem is.
p.s. - I did leave a comment on a Grafana issue that looked relevant. https://github.com/grafana/grafana/issues/2066#issuecomment-380647174
I was able to fix this by using PathPrefixStrip instead of PathPrefix
This issue is resolved as I write this in the latest image. I was experiencing the same issue with grafana/grafana:5.1.0.
My working configuration for reference:
version: '2'
services:
grafana:
image: grafana/grafana
environment:
- GF_SERVER_ROOT_URL=%(protocol)s://%(domain)s:%(http_port)s/grafana
- GF_SERVER_DOMAIN=devserver1.mydomain
- GF_SERVER_SERVE_FROM_SUB_PATH=true
labels:
- "traefik.http.routers.grafana.rule=Host(`devserver1.mydomain`) && PathPrefix(`/grafana`)"
- "traefik.http.services.grafana.loadbalancer.server.port=3000"
networks:
- traefik_default
volumes:
- grafana-storage:/var/lib/grafana
networks:
traefik_default:
external: true
volumes:
grafana-storage:
Grafana's documentation on running behind a reverse proxy Run Grafana behind a reverse proxy. An explanation on how to Configure with environment variables.