wodby / docker4drupal

Docker-based Drupal stack
https://wodby.com/docker4drupal
MIT License
1.24k stars 532 forks source link

Traefik v2 https support #407

Open 2dareis2do opened 4 years ago

2dareis2do commented 4 years ago

I am am trying to up the latest version of docker4drupal to work with Traefik v2. I had it working previously with v1.x of Traefik but am struggling to transition to configure v2.

I have tried to follow the lets encrypt guidelines on traefik website but there seems to be some issue with generating the certificate on a dev environment.

Any guidelines on how to achieve this would be welcome.

MenkaHosting commented 4 years ago

https://blog.containo.us/traefik-2-0-docker-101-fc2893944b9d

dhaley commented 4 years ago

Hi, if anyone is interested, I got Traefik 2.0 working with TLS with support for multiple projects.

Here is my setup:

docker-compose.yml

nginx:
  image: wodby/nginx:$NGINX_TAG
  container_name: "${PROJECT_NAME}_nginx"
  depends_on:
    - php
  environment:
    NGINX_STATIC_OPEN_FILE_CACHE: "off"
    NGINX_ERROR_LOG_LEVEL: debug
    NGINX_BACKEND_HOST: php
    NGINX_SERVER_ROOT: /var/www/html/web
    NGINX_VHOST_PRESET: $NGINX_VHOST_PRESET
  volumes:
    - ./:/var/www/html
    - docker-sync-unison:/var/www/common # Docker-sync for macOS users
  labels:
      - "traefik.http.routers.${PROJECT_NAME}_nginx.entrypoints=web"
      - "traefik.http.middlewares.${PROJECT_NAME}_https_nginx.redirectscheme.scheme=https"
      - "traefik.http.routers.${PROJECT_NAME}_https_nginx.rule=Host(`${PROJECT_NAME}.docker.localhost`)"
      - "traefik.http.routers.${PROJECT_NAME}_https_nginx.entrypoints=web-secure"
      - "traefik.http.routers.${PROJECT_NAME}_https_nginx.tls=true"

traefik.yml

Place it separately from your projects and start with docker-compose -f traefik.yml up

version: '3'

services:
  traefik:
    image: traefik:v2.0
    command:
      - "--api.insecure=true"
      - "--providers.docker"
      - "--log.level=DEBUG"
      - "--configFile=./myconfigfile.toml"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.web-secure.address=:443"
    networks:
      - "xxxx"
    ports:
      - '80:80'
      - '8080:8080'
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./certs/:/certs/
    container_name: traefik

networks:
  xxxx:
    external:
      name: xxxx_default

myconfigfile.toml

# dynamic configuration
[http.routers]
  [http.routers.Router-1]
    rule = "Host(`docker.localhost`)"
    service = "service-id"
    # will terminate the TLS request
    [http.routers.Router-1.tls]
      options = "myTLSOptions"

[[tls.certificates]]
    certFile = "/certs/cert.pem"
    keyFile = "/certs/key.pem"

[tls.options]
  [tls.options.default]
    minVersion = "VersionTLS12"

  [tls.options.myTLSOptions]
    minVersion = "VersionTLS13"
    cipherSuites = [
        "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
        "TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384",
        "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305",
        "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305",
        "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256",
        "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256",
        ]
dmitrymenshikov commented 4 years ago

To enable https you need to do only 2 steps

  1. Enable automatic cetrifs generation
services:
  traefik:
    image: "traefik:v2.0.0"
    command:
      # add these lines
      - --entrypoints.websecure.address=:443
      - --certificatesresolvers.le.acme.email=my@email.com
      - --certificatesresolvers.le.acme.storage=/acme.json
      - --certificatesresolvers.le.acme.tlschallenge=true
    ports:
      # add 443 port
      - "443:443"

my@email.com - change on yours email address

  1. Enable automatic certifs generation for container (e.g. nginx)

    nginx:
    image: wodby/nginx:$NGINX_TAG
    container_name: "${PROJECT_NAME}_nginx"
    depends_on:
      - php
    environment:
      NGINX_STATIC_OPEN_FILE_CACHE: "off"
      NGINX_ERROR_LOG_LEVEL: debug
      NGINX_BACKEND_HOST: php
      NGINX_SERVER_ROOT: /var/www/html/web
      NGINX_VHOST_PRESET: $NGINX_VHOST_PRESET
    volumes:
      - ./:/var/www/html
    labels:
      - "traefik.http.routers.${PROJECT_NAME}_nginx.rule=Host(`${PROJECT_BASE_URL}`)"
     # add these lines
      - "traefik.http.routers.${PROJECT_NAME}_nginx.tls.certresolver=le"
      - "traefik.http.routers.${PROJECT_NAME}_nginx.entrypoints=websecure"

