it:server:traefik-reverzni-proxy

Toto je starší verze dokumentu!


Traefik - reverzní proxy

Tento návod ukazuji, jak jsem nasadil Traefik jako reverse proxy v Dockeru a použil jej pro přístup k aplikaci Grafana. Vysvětluji, jak Traefik funguje, popisuji klíčové části konfiguračních souborů a krok za krokem ukazuji proces nasazení.

Traefik je reverse proxy a load balancer, který:

  • Směruje HTTP/S požadavky z vašeho prohlížeče na různé služby běžící v Docker kontejnerech.
  • Umožňuje používat HTTPS díky certifikátům (v našem případě self-signed certifikáty).
  • Automaticky se integruje s Dockerem a nakonfiguruje směrování na základě labelů v „docker-compose.yml“.

V tomto návodu budete přistupovat ke Grafaně přes URL:

https://<IP_adresa>/grafana

Namísto přístupu přes konkrétní port, jako je:

http://<IP_adresa>:3000

Zjistil jsem, že Traefik lze použít i pro vygenerování a správu certifikátů z Let's Encrypt - včetně použití pro generování certifikátu pro vnitřní síť. O tento postup jsem rozšířil návod.

  • Docker a Docker Compose nainstalovány na vašem systému.
  • Generované self-signed certifikáty:
    • Soubor certifikátu: selfsigned.crt
    • Soubor privátního klíče: selfsigned.key

Ve složce projektu budete mít následující soubory:

.
├── docker-compose.yml
├── traefik.yml
├── certs/
│   ├── selfsigned.crt
│   ├── selfsigned.key

Tento soubor definuje Traefik jako proxy a službu Grafana. Uvnitř jsou klíčové části:

version: "3.9"

services:
  grafana:
    container_name: grafana-teslamate
    image: teslamate/grafana:latest
    restart: always
    environment:
      - GF_SERVER_ROOT_URL=%(protocol)s://%(domain)s/grafana
      - GF_SERVER_SERVE_FROM_SUB_PATH=true
    labels:
      # aktivace Traefiku pro tento kontejner
      traefik.enable: "true"
 
      # Router pro HTTP přesměrování na HTTPS
      traefik.http.routers.grafana-insecure.rule: "PathPrefix(`/grafana`)"
      traefik.http.routers.grafana-insecure.entrypoints: "web"
      traefik.http.routers.grafana-insecure.middlewares: "redirect"
 
      # Middleware pro přesměrování z HTTP na HTTPS
      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"

      traefik.http.routers.grafana.rule: "PathPrefix(`/grafana`)"
      traefik.http.routers.grafana.entrypoints: "websecure"
      traefik.http.services.grafana.loadbalancer.server.port: "3000"

    volumes:
      - teslamate-grafana-data:/var/lib/grafana

  proxy:
    image: traefik
    restart: always
    container_name: traefik
    ports:
      - "443:443"
      - "80:80"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./certs:/etc/traefik/certs:ro
      - ./traefik.yml:/etc/traefik/traefik.yml:ro

volumes:
  teslamate-grafana-data:
  certs:

Vysvětlení klíčových částí:

`grafana` služba:

  • `environment`:
    • `GF_SERVER_ROOT_URL`: Definuje základní URL, včetně `/grafana`, protože Grafana běží za proxy na subcestě.
    • `GF_SERVER_SERVE_FROM_SUB_PATH`: Umožňuje aplikaci fungovat na subcestě.
  • `labels`:
    • Říkají Traefiku, aby směroval požadavky na `/grafana` na tento kontejner.
    • `traefik.http.services.grafana.loadbalancer.server.port` zajišťuje, že Traefik přesměruje na správný interní port (3000) uvnitř kontejneru.

`proxy` služba (Traefik):

  • `ports`: Otevírá porty 80 (HTTP) a 443 (HTTPS) pro příchozí požadavky.
  • `volumes`:
    • Připojuje certifikáty a konfigurační soubor traefik.yml, který obsahuje další nastavení.

Soubor traefik.yml obsahuje statickou konfiguraci pro Traefik:

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
    http:
      tls: {}

tls:
  certificates:
    - certFile: "/etc/traefik/certs/selfsigned.crt"
      keyFile: "/etc/traefik/certs/selfsigned.key"

