Docker - Traefik - Wildcard Subdomain
Intro
I was wondering if you can have Wildcart Certs for certain Subdomain. Idea is to provide a Service with “myservice.auth.your.domain” which automatically requests Authentication, while the same Service “myservice.whitelist.your.domain” is reachable through some Whitelisted IP only.
As Traefik can Chain Middleware, but not implements some logic (If Whitelist -> ok, else do Basic Auth …), i have to build another solution.
let’s have a look
Prepare Folders
cd /your/traffic/rootfolder
mkdir -p config/dynamic
.env File
we need two variables, so let’s put them in the .env File
cat << 'EOF' > .env
DOMAIN="your.domain"
INFOMANIAK_ACCESS_TOKEN=2Bxxxxxxxxxxxxxxxxxxxx
EOF
DNS Provider
i used infomaniak as DNS Provider. you have to pick one from the list (https://doc.traefik.io/traefik/https/acme/#providers) and adapt accordingly. if you go with infomaniak and can’t find the page to create a new api token …
traefik.conf
we extend the existing traefik.conf with few lines for auth.your.domain and list.your.domain
cat << 'EOF' > docker-compose.yml
version: "3"
services:
traefik:
container_name: traefik
hostname: traefik
image: traefik:latest
restart: always
security_opt:
- no-new-privileges:true
ports:
- 80:80/tcp
- 443:443/tcp
environment:
- "TZ=Europe/Zurich"
# DNS Provider
- "INFOMANIAK_ACCESS_TOKEN=${INFOMANIAK_ACCESS_TOKEN}"
volumes:
- ./config/dynamic:/dynamic
- ./config/traefik.yml:/traefik.yml
- ./plugins-local/:/plugins-local/
- /etc/localtime:/etc/localtime:ro
- /var/run/docker.sock:/var/run/docker.sock:ro
- traefik-acme:/acme
networks:
- traefik
labels:
# Traefik
traefik.enable: true
traefik.http.routers.traefik.entrypoints: websecure
traefik.http.routers.traefik.middlewares: simpleAuth@file
traefik.http.routers.traefik.rule: Host(`traefik.${DOMAIN}`)
traefik.http.routers.traefik.service: api@internal
traefik.http.routers.traefik.tls.certresolver: infomaniakdns
traefik.http.routers.traefik.tls: true
# your.domain
traefik.http.routers.traefik.tls.domains[0].main: "${DOMAIN}"
traefik.http.routers.traefik.tls.domains[0].sans: "*.${DOMAIN}"
# auth.your.domain
traefik.http.routers.traefik.tls.domains[1].main: "auth.${DOMAIN}"
traefik.http.routers.traefik.tls.domains[1].sans: "*.auth.${DOMAIN}"
# list.your.domain
traefik.http.routers.traefik.tls.domains[2].main: "list.${DOMAIN}"
traefik.http.routers.traefik.tls.domains[2].sans: "*.list.${DOMAIN}"
networks:
traefik:
name: traefik
volumes:
traefik-acme:
name: traefik-acme
EOF
Traefik.conf
cat << 'EOF' > config/traefik.yml
# Traefik static config
log:
# PANIC, DEBUG, FATAL, ERROR, WARN, and INFO.
level: DEBUG
api:
dashboard: true
global:
checkNewVersion: false
sendAnonymousUsage: false
entrypoints:
web:
address: ":80"
http:
redirections:
entryPoint:
to: websecure
scheme: https
permanent: true
websecure:
address: ":443"
certificatesResolvers:
infomaniakdns:
acme:
email: [email protected]
storage: "/acme/acme.json"
dnsChallenge:
provider: infomaniak
delayBeforeCheck: 60
providers:
docker:
exposedByDefault: false
network: traefik
watch: true
file:
directory: "./dynamic"
watch: true
EOF
Prepare Middlware
cat << 'EOF' > config/dynamic/whitelists.yml
---
http:
middlewares:
blog-ipwhitelist:
ipwhitelist:
sourcerange:
- 127.0.0.1
- your.allowed.ip.address
EOF
cat << 'EOF' > config/dynamic/simpleauth.yml
---
# user=myuser; echo " - \"$(htpasswd -nB $user)\"" >> simpleauth.yml
http:
middlewares:
simpleAuth:
basicAuth:
users:
- "myuser:$2y$05$xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
EOF
Service
let’s fireup a Nginx Server, which will be reached under the following URL:
- https://nginx.your.domain -> public available
- https://nginx.auth.your.domain -> same Service, with Basic Auth
- https://nginx.list.your.domain -> same Service, with Whitelist
version: '3'
services:
nginx_top:
container_name: nginx_container_name
hostname: nginx_hostname
image: nginx
networks:
- traefik
volumes:
- ./templates:/etc/nginx/templates
environment:
- NGINX_HOST=nginx.${DOMAIN}
- NGINX_PORT=80A
labels:
- "traefik.enable=true"
- "traefik.http.routers.nginx.rule=Host(`nginx.${DOMAIN}`)"
- "traefik.http.routers.nginx.tls=true"
- "traefik.http.routers.nginx-auth.rule=Host(`nginx.auth.${DOMAIN}`)"
- "traefik.http.routers.nginx-auth.tls=true"
- "traefik.http.routers.nginx-auth.middlewares=simpleAuth@file"
- "traefik.http.routers.nginx-list.rule=Host(`nginx.list.${DOMAIN}`)"
- "traefik.http.routers.nginx-list.tls=true"
- "traefik.http.routers.nginx-list.middlewares=blog-ipwhitelist@file"
networks:
traefik:
external: true
Test Config
Test the URL’s without any authentication and from an IP Address not whitelisted
$ curl -s -I https://blog.norma.li |grep HTTP
HTTP/2 200
$ curl -s -I https://blog.auth.norma.li |grep HTTP
HTTP/2 401
$ curl -s -I https://blog.list.norma.li |grep HTTP
HTTP/2 403
Test Basic Auth
$ curl -s -I -u "myuser:myfamouspassword" https://blog.auth.norma.li
HTTP/2 200
accept-ranges: bytes
content-type: text/html; charset=utf-8
date: Sun, 23 Oct 2022 08:05:38 GMT
last-modified: Sun, 23 Oct 2022 07:54:39 GMT
content-length: 55832
Restart Docker Container
and what happens, when we bring a container down and up again while looking at the HTTP Response Code ?
$ while true; do timeout 1 curl -s -I https://blog.list.norma.li |grep -E "HTTP|Term" |ts; sleep 1; done
Oct 23 09:54:23 HTTP/2 200
Oct 23 09:54:25 HTTP/2 200
Oct 23 09:54:26 HTTP/2 200
Oct 23 09:54:27 HTTP/2 404
Oct 23 09:54:28 HTTP/2 404
Oct 23 09:54:29 HTTP/2 404
Terminated
Terminated
Terminated
Terminated
Oct 23 09:54:39 HTTP/2 200
Oct 23 09:54:40 HTTP/2 200
Oct 23 09:54:41 HTTP/2 200
here we are … :)
Any Comments ?
sha256: 8b9c51c93e5809946904cb52accbfe492ba35c5e53fef9328b7bac839a4f278d