Complete.

jcisio commented 4 years ago

Could you explain it? I use it for local development so no LE ACME for me. I use mkcert to generate certificates and Traefik works with both http and https. However it shows the website in http, and the "404 page not found" error in https. How to setup to redirect https traffic to the other containers (Apache, phpMyAdmin etc.) on port 80 so that Traefik does the TLS termination here?

divined commented 4 years ago

docker-compose.yml

  nginx:
    image: wodby/nginx:$NGINX_TAG
    container_name: "${PROJECT_NAME}_nginx"
    depends_on:
      - php
    environment:
      NGINX_STATIC_OPEN_FILE_CACHE: "off"
      NGINX_CLIENT_MAX_BODY_SIZE: 10G
      NGINX_ERROR_LOG_LEVEL: debug
      NGINX_BACKEND_HOST: php
      NGINX_VHOST_PRESET: php
      NGINX_SERVER_ROOT: /var/www/html/public
      NGINX_STATIC_EXT_REGEX: css|cur|js|htc|ico|xml|otf|ttf|eot|woff|woff2|svg|mp4|svgz|ogg|ogv|pdf|pptx?|zip|tgz|gz|rar|bz2|doc|xls|exe|tar|mid|midi|wav|bmp|rtf|txt|map
    volumes:
      - ./:/var/www/html
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.${PROJECT_NAME}_nginx.entrypoints=http"
      - "traefik.http.routers.${PROJECT_NAME}_nginx.rule=Host(`${PROJECT_NAME}.localhost`)"
      - "traefik.http.middlewares.${PROJECT_NAME}_nginx-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.${PROJECT_NAME}_nginx.middlewares=${PROJECT_NAME}_nginx-https-redirect"
      - "traefik.http.routers.${PROJECT_NAME}_nginx-secure.entrypoints=https"
      - "traefik.http.routers.${PROJECT_NAME}_nginx-secure.rule=Host(`${PROJECT_NAME}.localhost`)"
      - "traefik.http.routers.${PROJECT_NAME}_nginx-secure.tls=true"
      - "traefik.http.routers.${PROJECT_NAME}_nginx-secure.service=${PROJECT_NAME}_nginx"
      - "traefik.http.services.${PROJECT_NAME}_nginx.loadbalancer.server.port=80"

traefik:
    image: traefik
    container_name: traefik
    security_opt:
      - no-new-privileges:true
    ports:
      - 80:80
      - 443:443
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./traefik.yml:/traefik.yml:ro
      - ./traefik.dynamic.yml:/traefik.dynamic.yml:ro
      - ./certs:/certs
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.entrypoints=http"
      - "traefik.http.routers.traefik.rule=Host(`traefik.localhost`)"
      - "traefik.http.middlewares.traefik-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.traefik.middlewares=traefik-https-redirect"
      - "traefik.http.routers.traefik-secure.entrypoints=https"
      - "traefik.http.routers.traefik-secure.rule=Host(`traefik.localhost`)"
      - "traefik.http.routers.traefik-secure.tls=true"
      - "traefik.http.routers.traefik-secure.service=api@internal"
      - "traefik.http.services.traefik.loadbalancer.server.port=8080"

traefik.yml

api:
  dashboard: true

log:
  level: DEBUG
  filePath: "debug.log"
  format: json

accessLog:
  filePath: "access.log"
  bufferingSize: 100

entryPoints:
  http:
    address: ":80"
  https:
    address: ":443"

providers:
  file:
    filename: /traefik.dynamic.yml
    watch: true
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false

traefik.dynamic.yml

tls:
  stores:
    default:
      defaultCertificate:
        certFile: /certs/localhost+5.pem
        keyFile: /certs/localhost+5-key.pem
  certificates:
    - certFile: /certs/localhost+5.pem
      keyFile: /certs/localhost+5-key.pem

