Share

Traefik: an easy way to secure your applications behing HTTPS

What is Traefik?

  Traefik is a very flexible tool :

    1. It can replace Nginx or Apache2 as proxy manager, especially for cloud-based infrastructures for which it has been conceived
    2. It can manage SSL certificates so that your websites are alway HTTPS-secure using automatically renewed LetEncrypt certificates.
    3. You can check traffic health for routers, services or middleware using the built-in dashboard

 

How are elements structured ? 

For our current need, Traefik will be set as the entrypoint to our private network in which we will be deploying our different ressources.

 

  Traefik will be in charge of redirecting users to the correct server but also generating SSL certificates for all declared sub-domains.

We will also be forcing redirections from HTTP to HTTPS for all available ressources.  

 

Pros and cons

 

Traefik is a very complete but also complex application so I will only evaluate the implementation described in this article.

Many other possibilities exist and are documented so if you don't find the solution here, either check the Traefik documentation which provides configuration examples for different scenarios or use your favorite search engine.

 

Pro
Cons
  • Your network security has only 1 point of exposure, reducing security risks
  • All the certificates are managed in a centralised system: no more issue of having 1 server failing to renew it's certificates while the others are OK
  • You can easily add new ressources/services by only registering the new sub-domain, adding the corresponding YML configuration file and restarting Traefik
  • HTTP/HTTPS/TCP communication can be managed & monitored either as a global unit or on a per-service basis
  • Facilitates secure scaling when associated with container deployment & management tools such as Docker Swarm or Kubernetes
  • Your network security has only 1 point exposed which means no ressources are available if your Traefik service fails
  • Very large number of configuration possibilities and permutations producing a vast and very diverse documentation which makes finding the correct configuration for you harder
  • Requires technical knowledge in network, firewalls and applications to properly understand and deploy

 

Important note: the Trafik version described here is V2.

V1 is no longer supported but quite a lot of documentation and articles remains available on-line that are based on it.

And V3 is in it's testing phase as of Oct. 2023 .

Check the release date and support plan.

Setting up Traefik

  If you haven't installed docker and docker-compose packages:  

 

Ubuntu
Redhat
sudo apt update -y
sudo apt upgrade -y
sudo apt install docker-compose -y                

sudo dnf upgrade
sudo dnf config-manager --add-repo=https://download.docker.com/linux/centos/docker-ce.repo
sudo dnf repolist -v
sudo dnf install --nobest docker-ce
sudo dnf install docker-ce -y
sudo systemctl enable --now docker
systemctl is-active docker
curl -L "https://github.com/docker/compose/releases/download/1.23.2/docker-compose-$(uname -s)-$(uname -m)" -o docker-compose
sudo mv docker-compose /usr/local/bin && sudo chmod +x /usr/local/bin/docker-compose
sudo ln -s /usr/local/bin/docker-compose /usr/bin/docker-compose

Simply running an update & upgrade will add Docker packages to APT cache.

Installing docker-compose will automatically install all dependencies.

Docker is enabled by default.

We need to add the proper repository and then install the packages.

Docker is disabled by default, so we need to enable it.

And then Docker-compose package must be installed.

 

Pre-requisites

  • A server with Docker installed with the docker-compose package
  • Access to the DNS entry management for your domain
  • Firewall and network configuration that allows
    • HTTP & HTTPS on Traefik server
    • communication from Traefik server to our other servers & services in the private network (HTTP/S, TCP, UDP or custom ports)
  • Setting up a dedicated Docker Network. In our example, the network traefik has been created running command:

docker network create traefik

 

Files and folders configuration & setup

  For this demonstration, a sub-directory "traefik2" has been created in /opt containing:

File docker-compose.yml File at the root of the directory containing our running image configuration (see below)
Folder dynamic Folder where the .yml configuration files for each service managed through Traefik will be saved.
File acme.json File used to store certificate generation information. It must have permission mode 600


 

To create the files and folder, go to your chosen directory and run the below commands:

cd /opt/traefik2/
sudo touch acme.json
sudo chmod 600 acme.json
sudo mkdir dynamic

 

We will then set up the docker-compose.yml to have a working Traefik container and finally add our applications.

 

docker-compose.yml

