secure https connection: Traefik Reverse Proxy + Let's Encrypt

 

Ready-made Docker containers are available for almost all known web systems, or they can be created relatively easily or existing containers can be adapted. As Docker containers, the web services can be installed and operated very easily.

Secure access from the Internet to web services in the home network

Anyone publishing a web service over the Internet should definitely offer an encrypted connection (SSL) for this today. To control access to one or more containers, a reverse proxy with free Let's Encrypt certificates can be used for SSL offloading, see also: own web server - how does the Internet actually work? To provide web services from the home network over the internet maybe Cloudflare offers a simpler alternative to this setup.

Aim of this article

Running one or more Docker web services (websites) including HTTPS encryption on a server.
The web services can be published via their properties (Docker container labels) and controlled via their own web interface.

Effort

Reading time: approx. 17 Minutes

Prerequisite

A server published on the Internet with Docker and a registered web domain

From NGINX Proxy to Traefik

I originally used "NGINX Proxy Automation" by Evert Ramos to run multiple Docker-based websites, see: nginx-LetsEncrypt Reverse Proxy in Practice . The project works wonderfully, but has some limitations compared to the alternative reverse proxy: Traefik. With Traefik it feels even easier and thanks to the web dashboard clearer to run multiple websites on one server. Numerous middleware also offer additional functions.

Schematic representation

The Traefik Docker container takes care of issuing, managing and using SSL certificates and redirects web requests to the respective web services, which in turn are also deployed as Docker containers.

Traefik takes care of

      • access to one or more Docker web services
      • the certificate management for HTTPS encryption
 

Shown here schematically, using the pages www.libe.net and www.script-example.com:

The websites www.libe.net and www.script-example.com are operated by the respective Docker containers. The connection from the Internet is received on the public IP address of the web server by the Traefik container. Traefik scans all Docker containers to see if certain labels are present and publishes them based on those. Traefik's management interface is also provided as a web service. Since Traefik issues and manages an SSL certificate for the deposited host (traefik.http.routers.website.rule=host(`webdomain.tld`)) and thus for access to its container when using "websecure" as a label, the connection from the browser to Traefik's management interface is encrypted. The Traefik container establishes an unencrypted connection to the container with the web service, which means that the published web service does not have to worry about certificate management. The current version of the project is:
SoftwareTraefik
GitHubhttps://github.com/traefik/traefik
current version 3.2.0
found2024-10-28

Installation: server, domain (DNS), Docker.

Docker Basics

Docker allows applications to be launched by command in a so-called container.
A container is an isolated environment independent of the operating system (OS):
When a container is first launched, Docker independently loads all the necessary sources
from the internet.
Docker can be installed on Windows, macOS or an Linux Distribution
As a prerequisite for the setup, we of course need a server and a web domain. The web domain can ideally be managed by the DNS server of the provider. If you don't have a domain yet, you can order one from one of the countless hosting providers, see also: own web server - how does the Internet actually work?
Docker is required for the setup presented here, see: Docker Installation Linux or Docker Installation Windows. If you have a server from a hosting provider, Docker and the setup presented here can be done via an SSH connection to the server and via the described Bash commands. If the server is located in your own home network, you have to set up port forwarding on the router, see: Making available from the Internet: Port Forwarding - OpenWRT. If your Internetprovider regular changes your public-ip, see also: free DynDNS Service - Zugriff bei wechselnder öffentlicher IP.

For those who have already installed Docker and entered the appropriate A-records in the domain management, you need a folder with the following text file and 2 simple commands.
As an example, I create the text file in the following folder: /var/web/traefik. On Linux the folder can be created with mkdir /var/web/traefik.
In the folder, as a template, a text file named docker-compose.yml can be created with the following content.

docker-compose.yml

