Attempt to rewrite minimal Traefik example to use TLS does not work - docker-compose

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.

Related

Using https with grafana/caddy on docker compose

I'm trying to understand how to implement https with grafana/caddy in docker compose without a domain name.
Currently, I access grafana via http://xx.xxx.xx.xx:3000/
I would like this to be https, but am struggling to understand how to generate the cert and have it work as expected. I think letsencrypt requires a domain which I don't have.
version: "3"
networks:
monitor-net:
driver: bridge
volumes:
grafana_data: {}
services:
grafana:
image: grafana/grafana:8.4.4
container_name: grafana
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning/dashboards:/etc/grafana/provisioning/dashboards
- ./grafana/provisioning/datasources:/etc/grafana/provisioning/datasources
environment:
- GF_SECURITY_ADMIN_USER=${GF_ADMIN_USER}
- GF_SECURITY_ADMIN_PASSWORD=${GF_ADMIN_PASS}
- GF_USERS_ALLOW_SIGN_UP=false
restart: unless-stopped
expose:
- 3000
networks:
- monitor-net
labels:
org.label-schema.group: "monitoring"
caddy:
image: caddy:2.3.0
container_name: caddy
ports:
- "3000:3000"
- "9090:9090"
- "9093:9093"
- "9091:9091"
volumes:
- ./caddy:/etc/caddy
environment:
- ADMIN_USER=${GF_ADMIN_USER}
- ADMIN_PASSWORD=${GF_ADMIN_PASS}
- ADMIN_PASSWORD_HASH=${ADMIN_PASS_HASH}
restart: unless-stopped
networks:
- monitor-net
labels:
org.label-schema.group: "monitoring"
I'm assuming I would create a volume on /etc/caddy/certs where I'd store the certificates, but don't know how to generate it for IP only or how it gets recognized by caddy.
Caddy for IP with SSL
By default, Caddy serves all sites over HTTPS.
Caddy serves IP addresses and local/internal hostnames over HTTPS using self-signed certificates that are automatically trusted locally (if permitted).
Examples: localhost, 127.0.0.1
Offical Docs Here
in your Caddyfile you have to add something like this
http://192.168.1.25:3000 {
reverse_proxy grafana_ip:3000
}
It looks like Caddy does not support generating HTTPS certificates for IP addresses. Additionally, Let's Encrypt does not currently support issuing certificates for bare IP addresses.
However, it does appear that ZeroSSL supports generating certificates for IPs. You could try using these instructions to change one or all of your sites to use ZeroSSL, but I wasn't able to get this to work on my test server.
The best option is probably to get a domain that you can point at your server, and then serve it from there.

Port forwarding problem using Traefik and docker-compose

I want the next port forwarding:
http://traefik.service.localhost/ -> Traefik UI
http://api.service.localhost/ -> 'Hello-Word' page from api-service
Here is my attempt to create appropriate docker-compose.yml file:
version: '3.8'
services:
reverse-proxy:
image: traefik:v2.4
container_name: reverse-proxy
command:
- "--api.insecure=true"
- "--providers.docker"
ports:
- "80:80"
- "8080:8080"
labels:
- traefik.enable=true
- traefik.docker.network=pred-network
volumes:
- /var/run/docker.sock:/var/run/docker.sock
networks:
- pred-network
api-service:
image: x86_64/prediction-service:0.8.1
container_name: api-service
environment:
SERVING_SERVICE: model-service
expose:
- 80
labels:
- traefik.enable=true
- traefik.http.routers.api-service.rule=Host(`api.service.localhost`)
networks:
- pred-network
networks:
pred-network:
I am getting the following:
http://traefik.service.localhost/ -> HTTP Error 404. The requested resource is not found.
http://api.service.localhost/ -> HTTP Error 404. The requested resource is not found.
The only link that actually works:
http://api.service.localhost:8080/ -> Traefik UI
If I would include:
ports:
- "8070:80"
into api-service part of docker-compose.yml I could access my 'Hello-Word':
http://localhost:8070/ -> "Hello-Word"
In this case it not get routed through Traefik but directly through api-service. Is it possible to do it over Traefik?
To have a service routed by traefik with docker, you should have labels. Traefik will fetch those labels in order to know how to do routing.
Thus, to have request of api.service.localhost routed to your api service, you should have the label that define the corresponding rule:
traefik.http.routers.service.rule=Host(`api.service.localhost`)
As for the label of Traefik, you should have two labels:
traefik.http.routers.traefik.rule=Host(`traefik.service.localhost`)
traefik.http.routers.traefik.service=api#internal
Beside those traefik labels, you should tell traefik to expose the dashboard using a specific service by adding this configuration: --api.dashboard (More help to expose the dashboard here).
Note, if your api service uses another port than the one by default, you could add this label:
traefik.http.services.service.loadbalancer.server.port=80
Ps, do not expose twice the port 80, you only need to expose it for the traefik container.

