Skip to content

Instantly share code, notes, and snippets.

@mattdy
Last active April 7, 2024 01:02
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mattdy/d741344366a4fbb86f5034adfd1ad191 to your computer and use it in GitHub Desktop.
Save mattdy/d741344366a4fbb86f5034adfd1ad191 to your computer and use it in GitHub Desktop.
Traefik on Docker Swarm accessed via Cloudflare Tunnel
Please see https://mattdyson.org/blog/2024/02/using-traefik-with-cloudflare-tunnels for a detailed write-up of this configuration
ROOT_DOMAIN=yourdomain.com
HTTP_TIMEOUT=60
POLLING_INTERVAL=10
PROPAGATION_TIMEOUT=3600
TTL=300
PROVIDERS_GOOGLE_CLIENT_ID=<GOOGLE CLIENT ID>
PROVIDERS_GOOGLE_CLIENT_SECRET=<GOOGLE CLIENT SECRET>
SECRET=RandomTextGoesHere
WHITELIST=<YOUR GOOGLE ACCOUNT EMAIL>
LOG_LEVEL=INFO
ZONE_ID=<YOUR CLOUDFLARE ZONE ID>
version: '3.7'
services:
whoami:
image: traefik/whoami
command:
- --name=externalapp
deploy:
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik"
- "traefik.http.routers.external.rule=Host(`external.yourdomain.com`)"
- "traefik.http.routers.external.entrypoints=websecure"
- "traefik.http.routers.external.tls=true"
- "traefik.http.routers.external.middlewares=forward-auth"
- "traefik.http.services.external.loadbalancer.server.port=80"
- "traefik.constraint=proxy-public"
networks:
traefik:
external: true
version: '3.7'
services:
whoami:
image: traefik/whoami
command:
- --name=internalapp
deploy:
labels:
- "traefik.enable=true"
- "traefik.docker.network=traefik"
- "traefik.http.routers.internal.rule=Host(`internal.yourdomain.com`)"
- "traefik.http.routers.internal.entrypoints=websecure"
- "traefik.http.routers.internal.tls=true"
- "traefik.http.services.internal.loadbalancer.server.port=80"
networks:
traefik:
external: true
version: '3.7'
services:
reverse-proxy:
image: traefik:v2.10
command:
- "--log"
- "--log.level=${LOG_LEVEL:-INFO}"
- "--log.format=json"
- "--api.insecure=true"
- "--providers.docker"
- "--providers.docker.swarmMode=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.file.directory=/config"
- "--providers.file.watch=true"
- "--serversTransport.insecureSkipVerify=true" # Allow self-signed certificates for target hosts - https://doc.traefik.io/traefik/routing/overview/#insecureskipverify
- "--metrics"
- "--metrics.prometheus.buckets=0.1,0.3,1.2,5.0"
- "--entrypoints.web.address=:80"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--entrypoints.websecure.address=:443"
- "--entrypoints.websecure.http.tls=true"
- "--entrypoints.websecure.http.tls.certresolver=letsencrypt"
- "--entrypoints.webinternal.address=:82"
- "--certificatesresolvers.letsencrypt.acme.email=<YOUR EMAIL>"
- "--certificatesresolvers.letsencrypt.acme.storage=/etc/traefik/acme/letsencrypt.json"
- "--certificatesresolvers.letsencrypt.acme.dnschallenge.provider=cloudflare"
- "--certificatesresolvers.letsencrypt.acme.dnschallenge.delaybeforecheck=300"
- "--certificatesresolvers.letsencrypt.acme.dnschallenge.resolvers=8.8.8.8:53"
secrets:
- cf_token
environment:
- CLOUDFLARE_DNS_API_TOKEN_FILE=/run/secrets/cf_token
- CLOUDFLARE_HTTP_TIMEOUT=${HTTP_TIMEOUT}
- CLOUDFLARE_POLLING_INTERVAL=${POLLING_INTERVAL}
- CLOUDFLARE_PROPAGATION_TIMEOUT=${PROPAGATION_TIMEOUT}
- CLOUDFLARE_TTL=${TTL}
deploy:
restart_policy:
condition: any
delay: 5s
max_attempts: 3
window: 120s
update_config: # Start new instance before stopping existing one
delay: 10s
order: start-first
parallelism: 1
rollback_config:
parallelism: 0
order: stop-first
placement:
constraints:
- node.role == manager
labels:
- traefik.enable=true
- traefik.http.routers.api.rule=Host(`traefik.${ROOT_DOMAIN}`)
- traefik.http.routers.api.service=api@internal
- traefik.http.routers.api.entrypoints=websecure
- traefik.http.routers.api.tls=true
- traefik.http.services.api.loadbalancer.server.port=8080
ports:
# HTTP
- target: 80
published: 80
# HTTPS
- target: 443
published: 443
# Web UI (enabled by --api.insecure=true)
- target: 8080
published: 8080
networks:
- traefik
- internal
volumes:
# So that Traefik can listen to the Docker events
- /var/run/docker.sock:/var/run/docker.sock
- acme:/etc/traefik/acme
- traefik:/config
- cloudflare:/cloudflare
traefik-forward-auth:
image: thomseddon/traefik-forward-auth:2.1.0
networks:
- traefik
environment:
- PROVIDERS_GOOGLE_CLIENT_ID=${PROVIDERS_GOOGLE_CLIENT_ID}
- PROVIDERS_GOOGLE_CLIENT_SECRET=${PROVIDERS_GOOGLE_CLIENT_SECRET}
- SECRET=${SECRET}
- AUTH_HOST=auth.${ROOT_DOMAIN}
- COOKIE_DOMAIN=${ROOT_DOMAIN}
- WHITELIST=${WHITELIST}
deploy:
labels:
- traefik.enable=true
- traefik.docker.network=traefik
- traefik.http.routers.auth.rule=Host(`auth.${ROOT_DOMAIN}`)
- traefik.http.routers.auth.entrypoints=websecure
- traefik.http.routers.auth.tls=true
- traefik.http.routers.auth.tls.domains[0].main=${ROOT_DOMAIN}
- traefik.http.routers.auth.tls.domains[0].sans=*.${ROOT_DOMAIN}
- traefik.http.routers.auth.tls.certresolver=letsencrypt
- traefik.http.routers.auth.service=auth@docker
- traefik.http.services.auth.loadbalancer.server.port=4181
- traefik.http.middlewares.forward-auth.forwardauth.address=http://traefik-forward-auth:4181
- traefik.http.middlewares.forward-auth.forwardauth.trustForwardHeader=true
- traefik.http.middlewares.forward-auth.forwardauth.authResponseHeaders=X-Forwarded-User
- traefik.http.routers.auth.middlewares=forward-auth
- traefik.constraint=proxy-public
tunnel:
container_name: cloudflared-tunnel
image: cloudflare/cloudflared
restart: unless-stopped
command: tunnel run
deploy:
mode: replicated
replicas: 3
update_config:
delay: 30s
order: start-first
monitor: 20s
networks:
- traefik
environment:
- TUNNEL_TOKEN=${TUNNEL_TOKEN}
error-pages:
image: tarampampam/error-pages:2.26.0
environment:
TEMPLATE_NAME: l7-dark
networks:
- traefik
deploy:
mode: replicated
replicas: 2
update_config:
delay: 20s
order: start-first
monitor: 10s
labels:
- traefik.enable=true
- traefik.docker.network=traefik
# use as "fallback" for any non-registered services (with priority below normal)
- traefik.http.routers.error-pages.rule=HostRegexp(`{host:.+}`)
- traefik.http.routers.error-pages.priority=10
# should say that all of your services work on https
- traefik.http.routers.error-pages.tls='true'
- traefik.http.routers.error-pages.entrypoints=websecure
- traefik.http.routers.error-pages.middlewares=error-pages
- traefik.http.services.error-pages.loadbalancer.server.port=8080
# "errors" middleware settings
- traefik.http.middlewares.error-pages.errors.status=400-599
- traefik.http.middlewares.error-pages.errors.service=error-pages
- traefik.http.middlewares.error-pages.errors.query=/{status}.html
cloudflare-companion:
image: ghcr.io/tiredofit/docker-traefik-cloudflare-companion:latest
volumes:
- /var/run/docker.sock:/var/run/docker.sock
deploy:
placement:
constraints:
- node.role == manager
environment:
- TIMEZONE=Europe/London
- LOG_TYPE=CONSOLE
- LOG_LEVEL=INFO
- TRAEFIK_VERSION=2
- RC_TYPE=CNAME
- TARGET_DOMAIN=${ROOT_DOMAIN}
- REFRESH_ENTRIES=TRUE
- DOCKER_SWARM_MODE=TRUE
- ENABLE_TRAEFIK_POLL=TRUE
- TRAEFIK_POLL_URL=https://traefik.${ROOT_DOMAIN}/api
- TRAEFIK_FILTER_LABEL=traefik.constraint
- TRAEFIK_FILTER=proxy-public
- DOMAIN1=${ROOT_DOMAIN}
- DOMAIN1_ZONE_ID=${ZONE_ID}
- DOMAIN1_PROXIED=TRUE
restart: always
networks:
- internal
secrets:
- cf_token
networks:
traefik:
external: true
internal:
volumes:
acme:
traefik:
cloudflare:
secrets:
cf_token:
external: true
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment