Files
admin/.wiki/concepts/traefik-on-windows-docker-desktop.md

172 lines
8.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
title: Traefik на Windows Docker Desktop — нюансы
type: concept
tags: [traefik, docker, windows, letsencrypt, reverse-proxy]
sources: [../sources/nas-recovery-session-2026-05-18.md]
updated: 2026-05-19
---
# Traefik на Windows Docker Desktop
Запуск traefik 2.6.6 на Windows Docker Desktop (WSL2 backend) с импортированным production-конфигом и сертификатами — пара ловушек.
## Pitfall 1: traefik.yml не загружается автоматом
Symptom: traefik стартует, но routers из `data/custom/*.yml` ругаются на "non-existent resolver: letsEncrypt", сертификаты не отдаются.
Reason: traefik 2.x при отсутствии `--configFile=` ищет в дефолтных путях (`/etc/traefik/`, `./traefik.yml`), но Linux-контейнер с CWD=`/` и bind-mount `./data/traefik.yml:/traefik.yml` — почему-то не подхватывает.
**Fix:** явно указать в compose `command:`:
```yaml
command:
- "--configFile=/traefik.yml"
- "--log.level=DEBUG"
```
Подтверждение в логах: `Configuration loaded from file: /traefik.yml`.
## Pitfall 2: acme.json permissions через bind-mount
Symptom: traefik ругается `permissions 777 for /letsencrypt/acme.json are too open, please use 600` и **отключает** letsEncrypt resolver (даже если у него есть валидные certs).
Reason: bind-mount Windows-файла в Linux-контейнере **всегда** показывает permissions `0777`. Изменить через `chmod` нельзя — bind-mount не транслирует POSIX-perms на Windows-side.
**Fix:** named volume вместо bind для `/letsencrypt`:
```yaml
volumes:
- "traefik_letsencrypt:/letsencrypt" # вместо ./letsencrypt:/letsencrypt
- "./data/traefik.yml:/traefik.yml:ro"
- "./data/custom/:/custom/:ro"
# и снизу:
volumes:
traefik_letsencrypt:
```
Population volume единоразово:
```powershell
docker volume create traefik_traefik_letsencrypt
docker run --rm \
-v traefik_traefik_letsencrypt:/dest \
-v <local-path>/traefik/letsencrypt:/src:ro \
alpine sh -c "cp /src/acme.json /dest/acme.json && cp /src/acme.old.json /dest/acme.old.json && chmod 600 /dest/*"
```
## Pitfall 3: docker.sock provider не работает
Symptom:
```
Failed to retrieve information of the docker client and server host: Error response from daemon:
providerName=docker
```
С `-v /var/run/docker.sock:/var/run/docker.sock:ro` сокет монтируется (видно `srw-rw---- 1 root root` внутри контейнера), но соединение с daemon обрывается.
Reason: Docker Desktop на Windows транслирует docker.sock через WSL2 layer. Иногда permission-моэль клиента (libdocker) не принимает то, что предоставляет Desktop's proxy.
**Workaround:** **полностью отключить docker-provider, использовать только file-provider** для всех routes.
Маршруты, которые в производстве были как traefik labels на контейнерах (minio, elasticsearch, imgproxy с `traefik.http.routers...labels`), переписываются в `data/custom/<name>.yml` руками:
```yaml
# data/custom/minio.yml
http:
routers:
minio:
entryPoints: [https]
rule: Host(`minio.kzntsv.site`)
tls:
certResolver: letsEncrypt
service: minio
services:
minio:
loadBalancer:
servers:
- url: http://minio:9000 # docker DNS name (работает потому что все на одной network=proxy)
```
Подобно для elasticsearch (с basicAuth middleware), imgproxy (→ imgproxy-nginx:80), etc.
## Pitfall 4: file-provider не подхватывает изменения через bind-mount
`traefik.yml` имеет `providers.file.watch: true`, но на Windows bind-mount inotify не работает через WSL2-слой. Изменения в `data/custom/*.yml` не подхватываются автоматически.
**Workaround:** `docker compose restart traefik` после изменений в custom/. Несколько секунд downtime, ничего страшного.
## Production порты vs нашa конфигурация
Production traefik compose биндил на host:
```yaml
ports:
- "8000:80" # router forwards public 80 → host 8000 → container 80
- "4443:443"
- "8080:8080"
```
То есть **роутер делает port-translation** 80→8000, 443→4443. Не стандартные порты, но работает.
На Windows-хосте оставили те же порты (8000/4443) — не конфликтуют с локальным IIS на 80 (который не используется, но не выключен). И не требуют admin для bind.
## host.docker.internal
Из traefik-контейнера достучаться до VM (которая в VBox NAT, не в docker network) — через **`host.docker.internal`**:
```yaml
servers:
- url: http://host.docker.internal:18080/ # 18080 = VBox NAT-forwarded port → VM:80
```
Docker Desktop резолвит `host.docker.internal` в IP хоста (обычно 172.x.x.1 из docker bridge perspective). Дальше Windows-host обрабатывает 18080 → VBox NAT → VM:80 → IIS → CMS.
## Pitfall 5: X-Forwarded не используется ASP.NET — port utечка в admin URLs (RESOLVED 2026-05-19)
**Симптом:** CMS-admin генерирует URLs с internal IIS portом (`:8089` после attempt 2; ранее `:4443` от traefik) → browser HSTS auto-upgrade ломает TLS на нестандартном портe → admin SPA broken.
**Root cause:** Traefik 2.x **шлёт** `X-Forwarded-Proto: https` / `X-Forwarded-Host` / `X-Forwarded-Port` по-default (когда entrypoint https). НО CMS-код (`MoreThenCms.Admin\Mis\Web\Mvc\UrlHelpers\UrlHelpers.cs`) читает **socket-level** `SERVER_PORT` / `SERVER_PORT_SECURE`, **игнорируя** X-Forwarded-* headers. На VM работало случайно потому что IIS binding `:80``SERVER_PORT=80` стрипалось whitelist'ом `{80,443}` в коде.
**Fix:** URL Rewrite 2.1 + `<serverVariables>` rule на host IIS — переписывает `HTTPS`/`SERVER_PORT`/`SERVER_PORT_SECURE` на основе `X-Forwarded-Proto`. Детально в [[cms-server-port-leak-fix]].
Path B (поменять traefik http entrypoint :80 inside container чтобы IIS-binding :80 не получал loop от Docker NAT) — **не работает** на Windows Docker Desktop из-за WSL2 NAT quirk, см. там же.
## DNS-01 challenge через REGRU
`traefik.yml` имеет HTTP-01 challenge:
```yaml
certificatesResolvers:
letsEncrypt:
acme:
email: vitya.kuznetsov@gmail.com
storage: /letsencrypt/acme.json
httpChallenge:
entryPoint: http
```
В compose env уже:
```
REGRU_USERNAME=OpeItcLoc03
REGRU_PASSWORD=ytyYqC%u%QAJ
```
Эти креды для **DNS-01** через REGRU API. Просто закомментировать `httpChallenge` и активировать `dnsChallenge` в traefik.yml:
```yaml
certificatesResolvers:
letsEncrypt:
acme:
email: ...
storage: /letsencrypt/acme.json
dnsChallenge:
provider: regru
```
DNS-01 более надёжный (не требует public:80 reachable для validation) и работает даже если HTTP-01 challenge не пройдёт (например, домен временно на другой хостинге).
**Текущие 40 LE-сертификатов из acme.json валидны ~3 месяца** (LE default). Когда подойдут к истечению — пора переключать на DNS-01.
Связано: [[snolla-recovery-vm]], [[windows-recovery-host]], [[recovery-architecture-snapshot]].