version: '3.9'
  services:
    traefik:
      image: traefik:v2.10.5
      restart: unless-stopped
      container_name: traefik
      command:
        - --api.dashboard=true
        - --api.insecure=false
        - --providers.docker=true
        - --providers.docker.exposedByDefault=false
        - --providers.file=true
        - --providers.file.watch=true
        - --providers.file.directory=/opt/traefik2/dynamic/
        - --accesslog=true
        - --accesslog.filePath=/opt/traefik2/logs/access.log
        - --log.level=DEBUG
        - --entryPoints.web.address=:80
        - --entryPoints.websecure.address=:443
        - --certificatesResolvers.letsencrypt.acme.email=contact@somedomain.com
        - --certificatesResolvers.letsencrypt.acme.storage=/opt/traefik2/acme.json
        - --certificatesResolvers.letsencrypt.acme.caServer=https://acme-v02.api.letsencrypt.org/directory
        - --certificatesResolvers.letsencrypt.acme.httpChallenge=true
        - --certificatesResolvers.letsencrypt.acme.httpChallenge.entryPoint=web
      ports:
      # The HTTP port
        - "80:80"
      # The HTTPS port
        - "443:443"
      # The Web UI (enabled by --api.insecure parameter)
        - "8080:8080"
      volumes:
        - /var/run/docker.sock:/var/run/docker.sock
        - ./logs:/var/log/traefik
      # ACME certificates can be stored in a JSON file with permission mode 600 
        - ./acme.json:/acme.json
        - ./dynamic:/opt/traefik2/dynamic:ro
      labels:
        - "traefik.enable=true"
      ###ROUTERS & DASHBOARD##
        - "traefik.http.routers.traefik_https.rule=Host(whoami.somedomain.com)"
        - "traefik.http.routers.traefik_https.entrypoints=websecure"
        - "traefik.http.routers.traefik_https.tls=true"
        - "traefik.http.routers.traefik_https.tls.certResolver=letsencrypt"
        - "traefik.http.routers.traefik_https.service=api@internal"
        - "traefik.http.routers.traefik_https.middlewares=traefik-auth"
        - "traefik.http.routers.http_traefik.entrypoints=web"
        - "traefik.http.routers.http_traefik.middlewares=https_redirect"
      ##AUTH-DASHBOARD##
        - "traefik.http.middlewares.traefik-auth.basicauth.users=dashboarduser:$$2y$$05$$dashboard_hashed_password"
      ##REDIRECT-TO SSL##
        - "traefik.http.middlewares.https_redirect.redirectscheme.scheme=https"
        - "traefik.http.middlewares.https_redirect.redirectscheme.permanent=true"
        - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
      # AUTOMATIC REDIRECT TO HTTPS
        - "traefik.http.routers.redirs.rule=hostregexp({host:.+})"
        - "traefik.http.routers.redirs.entrypoints=web"
        - "traefik.http.routers.redirs.middlewares=redirect-to-https"
      networks:
        - traefik

  networks:
    traefik:
      driver: bridge

 

Below is a light and quick explaination of what makes the system work. Some elements aren't technically correct but allow for a better understanding of the interactions of the different modules and functions.

If in doubt, always check the official documentation.

 

  • Setting up the web dashboard

This gives you a visual details of your traefik configurations:

 

Based on parameters:

      command:
        - --api.dashboard=true
        - --api.insecure=false
[...]
      ports:
[...]
      # The Web UI (enabled by --api.insecure parameter)
        - "8080:8080"
[...]
      labels:
        - "traefik.enable=true"
      ###ROUTERS & DASHBOARD##
        - "traefik.http.routers.traefik_https.rule=Host(whoami.somedomain.com)"
[...]
        - "traefik.http.routers.traefik_https.middlewares=traefik-auth"
        - "traefik.http.routers.http_traefik.middlewares=https_redirect"
      ##AUTH-DASHBOARD##
        - "traefik.http.middlewares.traefik-auth.basicauth.users=dashboarduser:$$2y$$05$$dashboard_hashed_password"

Parameter "--api.dashboard" will enable the dashboard but with no authentication required.

To make authentication mandatory, "--api.insecure" is set to false to avoid anonymous API users and "traefik.http.routers.traefik_https.middlewares" is set to "traefik-auth".

This will enable a login prompt to appear when accessing the URL set for "traefik.http.routers.traefik_https.rule=Host":

Parameter "traefik.http.routers.http_traefik.middlewares" is set for security measures so that we are alway using HTTPS.

