Traefik routing to both known and wildcard subdomain on a single domain - docker

Is it possible to have a wildcard subdomain that does not include a specific subdomain?
*.mydomain.com OK
login.mydomain.com SKIP
I cannot access my login container when using a wildcard on my app container. Below is an image of what I am trying to accomplish. (the traffic logo should technically be between the list of routes and the containers)
The following configuration does not work if the rule HostRegexp:{subdomain:[a-z]+}.${HOST_DOMAIN} is included.
This configuration successfully works after removing the host regex for everything EXCEPT the wildcard subdomains.
services:
traefik:
image: traefik
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- .traefik.toml:/etc/traefik/traefik.toml:ro
ports:
- "80:80"
- "443:443"
api:
image: my-api-image
labels:
- "traefik.enable=true"
- "traefik.port=80"
- "traefik.frontend.rule=Host:app.${HOST_DOMAIN}; PathPrefix: /api"
app:
image: my-app-image
labels:
- "traefik.enable=true"
- "traefik.port=80"
- "traefik.frontend.rule=Host:app.${HOST_DOMAIN}"
- "traefik.frontend.rule=HostRegexp:{subdomain:[a-z]+}.${HOST_DOMAIN}" # this second rule overwrites the first rule and I am aware of that, I am just showing what rules i've tried :)
login:
image: my-login-image
labels:
- "traefik.enable=true"
- "traefik.port=80"
- "traefik.frontend.rule=Host:login.${HOST_DOMAIN}"
My problem is currently with the app container. I am getting a bad gateway if I have the following included as a frontend rule:
"traefik.frontend.rule=HostRegexp:{subdomain:[a-z]+}.${HOST_DOMAIN}"
I also tried leaving the above under app, and removing the following without any luck:
"traefik.frontend.rule=Host:app.${HOST_DOMAIN}"
Any suggestions or ideas would be appreciated. Thanks.
Edit:
Rewording this a bit.

So this is what worked for me:
version: '2'
services:
traefik:
image: traefik
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
# I removed your traefik.toml as you did not specify what is in it, so it's irrelevant
ports:
- "80:80"
- "443:443"
# Very helpful for debugging dashboard can be seen at http://localhost:8080 if the port is exposed
- "8080:8080"
labels:
# You don't want traefik trying to create proxy for itself
- "traefik.enable=false"
# Since we have no traefik.toml any longer, let's put the essentials on the command line
command: ["--api","--docker"]
app:
# this is my test image of a web server that dumps request back to the caller
image: andrewsav/talkback
# the hostname is a part of the dump, so let's specify something that we can relate to
hostname: "app"
labels:
# note that you want this frontened to match the last. otherwise it will match login.${HOST_DOMAIN}"
- "traefik.frontend.priority=1"
- "traefik.enable=true"
- "traefik.port=80"
- "traefik.frontend.rule=HostRegexp:{subdomain:[a-z]+}.${HOST_DOMAIN}"
api:
image: andrewsav/talkback
hostname: "api"
labels:
# this frontend needs to match before the one above
- "traefik.frontend.priority=2"
- "traefik.enable=true"
- "traefik.port=80"
- "traefik.frontend.rule=Host:app.${HOST_DOMAIN}; PathPrefix: /api"
login:
image: andrewsav/talkback
hostname: "login"
labels:
- "traefik.frontend.priority=3"
- "traefik.enable=true"
- "traefik.port=80"
- "traefik.frontend.rule=Host:login.${HOST_DOMAIN}"
A few notes:
Bad Gateway indicates that the endpoint you told traefik to talk to is not listening. Look at at the dashboard and find out which backend is used and double check that ip/port is correct.
You have to use priorities for matching order. Please refer to the documentation on how it works.

Related

Allow both http and https with Traefik on Docker

