I got a basic Traefik 2 setup working for HTTP here...
Now I'm trying to get HTTPS working, and basic auth for the dashboard with TLS and redirects...
docker_compose.yml:
version: '3.8'
networks:
myweb:
external: true
services:
proxy:
image: traefik:v2.3.0-rc4-windowsservercore-1809
container_name: traefik
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
# Mount the certs drive
- ./traefik-ssl-certs/:c:/certs/
# Mount the config folder
- ./traefik-config/:c:/config/
# Mount the host docker engine pipe ("docker volume ls")
- source: '\\.\pipe\docker_engine'
target: '\\.\pipe\docker_engine'
type: npipe
command:
- "--api.insecure=true"
# Register the traefik config directory as per: https://docs.traefik.io/providers/file/#directory
- --providers.file.directory=c:/config/
- --providers.file.watch=true
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
# Redirect http to https
- --entrypoints.web.http.redirections.entryPoint.to=websecure
- --entrypoints.web.http.redirections.entryPoint.scheme=https
- --entrypoints.web.http.redirections.entrypoint.permanent=true
# Configure Docker provider
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.endpoint=npipe:////./pipe/docker_engine"
- "--providers.docker.network=myweb"
- "--providers.docker.watch=true"
networks:
- myweb
labels:
- traefik.http.routers.api.service=api#internal
- traefik.enable=true
- traefik.docker.network=myweb
remoteling:
image: remoteling:latest
container_name: remoteling
networks:
- myweb
labels:
- traefik.enable=true
- traefik.docker.network=myweb
- traefik.http.middlewares.http2https.redirectscheme.scheme=https
- traefik.http.routers.remoteling-http.middlewares=http2https
- traefik.http.routers.remoteling-http.rule=Host(`example.com`) || Host(`example.example.com`)
- traefik.http.routers.remoteling-http.entrypoints=web
- traefik.http.services.remoteling.loadbalancer.server.port=443
- traefik.http.routers.remoteling-https.rule=Host(`example.com`) || Host(`example.example.com`)
- traefik.http.routers.remoteling-https.entrypoints=websecure
- traefik.http.routers.remoteling-https.tls=true
depends_on:
- proxy
From powershell I'm running:
docker network create -d nat myweb
docker-compose -p myweb up
In my working dir I have the following file structure:
./docker-compose.yml
./traefik-config/traefik.yml
./traefik-ssl-certs/example.com.crt
./traefik-ssl-certs/example.com.key
./traefik-ssl-certs/example.example.com.key
./traefik-ssl-certs/example.example.com.key
And the traefik.yml file looks like this:
tls:
certificates:
- certFile: c:/certs/example.example.com.crt
keyFile: c:/certs/example.example.com.key
- certFile: c:/certs/example.com.crt
keyFile: c:/certs/example.com.key
The traefik dashboard shows my service defined as a loadbalancer, which is good (is there any other type of service other than loadbalancer? I'm not sure how else to define the port other than via a loadbalancer). Clicking on the service gives me the local network IP - and when I visit that IP my site loads just fine (albeit with an SSL cert mismatch warning - expected).
However when I try to visit https://example.com or https://example.example.com - I get a 502 Bad Gateway. Any idea why I'd be getting this?
The browser shows the SSL certs are valid, with no warnings, so I assume my certificates config is fine.
The http to https redirect seems to be working, if I visit http://example.com it forwards me to https://example.com
What is wrong with my routing config to cause these Bad Gateways?
I have solved my problem! There were a few issues:
I had configured a TLS cert in my Dockerfile, back from when I was running the image as a single service on my server. So my image already had port 443 bound with the TLS cert. I think that caused issues when the Traefik router tried to configure TLS for the service. So I had to rebuild my image, removing the TLS. I also removed the code that required HTTPS attributes too, that is now the responsibility of traefik.
Image only needs to expose 1 port: My understanding now is that my web application should only run through port 80 (e.g. don't even need port 443 exposed in the image's firewall) and the Traefik router configures and handles the TLS/443 etc through port 80.
I hadn't actually defined my HTTP and HTTPS services properly. I have shared my docker-compose.yml file below - notice for both traefik and my other service I have separated the labels into sections: defining the service, routing for HTTP, routing for HTTPS, redirect middleware, and basic auth for the traefik dashboard. I couldn't find any good documentation or tutorials that really broke down the necessary aspects for traefik 2.0 and categorized them clearly.
The service definition label / loadbalancer port should point to whatever port the image's service is hosted from, e.g. port 80 in most cases, or in the case of the traefik service 8080. But crucially it doesn't need to also point to 443 for https.
In summary my (verbose) learning notes afterall are, you need to:
Define the service again as a label: Didn't work for me without the service definition label. I had to add a service label pointing to the name of the service under which the image is defined.
Define routers for http and https: Whatever text you put after the traefik.http.routers.YOUR_ROUTER_NAME becomes your router. It wasn't clear to me I needed separate routers for both http and https. You must do this for each service (e.g. traefik, whoami, remoteling, etc.)
Define the routers' entrypoints: AFAIK the name you put after the entrypoint in the traefik command defines a new entrypoint that you can use for other services. So in the traefik service definition you'd have --entrypoints.WEBNAME.address=:80 and --entrypoints.WEBSECURENAME.address=:443 (replacing those caps with your own name to use throughout the docker-compose.yml file.
Define the routers' domain names to capture: Just like you define routers for each http and https you must define the domains to capture for both of these routers, even just the same domain/path.
Add tls for https router: For the https router you need the tls.true label.
Separate middleware redirection definitions for traefik and each of your services: I've read that you can declare a global one, but AFAIK each service must opt-in by assigning the middleware to the http router.
Providing your own SSL certs on Docker for Windows: There was precious little information about running Docker for Windows with your own SSL certs. I have a batch script in my 'traefik' working dir with a subfolder containing the SSL certs (crt and key files). I mount this as the first volume, - ./traefik-ssl-certs/:c:/certs/. Then, in my working dir I have another folder called traefik-config containing my traefik.yml file (details in the question above). In linux everyone seems to just mount the config file directly, but mounting files doesn't work in Windows so I had to mount as a folder instead, then I used the command providers.file.directory=c:/config/, which tells traefik to look for the config file in there. The config file provides the location of the SSL certs for traefik to load. If you enable TLS for a router, Traefik will automatically use any cert that matches the domain you've specified on that router.
Basic Auth: Must define the middleware as one label, and then assign that middleware to your https router. And I suppose if you're not using HTTPS redirect then you could assign it to your http router but it wouldn't be secure obviously.
docker-compose.yml:
version: '3.8'
networks:
myweb:
external: true
services:
proxy:
image: traefik:v2.3.0-rc4-windowsservercore-1809
container_name: traefik
ports:
- "80:80"
- "443:443"
- "8080:8080"
volumes:
# Mount the certs drive
- ./traefik-ssl-certs/:c:/certs/
# Mount the config folder
- ./traefik-config/:c:/config/
# Mount the host docker engine pipe ("docker volume ls")
- source: '\\.\pipe\docker_engine'
target: '\\.\pipe\docker_engine'
type: npipe
command:
- --api=true
- --api.dashboard=true
- --api.insecure=false
# Register the traefik config directory as per: https://docs.traefik.io/providers/file/#directory
- --providers.file.directory=c:/config/
- --providers.file.watch=true
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
# Configure Docker provider
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --providers.docker.endpoint=npipe:////./pipe/docker_engine
- --providers.docker.network=myweb
- --providers.docker.watch=true
networks:
- myweb
labels:
- traefik.enable=true
- traefik.docker.network=myweb
# Define the service
- traefik.http.services.proxy.loadbalancer.server.port=8080
# Routing for dashboard HTTP
- traefik.http.routers.dash-http.service=api#internal
- traefik.http.routers.dash-http.rule=Host(`example.com`)
- traefik.http.routers.dash-http.entrypoints=web
# Routing for dashboard HTTPS
- traefik.http.routers.dash-https.service=api#internal
- traefik.http.routers.dash-https.rule=Host(`example.com`)
- traefik.http.routers.dash-https.entrypoints=websecure
- traefik.http.routers.dash-https.tls=true
# Http-to-Https redirect Middleware
- traefik.http.middlewares.dash-http2https.redirectscheme.scheme=https
- traefik.http.middlewares.dash-http2https.redirectscheme.permanent=true
- traefik.http.routers.dash-http.middlewares=dash-http2https
# BasicAuth for dashboard
# Windows doesn't have htpasswd command so I generated one here: https://hostingcanada.org/htpasswd-generator/
# As per Traefik documentation, escaped single $ char with $$ for the yml parser
# user/pass = admin/testpassword
- traefik.http.middlewares.api-auth.basicauth.users=admin:$$2y$$10$$mfWQ11K16V6gVK.8Y6q1Eeh765NZscmjCrjJlAtaWubEsjU8HLYOO
- traefik.http.routers.dash-https.middlewares=api-auth
remoteling:
image: remoteling:latest
container_name: remoteling
networks:
- myweb
labels:
- traefik.enable=true
- traefik.docker.network=myweb
# Define the service
- traefik.http.services.remoteling.loadbalancer.server.port=80
# Routing for remoteling HTTP
- traefik.http.routers.remoteling-http.service=remoteling
- traefik.http.routers.remoteling-http.entrypoints=web
- traefik.http.routers.remoteling-http.rule=Host(`services.example.com`)
# Routing for remoteling HTTPS
- traefik.http.routers.remoteling-https.service=remoteling
- traefik.http.routers.remoteling-https.entrypoints=websecure
- traefik.http.routers.remoteling-https.rule=Host(`services.example.com`)
- traefik.http.routers.remoteling-https.tls=true
# Http-to-Https redirect Middleware
- traefik.http.middlewares.remoteling-http2https.redirectscheme.scheme=https
- traefik.http.middlewares.remoteling-http2https.redirectscheme.permanent=true
- traefik.http.routers.remoteling-http.middlewares=remoteling-http2https
depends_on:
- proxy
Hopefully someone else finds that useful.
Related
I have Traefik container (traefik v. 2.8) running as reverse proxy for local development.
I also use docker-compose.yml file to define my services.
Also, I have exposed the services present in docker-compose.yml via host.docker.internal by setting these lines in my /etc/hosts file:
127.0.0.1 host.docker.internal
localhost host.docker.internal
My setup is as such:
host.docker.internal:8443 used for service_a
host.docker.internal:8453 used for service_b
I have setup Traefik route from a.localhost that goes to host.docker.internal:8443.
I can access a.localhost from the host outside the containers just fine, and Traefik does really route the traffic to host.docker.internal:8443 as I want.
Problem is that I have a reason to have service B (host.docker.internal:8453) call service A via the a.localhost hostname.
This does not work, as in service B, I get unknown host when trying to access a.localhost
Here is extract from my docker-compose.yml file:
version: '3'
services:
reverse_proxy:
image: traefik:v2.8
# Enables the web UI and tells Traefik to listen to docker
command: --api.insecure=true --providers.docker
ports:
- "80:80"
- "443:443"
# The Web UI (enabled by --api.insecure=true)
- "9000:8080"
volumes:
# So that Traefik can listen to the Docker events
- /var/run/docker.sock:/var/run/docker.sock
- ./dev-traefik/traefik.yml:/etc/traefik/traefik.yml
- ./dev-traefik:/configurations
service_a:
ports:
- "8443:8443"
service_b:
ports:
- "8453:8453"
Also I'm using a yml-based configuration for Traefik, present in dynamic-config.yml:
http:
routers:
service-a-router:
service: service-a
rule: "Host(`a.localhost`)"
tls: "true" # using tls
services:
service-a:
loadBalancer:
servers:
- url: "https://host.docker.internal:8443" # service A
tls:
certificates:
- certFile: "/etc/https/tls.crt"
keyFile: "/etc/https/tls.key"
stores:
default:
defaultCertificate:
certFile: "/etc/https/tls.crt"
keyFile: "/etc/https/tls.key"
It seems like Traefik is able to listen to requests made from host network, as accessing https://a.localhost from browser outside the container network works just fine.
On the other hand, requests made by service_a container don't seem to be caught by Traefik.
What I have also tried is to add a.localhost to /etc/hosts in the host machine running the containers like this:
127.0.0.1 a.localhost
localhost a.localhost
And then using curl inside service B container to access service A.
This resulted in getting connection refused as opposed to Could not resolve host: a.localhost. This leads me to suggest that traefik couldn't intercept traffic from service b container
What am I doing wrong?
Is there a way to make such setup work? I do have a legit reason for it, which relates to having as close setup as possible in local development as on other environments which are deployed to cloud.
I wasn't able to have Traefik intercept container -> container traffic the way I originally specified I'd want, but was able to actually nevertheless get similar setup working.
Here's the scenario:
I have two services that are accessed via HTTP & TLS:
service_a
service_b
I want to be able to use host.docker.internal special DNS name to my advantage and actually have Traefik to proxy traffic from https://host.docker.internal/service_a to service_a port 8443 both outside Docker container network (from the host machine running Docker) AND from service_b via the fact that both the host machine running Docker can access host.docker.internal and also hosts inside the Docker network.
Using this fact to my advantage, I just defined a path for service_a as such in Traefik's YML configuration file:
http:
routers:
service-a-router:
service: service-a
rule: "Host(`host.docker.internal`) && PathPrefix(`/service_a`)"
tls: "true" # using tls
services:
service-a:
loadBalancer:
servers:
- url: "https://host.docker.internal:8443" # service A
tls:
certificates:
- certFile: "/etc/https/tls.crt"
keyFile: "/etc/https/tls.key"
stores:
default:
defaultCertificate:
certFile: "/etc/https/tls.crt"
keyFile: "/etc/https/tls.key"
And docker-compose.yml was made to look like this:
version: '3'
services:
reverse_proxy:
image: traefik:v2.8
# Enables the web UI and tells Traefik to listen to docker
command: --api.insecure=true --providers.docker
ports:
- "80:80"
- "443:443"
# The Web UI (enabled by --api.insecure=true)
- "9000:8080"
volumes:
# So that Traefik can listen to the Docker events
- /var/run/docker.sock:/var/run/docker.sock
- ./dev-traefik/traefik.yml:/etc/traefik/traefik.yml
- ./dev-traefik:/configurations
service_a:
ports:
- "8443:8443"
service_b:
ports:
- "8453:8453"
Now as I use host.docker.internal in Host rule in my Traefik YML config, Traefik is in fact able to intercept both traffic from the Docker host machine and from the Docker container service_b.
service_b just need to configure URL of https://host.docker.internal/service_a to access service_a through Traefik.
i followed the guide for Deploying FastAPI with HTTPS powered by Traefik enter link description here
can someone tell me what changes should i make to access https
the docker compose consist 3 files
1)docker-compose.traefik.yml
services:
traefik:
# Use the latest v2.3.x Traefik image available
image: traefik:v2.3
ports:
# Listen on port 80, default for HTTP, necessary to redirect to HTTPS
- 80:80
# Listen on port 443, default for HTTPS
- 443:443
restart: always
volumes:
# Add Docker as a mounted volume, so that Traefik can read the labels of other services
- /var/run/docker.sock:/var/run/docker.sock:ro
# Mount the volume to store the certificates
# Mount the volume to store the certificates
- traefik-public-certificates:/certificates
command:
# Enable Docker in Traefik, so that it reads labels from Docker services
- --providers.docker
# Do not expose all Docker services, only the ones explicitly exposed
- --providers.docker.exposedbydefault=false
# Create an entrypoint "http" listening on port 80
- --entrypoints.http.address=:80
- --entrypoints.https.address=:443
# Create the certificate resolver "le" for Let's Encrypt, uses the environment variable EMAIL
- --certificatesresolvers.le.acme.email=spb7221#gmail.com
# Store the Let's Encrypt certificates in the mounted volume
- --certificatesresolvers.le.acme.storage=/certificates/acme.json
# Use the TLS Challenge for Let's Encrypt
- --certificatesresolvers.le.acme.tlschallenge=true
- --accesslog
# Enable the Traefik log, for configurations and errors
- --log
networks:
# Use the public network created to be shared between Traefik and
# any other service that needs to be publicly available with HTTPS
- traefik-public
volumes:
# Create a volume to store the certificates, there is a constraint to make sure
# Traefik is always deployed to the same Docker node with the same volume containing
# the HTTPS certificates
traefik-public-certificates:
networks:
# Use the previously created public network "traefik-public", shared with other
# services that need to be publicly available via this Traefik
traefik-public:
external: true
2)docker-compose.yml
#!/bin/bash
services:
backend:
build: .
command: uvicorn app.main:app --host 0.0.0.0 --port 8000
ports:
# Listen on port 80, default for HTTP, necessary to redirect to HTTPS
- 8000:8000
labels:
# Enable Traefik for this specific "backend" service
- traefik.enable=true
# Define the port inside of the Docker service to use
- traefik.http.services.app.loadbalancer.server.port=80
# Make Traefik use this domain in HTTP
- traefik.http.routers.app-http.entrypoints=http
- traefik.http.routers.app-http.rule=Host(`inskade.in`)
# Use the traefik-public network (declared below)
- traefik.docker.network=traefik-public
- traefik.http.routers.app-https.entrypoints=https
- traefik.http.routers.app-https.rule=Host(`inskade.in`)
- traefik.http.routers.app-https.tls=true
# Use the "le" (Let's Encrypt) resolver
- traefik.http.routers.app-https.tls.certresolver=le
# https-redirect middleware to redirect HTTP to HTTPS
- traefik.http.middlewares.https-redirect.redirectscheme.scheme=https
- traefik.http.middlewares.https-redirect.redirectscheme.permanent=true
# Middleware to redirect HTTP to HTTPS
- traefik.http.routers.app-http.middlewares=https-redirect
networks:
# Use the public network created to be shared between Traefik and
# any other service that needs to be publicly available with HTTPS
- traefik-public
networks:
traefik-public:
external: true
3)docker-compose.override.yml
services:
backend:
ports:
- 80:80
networks:
traefik-public:
external: false
here where i am checking the container
in browser I am able to access through http
i have checked the docker container of traefik and the certificate is present
the certificate
{
"le": {
"Account": {
"Email": "spb7221#gmail.com",
"Registration": {
"body": {
"status": "valid",
"contact": [
"mailto:spb7221#gmail.com"
]
},
"uri": "https://acme-v02.api.letsencrypt.org/acme/acct/45w5706050"
},
"PrivateKey": "MIIJJwIBAAKCAgEAqmkfY8MqLzdnq8IV573RdqNIIHIev0WDpd8Y3QnzaN9HN7U8wBRQIhpSf98bRbZ77UQOGwzzSCObU5KE9lx80anU0zKPDQV0
"KeyType": "4096"
},
"Certificates": [
{
"domain": {
"main": "inskade.in"
},
"certificate": "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS10tCk1JSUdHVENDQlFHZ0F3SUJBZ0lTQkV1aHo3WU1MdHJNUGV6QUlrTkc3OFVXTUEwR0NTcUdT
"key": "LS0tLS1CRUdJTiBSU0EgUFJJVkFU1RSBLRVktLS0tLQpNSUlKS1FJQkFBS0NBZ0VBbkFLYzlNRFRQdUkxcW9sOWFYUEhHWTNlMFBDVWNyQ3NnaVFpS09Y
"Store": "default"
}
]
}
}
first, you should probably not post your certificates in plain clear sight, those are supposed to be private, especially "PrivateKey"
second, you are using traefik labels, and the names of these labels are relevant as you can see in the traefik docs
You named your docker service "backend" and should refer to it using this name so
- traefik.http.routers.app-https.entrypoints=https
changes to
- traefik.http.routers.backend.entrypoints=https
I didn't tested it but i think if you change every "app-https" and "app" with the correct "backend" name it should work.
last point, I do recommand putting the redirection from http to https in the traefik configuration, I personnaly use the following commands :
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --entrypoints.web.http.redirections.entrypoint.to=:443
- --entrypoints.web.http.redirections.entrypoint.scheme=https
- --entrypoints.web.http.redirections.entrypoint.permanent=true
I am trying to setup a certificate for a locally running react app on a virtual host local.example.com. This has to just work locally on docker setup. After going through some articles, I came up with this docker-compose.yml:
version: "3"
services:
mongo:
image: mongo
restart: always
ports:
- "27017:27017"
volumes:
- mongodbdata:/data/db
networks:
- proxy
mongo-express:
image: mongo-express
restart: always
ports:
- "8081:8081"
networks:
- proxy
react:
build:
context: ./client
dockerfile: ./Dockerfile
ports:
- "3001:3001"
stdin_open: true
volumes:
- ./client:/client
- /client/node_modules
labels:
# this enables traefik for your service
- "traefik.enable=true"
# this defines the url, traefik will get the ssl certificate for
- "traefik.http.routers.myapplication.rule=Host(`local.example.com`)"
# this tells traefik to use https to access the website
- "traefik.http.routers.myapplication.entrypoints=websecure"
# this tells traefik to use the certresolver, that we defined above for resolving tls (in our case letsencrypt)
- "traefik.http.routers.myapplication.tls.certresolver=myresolver"
# this let's us forward the port we set above. Change this to the port you expose in your application (3000, 4000, ...) or remove the line, if your application already exposes port 80/443
- "traefik.http.services.myapplication.loadbalancer.server.port=3000"
depends_on:
- "server"
networks:
- proxy
server:
build:
context: ./server
dockerfile: ./Dockerfile
ports:
- "5001:5001"
volumes:
- traefik.toml:/traefik.toml
- acme.json:/acme.json
- ./server:/server
- /server/node_modules
labels:
# this enables traefik for your service
- "traefik.enable=true"
# this defines the url, traefik will get the ssl certificate for
- "traefik.http.routers.myapplication.rule=Host(`local.example.com`)"
# this tells traefik to use https to access the website
- "traefik.http.routers.myapplication.entrypoints=websecure"
# this tells traefik to use the certresolver, that we defined above for resolving tls (in our case letsencrypt)
- "traefik.http.routers.myapplication.tls.certresolver=myresolver"
# this let's us forward the port we set above. Change this to the port you expose in your application (3000, 4000, ...) or remove the line, if your application already exposes port 80/443
- "traefik.http.services.myapplication.loadbalancer.server.port=5001"
depends_on:
- "mongo"
networks:
- proxy
whoami:
image: "containous/whoami"
container_name: "myapplication"
restart: unless-stopped
ports:
- "4000:4000"
networks:
- proxy
traefik:
image: "traefik:v2.2"
container_name: "traefik"
command:
# this can be uncommented to get more information, in case something doesn't work
- "--log.level=DEBUG"
# set this to true to get access to the traefik web interface unter http://YOURIP:8080
- "--api.insecure=false"
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=true"
- "--entrypoints.web.address=:80"
- "--entrypoints.websecure.address=:443"
- "--certificatesresolvers.myresolver.acme.httpchallenge=true"
- "--certificatesresolvers.myresolver.acme.httpchallenge.entrypoint=web"
#- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory" # uncomment this line to only test ssl generation first (to make sure you don't run into letsencrypt limits)
- "--certificatesresolvers.myresolver.acme.email=kamlekar.venkatesh#gmail.com"
- "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
ports:
- "80:80"
- "443:443"
- "8080:8080" # this is used for the web interface, that let's you check and monitor traefik and your configuration. It's very nice for debugging your config - only available if "api.insecure" above is set to true
volumes:
- "./letsencrypt:/letsencrypt"
- "/var/run/docker.sock:/var/run/docker.sock:ro"
networks:
- proxy
# The following is only necessary if you want to enforce https!
# if you don't need that, you can just remove the labels here
labels:
- "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
- "traefik.http.routers.http-catchall.entrypoints=web"
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
networks:
proxy:
external: true
volumes:
mongodbdata:
traefik.toml:
acme.json:
On doing docker-compose up, I am seeing the following error in traefik service. Also, though the react app service is running in docker but I hit https://local.example.com:3001, is unreachable
time="2022-01-23T08:30:52Z" level=error msg="Unable to obtain ACME certificate for domains "local.example.com": unable to generate a certificate for the domains [local.example.com]: error: one or more domains had a problem:\n[local.example.com] acme: error: 400 :: urn:ietf:params:acme:error:dns :: DNS problem: NXDOMAIN looking up A for local.example.com - check that a DNS record exists for this domain; DNS problem: NXDOMAIN looking up AAAA for local.example.com - check that a DNS record exists for this domain, url: \n" providerName=myresolver.acme routerName=myapplication#docker rule="Host(local.example.com)"
Here are the research notes which I tried till now (These notes for only my personal understanding so could be not in detail)
You can start from this fiddle: https://github.com/kamlekar/react-docker-ssl-virtualhost
You need to use TLS for your local setup. The host you need a certificate for is local.example.com. There is no way to obtain a certificate from Letsencrypt for this name, because you're not controlling the example.com domain. One of the ways Letsencrypt creates a certificate is a challenge - you prove that you own the domain by creating a TXT DNS record. If you own a domain you can do that, but your case is different, because you only need this for local development.
However, you can just use openssl to generate a self signed certificate for whichever domain name you want. This is a good reference on how to do this. You can use the local.example.com domain name for the generated certificate. If you're successful, you'll end up with the certificate and it's private key. Note where you save those files, as you'll need them. Keep in mind that the certificate is self-signed, so your browser will give you a warning, unless you add this certificate to the trust store of your operating system.
The next step in your case is to make Traefik use those self signed certificates when serving content from your application. I think this answer has a good example of that.
After having this, you'll only need to edit your hosts file and redirect your localhost:8080 (the port on which your Traefik serves your application) to local.example.com.
Also, Traefik is not the only solution for your case. You can also achieve the same using Nginx, for example. Choose which one satisfies your use case. My suggestion would be to use the one that's easiest to configure, because it's for local development. Here's the first result I got when searching for a nginx docker-compose self-signed certificate.
UPDATE
Here's a quick example of what I'm describing above.
First generate the certificate:
openssl req -x509 -newkey rsa:4096 -nodes -keyout key.pem -out cert.pem -sha256 -days 1000 -subj '/CN=local.example.com'
You'll end up with two files in your current directory (key.pem and cert.pem). Now create the nginx.conf:
worker_processes 1;
events {
worker_connections 1024;
}
http {
server {
listen 443 ssl;
server_name local.example.com;
ssl_certificate cert.pem;
ssl_certificate_key key.pem;
ssl_session_timeout 5m;
location / {
proxy_pass http://myapp:8080;
proxy_set_header Host $host;
proxy_read_timeout 1800;
proxy_connect_timeout 1800;
}
}
}
And now the docker-compose.yaml file:
version: "3"
services:
nginx:
image: nginx
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf
- ./cert.pem:/etc/nginx/cert.pem
- ./key.pem:/etc/nginx/key.pem
ports:
- "443:443"
networks:
- local-dev-01
myapp:
image: your-react-app-image
command: "command-that-starts-your-app"
networks:
- local-dev-01
networks:
local-dev-01:
Docker creates a network called local-dev-01 for you, which allows both services to be able to resolve each other by their name. That's why we have myapp:8080 in the nginx.conf. We also mount the configuration and the generated certificate and key for local.example.com.
The final step is to edit your hosts file and add the following line:
127.0.0.1 local.example.com
After that, you should have no trouble reaching your application on https://local.example.com on your machine. Keep in mind that your browser will keep warning you that the certificate is self-signed, so you should add it as an exception.
I am trying to set up a sample application with the Traefik reverse proxy in Docker.
I am using Traefik v2.2 for this project which has significant differences from Traefik.v1.0.
Here is my docker-compose.yml file:
version: '3'
services:
traefik:
# The official v2 Traefik docker image
image: traefik:v2.2
# Enables the web UI and tells Traefik to listen to docker
command:
- --api.insecure=true
- --providers.docker=true
- --providers.docker.exposedbydefault=false
- --entrypoints.web.address=:80
ports:
# The HTTP port
- "89:80"
# The Web UI (enabled by --api.insecure=true)
- "8089:8080"
volumes:
# So that Traefik can listen to the Docker events
- "/var/run/docker.sock:/var/run/docker.sock:ro"
whoami:
# A container that exposes an API to show its IP address
image: containous/whoami
labels:
- "traefik.enable=true"
- "traefik.http.routers.whoami.rule=Host(`whoami.localhost`)"
- "traefik.http.routers.whoami.entrypoints=web"
I can access Traefik's dashboard when I go to localhost:8089 on my web browser, but I cannot access the whoami application when I type in the whoami.localhost address on my web browser. I'm just wondering if there is anything I need to change before I can access it, or do I need to change the host from whoami.localhost to localhost:3000 since that's the port I want to access the application in.
One problem I am spotting is that you exposed container port 80 of the traefik container to the host port 89. If you type in whoami.localhost in your web browser, your browser is going to search for an application on host port 80 at that address (since localhost maps natively to port 80), but it is not going to find anything there, because it can only be found at port 89. From my understanding, you should be able to access the application via the command line with the command curl -H Host:whoami.localhost http://127.0.0.1:89. Unfortunately, I am unsure how the URL whoami.localhost:89 is handled by your browser respectively by your DNS.
You can to modify the docker-compose.yml file this way:
version: "3"
services:
traefik:
# The official v2 Traefik docker image
image: traefik:v2.2
# Enables the web UI and tells Traefik to listen to docker
command:
- --api.insecure=true
- --providers.docker=true
ports:
# The HTTP port
- "89:80"
# The Web UI (enabled by --api.insecure=true)
- "8089:8080"
volumes:
# So that Traefik can listen to the Docker events
- /var/run/docker.sock:/var/run/docker.sock
whoami:
# A container that exposes an API to show its IP address
image: containous/whoami
labels:
- traefik.http.routers.whoami.rule=Host(`whoami.localhost`)
And then you can access the application on your command terminal by typing in:
curl -H Host:whoami.localhost http://127.0.0.1:89
Note: whoami.localhost can be whoami.docker.localhost or app.localhost or whatever you want. The thing here is that you should localhost attached to the end, except if you're adding a Fully Qualifies Domain name (FQDN).
That's all.
I hope this helps
I have followed the instructions for the TLS challenge and read through the grpc guide for traefik but I can't figure out how to put them together.
Currently I have the traefik dashboard at my domain working, and I could get a http server working, but I can't get the grpc service to be reachable. It shows up under HTTP Services in the dashboard, but when I attempt to hit the endpoint with a request it just times out saying it's unreachable.
my docker-compose (I don't have a TOML file):
traefik:
image: "traefik:v2.0.0"
container_name: traefik
command:
- --entrypoints.web.address=:80
- --entrypoints.websecure.address=:443
- --entrypoints.grpc.address=:8090
- --providers.docker
- --api
# Lets Encrypt Resolvers
- --certificatesresolvers.leresolver.acme.email=${EMAIL}
- --certificatesresolvers.leresolver.acme.storage=/etc/acme/cert.json
- --certificatesresolvers.leresolver.acme.tlschallenge=true
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
- "/etc/acme/:/etc/acme/"
labels:
# Dashboard
- "traefik.http.routers.traefik.rule=Host(`traefik.${DOMAIN}`)"
- "traefik.http.routers.traefik.service=api#internal"
- "traefik.http.routers.traefik.tls.certresolver=leresolver"
- "traefik.http.routers.traefik.entrypoints=websecure"
# Auth
- "traefik.http.routers.traefik.middlewares=authtraefik"
- "traefik.http.middlewares.authtraefik.basicauth.users=admin:xxx"
# global redirect to https
- "traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)"
- "traefik.http.routers.http-catchall.entrypoints=web"
- "traefik.http.routers.http-catchall.middlewares=redirect-to-https"
# middleware redirect
- "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
ports:
- 80:80
- 443:443
- 8080:8080
networks:
- internal
- proxied
grpc_server:
image: xxx
container_name: grpc_server
labels:
- "traefik.http.routers.grpc_server.rule=Host(`grpc.${DOMAIN}`)"
- "traefik.http.routers.grpc_server.entrypoints=grpc"
- "traefik.http.routers.grpc_server.tls=true"
- "traefik.http.routers.grpc_server.tls.certresolver=leresolver"
expose:
- 8090 # grpc server
I don't need the layer from traefik to grpc to be encrypted which is why I haven't set up the self signed cert as per the grpc example. My grpc service is running in insecure mode and words when not behind traefik.
Anything obvious that I missed?
On gRPC endpoint please use scheme as h2c then traefik will work,
gRPC connection for example in YAML:
spec:
entryPoints:
- grpc
routes:
- kind: Rule
match: Host(`xyz.com`)
services:
- name: service_name
port: 50051
scheme: h2c
tls:
options:
name: mytlsoption
secretName: secret_name
You are using port 8090 as entrypoint for grpc - in your docker compose file you don't map the port to traefik.
Assuming that you use port 80 to reach traefik it will result in a 404, if you use port 8080 it will be mapped to traefik, but traefik is not listening on that port and if you use 8090 the port is not mapped to a docker container, both resulting in a port unreachable error.