Traefik and using Let's Encrypt

Traefik and using Let's Encrypt

Traefik is a very interesting tool which I've come across just recently. Originally I was looking to use nginx as a reverse proxy and setup some automation in order to request a certificate from let's encrypt so I could setup my site for https.

Then I found out Traefik could do exactly what I want. This website is in fact being handled with Traefik right now and will request a new cert when it's close to the expiry, but also act as a reverse proxy with very little setup.

So with my setup, I'm using docker containers for my sites. So this will be from that perspective.

To start, I created a docker network which can be used either with single instances or in a swarm and attach containers. This is the command to do that.

docker network create -d overlay --attachable web

After the network is created, let's create a username and password so we can access traefik later on.

htpasswd -nb username securepassword

Be sure to keep the output for now as we will need that for later.

Now let's create a folder to make sure we don't get confused with all this. So create a folder in a path you have permissions and want to continue with this and call it traefik.

mkdir traefik
cd traefik

Create two files and we also need to change the permissions of one to make sure we can write the cert information later on when we run traefik.

touch acme.json traefik.toml
chmod 600 acme.json

Edit traefik.toml and add the following, but replace the email with your domain email and the user being setup with the secure password you created earlier.

defaultEntryPoints = ["http", "https"]

[entryPoints]
  [entryPoints.dashboard]
    address = ":8080"
    [entryPoints.dashboard.auth]
      [entryPoints.dashboard.auth.basic]
        users = ["username:securepassword"]
  [entryPoints.http]
    address = ":80"
      [entryPoints.http.redirect]
        entryPoint = "https"
  [entryPoints.https]
    address = ":443"
      [entryPoints.https.tls]

[api]
entrypoint="dashboard"

[acme]
email = "[email protected]"
storage = "acme.json"
entryPoint = "https"
onHostRule = true
  [acme.httpChallenge]
  entryPoint = "http"

[docker]
watch = true

For the next part, you'll need to go to your domain provider and create a new A name value. This is so we can access traefik over a sub domain. For this example we'll use traefik.yourdomain.example. So make sure you add a new A name for the sub domain traefik.

When that's complete, let's run traefik in docker whilst you're in the traefik folder.

docker run -d \
  -v /var/run/docker.sock:/var/run/docker.sock \
  -v $PWD/traefik.toml:/traefik.toml \
  -v $PWD/acme.json:/acme.json \
  -p 80:80 \
  -p 443:443 \
  -l traefik.frontend.rule=Host:traefik.yourdomain.example \
  -l traefik.port=8080 \
  --network web \
  --name traefik \
  traefik:1.7.19-alpine

Give it a few minutes and then try accessing traefik by going to the sub domain we created earlier. It should ask you for a username and password. Enter what you put in earlier and it should load.

Next step is to setup our containers with traefik as a reverse proxy. To do this, look at the example below and we will go through it.

version: '3.1'

services:

  ghost:
    image: ghost:3.0.3-alpine
    depends_on:
      - db
    environment:
      # see https://docs.ghost.org/docs/config#section-running-ghost-with-config-env-variables
      database__client: mysql
      database__connection__host: db
      database__connection__user: root
      database__connection__password: example
      database__connection__database: ghost
    labels:
      - traefik.backend=ghost
      - traefik.frontend.rule=Host:yourdomain.example
      - traefik.docker.network=web
      - traefik.port=2368
    networks:
      - web
      - internal

  db:
    image: mysql:5.7
    volumes:
       - ghost_data:/var/lib/mysql
    environment:
      MYSQL_ROOT_PASSWORD: example
    networks:
      - internal
    labels:
      - traefik.enable=false

networks:
  web:
    external: true
  internal:
    external: false

volumes:
  ghost_data:

So what's going on here? Let me explain some of the not so obvious parts.
In the following:

labels:
      - traefik.backend=ghost
      - traefik.frontend.rule=Host:yourdomain.example
      - traefik.docker.network=web
      - traefik.port=2368
    networks:
      - web
      - internal

What we are doing here is setting a bunch of labels for traefik to interpret.
We're setting the backend to look for the service called ghost. We set the frontend to be the domain we have purchased. We set the docker network to web as traefik to monitoring that network and we set it to port 2368 as that is the port exposed by default for ghost.

We then set this container to attach to web and internal. The reason for this is everything on the web network will be on the frontend and so long as we provide a sub domain and set the same kind of labels as we did for ghost, we should be able to access it through traefik. We also attach internal which is the backend and we add any services we don't want access via the reverse proxy.

For the db we have:

networks:
      - internal
    labels:
      - traefik.enable=false

All we're doing here is setting the db to work on the internal network so it can communicate with ghost and making sure traefik doesn't monitor this container by setting traefik to false.

And the last part is adding the networks:

networks:
  web:
    external: true
  internal:
    external: false

So all we're doing here is adding the web network we created earlier so the containers can attached to the pre existing network and communicate with traefik.

If you run docker-compose on this file everything should start going. I haven't talked much about let's encrypt because when you bring up the containers, check the acme.json file and you'll most likely see the certs setup. This is because of the http challenge which was setup in the first config file. It's that easy. Now traefik will be used as a reverse proxy and request new certs close to expiry.