I am using Traefik in my VPS to route the traffic to my websites, after hours of messing around with it I finally managed to get it working with https using Le's Encrypt.
Now, one thing that I need to do is be able to also access my website via plain http as this is a hobby project for older browsers and the only reason I added tls is because Firefox doesn't like my website without it.
The problem is that, with my current configuration, I can access my website via https normally but when I try it with plain http I get a 404 error.
Here's what my config on docker-compose looks like:
version: "3"
services:
traefik:
image: "traefik:v2.5"
container_name: "traefik"
command:
#- "--log.level=DEBUG"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.teeresolver.acme.tlschallenge=true"
- "--certificatesresolvers.teeresolver.acme.email=me#gmail.com"
- "--certificatesresolvers.teeresolver.acme.storage=/letsencrypt/acme.json"
ports:
- "443:443"
- "80:80"
- "8080:8080"
volumes:
- "./letsencrypt:/letsencrypt"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
networks:
- mywebsite
# [...]
mywebsite:
image: my-web-site/site
build:
context: ~/mywebsite-runner/_work/my-web-site-php/my-web-site-php
dockerfile: Dockerfile
volumes:
- ./tee-downloads:/var/www/build/downloads
- ./tee-contents:/var/www/build/contents
ports:
- "0.0.0.0:8001:80"
labels:
- "traefik.enable=true"
- "traefik.http.routers.themywebsite.rule=Host(`mywebsite.com`, `www.mywebsite.com`)"
- "traefik.http.routers.themywebsite.entrypoints=websecure,web"
- "traefik.http.routers.themywebsite.tls.certresolver=teeresolver"
networks:
- mywebsite
networks:
mywebsite:
I have been searching for a solution for hours but the only things I can find on google are configs to redirect http to https, which I can't do.
Does anyone know how to do that?
Thanks in advance for the help.
In traefik , each router defines a set of policies to apply depending of rules and entrypoints.
If you want 2 policies, one for http and one for https, you need to define 2 traefik routers :
mywebsite:
...
labels:
- "traefik.enable=true"
- "traefik.http.routers.themywebsite.entrypoints=websecure"
- "traefik.http.routers.themywebsite.tls.certresolver=teeresolver"
- "traefik.http.routers.themywebsite.rule=Host(`mywebsite.com`, `www.mywebsite.com`)"
- "traefik.http.routers.httpwebsite.entrypoints=web"
- "traefik.http.routers.httpwebsite.rule=Host(`mywebsite.com`, `www.mywebsite.com`)"
One called themywebsite
Another one called httpwebsite
As a result, you do not use a certresolver for httpwebsite router.

How to set Traefik 2.4 service name in docker-compose labels

