import: merge .wiki/concepts/ from temp prefix into existing dir (history preserved via merge+rename)
This commit is contained in:
66
.wiki/concepts/traefik-tcp-passthrough-vs-starttls.md
Normal file
66
.wiki/concepts/traefik-tcp-passthrough-vs-starttls.md
Normal file
@@ -0,0 +1,66 @@
|
||||
---
|
||||
title: Traefik TCP passthrough vs STARTTLS-protocols
|
||||
type: concept
|
||||
tags: [traefik, tls, tcp, sni, postgres, mariadb, mongo, redis, gotcha]
|
||||
sources: [../sources/vds-kzntsv-bootstrap-2026-05-20.md]
|
||||
updated: 2026-05-20
|
||||
---
|
||||
|
||||
# Traefik TCP passthrough не работает с STARTTLS
|
||||
|
||||
## Симптом
|
||||
|
||||
Traefik TCP router с `HostSNI(<hostname>)` + `tls.passthrough=true`. Mongo / Redis (TLS-from-start) — TLS handshake проходит, виден правильный cert. **Postgres / MariaDB — TLS connection hangs / timeout**, никакого handshake'а не происходит.
|
||||
|
||||
Probe `openssl s_client -connect postgres.vds.kzntsv.site:5432 -servername postgres.vds.kzntsv.site -starttls postgres` → timeout 10s.
|
||||
|
||||
## Root cause
|
||||
|
||||
`tls.passthrough=true` означает: traefik не терминирует TLS, а маршрутизирует TCP-connection raw. Для маршрутизации по `HostSNI` traefik читает SNI **из TLS ClientHello** — первого TLS-сообщения, отправляемого клиентом сразу после TCP handshake.
|
||||
|
||||
**Mongo / Redis** (TLS-from-start) — клиент шлёт TLS ClientHello как первое сообщение. SNI там присутствует. Traefik читает, маршрутизирует, остаток TCP forwarding'ом. Работает.
|
||||
|
||||
**Postgres / MariaDB / MySQL** — используют **STARTTLS pattern**:
|
||||
1. Клиент после TCP-handshake шлёт `SSLRequest` (plaintext, специфичный для протокола).
|
||||
2. Сервер отвечает `'S'` (готов на TLS).
|
||||
3. **Только после этого** клиент начинает TLS ClientHello.
|
||||
|
||||
Первые байты от клиента — **plaintext protocol bytes**, не TLS ClientHello. Traefik ищет SNI в TLS ClientHello, не находит → не может смаршрутизировать → connection hangs (висит на чтении от клиента).
|
||||
|
||||
## Решение — Raw TCP forward + HostSNI(*)
|
||||
|
||||
```yaml
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- "traefik.tcp.routers.postgres.rule=HostSNI(`*`)"
|
||||
- traefik.tcp.routers.postgres.entrypoints=postgres
|
||||
- traefik.tcp.routers.postgres.service=postgres
|
||||
- traefik.tcp.services.postgres.loadbalancer.server.port=5432
|
||||
# NO tls.* — traefik forwards raw TCP без inspect
|
||||
```
|
||||
|
||||
Каждая DB — на **своей dedicated entrypoint port** (`postgres:5432`, `mariadb:3306`, `mongo:27017`, `redis:6379`). Routing — по entrypoint, не по SNI. Traefik просто forwards bytes к backend без чтения TLS ClientHello. STARTTLS protocols договариваются о TLS напрямую с DB-контейнером.
|
||||
|
||||
`HostSNI(\`*\`)` — единственное значение для TCP router'а **без** `tls.*` секции (traefik требует какое-то правило, wildcard catch-all уместен здесь).
|
||||
|
||||
## Trade-off
|
||||
|
||||
- ✔ Работает для всех 4 protocols (STARTTLS + TLS-from-start).
|
||||
- ✔ Uniform config, не нужно ветвить compose под тип protocol'а.
|
||||
- ✗ Каждая DB требует своего entrypoint:port в traefik static config. 4 DB → 4 entrypoints. Если хотим много инстансов одного движка — теряем muxing на один port через SNI.
|
||||
- ✗ Traefik в этом случае не видит контент трафика — только TCP-bytes мимо. Невозможно сделать middleware (rate limit, IP allowlist на уровне traefik) — это нужно делать на DB side.
|
||||
|
||||
## Альтернативный путь (отвергнут)
|
||||
|
||||
Traefik TLS termination (без passthrough) + SNI muxing на 443 entrypoint. Не работает для PG/MariaDB по той же причине — STARTTLS встроен в protocol stack, и traefik терминирующий TLS отдаёт plaintext бэкенду, который ждёт SSLRequest и предлагает свой TLS handshake. Двойная TLS обёртка вокруг STARTTLS — broken.
|
||||
|
||||
LE-cert provisioning при passthrough TCP — на DB side, не traefik. Сейчас self-signed; follow-up — lego sidecar extracting из traefik acme.json.
|
||||
|
||||
## Где применено
|
||||
|
||||
Все 4 DBs на [`vds-kzntsv`](../entities/vds-kzntsv.md): postgres:5432, mariadb:3306, mongo:27017, redis:6379. Traefik v2.11 LTS. См. также [`db-tls-self-signed-via-traefik-raw-tcp`](db-tls-self-signed-via-traefik-raw-tcp.md) — связанный паттерн self-signed cert provisioning'а.
|
||||
|
||||
## Ссылки
|
||||
|
||||
- Traefik docs: [TCP routers](https://doc.traefik.io/traefik/routing/routers/#configuring-tcp-routers)
|
||||
- Сессия где обнаружили: [`vds-kzntsv-bootstrap-2026-05-20`](../sources/vds-kzntsv-bootstrap-2026-05-20.md) Phase 2.
|
||||
Reference in New Issue
Block a user