[+]
services:
  traefik:
    image: "traefik:v2.8"
    container_name: "traefik"
    command:
      #- "--log.level=DEBUG"
      - "--api.dashboard=true"
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      - "--certificatesresolvers.myresolver.acme.tlschallenge=true"
      - "--certificatesresolvers.myresolver.acme.email=admin@domain.tld"
      - "--certificatesresolvers.myresolver.acme.storage=/letsencrypt/acme.json"
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
    expose:
      # traefik dashboard port
      - 8080
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.traefik.rule=Host(`traefik.domain.tld`)"
      - "traefik.http.routers.traefik.entrypoints=websecure"
      - "traefik.http.routers.traefik.tls.certresolver=myresolver"
      - "traefik.http.routers.traefik.service=api@internal"
      - "traefik.http.routers.traefik.middlewares=traefik-basic-auth"
      - "traefik.http.middlewares.traefik-basic-auth.basicauth.users=admin:??????"
    ports:
      - "80:80"
      - "443:443"
      - "8080:8080"
    volumes:
      - "./letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"     
    restart: "always"
networks:
  default:
    name: webproxy
    external: true

The example uses bind mounts and not volumes, see: Docker Data Storage: Docker Volumes vs. Host Folders..

Customizing the docker-compose.yml file

The docker-compose-yml template uses a certificate not only for the published web service containers, but also for the Traefik dashboard, which means that access to the dashboard is also encrypted. To prevent the dashboard from being accessed by just anyone, a basic authentication with an admin user has been stored.
Before the actual start the file must be adapted of course a little:

  • traefik.domain.tld should be created with an own domain for the traefik dashboard,
  • acme.email with a mail address and
  • basicauth.users should be replaced with its own username and a corresponding md5 password.

Instead of traefik.domain.tld, a subdomain of an existing domain can be used to access the dashboard. As an example, in the DNS zone management for the domain libe.net, an A record for a subdomain traefik.libe.net could be created on the IP address of the web server.

Of course, in the docker-compose.yml file for this example, traefik.libe.net must also be used instead of traefik.domain.tld.

The mail address is used by Let's Encrypt to send certain information or warnings: For example, if a certificate is about to expire. Don't worry, Traefik takes care of certificate renewal, accordingly emails with information about certificate expiration should be taken seriously.

The password for authentication cannot be entered in plain text and therefore must be generated in advance via a command as an MD5 hash, and for use in docker-compose simple $ must be replaced with $$, see: doc.traefik.io/traefik/middlewares/http/basicauth/

On Linux, the following command can be used for this:

echo $(htpasswd -nB admin) | sed -e s/\\$/\\$\\$/g

root@Webserver:~# echo $(htpasswd -nB admin) | sed -e s/\\$/\\$\\$/g
New password:
Re-type new password:
admin:$$2y$$05$$7liwlteyGcPyCr2oXc4Do.G/wWxjwWLYpTWg6vvQ4pU7PLAS4ysMm

Instead of admin, another username can be used here.

After customizing the file, we can start the Traefik container. Subsequently, started Docker containers with the stored labels for its web service are processed and published by Traefik during operation.

Start the Traefik container

As network for Traefik and for the later created web services I use a custom network named “webproxy”, which I create before starting Traefik (docker compose up):

cd /FolderContainingDockerComposeFile4Traefik
docker network create webproxy
docker compose up -d

If everything works correctly, the Traefik dashboard can be accessed via the specified domain and after entering the specified user:

Create and host websites or services

If another Docker container is launched with the network webproxy and appropriate labels for Traefik, it can be accessed via Traefik.

If you want to launch multiple web services, you need to make sure that different labels are used in the respective configurations:
z. E.g. web service 1:

    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.webservice1.rule=Host(`service1.domain.tld`)"          
      - "traefik.http.routers.webservice1.entrypoints=web"
      - "traefik.http.routers.webservice1.entrypoints=websecure"
...

and for another Docker container with another service:

    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.webservice2.rule=Host(`service2.domain.tld`)"          
      - "traefik.http.routers.webservice2.entrypoints=web"
      - "traefik.http.routers.webservice2.entrypoints=websecure"
...

"web" as entrypoint allows unencrypted access to the service and "websecure" encrypted access via HTTPS.

See also: own web server - how does the internet actually work?

Authentication

If you want to add simple basic authentication for a container, you can do so by adding the Basic Authentication middleware. (same as used in die the Traefik dashboard Configuration)

