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 Effort Prerequisite
The web services can be published via their properties (Docker container labels) and controlled via their own web interface.
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:
Software | Traefik |
---|---|
GitHub | https://github.com/traefik/traefik |
current version | 3.2.0 |
found | 2024-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
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:
{{percentage}} % positive