certificates are generated by mkcert for ${PROJECT_NAME}.localhost, varnish.${PROJECT_NAME}.localhost traefik.localhost and localhost.

Need to replace ${PROJECT_NAME} to your project name.

kevinquillen commented 4 years ago

Can folks actually explain the examples beyond just pasting code above? The setup for v1 was pretty simple but v2 was not working at all.

However it shows the website in http, and the "404 page not found" error in https.

This is exactly my experience. I was looking here, which a handful of places on the net keep referencing: https://containo.us/blog/traefik-2-0-docker-101-fc2893944b9d/

I found that article confusing, the blog post links to multiple files in git and each one is slightly different.

To get HTTP -> HTTPS redirection working, I finally stumbled upon this post. Adapting that, I was finally able to get a self signed certificate and https redirection working.

Here is what I have:

  apache:
    image: wodby/apache:$APACHE_TAG
    container_name: "${PROJECT_NAME}_apache"
    environment:
      APACHE_LOG_LEVEL: debug
      APACHE_BACKEND_HOST: php
      APACHE_VHOST_PRESET: php
      APACHE_DOCUMENT_ROOT: /var/www/html/docroot
    volumes:
      - ./:/var/www/html:cached # User-guided caching
    labels:
      - traefik.http.middlewares.${PROJECT_NAME}_apache_https.redirectscheme.scheme=https
      - traefik.http.routers.${PROJECT_NAME}_apache.entrypoints=web
      - traefik.http.routers.${PROJECT_NAME}_apache.rule=Host(`${PROJECT_BASE_URL}`)
      - traefik.http.routers.${PROJECT_NAME}_apache.middlewares=${PROJECT_NAME}_apache_https@docker
      - traefik.http.routers.${PROJECT_NAME}_apache_https.rule=Host(`${PROJECT_BASE_URL}`)
      - traefik.http.routers.${PROJECT_NAME}_apache_https.tls=true
      - traefik.http.routers.${PROJECT_NAME}_apache_https.entrypoints=websecure

  traefik:
    image: traefik:v2.0
    container_name: "${PROJECT_NAME}_traefik"
    command:
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --providers.docker
      - --providers.file.directory=/etc/traefik/dynamic_conf
    ports:
      - '80:80'
      - '443:443'
    volumes:
      - ./tools/certs:/tools/certs
      - ./tools/traefik/config.yml:/etc/traefik/dynamic_conf/conf.yml:ro
      - /var/run/docker.sock:/var/run/docker.sock

traefik config.yml:

tls:
  certificates:
    - certFile: /tools/certs/cert.crt
      keyFile: /tools/certs/cert.key

Note that the labels in each service are very particular about format, and double check you have no spelling errors. The apache labels are important, as it describes the entrypoints and what to do with them.

This was a lot harder to figure out than I was expecting, and depending on where you look on the internet there are various write ups on what to do. Nothing worked except for the blog post above. The contanious one is confusing and was of no help whatsoever.

I am using a self signed certificate (openssl, but mkcert is probably very similar) so we can get local https with browser trust supported - my containers can't be seen by Lets Encrypt since these are for local development only, so no certificate is ever sent back.

This answer was helpful: https://stackoverflow.com/a/60072381/295112

IMO it would be super helpful for docker4drupal to come with HTTPS enabled and redirecting by default since that is how all production applications are running these days anyway. It shouldn't take hours to figure out how to make it happen (blaming traefik not d4d maintainers) and it seems we are not the only ones.

mradcliffe commented 4 years ago

Unfortunately I haven't had luck with getting the http to https redirect to work.

I keep getting field not found redirectscheme and when commenting out the first line, I also then get middleware \"docker4drupal_apache_https@docker\" does not exist".

Otherwise the configuration worked for https and http.

mradcliffe commented 4 years ago

I think the issue for me ended up being - traefik.http.routers.${PROJECT_NAME}_apache.middlewares=${PROJECT_NAME}_apache_https@docker, and removing @docker from the end. Then Kevin's configuration worked for me.

Mykola-Veryha commented 4 years ago

It helped me. There are enough instructions to understand how to configure that

Note: remember that if you make a mistake in the labels with route name you will get a 404 error in the browser without any logs for traefik.