providers:
  docker:
    exposedByDefault: false

global:
  sendAnonymousUsage: false

Vysvětlení klíčových částí:

  • `entryPoints`:
    • Definuje, na jakých portech Traefik přijímá požadavky:
      • `web`: HTTP (port 80).
      • `websecure`: HTTPS (port 443).
    • `tls`: U HTTPS (`websecure`) aktivuje TLS šifrování.
  • `tls.certificates`:
    • Určuje cestu k certifikátům (selfsigned.crt a selfsigned.key), které se použijí pro HTTPS.
  • `providers`:
    • `docker`: Umožňuje Traefiku načítat konfiguraci z Docker labelů.
  • `global`:
    • `sendAnonymousUsage`: Zakazuje odesílání statistik o používání.

Do složky certs/ umístěte vaše self-signed certifikáty:

  • selfsigned.crt: Veřejný certifikát.
  • selfsigned.key: Privátní klíč.

Pokud nemáte certifikáty, můžete je vytvořit pomocí OpenSSL:

openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout certs/selfsigned.key -out certs/selfsigned.crt
  • Vytvořte potřebné složky a soubory (docker-compose.yml, traefik.yml, certs/).
  • Spusťte Traefik a Grafanu pomocí:
docker-compose up -d
  • Otevřete prohlížeč a přejděte na:
https://<IP_adresa>/grafana
  • Pro první použití musíte potvrdit výjimku u self-signed certifikátu.

Pokud máte potíže s přihlášením do Grafany, resetujte heslo administrátora pomocí následujícího příkazu:

docker compose exec grafana grafana-cli admin reset-admin-password

Během konfigurace Teslamate jsem narazil na několik omezení a problémů spojených se self-signed certifikáty:

  • Teslamate nemá žádné vestavěné přihlašovací mechanismy, proto jsem přidal možnost přihlašování pomocí Basic Auth prostřednictvím Traefiku. K vygenerování hesla pro .htpasswd soubor lze použít například tento generátor: http://www.htaccesstools.com/htpasswd-generator/
  • Původně jsem chtěl, aby bylo možné službu volat na adrese `https://192.168.1.123/teslamate`, podobně jako pro Grafanu na `https://192.168.1.123/grafana`. Bohužel Teslamate předpokládá, že bude dostupný přímo na kořenové adrese (např. `https://192.168.1.123/`) a nelze jej snadno nakonfigurovat pro subcestu, jako je `/teslamate`.
  • Tato konfigurace funguje pouze tehdy, pokud na stejném serveru neprovozujete další aplikace (například Nextcloud). Jakmile je potřeba provozovat více služeb, vzniká konflikt při směrování subdomén nebo URL.

Abych tento problém vyřešil, plánuji v interní síti zavést DNS server. Tento server zajistí, že při přístupu na veřejnou doménu, například `teslamate.nosekpetr.cz`, bude interním klientům vrácena lokální IP adresa serveru. Díky této konfiguraci mohu pro každou službu použít vlastní subdoménu a zároveň zajistit validní certifikát od Let's Encrypt.

Následující je aktuální konfigurace Docker-Compose s komentáři vysvětlujícími jednotlivé části:

version: "3.3"  # Verze Docker Compose

