Traefik Reverse Proxy – Hosting HTTPS web services yourself
There are many ways to access web services that run in our home from the Internet.
The worst possibility would be portforwarding and forwarding to a device with an unencrypted protocol such as HTTP or FTP. You should really avoid simple port forwarding on HTTP or FTP.
SFTP and / or a VPN would be better. Both require certificates to authorize clients and the datatransfer is encrypted.
With many routers and devices, however, the setup is complex and, for example, the in-house VPN of a AVM FritzBox, which uses IPSec, is not particularly modern or fast.
PiVPN is a really easy solution for the Pi / ei23 Smart Home Server, with which WireGuard or OpenVPN can be easily installed.
A VPN doesn’t solve all problems
What if an indefinite number of clients should access our system, or we don’t want to provide a certificate to every user manually?
Or what if we run a service that at times requires more resources than a small Raspberry Pi can provide?
This is where reverse proxies and SSL certificates come into play.
With a reverse proxy, for example, a domain or subdomain can be called and the reverse proxy then decides which content is made available to the user. In addition, the content can be encrypted via SSL.
For example, it is possible to use multiple domains even if you have only one external ip available. Traefik Architecture (Soruce: traefik.io) There are of course other solutions, such as via Nginx. However, since the ei23 Smart Home Server runs many programs as Docker containers, we use Traefik. Which has some advantages for programs in Docker and which has a good visual interface.
Proxy Configuration of Docker-Containers
To configure a Docker container for Traefik, it is only necessary to adapt of the /home/pi/ei23-dockerdocker-compose.yaml
.
I show how this works in the video for version 1 of the Smart Home script
Docker-Compose Example for Bitwarden
bitwarden: image: bitwardenrs/server:latest container_name: bitwarden restart: unless-stopped # ports:* # - 2223:80 labels: - traefik.enable=true - traefik.http.routers.bitwarden.rule=Host(`example.com`) - traefik.http.routers.bitwarden.entrypoints=web-secured - traefik.http.routers.bitwarden.tls=true - traefik.http.routers.bitwarden.tls.certresolver=letsEncrypt volumes: - ./volumes/bitwarden:/data
With Traefik it is not necessary to give access to the *ports via Docker as long as Traefik is in the same Docker network as the container. Therefore these are commented out with # in the example above. Example: Websit in your LAN (in this case the ei23 Dashboard)
labels: - traefik.enable=true - traefik.http.routers.ei23-lan.rule=(Host(`192.168.178.10`) || Host(`smarthome`)) - traefik.http.routers.ei23-lan.priority=1 - traefik.http.routers.ei23-lan.entrypoints=lan
Docker-Compose Example for Grafana
labels: - traefik.enable=true - traefik.http.routers.grafana.rule=Host(`grafana.example.com`) - traefik.http.services.grafana.loadbalancer.server.port=3000 - traefik.http.routers.grafana.entrypoints=web-secured - traefik.http.routers.grafana.tls=true - traefik.http.routers.grafana.tls.certresolver=letsEncrypt
Docker-Compose Example for Nextcloud
labels: - traefik.enable=true - traefik.http.routers.nextcloud.middlewares=nextcloud,nextcloud_redirect - traefik.http.routers.nextcloud.tls.certresolver=letsEncrypt - traefik.http.routers.nextcloud.rule=Host(`nextcloud.example.com`) - traefik.http.routers.nextcloud.entrypoints=web, web-secured - traefik.http.routers.nextcloud.tls=true - traefik.http.middlewares.nextcloud.headers.stsSeconds=15552000 - traefik.http.middlewares.nextcloud.headers.stsPreload=true - traefik.http.middlewares.nextcloud_redirect.redirectregex.permanent=true - traefik.http.middlewares.nextcloud_redirect.redirectregex.regex=^https://(.*)/.well-known/(card|cal)dav - traefik.http.middlewares.nextcloud_redirect.redirectregex.replacement=https://$${1}/remote.php/dav/
As you can see, there are “routers, services, and middleware” for Traefik. Each service requires at least one traefik router of its own.
Proxy configuration of load balancers / external (IP) addresses and other devices in the network
For example, an external load balancer is created in /home/pi/ei23-docker/volumes/traefik/traefik/dynamic/config.yml
(attention! The line indentation must be correct – the Yaml parser wants it that way)
http: routers: home-assistant: rule: Host(`homeassistant.example.com`) service: home-assistant tls: certresolver: letsEncrypt services: home-assistant: loadBalancer: servers: - url: http://172.17.0.1:8123 # 172.17.0.1 is the default Docker gateway
Instead of a Docker container, this refers to an http url. This is then also encrypted via SSL via the “certresolver”.
Initialising Traefik / creating SSL certificates
Traefik itself is operated as a Docker container and in the current version of the ei23 Smart Home Server, Traefik is already preconfigured, only small changes need to be made. Traefik’s Docker-Compose
traefik: image: traefik:v2.4 container_name: traefik ports: - "80:80" # as internal http - "591:591" # as external http - "2280:8080" # config port - "443:443" volumes: - /var/run/docker.sock:/var/run/docker.sock:ro - ./volumes/traefik/traefik/:/etc/traefik/ - ./volumes/traefik/letsencrypt:/letsencrypt restart: unless-stopped
Under /home/pi/ei23-docker/volumes/traefik/traefik/
you find the traefik.yaml
[...] entryPoints: lan: address: :80 web: address: :591 http: redirections: entrypoint: to: web-secured scheme: https web-secured: address: :443 certificatesResolvers: letsEncrypt: acme: email: mail@example.com storage: /letsencrypt/acme.json caserver: https://acme-staging-v02.api.letsencrypt.org/directory # this is for testing # caserver: https://acme-v02.api.letsencrypt.org/directory httpChallenge: entryPoint: web [...]
Three entrypoints are defined here.
- Port 80 (lan) is reserved for HTTP in the home network (LAN)
- Port 591 (web) is reserved for external HTTP (WAN), the external port 80 must be forwarded to the internal port 591 of the Raspberry Pi. Since we only want to use encrypted connections, we use Traefik to force a redirection to the web-secured entrypoint (443).
- Port 443 (web-secured) is finally the port for HTTPS. This must be accesable from the outside on the router and a forwarding to the Raspberry Pi on port 443
The encryption works with SSL.
After running Docker Compose (ei23 dc) and restarting Traefik, the labels are read in by Traefik and the certificates are created.
But! Before a certificate can be created at all, an email address must be entered. You will be informed about the expiry of a certificate or other warnings regarding security. The Let’s Encrypt service is so kind as to provide us with the otherwise very expensive certificates free of charge. However, there is a daily and weekly limit, as long as we only test whether everything works, we should use the “staging” caserver.
No official certificate is issued here. A good web browser shows a warning when the certificate is not valid.
Because it is just testing, you can accept this warning and all the desired services are still available, then you can switch from staging caserver to the official one.
Official certificates are then created and the web services are officially encrypted and accessible after a short time. If Traefik and the port forwarding have been configured correctly according to these instructions, then in the file /home/pi/ei23-docker/volumes/traefik/letsencrypt/acme.json
something like this will appear for each domain after some time:
[...] "Certificates": [ { "domain": { "main": "nextcloud.example.com" }, "certificate": [...]
This basic configuration should already offer more than enough functionality for your home server applications, if you still want to get into the matter further:
Traefik has a very good documentation:
doc.traefik.io/traefik