https://docs.traefik.io/user-guides/docker-compose/acme-tls/

   nginx:
    image: wodby/nginx:$NGINX_TAG
    container_name: "${PROJECT_NAME}_nginx"
    depends_on:
      - php
    environment:
      NGINX_STATIC_OPEN_FILE_CACHE: "off"
      NGINX_ERROR_LOG_LEVEL: debug
      NGINX_BACKEND_HOST: php
      NGINX_SERVER_ROOT: /var/www/html/docroot
      NGINX_VHOST_PRESET: $NGINX_VHOST_PRESET
    #      NGINX_DRUPAL_FILE_PROXY_URL: http://example.com
    volumes:
      - ./www:/var/www/html
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.${PROJECT_NAME}_nginx.rule=Host(`${PROJECT_BASE_URL}`)"
      - "traefik.http.routers.${PROJECT_NAME}_nginx.entrypoints=websecure"
      - "traefik.http.routers.${PROJECT_NAME}_nginx.tls.certresolver=myresolver"
  traefik:
    image: traefik
    container_name: "traefik"
    command:
      #- "--log.level=DEBUG"
      - "--api.insecure=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
      #- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
      - "--certificatesresolvers.myresolver.acme.email=your_email@gmail.com"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    ports:
      - "443:443"
      - "8080:8080"
    volumes:
      - "./letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
MrUpsidown commented 3 years ago

It helped me. There are enough instructions to understand how to configure that

https://docs.traefik.io/user-guides/docker-compose/acme-tls/

That page mentions:

For the TLS challenge you will need:

Are we not talking about local dev environments here?? So this is unrelated + again, everyone, please stop just dumping code here without any further explanation.

MrUpsidown commented 3 years ago

On an older Traefik version on a LOCAL development environment, on macOS, I got HTTPS working the following way:

docker-compose.yml:

  traefik:
    image: traefik:v1.7.16-alpine
    container_name: "${PROJECT_NAME}_traefik"
    command: -c /dev/null --web --docker --logLevel=INFO --defaultEntryPoints='https' --entryPoints="Name:https Address::443 TLS:/certs/mysite_cert.pem,/certs/mysite_cert-key.pem" --entryPoints="Name:http Address::80 Redirect.EntryPoint:https"
    ports:
      - '8000:80'
      - '443:443'
    volumes:
      - ./certs:/certs/
      - /var/run/docker.sock:/var/run/docker.sock

mysite_cert.pem and mysite_cert-key.pem needs to be adjusted to your own certificates naming.

I generated self-signed certificates using mkcert, see: https://github.com/FiloSottile/mkcert/blob/master/README.md

I placed the certificates in a certs folder, at the project root (where docker-compose.yml file is). There is no redirection. Only direct access on HTTPS, but it works and is sufficient for my needs.

Note that I was still getting errors with the above settings until I restarted Docker desktop then all went fine.

SegaWeb commented 3 years ago

Hello everyone

Can anyone drop a complete example of a working configuration for traefik 2? I tried everything, and I still can't configure it.

The maximum that was achieved is that an empty acme.json file appears and, accordingly, the certificate is not valid in the browser

mradcliffe commented 3 years ago