services:
  teslamate:
    container_name: teslamate
    image: teslamate/teslamate:latest
    restart: always
    environment:
      - ENCRYPTION_KEY=*** # Bezpečný klíč pro šifrování Tesla API tokenů
      - DATABASE_USER=***
      - DATABASE_PASS=*** # Heslo pro přístup k databázi
      - DATABASE_NAME=***
      - DATABASE_HOST=database
      - VIRTUAL_HOST=192.168.1.123 # Hostitel pro Traefik
      - MQTT_HOST=192.168.1.150 # MQTT broker
      - MQTT_PORT=8883 # Port pro MQTT
      - MQTT_USERNAME=***
      - MQTT_PASSWORD=***
      - MQTT_TLS=true # Povolení šifrovaného připojení k MQTT
      - MQTT_TLS_ACCEPT_INVALID_CERTS=true # Akceptování self-signed certifikátů pro MQTT
      - TZ=Europe/Prague # Nastavení časové zóny
    volumes:
      - ./import:/opt/app/import
    labels:
      traefik.enable: "true" # Aktivace Traefiku pro tento kontejner
 
      # Nastavení přesměrování HTTP -> HTTPS
      traefik.http.routers.teslamate-insecure.rule: "Host(`192.168.1.123`)"
      traefik.http.routers.teslamate-insecure.entrypoints: "web"
      traefik.http.routers.teslamate-insecure.middlewares: "redirect"
 
      # Middleware pro přesměrování HTTP -> HTTPS
      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"
 
      # Nastavení pro WebSocket Teslamate
      traefik.http.routers.teslamate-ws.rule: "Host(`192.168.1.123`) && Path(`/live/websocket`)"
      traefik.http.routers.teslamate-ws.entrypoints: "websecure"
 
      # Zabezpečení pomocí Basic Auth
      traefik.http.routers.teslamate.middlewares: "teslamate-auth"
      traefik.http.routers.teslamate.rule: "Host(`192.168.1.123`)"
      traefik.http.routers.teslamate.entrypoints: "websecure"
      traefik.http.services.teslamate.loadbalancer.server.port: "4000"
      traefik.http.middlewares.teslamate-auth.basicauth.realm: "teslamate"
      traefik.http.middlewares.teslamate-auth.basicauth.usersfile: "/auth/.htpasswd"

    cap_drop:
      - all # Omezení privilegovaných operací kontejneru

  database:
    container_name: postgres
    image: postgres:17
    restart: always
    environment:
      - POSTGRES_USER=***
      - POSTGRES_PASSWORD=*** # Heslo k databázi
      - POSTGRES_DB=***
      - TZ=Europe/Prague # Nastavení časové zóny
    volumes:
      - teslamate-db:/var/lib/postgresql/data

  grafana:
    container_name: grafana-teslamate
    image: teslamate/grafana:latest
    restart: always
    environment:
      - DATABASE_USER=***
      - DATABASE_PASS=*** # Heslo k databázi
      - DATABASE_NAME=***
      - DATABASE_HOST=database
      - TZ=Europe/Prague
      - GF_SERVER_ROOT_URL=%(protocol)s://%(domain)s/grafana # URL pro Grafanu
      - GF_SERVER_SERVE_FROM_SUB_PATH=true # Povolení subcesty pro Grafanu
    labels:
      traefik.enable: "true" # Aktivace Traefiku
 
      # Přesměrování HTTP -> HTTPS
      traefik.http.routers.grafana-insecure.rule: "Host(`192.168.1.123`)"
      traefik.http.routers.grafana-insecure.entrypoints: "web"
      traefik.http.routers.grafana-insecure.middlewares: "redirect"

      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"

      traefik.http.routers.grafana.rule: "Host(`192.168.1.123`) && (Path(`/grafana`) || PathPrefix(`/grafana/`))"
      traefik.http.routers.grafana.entrypoints: "websecure"
      traefik.http.services.grafana.loadbalancer.server.port: "3000"
    volumes:
      - teslamate-grafana-data:/var/lib/grafana

  proxy:
    image: traefik
    restart: always
    container_name: traefik
    ports:
      - "443:443" # HTTPS
      - "80:80"   # HTTP (přesměrování na HTTPS)
    volumes:
      - ./.htpasswd:/auth/.htpasswd # Soubor pro Basic Auth
      - /var/run/docker.sock:/var/run/docker.sock # Přístup k Docker API
      - ./certs:/etc/traefik/certs:ro # Certifikáty
      - ./traefik.yml:/etc/traefik/traefik.yml:ro # Konfigurace Traefiku

volumes:
  teslamate-db:
  teslamate-grafana-data:
  certs:
  

Traefik je výborný na správu a generování certifikátů z Let's Encrypt. Pro veřejnou IP adresu bych použil HTTP Challenge, ale pro IP adresu, kterou používám čistě ve vnitřní síti potřebuji použít tzv. DNS-01 chalenge pro získání certifikátů. Jak to funguje jsem popsal zde: Certifikáty Let's Encrypt v uzavřené síti.

docker-compose.yml:

version: "3.3"  # Nastavení verze Docker Compose