Bad Gateway with Traefik and Docker Compose

I'm trying to deploy a React + FastApi + Postgres application on docker compose with Traefik as the reverse proxy. I'm running into issues with Bad Gateway errors. Running my FastAPI locally runs it on port 8888 and exposes the path /docs to view the api documentation. I'd like to eventually have the application running on example.local with the docs available on example.local/api/docs. My docker-compose.yaml is as follows (loosely based on this one):
version: '3.8'
services:
proxy:
image: traefik:v2.4
networks:
- web
volumes:
- /var/run/docker.sock:/var/run/docker.sock
ports:
- '80:80'
- '8080:8080'
- '443:443'
command:
- --providers.docker
- --api.insecure=true
- --providers.docker.exposedbydefault=false
- --providers.docker.network=web
- --entrypoints.web.address=:80
labels:
- traefik.enable=true
- traefik.http.routers.example-proxy-http.rule=Host(`example.local`)
- traefik.http.routers.example-proxy-http.entrypoints=web
- traefik.http.services.example-proxy.loadbalancer.server.port=80
backend:
build:
context: ./backend
dockerfile: Dockerfile
command: python app/main.py
volumes:
- ./backend/app:/app
env_file:
- .env
networks:
- web
- backend
labels:
- traefik.enable=true
- traefik.http.routers.example-backend-http.rule=PathPrefix(`api/docs`)
- traefik.http.routers.example-backend-http.entrypoints=web
- traefik.http.services.example-backend.loadbalancer.server.port=8888
networks:
web:
external: true
backend:
external: false
I've added 127.0.0.1 example.local to my /etc/hosts file.
From reading around it seems like Bad Gateway errors tend to occur from traefik and related services not being on the same network, or traefik routing traffic to the wrong port on the service container. However if I set ports: - '8888:8888' in my backend service I can access the docs from localhost:8888/docs so I'm pretty sure 8888 is the correct port for the backend loadbalancer. From what I can see traefik and the backend service are on the same network too and I've set it as the default traefik network with --providers.docker.network=web. Interestingly if I visit localhost/api/docs in my browser I'm served up a page from FastAPI. So it could be an issue with my traefik http router labels? I'm quite new to traefik and proxies so would appreciate any help or guidance, thanks!
UPDATE
If I specify the host for the backend by adding
- traefik.http.routers.infilmation-backend-http.rule=Host(`example.local`) && PathPrefix(`/docs`)
to the backend service labels, then visiting example.local/docs does serve up page from FastApi. So I guess my question would be what is the best way of setting up a host for this application? Is there a way I can specify a default host for all services then any PathPrefix rules would be in relation to that host?

Traefik intermittent 404 on 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

Debugging Traefik when the Site Cannot Be Reached from outside Company's Intranet

Using docker-compose I have deployed a web application that uses Traefik as the reverse proxy, listening on port 80. This works without problem when I'm inside my company's intranet. Outside of the intranet, however, I get a 'site cannot be reached' response. Pinging the address from outside shows that the address is reachable and port 80 is open.
I've also tried to use segments in my Traefik configuration to route both the internal and external hostname I have been provided but this has no effect:
version: "3.5"
services:
test:
image: emilevauge/whoami
deploy:
labels:
traefik.enable: "true"
traefik.foo.frontend.rule: "Host:${HOSTNAME};PathPrefixStrip:/test"
traefik.bar.frontend.rule: "Host:${EXTERNAL_HOSTNAME};PathPrefixStrip:/test"
traefik.port: 80
networks:
- frontend
...
I have configured the access logs to see if my requests are reaching Traefik, can anyone advise me what I should be looking for and how to filter the huge amount of text produced to find it? This is my Traefik setup configuration:
version: '3.5'
services:
traefik:
image: traefik:alpine
command: |-
--entryPoints="Name:http Address::80"
--entryPoints="Name:https Address::443 TLS"
--defaultentrypoints="http,https"
--acme
--acme.acmelogging="true"
--acme.domains="${HOSTNAME}"
--acme.domains="${EXTERNAL_HOSTNAME}"
--acme.email="${ACME_EMAIL}"
--acme.entrypoint="https"
--acme.httpchallenge
--acme.httpchallenge.entrypoint="http"
--acme.storage="/opt/traefik/acme.json"
--acme.onhostrule="true"
--docker
--docker.swarmmode
--docker.domain="${HOSTNAME}"
--docker.network="frontend"
--docker.watch
--api
--api.statistics
--logLevel="DEBUG"
networks:
- frontend