Is there any way to set traefik.http.services name in docker-compose labels?
Lets say i have simple docker-compose.yml:
version: '3.4'
services:
traefik:
image: "traefik:v2.4.2"
command:
- --log.level=warning
- --api.insecure=true
- --api.dashboard=true
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.web.address=:80
ports:
- "80:80"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
labels:
- traefik.enable=true
# Dashboard
- traefik.http.routers.traefik.rule=Host(`traefik.localhost`)
# this is interesting - traefik is naming his api service somehow
- traefik.http.routers.traefik.service=api#internal
- traefik.http.services.traefik.loadbalancer.server.port=8080
whoami:
image: "traefik/whoami"
labels:
- traefik.enable=true
- traefik.http.routers.webwho.rule=Host(`who.localhost`)
This works great, after docker-compose up i can see the dashboard at http://traefik.localhost and "whoami" at http://who.localhost
The problem is the name of 'whoami' traefik service - it is something like whoami-{name_of_project} which is problem when i want to reference it in other label.
For example, i want to use new 'foo' docker service as 404.html provider (in this example i will use traefik/whoami image, which is silly, but hey, this is only example ;) )
I do that by using low priority "catch all" router:
version: '3.4'
services:
traefik:
image: "traefik:v2.4.2"
command:
- --log.level=warning
- --api.insecure=true
- --api.dashboard=true
- --api.debug=true
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.web.address=:80
ports:
- "80:80"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
labels:
- traefik.enable=true
# Dashboard
- traefik.http.routers.traefik.rule=Host(`traefik.localhost`)
# this is interesting - traefik is naming his api service somehow
- traefik.http.routers.traefik.service=api#internal
- traefik.http.services.traefik.loadbalancer.server.port=8080
whoami:
image: "traefik/whoami"
labels:
- traefik.enable=true
- traefik.http.routers.webwho.rule=Host(`who.localhost`)
foo:
image: "traefik/whoami"
labels:
- traefik.enable=true
- traefik.http.routers.error-router.rule=HostRegexp(`{host:.+}`)
- traefik.http.routers.error-router.priority=1
- traefik.http.routers.error-router.middlewares=error-pages-middleware
- traefik.http.middlewares.error-pages-middleware.errors.status=400-599
# nope!
# actual name of this service (as seen in traefik dashboard) is something like 'foo-test'
# and this will not work.
# (btw, there is very clear Error Message in traefik console about service foo does not exists,
# making debugging pleasant experience :) Thank you Traefik!)
- traefik.http.middlewares.error-pages-middleware.errors.service=foo
- traefik.http.middlewares.error-pages-middleware.errors.query=/{status}.html
but i have no idea how to set name of traefik service so i can reference it as value of the traefik.http.middlewares.error-pages-middleware.errors.service=??? label - service name foo-test isnt static (that 'test' is name of my directory which contains docker-compose.yml) and it keep changing from time to time (especially when used with visual studio .dcproj)
So - is there any way to set name of service?
What did i try:
Google & doc. Nope, but maybe because i dont know what to ask.
Setting container_name: foo does not help at all.
Interestlingly, if i add label - traefik.http.services.foo.loadbalancer.server.port=80 it automagically name the traefik service as foo which is exactly what i want and everything works. But this feels like "Plan B" because i dont want to set port, i want to set the name of the service.
using traefic dynamic configuration
[http.services]
[http.services.foo.loadBalancer]
[[http.services.foo.loadBalancer.servers]]
url = "http://foo:80/"
should work (i didnt tested it) -- but again, i dont want to set whole url, i want to set the name of service...
Looks like (at least in v2.4) there isnt any other way than
labels:
- traefik.http.services.<<name_of_service>>.loadbalancer.server.port=80
AFAIK even in the documentation, there is only one mention:
labels:
- "traefik.http.routers.myproxy.rule=Host(`example.net`)"
# service myservice gets automatically assigned to router myproxy
- "traefik.http.services.myservice.loadbalancer.server.port=80"
(source: https://doc.traefik.io/traefik/routing/providers/docker/ )

Traefik route 404

I am trying to host a few different websites on a single Raspberry Pi using Docker. I was told that I would need to use something like Traefik to route everything properly.
I tried using the Docker Basic Example under User Guides in their documentation as a test. I followed along with the example and created the docker-compose.yml file and copied in the example from the docs:
version: "3.3"
services:
traefik:
image: "traefik:v2.2"
container_name: "traefik"
command:
# - "--log.level=DEBUG"
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--entrypoints.web.address=:80"
ports:
- "80:80"
- "8080:8080"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
whoami:
image: "containous/whoami"
container_name: "simple-service"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
- "traefik.http.routers.whoami.entrypoints=web"
Using this example going to the devices local IP I recieve a page that simply says:
404 Page not found
I've tried changing the Traefik container image version, and editing the example with my relevant information as well as recreating the example on another host and still I receive the above 404.
Am I doing something incorrect with Traefik to receive this 404?
In the user guide you have mentioned, there is this note:
Replace whoami.localhost by your own domain within the traefik.http.routers.whoami.rule label of the whoami service.
So, after you replace whoami.localhost with you local IP, you should be able to see whoami service responding correctly.

How to setup basic auth globally in traefik v2?

I'm using traefik 2.1 and would like to setup basic auth for all containers, in 1.x it was easy to do:
[entryPoints.http.auth.basic]
usersFile = "/etc/traefik/.htpasswd"
But how to setup it in 2.x? I'd like to avoid inserting code in all my docker-compose.yml files - got a lot of them.
The main idea is:
we can use built-in middleware https://docs.traefik.io/middlewares/basicauth/
we need to connect middleware from above to a container which needs to be secured
Please find an example of docker-compose.yml
version: '3.7'
services:
traefik:
image: "traefik:v2.2.1"
command:
- "--api.insecure=true"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.network=default"
- "--entrypoints.web.address=:80"
- "--api"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
ports:
- 80:80
# management console
- 8080:8080
labels:
- "traefik.enable=true"
- "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
- "traefik.http.routers.http-catchall.entrypoints=web"
# auth middleware with user / password
- "traefik.http.middlewares.my-auth.basicauth.users=test:$$apr1$$H6uskkkW$$IgXLP6ewTrSuBkTrqE8wj/,test2:$$apr1$$d9hr9HBB$$4HxwgUir3HP4EsggP/QNo0"
whoami:
image: "containous/whoami"
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
- "traefik.http.routers.whoami.entrypoints=web"
# connect your auth middleware to a service which you want to protect
- "traefik.http.routers.whoami.middlewares=my-auth"
So, now we can check that:
auth has prompted on http://whoami.localhost
the valid user name is "test:test" or "test2:test2"
You should use htpasswd command for generating a list of users and connect it according to the Traefik docs.
That's it. Happy coding ;)

How to map specific port inside docker container when using traefik?

Here's my docker-compose.yml:
version: '3'
services:
website:
build: ./website
expose: [3000]
labels:
- "traefik.frontend.rule=Host:localhost"
blog:
build: ./blog
expose: [4000]
labels:
- "traefik.frontend.rule=Host:localhost;PathPrefix:/blog"
docs:
build: ./docs
expose: [3000]
labels:
- "traefik.frontend.rule=Host:localhost;PathPrefix:/docs"
proxy:
image: traefik
command: --api.insecure=true --providers.docker
networks:
- webgateway
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
networks:
webgateway:
driver: bridge
What I want is access three different node.js websites via different routes. But these three node.js websites actually expose different ports. Now my treafik is running. I can config via localhost:8080 But localhost localhost/blog and localhost/docs are all 404 page not found
P.S: I'm not sure whether port is the issue I should investigate, because changing one node.js service to port 80 doesn't solve the puzzle. And I saw on traefik dashboard the rule is Host(blog-dev)
PathPrefix:/blog
When you have this as a routing rule, traefix won't automatically remove the prefix when sending to the container.
So unless you have a route /blog inside your container you will get a 404.
So what you normally do is also add a middleware to strip this ->
https://docs.traefik.io/middlewares/stripprefix/
Also you appear not to be setting your rules based on your service.
So as an example for your first service blog,
try->
labels:
- "traefik.http.routers.blog.rule=Host(`localhost`) && PathPrefix(`/blog`)"
- "traefik.http.routers.blog.middlewares=strip-blog"
- "traefik.http.middlewares.strip-blog.stripprefix.prefixes=/blog"
And then do the same for your other routes, don't forget to replace routers.blog with routers.docs etc..
labels:
- traefik.http.services.<YOUR-SERVICE-NAME>.loadbalancer.server.port=9763
EG:
services:
wso:
image: "my-custom-wso-image"
volumes:
- .....
labels:
- "traefik.enable=true"
- "traefik.http.routers.wso.tls=true"
- "traefik.http.routers.wso.rule=Host(`my.nice.url`)"
- "traefik.http.services.wso.loadbalancer.server.port=9763" #<-----
Thanks to #Keith I found the solution
version: '3'
services:
website:
build: ./website
expose: [3000]
networks: # It's essential to specify the same network in every service
- webgateway
labels:
- "traefik.http.routers.website.rule=Host(`localhost`)" # Use the right format
- "traefik.port=3000" # Let traefik find the right port
blog:
build: ./blog
expose: [4000]
networks:
- webgateway
labels:
- "traefik.http.routers.blog.rule=Host(`localhost`) && PathPrefix(`/blog`)" # blog has a root as `/blog` so no need to strip otherwise too many redirects
- "traefik.port=4000"
docs:
build: ./docs
expose: [3000]
networks:
- webgateway
labels:
- "traefik.http.routers.docs.rule=Host(`localhost`) && PathPrefix(`/docs`)"
- "traefik.http.routers.docs.middlewares=strip-docs" # Necessary as Keith mentioned
- "traefik.http.middlewares.strip-docs.stripprefix.prefixes=/docs"
- "traefik.port=3000"
proxy:
image: traefik
command: --api.insecure=true --providers.docker
networks:
- webgateway
ports:
- "80:80"
- "8080:8080"
volumes:
- /var/run/docker.sock:/var/run/docker.sock
networks:
webgateway:
driver: bridge

Resources