Docker-compose with let's encrypt: DNS Challenge

    Please also read the basic example for details on how to expose such a service.

    For the DNS challenge, you'll need:

    • A working along with the credentials allowing to create and remove DNS records.

    Variables may vary depending on the Provider.

    Please note this guide may vary depending on the provider you use. The only things changing are the names of the variables you will need to define in order to configure your provider so it can create DNS records.

    Please refer the list of providers given right above and replace all the environment variables with the ones described in this documentation.

    • Create a file with the following content:
    • Replace the environment variables by your own:
    1. environment:
    2. - "OVH_ENDPOINT=[YOUR_OWN_VALUE]"
    3. - "OVH_APPLICATION_KEY=[YOUR_OWN_VALUE]"
    4. - "OVH_APPLICATION_SECRET=[YOUR_OWN_VALUE]"
    5. - "OVH_CONSUMER_KEY=[YOUR_OWN_VALUE]"
    • Replace [email protected] by your own email within the certificatesresolvers.myresolver.acme.email command line argument of the traefik service.

    • Replace whoami.example.com by your own domain within the traefik.http.routers.whoami.rule label of the whoami service.

    • Optionally uncomment the following lines if you want to test/debug:
    1. #- "--log.level=DEBUG"
    2. #- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
    • Wait a bit and visit to confirm everything went fine.

    Note

    If you uncommented the acme.caserver line, you will get an SSL error, but if you display the certificate and see it was emitted by Fake LE Intermediate X1 then it means all is good. (It is the staging environment intermediate certificate used by let's encrypt). You can now safely comment the acme.caserver line, remove the letsencrypt/acme.json file and restart Traefik to issue a valid certificate.

    What changed between the initial setup:

    • We configure a second entry point for the https traffic:
    1. command:
    2. # Traefik will listen to incoming request on the port 443 (https)
    3. - "--entrypoints.websecure.address=:443"
    4. ports:
    5. - "443:443"
    • We configure the DNS let's encrypt challenge:
    • We provide the required configuration to our provider via environment variables:
    1. environment:
    2. - "OVH_ENDPOINT=xxx"
    3. - "OVH_APPLICATION_KEY=xxx"
    4. - "OVH_APPLICATION_SECRET=xxx"
    5. - "OVH_CONSUMER_KEY=xxx"

    Note

    This is the step that may vary depending on the provider you use. Just define the variables required by your provider. (see the prerequisite for a list)

    • We add a volume to store our certificates:
    1. volumes:
    2. # Create a letsencrypt dir within the folder where the docker-compose file is
    3. - "./letsencrypt:/letsencrypt"
    4. command:
    5. # Tell to store the certificate on a path under our volume
    6. - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    • We configure the whoami service to tell Traefik to use the certificate resolver named myresolver we just configured:
    1. labels:

    To configure the provider, and avoid having the secrets exposed in plaintext within the docker-compose environment section, you could use docker secrets.

    • Create a directory named secrets, and create a file for each parameters required to configure you provider containing the value of the parameter:

      for example, the ovh_endpoint.secret file contain

    Note

    You could store those secrets anywhere on the server, just make sure to use the proper path for the file directive for the secrets definition in the docker-compose.yml file.

    • Use this docker-compose.yml file:
    1. version: "3.3"
    2. secrets:
    3. ovh_endpoint:
    4. file: "./secrets/ovh_endpoint.secret"
    5. ovh_application_key:
    6. file: "./secrets/ovh_application_key.secret"
    7. ovh_application_secret:
    8. file: "./secrets/ovh_application_secret.secret"
    9. ovh_consumer_key:
    10. file: "./secrets/ovh_consumer_key.secret"
    11. services:
    12. traefik:
    13. image: "traefik:v2.2"
    14. container_name: "traefik"
    15. command:
    16. #- "--log.level=DEBUG"
    17. - "--api.insecure=true"
    18. - "--providers.docker=true"
    19. - "--providers.docker.exposedbydefault=false"
    20. - "--entrypoints.web.address=:80"
    21. - "--entrypoints.websecure.address=:443"
    22. - "--certificatesresolvers.myresolver.acme.dnschallenge=true"
    23. - "--certificatesresolvers.myresolver.acme.dnschallenge.provider=ovh"
    24. #- "--certificatesresolvers.myresolver.acme.caserver=https://acme-staging-v02.api.letsencrypt.org/directory"
    25. - "[email protected].com"
    26. - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
    27. ports:
    28. - "80:80"
    29. - "443:443"
    30. - "8080:8080"
    31. secrets:
    32. - "ovh_endpoint"
    33. - "ovh_application_key"
    34. - "ovh_application_secret"
    35. - "ovh_consumer_key"
    36. environment:
    37. - "OVH_ENDPOINT_FILE=/run/secrets/ovh_endpoint"
    38. - "OVH_APPLICATION_SECRET_FILE=/run/secrets/ovh_application_secret"
    39. - "OVH_CONSUMER_KEY_FILE=/run/secrets/ovh_consumer_key"
    40. volumes:
    41. - "./letsencrypt:/letsencrypt"
    42. - "/var/run/docker.sock:/var/run/docker.sock:ro"
    43. whoami:
    44. image: "containous/whoami"
    45. container_name: "simple-service"
    46. labels:
    47. - "traefik.enable=true"
    48. - "traefik.http.routers.whoami.rule=Host(`whoami.example.com`)"
    49. - "traefik.http.routers.whoami.entrypoints=websecure"
    50. - "traefik.http.routers.whoami.tls.certresolver=myresolver"

    Note

    Still think about changing [email protected] & whoami.example.com by your own values.

    Let's explain a bit what we just did:

    • The following section allow to read files on the docker host, and expose those file under /run/secrets/[NAME_OF_THE_SECRET] within the container:
    1. secrets:
    2. # secret name also used to name the file exposed within the container
    3. ovh_endpoint:
    4. # path on the host
    5. file: "./secrets/ovh_endpoint.secret"
    6. ovh_application_key:
    7. file: "./secrets/ovh_application_key.secret"
    8. ovh_application_secret:
    9. file: "./secrets/ovh_application_secret.secret"
    10. ovh_consumer_key:
    11. file: "./secrets/ovh_consumer_key.secret"
    12. services:
    13. traefik:
    14. # expose the predefined secret to the container by name
    15. secrets:
    16. - "ovh_endpoint"
    17. - "ovh_application_key"
    18. - "ovh_application_secret"
    19. - "ovh_consumer_key"
    • The acme client will read the content of those file to get the required configuration values.

    1. environment:
    2. # expose the path to file provided by docker containing the value we want for OVH_ENDPOINT.
    3. - "OVH_ENDPOINT_FILE=/run/secrets/ovh_endpoint"
    4. - "OVH_APPLICATION_KEY_FILE=/run/secrets/ovh_application_key"
    5. - "OVH_CONSUMER_KEY_FILE=/run/secrets/ovh_consumer_key"