(Basic-Authentication: Before the dashboard can be accessed, the stored username and password must be entered).

The following line creates a middleware named: webserver-basic-auth, defining the username and password:
- "traefik.http.middlewares.webserver-basic-auth.basicauth.users=admin:md5Password".

The password can be converted to an md5 hash with a Linux command, as described earlier: 

echo $(htpasswd -nB admin) | sed -e s/\$/\$\$/g

To make the router, here named "webserver" use the created middleware, I added the following line:

- "traefik.http.routers.webserver.middlewares=webserver-basic-auth".

    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.webserver.rule=Host(`domain.tld`)"      
      - "traefik.http.routers.webserver.entrypoints=web"
      - "traefik.http.routers.webserver.entrypoints=websecure"
      - "traefik.http.routers.webserver.tls.certresolver=myresolver"   
      - "traefik.http.middlewares.webserver-basic-auth.basicauth.users=admin:md5Password" 
      - "traefik.http.routers.webserver.middlewares=webserver-basic-auth"

Alternatively to Basic authentication, an external login provider can also be used. As an example, the use of Google as a login provider, i.e. using a Google account to log in to your own web services, see: Traefik Google Authentication.

Providing web services outside of Docker

To be able to access my router at home securely, for example, I also published it via Traefik. First, I created a new folder for possible additional web services: "/conf". The folder can then be mentioned in the docker-compose.yml file in "command:" and integrated using "volumes:":

version: "3.3"
services:
  traefik:
    image: "traefik"
    container_name: "traefik"
    command:
      ... 
      - "--providers.file.directory=/etc/traefik/conf"
      ...
    volumes:
      - "./letsencrypt:/letsencrypt"
      - "/var/run/docker.sock:/var/run/docker.sock:ro"
      - "./conf:/etc/traefik/conf"      
    restart: "always"

It is then possible to publish additional web services using your own config files:
As an example, for publishing the router web interface:
File ./conf/router.yml

http:
  routers:
    openwrt:
      entrypoints: "websecure"
      service: "openwrt@file"
      rule: "Host(`router.domain.tld`)"
      tls:
        certresolver: "myresolver"
      middlewares: "forward-auth-verify@docker"
  services:
    openwrt:
      loadbalancer:
        servers:
          - url: "http://192.168.1.1:80"

In the example, the previously mentioned upstream login via Google is activated for additional authentication: "forward-auth-verify" middleware, see: Traefik Google authentication.

Here is another example of a web service with basic authentication:

http:
  middlewares:
    webservice2-basic-auth:
      basicAuth:
        users: 
          - "root:???"
  routers:
    webservice2:
      entrypoints: "websecure"
      service: "webservice2@file"
      rule: "Host(`webservice2.domain.tld`)"
      tls:
        certresolver: "myresolver"
      middlewares: "webservice2-basic-auth"
  services:
    webservice2:
      loadbalancer:
        servers:
          - url: "http://192.168.1.15:80"

For basic authentication, the password in the YAML file does not need to be escaped, so the command for creating the password is as follows:

echo $(htpasswd -nB root)

https backend web service with self-signed certificate

If the backend web service is operated with https and a self-signed certificate, all certificates can be accepted with the "serverstransport.insecureskipverify" command:

version: "3.3"
services:
  traefik:
    image: "traefik"
    container_name: "traefik"
    command:
      ... 
      - '--serverstransport.insecureskipverify'
      ...

      

Additional SAN (Subject Alternative Names)