I can't post a full example because there's a lot of information that's not relevant in the YAML file, but I can post the relevant sections for my apache, mailhog, adminer, and traefik service definitions.

  1. Generate a self-signed certificate with openssl in a certs/ directory (default.crt, default.key) within docker4drupal directory.
  2. Create the following traefik.yml file in the docker4drupal directory.
    tls:
    certificates:
    - certFile: /certs/default.crt
      keyFile: /certs/default.key
  3. Configure the traefik service in docker-compose.yml
    traefik:
    image: traefik:v2.1
    container_name: "${PROJECT_NAME}_traefik"
    command:
      - --entrypoints.db.address=:5432
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      - --api.insecure=true
      - --providers.docker
      - --providers.file.directory=/etc/traefik/dynamic_conf
    ports:
      - '80:80'
      - '443:443'
      - '8080:8080' # Dashboard
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./traefik.yml:/etc/traefik/dynamic_conf/conf.yml:ro
      - ./certs:/certs
  4. Add the labels to relevant services in docker-compose.yml

    postgresql:
    labels:
      - "traefik.tcp.services.postgresql.loadbalancer.server.port=5432"
      - "traefik.tcp.routers.${PROJECT_NAME}_db.entrypoints=db"
      - "traefik.tcp.routers.${PROJECT_NAME}_db.rule=HostSNI(`${PROJECT_BASE_URL}`)"
      - "traefik.tcp.routers.${PROJECT_NAME}_db.service=postgresql"
    
    apache:
    labels:
      - "traefik.http.middlewares.${PROJECT_NAME}_apache_https.redirectscheme.scheme=https"
      - "traefik.http.routers.${PROJECT_NAME}_apache.entrypoints=web"
      - "traefik.http.routers.${PROJECT_NAME}_apache.middlewares=${PROJECT_NAME}_apache_https"
      - "traefik.http.routers.${PROJECT_NAME}_apache.rule=Host(`${PROJECT_BASE_URL}`)"
      - "traefik.http.routers.${PROJECT_NAME}_apache_https.rule=Host(`${PROJECT_BASE_URL}`)"
      - "traefik.http.routers.${PROJECT_NAME}_apache_https.tls=true"
      - "traefik.http.routers.${PROJECT_NAME}_apache_https.entrypoints=websecure"
    
    adminer:
    labels:
      - "traefik.http.routers.${PROJECT_NAME}_adminer.rule=Host(`admin.${PROJECT_BASE_URL}`)"
    
    mailhog:
    labels:
      - "traefik.http.services.${PROJECT_NAME}_mailhog.loadbalancer.server.port=8025"
      - "traefik.http.routers.${PROJECT_NAME}_mailhog.rule=Host(`mail.${PROJECT_BASE_URL}`)"
ghost commented 3 years ago

T̶h̶i̶s̶ ̶p̶o̶s̶t̶ ̶m̶u̶s̶t̶ ̶b̶e̶ ̶p̶i̶n̶n̶e̶d̶ ̶s̶o̶m̶e̶h̶o̶w̶.̶ All answers in this thread are trash (read: haven't worked for me). This yaml from this article actually works! Pay attention to the comments in the end of file.

In case of Docker4Drupal with nginx to enable https the code will be this one below:

  nginx:
    image: wodby/nginx:$NGINX_TAG
    container_name: "${PROJECT_NAME}_nginx"
    depends_on:
    - php
    environment:
      NGINX_STATIC_OPEN_FILE_CACHE: "off"
      NGINX_ERROR_LOG_LEVEL: debug
      NGINX_BACKEND_HOST: php
      NGINX_SERVER_ROOT: /var/www/html/web
      NGINX_VHOST_PRESET: $NGINX_VHOST_PRESET
    volumes:
    - ./:/var/www/html:cached

    labels:
    - "traefik.http.routers.${PROJECT_NAME}_nginx.rule=Host(`${PROJECT_BASE_URL}`)"
    - "traefik.http.routers.${PROJECT_NAME}_nginx.middlewares=auth"
    - "traefik.http.routers.${PROJECT_NAME}_nginx.entrypoints=websecure"
    - "traefik.http.routers.${PROJECT_NAME}_nginx.tls.certresolver=leresolver"
    - "traefik.http.middlewares.auth.basicauth.users=user:$$apr1$$q8eZFHjF$$Fvmkk//V6Btlaf2i/ju5n/" # user/password

  traefik:
    image: traefik:v2.0
    container_name: "${PROJECT_NAME}_traefik"
    command: 
      - --api.insecure=true
      - --providers.docker
      - --entrypoints.web.address=:80
      - --entrypoints.websecure.address=:443
      # ...
      - --certificatesresolvers.leresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory
      - --certificatesresolvers.leresolver.acme.email=your@email.com
      - --certificatesresolvers.leresolver.acme.storage=/acme.json
      - --certificatesresolvers.leresolver.acme.tlschallenge=true
    ports:
      - '80:80'
      - '443:443'
      - '8080:8080' # Dashboard
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./certs:/certs/
      - "./acme.json:/acme.json"

It is for dev environment and also activates Traefik Dashboard.

artemvd commented 2 years ago

for me solution from @dmitrymenshikov worked locally. I don't need automatic redirect. No other things are required. Tested on Drupal 9

2dareis2do commented 1 year ago

When using cert resolvers with '.internal' domain I seemed to have an issue with the cert resolvers. @kevinquillen answer worked for me, albeit switching from nginx to apache.

Not sure it there is any update on this as this is quite an old thread now but had issues adding a self signed cert when updating from 3 to 3.7 or specifically to traefik v2?