And finally, we declare the users we allow access the dashboard to using "traefik.http.middlewares.traefik-auth.basicauth.users":

  • Install apache2-utils package or any other that contains the htpasswd functionnality
  • Generate a hashed password
  • Double the $ symbols from the resulting string

 

To make it simple, here is an example with username dashboarduser:

sudo apt install apache2-utils -y
echo $( sudo htpasswd -nB dashboarduser) | sed -e s/\\$/\\$\\$/g

  • Setting up LetsEncrypt certificate generation

Based on parameters:

      command:
[...]
        - --entryPoints.web.address=:80
        - --entryPoints.websecure.address=:443
        - --certificatesResolvers.letsencrypt.acme.email=contact@somedomain.com
        - --certificatesResolvers.letsencrypt.acme.storage=/opt/traefik2/acme.json
        - --certificatesResolvers.letsencrypt.acme.caServer=https://acme-v02.api.letsencrypt.org/directory
        - --certificatesResolvers.letsencrypt.acme.httpChallenge=true
        - --certificatesResolvers.letsencrypt.acme.httpChallenge.entryPoint=web
[...]
      ports:
      # The HTTP port
        - "80:80"
      # The HTTPS port
        - "443:443"
[...]
      volumes:
[...]
      # ACME certificates can be stored in a JSON file with permission mode 600 
        - ./acme.json:/acme.json
[...]
      labels:
        - "traefik.enable=true"
      ###ROUTERS & DASHBOARD##
        - "traefik.http.routers.traefik_https.rule=Host(whoami.somedomain.com)"
        - "traefik.http.routers.traefik_https.entrypoints=websecure"
        - "traefik.http.routers.traefik_https.tls=true"
        - "traefik.http.routers.traefik_https.tls.certResolver=letsencrypt"

The "certificatesResolvers" parameters are used for the certificate generation and retrieval itself.

Ports 80 and 443 must both be exposed for Traefik even if we will only navigate using 443: port 80 is needed for initial certification.

We will later secure our system by forcing redirection to HTTPS.

 

File(s) "acme.json" are declared and must have 600 permissions.

 

"traefik.http.routers.traefik_https" parameter calls on the configuration set by "certificatesResolvers" parameters for a given resolver. For example we could have:

        - --certificatesResolvers.letsencrypt.acme.email=contact@somedomain.com
        - --certificatesResolvers.letsencrypt.acme.storage=/opt/traefik2/acme.json
        - --certificatesResolvers.letsencrypt.acme.caServer=https://acme-v02.api.letsencrypt.org/directory
        - --certificatesResolvers.letsencrypt.acme.httpChallenge=true
        - --certificatesResolvers.letsencrypt.acme.httpChallenge.entryPoint=web

        - --certificatesResolvers.OVH.acme.email=contact@somedomain.com
        - --certificatesResolvers.OVH.acme.storage=/opt/traefik2/ovh/acme.json
        - --certificatesResolvers.OVH.acme.dnschallenge.provider=ovh
        - --certificatesResolvers.OVH.acme.dnschallenge=true

 

And then select the certResolver solution depending on weither we want to use LetsEncrypt HTTP Challenge or OVH DNS challenge.

 

  • HTTP to HTTPS redirection

Based on parameters:

      command:
[...]
        - --entryPoints.web.address=:80
        - --entryPoints.websecure.address=:443
[...]
      ports:
      # The HTTP port
        - "80:80"
      # The HTTPS port
        - "443:443"
[...]
      ##REDIRECT-TO SSL##
        - "traefik.http.middlewares.https_redirect.redirectscheme.scheme=https"
        - "traefik.http.middlewares.https_redirect.redirectscheme.permanent=true"
        - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"
      # AUTOMATIC REDIRECT TO HTTPS
        - "traefik.http.routers.redirs.rule=hostregexp({host:.+})"
        - "traefik.http.routers.redirs.entrypoints=web"
        - "traefik.http.routers.redirs.middlewares=redirect-to-https"

 

Port 80 and 443 must be declared and set as web and websecure adresses.

"traefik.http.middlewares" will allow to redirect and tweak traffic from and to our applications through Traefik.

For example when wanting to send incoming to a non-standard port on your application server.

 

The "traefik.http.routers" block configured here will:

  1. take all incoming traffic from the entrypoint "web" which is set as port 80 in "entryPoints.web.address"
  2. redirect to another URL using the REGEX set in "traefik.http.routers.redirs.rule"

 