So that a web certificate can be issued for several subdomains, so-called SAN can be used:

    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.webservice.rule=Host(`domain.tld`,`www.domain.tld`,`dev.domain.tld`,`dev2.domain.tld)"      
      - "traefik.http.routers.webservice.entrypoints=web"
      - "traefik.http.routers.webservice.entrypoints=websecure"
      - "traefik.http.routers.webservice.tls.certresolver=myresolver"     

Redirects

For redirecting subdomains to another subdomain, regex expressions can be used.

Redirect various subdomains and/or non-WWW to WWW

The following example redirects all subdomains and the root domain (non-WWW) to a URL with WWW:
domain.tld, dev.domain.tld, and dev2.domain.tld are redirected to www.domain.tld. To ensure that the certificate includes all domain names, they must be specified in the host rule:

    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.webservice.rule=Host(`domain.tld`,`dev.domain.tld`,`dev2.domain.tld`,`www.domain.tld`)"      
      - "traefik.http.routers.webservice.entrypoints=web"
      - "traefik.http.routers.webservice.entrypoints=websecure"
      - "traefik.http.routers.webservice.tls.certresolver=myresolver"     
      
      - "traefik.http.routers.webservice.middlewares=redirect_???2www"
      - "traefik.http.middlewares.redirect_???2www.redirectregex.regex=^https://(dev.|dev2.)?domain.tld(.*)"
      - "traefik.http.middlewares.redirect_???2www.redirectregex.replacement=https://www.domain.tld$${2}"
      - "traefik.http.middlewares.redirect_???2www.redirectregex.permanent=true"

Various subdomains and/or WWW on non-WWW

    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.webservice.rule=Host(`domain.tld`,`dev.domain.tld`,`dev2.domain.tld`,`www.domain.tld`)"      
      - "traefik.http.routers.webservice.entrypoints=web"
      - "traefik.http.routers.webservice.entrypoints=websecure"
      - "traefik.http.routers.webservice.tls.certresolver=myresolver"     
      
      - "traefik.http.routers.webservice.middlewares=redirect_www2none"
      - "traefik.http.middlewares.redirect_www2none.redirectregex.regex=^https://(www.|dev.|dev2.)domain.tld(.*)"
      - "traefik.http.middlewares.redirect_www2none.redirectregex.replacement=https://domain.tld$${2}"
      - "traefik.http.middlewares.redirect_www2none.redirectregex.permanent=true"

Redirect HTTP to HTTPS (not globally, only for the respective container)

    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.webservice.rule=Host(`domain.tld`,`dev.domain.tld`,`dev2.domain.tld`,`www.domain.tld`)"      
      - "traefik.http.routers.webservice.entrypoints=web"
      - "traefik.http.routers.webservice.entrypoints=websecure"
      - "traefik.http.routers.webservice.tls.certresolver=myresolver"     
      
      - "traefik.http.routers.webservice_http.rule=Host(`domain.tld`,`www.domain.tld`,`dev.domain.tld`)"  
      - "traefik.http.routers.webservice_http.entrypoints=web"
      - "traefik.http.routers.webservice_http.middlewares=redirect-to-https"
      - "traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https"  

Access Log

To log client accesses in a file, it is sufficient to include the following settings in the docker-compose.yml file:

version: "3.3"
services:
  traefik:
    image: "traefik"
    container_name: "traefik"
    command:
...
      - "--accesslog=true"
      - "--accesslog.filePath=/logs/access.log"
...
    volumes:
...
      - "./logs/:/logs/"
...

FAQ

What is a reverse proxy?

A reverse proxy accepts web requests and forwards them to underlying web services. To ensure that the requests are forwarded to the correct web service, most reverse proxies use the host header (=domain name). A reverse proxy can provide additional functions for better protection, optimized traffic or additional authentication.

What is a reverse proxy used for?

A reverse proxy is mainly used for web access to one or more web servers upstream. Besides distributing the requests to several web services, a reverse proxy is often used for a so-called SSL offloading. The reverse proxy takes care of providing the web services via HTTPS and, in addition to encrypting and decrypting the data traffic, it also handles certificate management.

What is the difference between a reverse proxy and a load balancer?

The job of a load balancer is primarily to distribute the load between different servers or devices. A reverse proxy can perform other functions besides load balancing, such as SSL offloading or caching, and is primarily used for web servers (HTTP). A load balancer, on the other hand, can also be used for other protocols. 

Webservices, Examples

Here is a list of web services that I have tested so far:

positive Bewertung({{pro_count}})
Rate Post:
{{percentage}} % positive
negative Bewertung({{con_count}})

THANK YOU for your review!

Questions / Comments


By continuing to browse the site, you agree to our use of cookies. More Details