services:
  teslamate:
    container_name: teslamate
    image: teslamate/teslamate:latest
    restart: always
    environment:
      - ENCRYPTION_KEY=*** #replace with a secure key to encrypt your Tesla API tokens
      - DATABASE_USER=***
      - DATABASE_PASS=*** #insert your secure database password!
      - DATABASE_NAME=***
      - DATABASE_HOST=database
      - VIRTUAL_HOST=teslamate.nosekpetr.cz
        #- MQTT_HOST=mosquitto
      - MQTT_HOST=192.168.1.150
      - MQTT_PORT=8883
      - MQTT_USERNAME=***
      - MQTT_PASSWORD=***
      - MQTT_TLS=true
      - MQTT_TLS_ACCEPT_INVALID_CERTS=true
      - TZ=Europe/Prague
    volumes:
      - ./import:/opt/app/import
    labels:
      traefik.enable: "true"
 
      # Router pro HTTP přesměrování na HTTPS
      traefik.http.routers.teslamate-insecure.rule: "Host(`teslamate.nosekpetr.cz`)"
      traefik.http.routers.teslamate-insecure.entrypoints: "web"
      traefik.http.routers.teslamate-insecure.middlewares: "redirect"
 
      # Middleware pro přesměrování z HTTP na HTTPS
      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"

      traefik.http.routers.teslamate-ws.rule: "Host(`teslamate.nosekpetr.cz`) && Path(`/live/websocket`)"
      traefik.http.routers.teslamate-ws.entrypoints: "websecure"
      traefik.http.routers.teslamate-ws.tls: ""

      traefik.http.routers.teslamate.middlewares: "teslamate-auth"
      traefik.http.routers.teslamate.rule: "Host(`teslamate.nosekpetr.cz`)"
      traefik.http.routers.teslamate.entrypoints: "websecure"
      traefik.http.routers.teslamate.tls.certresolver: "tmdnschallenge"
      traefik.http.services.teslamate.loadbalancer.server.port: "4000"
 

      traefik.http.middlewares.teslamate-auth.basicauth.realm: "teslamate"
      traefik.http.middlewares.teslamate-auth.basicauth.usersfile: "/auth/.htpasswd"

    cap_drop:
      - all

  database:
    container_name: postgres
    image: postgres:17
    restart: always
    environment:
      - POSTGRES_USER=***
      - POSTGRES_PASSWORD=*** #insert your secure database password!
      - POSTGRES_DB=***
      - TZ=Europe/Prague
    volumes:
      - teslamate-db:/var/lib/postgresql/data

  grafana:
    container_name: grafana-teslamate
    image: teslamate/grafana:latest
    restart: always
    environment:
      - DATABASE_USER=***
      - DATABASE_PASS=*** #insert your secure database password!
      - DATABASE_NAME=***
      - DATABASE_HOST=database
      - TZ=Europe/Prague
      - GF_SERVER_DOMAIN=teslamate.nosekpetr.cz
      - GF_SERVER_ROOT_URL=%(protocol)s://%(domain)s/grafana
      - GF_SERVER_SERVE_FROM_SUB_PATH=true
    labels:
      # aktivace Traefiku pro tento kontejner
      traefik.enable: "true"
 
      # Router pro HTTP přesměrování na HTTPS
      traefik.http.routers.grafana-insecure.rule: "Host(`teslamate.nosekpetr.cz`)"
      traefik.http.routers.grafana-insecure.entrypoints: "web"
      traefik.http.routers.grafana-insecure.middlewares: "redirect"
 
      # Middleware pro přesměrování z HTTP na HTTPS
      traefik.http.middlewares.redirect.redirectscheme.scheme: "https"

      traefik.http.routers.grafana.rule: "Host(`teslamate.nosekpetr.cz`) && (Path(`/grafana`) || PathPrefix(`/grafana/`))"
      traefik.http.routers.grafana.entrypoints: "websecure"
      traefik.http.routers.grafana.tls.certresolver: "tmdnschallenge"
      traefik.http.services.grafana.loadbalancer.server.port: "3000"
    volumes:
      - teslamate-grafana-data:/var/lib/grafana

  proxy:
    image: traefik
    restart: always
    container_name: traefik
    ports:
      - "443:443" # HTTPS
      - "80:80"   # HTTP (případně přesměrování na HTTPS)
    environment:
      - CLOUDFLARE_EMAIL=***
      - CLOUDFLARE_DNS_API_TOKEN=***
      - LETSENCRYPT_EMAIL=***
    volumes:
      - ./acme/:/etc/acme/
      - ./.htpasswd:/auth/.htpasswd
      - /var/run/docker.sock:/var/run/docker.sock
      - ./traefik.yml:/etc/traefik/traefik.yml:ro

