http server with docker autodiscovery
Create a docker-compose.yml with this content
version: "3.3" services: traefik: image: "traefik:v2.0.0" command: - --entrypoints.web.address=:80 - --providers.docker=true ports: - "80:80" volumes: - "/var/run/docker.sock:/var/run/docker.sock:ro" myapp: image: containous/whoami:v1.3.0 labels: - traefik.http.routers.myapp.rule=Host(`test.raphaelpiccolo.com`) ports: - "8989:80"
Then
docker-compose up -d curl http://test.raphaelpiccolo.com curl http://test.raphaelpiccolo.com:8989
Both url work, to block second url, bind to localhost port :
- "127.0.0.1:8989:80"
It's also possible to remove ports specification completely if the image exposes the port, because traefik will find it.
http server with docker autodiscovery
automatically redirect http to https if using websecure entrypoint
automatically generate https when needed
This file will store https certificates : /traefik/acme.json
wait 10 secs for letsencrypt to generate certificates. In the meantime the site is working but there is a certificate alert.
traefik api is now available https://traefik.raphaelpiccolo.com
Create a docker-compose.yml
version: "3.3" services: traefik: image: "traefik:v2.0.0" command: # listen port 443 and name it websecure - --entrypoints.websecure.address=:443 # listen port 80 and name it web - --entrypoints.web.address=:80 # you need to enable traefik on each container - --providers.docker.exposedByDefault=false # listen for docker changes - --providers.docker=true # activate traefik api - --api # activate traefik ping service - --ping - --ping.manualrouting=true - --ping.entryPoint=websecure # use letencrypt to generate certificates - --certificatesresolvers.le.acme.email=rafi.piccolo@gmail.com - --certificatesresolvers.le.acme.storage=/traefik/acme.json - --certificatesresolvers.le.acme.tlschallenge=true # set logging level - --log.level=INFO # activate accesslog in a separate file - --accesslog=true - --accesslog.filepath=/traefik/access.log # activate metrics exporting to influxdb - --metrics.influxdb=true - --metrics.influxdb.protocol=http - --metrics.influxdb.address=https://influxdb.raphaelpiccolo.com - --metrics.influxdb.database=mydb - --metrics.influxdb.username=admin - --metrics.influxdb.password=${PASSWORD} # config file for more settings : tls ciphers for grade A on ssllabs - --providers.file.filename=/traefik/traefik.toml - --providers.file.watch=true # add a custom uniqid on every request - --tracing.jaeger=true - --tracing.jaeger.samplingParam=0 - --tracing.jaeger.traceContextHeaderName=X-Request-ID labels: - traefik.enable=true # catch all traefik errors and send them to the errorpage service - "traefik.http.routers.globalerrorpage.rule=hostregexp(`{host:.+}`)" - "traefik.http.routers.globalerrorpage.tls.certresolver=le" - "traefik.http.routers.globalerrorpage.entrypoints=websecure" - "traefik.http.routers.globalerrorpage.service=errorpage" - "traefik.http.routers.globalerrorpage.priority=1" # create a middleware to replace 404 errors by a standard page - "traefik.http.middlewares.errorpage.errors.status=404" - "traefik.http.middlewares.errorpage.errors.service=errorpage" - "traefik.http.middlewares.errorpage.errors.query=/{status}" # create a middleware named redirect-to-https, to redirect http to https - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https" - "traefik.http.middlewares.redirect-to-https.redirectscheme.permanent=true" # create a middleware to redirect to www - traefik.http.middlewares.redirect-to-www.redirectregex.regex=(https|http)://(?:www.)?(.*) - traefik.http.middlewares.redirect-to-www.redirectregex.replacement=https://www.$${2} # create a middleware to redirect to non-www - traefik.http.middlewares.redirect-to-nonwww.redirectregex.regex=(https|http)://(www\.(.*)) - traefik.http.middlewares.redirect-to-nonwww.redirectregex.replacement=https://$${3} # apply middleware "redirect-to-https", to all hosts connected to "web" entrypoint - "traefik.http.routers.redirs.rule=hostregexp(`{host:.+}`)" - "traefik.http.routers.redirs.entrypoints=web" - "traefik.http.routers.redirs.middlewares=redirect-to-https" # create a middleware named auth, to request a basic authentification of users # you can generate a user:password pair with this command : # echo $(htpasswd -nb user password) | sed -e s/\\$/\\$\\$/g - traefik.http.middlewares.auth.basicauth.users=user:$$apr1$$WH5iVjXW$$bda85jX7RQGGcCs3hnP8b0 # create a middleware named admin, to request a basic authentification for admin - traefik.http.middlewares.admin.basicauth.users=admin:$$apr1$$SIIXcmeY$$GAkMymzdfhwACgQvM9PNy1 # create a middleware named securityheaders to automatically set security headers to pass : https://securityheaders.com/ - "traefik.http.middlewares.securityheaders.headers.framedeny=true" - "traefik.http.middlewares.securityheaders.headers.customFrameOptionsValue=sameorigin" - "traefik.http.middlewares.securityheaders.headers.referrerPolicy=same-origin" - "traefik.http.middlewares.securityheaders.headers.BrowserXssFilter=true" - "traefik.http.middlewares.securityheaders.headers.ContentTypeNosniff=true" - "traefik.http.middlewares.securityheaders.headers.ForceSTSHeader=true" - "traefik.http.middlewares.securityheaders.headers.SSLRedirect=true" - "traefik.http.middlewares.securityheaders.headers.STSIncludeSubdomains=true" - "traefik.http.middlewares.securityheaders.headers.STSPreload=true" - "traefik.http.middlewares.securityheaders.headers.STSSeconds=315360000" # api secure - "traefik.http.routers.traefik.rule=Host(`traefik.raphaelpiccolo.com`)" - "traefik.http.routers.traefik.service=api@internal" - "traefik.http.routers.traefik.middlewares=securityheaders,admin" - "traefik.http.routers.traefik.tls.certresolver=le" - "traefik.http.routers.traefik.entrypoints=websecure" # ping secure - "traefik.http.routers.ping.rule=Host(`traefik.${DOMAIN}`) && PathPrefix(`/ping`)" - "traefik.http.routers.ping.service=ping@internal" - "traefik.http.routers.ping.middlewares=securityheaders" - "traefik.http.routers.ping.tls.certresolver=le" - "traefik.http.routers.ping.entrypoints=websecure" ports: # web entrypoint - "80:80" # websecure entrypoint - "443:443" volumes: # to listen docker changes - "/var/run/docker.sock:/var/run/docker.sock:ro" # to save https certificates - "./traefik:/traefik" myapp: image: containous/whoami:v1.3.0 labels: # domain name for this container : you can specify multiple domains - traefik.http.routers.myapp.rule=Host(`raphaelpiccolo.com`, `www.raphaelpiccolo.com`) # set a basic auth on a container + securityheaders - traefik.http.routers.myapp.middlewares=auth,securityheaders,redirect-to-www # use https - traefik.http.routers.myapp.tls.certresolver=le - traefik.http.routers.myapp.entrypoints=websecure ports: - "127.0.0.1:9090:80" # proxy to host (172.17.0.1) port (19999) # get docker host ip (usable from inside a container) : ip addr show docker0 # displays : 172.17.0.1 netdata: image: alpine/socat container_name: netdata restart: always command: tcp-listen:80,fork,reuseaddr tcp-connect:172.17.0.1:19999 labels: - traefik.http.routers.netdata.rule=Host(`netdata.flatbay.fr`) # when the container has many ports you can indicate to traefik which one he should connect to the domain - traefik.http.services.netdata.loadbalancer.server.port=80 # if you want to specify tls options (see traefik.toml) - traefik.http.routers.netdata.tls.options=notsafe@file - traefik.http.routers.netdata.tls.certresolver=le - traefik.http.routers.netdata.entrypoints=websecure - traefik.http.routers.netdata.middlewares=securityheaders,admin portainer: image: portainer/portainer container_name: portainer restart: always ports: - "127.0.0.1:19000:9000" # no auth because we add the admin basic auth with traefik command: --no-auth volumes: - /var/run/docker.sock:/var/run/docker.sock - ./portainer:/data labels: - traefik.http.routers.portainer.rule=Host(`portainer.flatbay.fr`) - traefik.http.routers.portainer.tls.certresolver=le - traefik.http.routers.portainer.entrypoints=websecure - traefik.http.routers.portainer.middlewares=securityheaders,admin
create traefik.toml
only usefull if grade A on ssllabs
[tls.options] [tls.options.default] minVersion = "VersionTLS12" cipherSuites = [ "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256", "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384", "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305", "TLS_AES_128_GCM_SHA256", "TLS_AES_256_GCM_SHA384", "TLS_CHACHA20_POLY1305_SHA256" ] sniStrict = true [tls.options.notsafe]
in traefik.toml
[http] [http.routers] [http.routers.netdata] rule = "Host(`netdata.flatbay.fr`)" service = "netdata" [http.services] [http.services.netdata.loadBalancer] [[http.services.netdata.loadBalancer.servers]] url = "http://172.17.0.1:19999/"
create a new service
errorpage: build: ./errorpage restart: always container_name: errorpage volumes: - ./errorpage:/usr/app/ working_dir: /usr/app command: forever -w server.js labels: - traefik.enable=true - traefik.http.services.errorpage.loadbalancer.server.port=3000 - traefik.http.routers.errorpage.rule=Host(`error.raphaelpiccolo.com`) - traefik.http.routers.errorpage.tls.certresolver=le - traefik.http.routers.errorpage.entrypoints=websecure - traefik.http.routers.errorpage.middlewares=securityheaders healthcheck: test: ['CMD', 'curl', '-f', 'http://localhost:3000/health']
add this in a service in which you want to replace the 404 page with the one from the errorpage service
- traefik.http.routers.d2cpartners.middlewares=securityheaders,redirect-to-nonwww,errorpage
add this in command
--tracing.jaeger=true --tracing.jaeger.samplingParam=0 --tracing.jaeger.traceContextHeaderName=X-Request-ID
in this exemple we relay the mysql connection through traefik in plain unencrypted tcp/IP.
ps: we need one entrypoint for every tcp connection because we can't switch services by hostname like we do on http (unless we use tls)
traefik: ... command: - --entrypoints.mysql.address=:3306 ports: - "3306:3306" mysql: ... labels: - "traefik.enable=true" - "traefik.tcp.routers.mysql.rule=HostSNI(`*`)" - "traefik.tcp.services.mysql.loadbalancer.server.port=3306" - "traefik.tcp.routers.mysql.entrypoints=mysql"
ps: tls doesnt work because of a bug in mysql, but this is how it would work :
add this label on mysql
- "traefik.tcp.routers.mysql.tls=true"
or this one to let mysql deal with tls itself
- "traefik.tcp.routers.mysql.tls.passthrough=true"
when you make on request on traefik, it will add a header when it will call your service.
X-Forwarded-For, with the ip of the client initiating connection.
for exemple:
X-Forwarded-For: X.X.X.X
if you add this in commands :
- --entryPoints.web.forwardedHeaders.insecure - --entryPoints.websecure.forwardedHeaders.insecure
and you set a custom X-forwarded-For before calling traefik, then X-forwarded-For will be the ip you specified, plus the real ip :
$> curl -H 'X-Forwarded-For: 1.1.1.1' https://xxx.com/ X-Forwarded-For: 1.1.1.1, X.X.X.X
you could also allow trust only your ip : only your ip (X.X.X.X) can set a X-Forwarded-For header.
--entryPoints.web.forwardedHeaders.trustedIPs=127.0.0.1/32,X.X.X.X
full exemple :
$> curl -H 'X-Forwarded-For: 1.1.1.1' https://xxx.com/ Hostname: 5c1b8960e103 IP: 127.0.0.1 IP: 172.21.0.46 RemoteAddr: 172.21.0.43:60138 GET / HTTP/1.1 Host: xxx.com User-Agent: curl/7.70.0 Accept: */* Accept-Encoding: gzip X-Forwarded-For: 1.1.1.1, X.X.X.X X-Forwarded-Host: xxx.com X-Forwarded-Port: 443 X-Forwarded-Proto: https X-Forwarded-Server: 00625e8b8443 X-Real-Ip: X.X.X.X X-Request-Id: 276a624864bd6164:56bfa7b03c583401:4f64762abc3cc711:0
add this
command: - --ping - --entrypoints.traefik.address=:8080 ports: - "8080:8080"
call this to check
curl http://X.X.X.X:8080/ping
add this
command: - --ping - --ping.manualrouting=true - --ping.entryPoint=websecure labels: - "traefik.http.routers.ping.rule=Host(`traefik.${DOMAIN}`) && PathPrefix(`/ping`)" - "traefik.http.routers.ping.service=ping@internal" - "traefik.http.routers.ping.middlewares=securityheaders" - "traefik.http.routers.ping.tls.certresolver=le" - "traefik.http.routers.ping.entrypoints=websecure"
call this to check
curl https://traefik.raphaelpiccolo.com/ping