This will force HTTPS redirection at all levels.

 

  • Providers: defining the applications & servuces managed by Traefik

 

The provider configuration allows us to set the parameters to be used when managing ressources depending on the type of ressource.

 

Based on parameters:

      command:
[...]
- --providers.docker=true
- --providers.docker.exposedByDefault=false
- --providers.file=true
- --providers.file.watch=true
- --providers.file.directory=/opt/traefik2/dynamic/
[...]
      volumes:
[...]
        - ./dynamic:/opt/traefik2/dynamic:ro

 

Using "providers.docker.exposedByDefault" ensures that the docker-based applications are only accessible through Traefik if we have labelled them to be so.

 

In order to be able to manually add services using YAML/TOML, we configure "providers.file.directory" with a directory also declared in the volumes.

"providers.file.watch=true" allows for the addition and removal of services without having to restart Traefik every time.

 

  • Log management

 

Logs can be accessed using the "docker logs" command but we can also access logs directly and set the level of logs.

 

Based on parameters:

      command:
[...]
        - --accesslog=true
        - --accesslog.filePath=/opt/traefik2/logs/access.log
        - --log.level=DEBUG
[...]
      volumes:
        - /var/run/docker.sock:/var/run/docker.sock
        - ./logs:/var/log/traefik

 

  • Docker Network 

 

A network has been created by running command:

docker network create traefik

 

Based on parameters:

      networks:
        - traefik
  networks:

    traefik:
      driver: bridge

 

The bridge network driver is what allows the communication between Traefik and the other ressources.

Configuration is required in the docker-compose.yml but can only work if it is allowed in other networking equipments used such as physical routers (Cisco, Juniper, Fortinet, Netgear...) or software configuration (Ubuntu UFW, AWS Security groups, ...).

 

Configuring services through file management

 

In our example, we have set the folder "/opt/traefik2/dynamic/" in which each service should have its own YML/TOML file to be managed.

 

For our example, a Redmine server has been set up on a server in our private network using the default port 3000.

Here is the configuration in Traefik:

/opt/traefik2/dynamic/redmine.yml

 

Content

http:
  services:
    redmine:
      loadBalancer:
        servers:
          - url: "http://172.192.1.238:3000"
  routers:
    redmine:
      rule: "Host(redmine.nicksopenworld.com)"
      tls:
        certResolver: letsencrypt
      entryPoints:
        - "websecure"
      service: redmine

 

It sets up a service named redmine in the Traefik dashboard that will reroute HTTP communication from/to the private network to/from the HTTPS websecure entrypoint using parameters defined for the "Letsencrypt" certResolver.

 

In parallel, a DNS entry has been created: sub-domain "redmine" redirects to the traefik server public IP.

 

Once the DNS update has propagated, Trafik will be able to generate the certificates for HTTPS and service will be made available and secure.

 

 

Setup conclusion

 

This is article is only a starting point for Traefik usage.

 

In future, we will explore integration with Kubernetes to see how to configure Traefik to manage much more services using the different automation tools available.

 

We will also be able to explore authentication forwarding using Traefik for example when setting up Single Sign On solutions.

 

Going further: Docker options, debugging and managing ressources

  1. Check logs and debug

If your container restarts all the time, you can check the logs:

sudo docker logs traefik

  It will give you the end of the logs. To define how much of the logs to see, option --tail can be used:

sudo docker logs traefik -f --tail 1000

  Or if you want to follow the logs as you are using the application

sudo docker logs traefik -f

This is particularly usefull when debuging a specific page loading issue.   To check all the log options, you can run:

docker logs --help

  Depending on the errors observed, you will need to either:

  • correct your docker-compose.yml file
  • review your network/database connectivity

 

  • Accessing the content of the container

Once connected to the Traefik container host, you can log in the container but executing the command:

sudo docker exec -it traefik ash

 

This can be needed when checking the volumes & files configurations.

 

  • Example of certificate generation failure due to file permission issues

The Traefik container rarely fails as a service but certificate retrieval and renewal can fail. Unable to add ACME provider to the providers list: unable to get ACME account: permissions 755 for /letsencrypt/acme.json are too open, please use 600

 
The solution is simple:
sudo chmod 600 acme.json sudo docker restart traefik2 
 

You may also like

0
Would love your thoughts, please comment.x
()
x