traefik.yml:

entryPoints:
  web:
    address: ":80"
  websecure:
    address: ":443"
    http:
      tls:
        certResolver: tmdnschallenge # Přidání certifikátového resolveru pro HTTPS
 

certificatesResolvers:
  tmdnschallenge:
    acme:
      email: "***"  # Email pro komunikaci s Let's Encrypt
      storage: "/etc/acme/acme.json"  # Úložiště certifikátů
      dnsChallenge:
        provider: cloudflare  # Použití Cloudflare API
        delayBeforeCheck: 10  # Volitelná hodnota pro zpoždění ověření

providers:
  docker:
    exposedByDefault: false

global:
  sendAnonymousUsage: false

Následující proměnné prostředí jsou klíčové pro správnou konfiguraci a umožňují provoz Grafany na specifické subcestě:

  • GF_SERVER_DOMAIN=teslamate.nosekpetr.cz

Tato proměnná definuje doménu, na které je Grafana dostupná. Umožňuje Traefiku směrovat požadavky na správný kontejner.

  • GF_SERVER_ROOT_URL=%(protocol)s://%(domain)s/grafana

Nastavuje kořenovou URL pro Grafanu včetně specifikace subcesty `/grafana`. Díky tomu je možné Grafanu provozovat na této subcestě bez konfliktů s jinými službami.

  • GF_SERVER_SERVE_FROM_SUB_PATH=true

Aktivuje podporu pro provozování Grafany na specifikované subcestě (`/grafana`). Bez této proměnné by Grafana nebyla správně dostupná.

Pro správné fungování DNS-01 Challenge s Let's Encrypt prostřednictvím Cloudflare je potřeba nastavit následující proměnné prostředí:

  • CLOUDFLARE_EMAIL=*

Emailová adresa spojená s vaším účtem Cloudflare. Používá se pro autentizaci k API.

  • CLOUDFLARE_DNS_API_TOKEN=*

API token s dostatečnými oprávněními k úpravě DNS záznamů vaší domény. Tento token musí být vytvořen v Cloudflare.

  • LETSENCRYPT_EMAIL=*

Emailová adresa použitá pro registraci u Let's Encrypt. Slouží k notifikacím, například o expiraci certifikátu.

Rozhodl jsem se využít existující docker s Postgres. Pro připojení k databázovému serveru Postgres jsem použil následující kroky.

Nejprve jsem se připojil k běžícímu kontejneru s Postgres pomocí příkazu:

docker exec -it postgres bash

V rámci kontejneru jsem se připojil k databázovému serveru s těmito údaji:

psql -U ev_tesla_user -d postgres

Pro zobrazení aktuálně existujících uživatelů jsem použil příkaz:

\du

Výsledek byl následující:

                                 List of roles
   Role name    |                         Attributes                         
----------------+------------------------------------------------------------
 ev_tesla_user  | Superuser, Create role, Create DB, Replication, Bypass RLS

Následně jsem vypsal všechny dostupné databáze příkazem:

\l

Nakonec jsem vytvořil nového uživatele, databázi a přiřadil jsem uživateli práva k této databázi následujícími příkazy:

CREATE USER nextcloud_user WITH PASSWORD 'secure_password';
CREATE DATABASE nextcloud_db OWNER nextcloud_user;

Traefik funguje jako reverse proxy, který směruje požadavky na služby podle pravidel definovaných v Docker label a konfiguračním souboru traefik.yml. Díky této integraci můžete snadno přidávat další služby (např. Nextcloud, Node-RED) bez nutnosti dalšího složitého nastavování.

Inspiroval jsem se v těchto zdrojích:

  • it/server/traefik-reverzni-proxy.1734914322.txt.gz
  • Poslední úprava: 2024/12/23 00:38
  • autor: Petr Nosek