6.1 KiB
title, type, tags, sources, updated
| title | type | tags | sources | updated | |||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| DB TLS через traefik raw TCP с self-signed certs | concept |
|
|
2026-05-20 |
Self-signed DB TLS через Traefik raw TCP
Pattern для выставления нескольких DB-контейнеров наружу (public internet) через traefik с минимумом сложности cert management. Self-signed на старте, LE-extraction позже.
Сочетается с traefik-tcp-passthrough-vs-starttls — там объяснено почему HostSNI(*) + raw TCP, не SNI passthrough.
Архитектура
Internet client
↓ TLS handshake (порт зависит от DB)
↓ <db>.vds.kzntsv.site:5432/3306/27017/6379
Traefik (raw TCP forward, без TLS inspection)
↓ docker network `proxy`
DB container (терминирует TLS своим self-signed cert)
↓ docker network `shared-dbs`
Other VDS containers (могут ходить без TLS по dns name `postgres`/`mariadb`/...)
Каждая DB:
- Подключена к двум networks:
proxy(для traefik) иshared-dbs(для других контейнеров VDS). - Имеет свой self-signed cert (CN =
<db>.vds.kzntsv.site) в./certs/. - Конфигурируется для TLS-required.
- Объявляет traefik label с
HostSNI(\*`)` на dedicated entrypoint port.
Traefik static config:
entryPoints:
postgres: { address: ":5432" }
mariadb: { address: ":3306" }
mongo: { address: ":27017" }
redis: { address: ":6379" }
ufw: allow на эти 4 порта.
Cert generation
for db in postgres mariadb mongo redis; do
openssl req -x509 -newkey rsa:2048 \
-keyout /opt/stacks/databases/$db/certs/server.key \
-out /opt/stacks/databases/$db/certs/server.crt \
-days 3650 -nodes \
-subj "/CN=$db.vds.kzntsv.site/O=kzntsv.site/C=RU" \
-addext "subjectAltName=DNS:$db.vds.kzntsv.site"
chmod 600 /opt/stacks/databases/$db/certs/server.key
done
DB-specific quirks
Postgres 16 (non-alpine, uid 999)
Postgres key file перм-checks: must be 600 + owned by postgres user OR root. Postgres alpine использует uid 70, не 999. Не-alpine Debian — uid 999. Если используем alpine — chown -R 70:70 certs/server.key; если debian — chown 999:999. Лучше debian (postgres:16) для совместимости с прочими стеками. Команда:
command:
- postgres
- -c
- ssl=on
- -c
- ssl_cert_file=/etc/postgres-certs/server.crt
- -c
- ssl_key_file=/etc/postgres-certs/server.key
MariaDB 11.4
command:
- --ssl-cert=/etc/mariadb-certs/server.crt
- --ssl-key=/etc/mariadb-certs/server.key
- --require-secure-transport=ON # форсит TLS для всех клиентов
MongoDB 7
Cert + key должны быть в одном PEM-файле (cat server.crt server.key > server.pem). Плюс Mongo 7 enforce'ит "chain of trust" — нужен --tlsCAFile (для self-signed — указываем server.crt сам как CA). --tlsAllowConnectionsWithoutCertificates нужен иначе сервер требует client cert.
command:
- --tlsMode=requireTLS
- --tlsCertificateKeyFile=/etc/mongo-certs/server.pem
- --tlsCAFile=/etc/mongo-certs/server.crt
- --tlsAllowConnectionsWithoutCertificates
- --bind_ip_all
Redis 7 alpine
command:
- redis-server
- --port
- "0" # disable non-TLS port
- --tls-port
- "6379"
- --tls-cert-file
- /etc/redis-certs/server.crt
- --tls-key-file
- /etc/redis-certs/server.key
- --tls-auth-clients
- "no" # без mTLS
- --requirepass
- <password>
Client connection examples
# Postgres (sslmode=require, не verify-full — self-signed)
psql "postgresql://postgres:$PG_PASS@postgres.vds.kzntsv.site:5432/postgres?sslmode=require"
# MariaDB (--ssl + --ssl-verify-server-cert=0)
mariadb -h mariadb.vds.kzntsv.site -P 3306 -u root -p$MARIA_PASS --ssl --ssl-verify-server-cert=0
# Mongo (tls=true + tlsAllowInvalidCertificates)
mongosh "mongodb://root:$MONGO_PASS@mongo.vds.kzntsv.site:27017/admin?tls=true&tlsAllowInvalidCertificates=true"
# Redis (--tls --insecure)
redis-cli --tls --insecure -h redis.vds.kzntsv.site -p 6379 -a $REDIS_PASS
Trade-off
- ✔ Уровень входа: 5 минут на cert + 5 минут на compose. Никаких lego/cert-manager headache на старте.
- ✔ Каждый клиент явно отключает verify — predictable failure mode (если cert меняется случайно — клиент сразу пишет в логи).
- ✗ Клиенты должны помнить
verify=disable. Production-tier клиенты обычно фейлят на self-signed по defaultу. - ✗ Cert не ротируется автоматически. 10-year validity = временное обходное.
- ✗ Для каждой DB — отдельный cert (не wildcard). Расширяемо, но если будет mongo + mongo-readonly — у каждого свой.
Roadmap к LE certs
Sidecar контейнер с lego (готовый container goacme/lego или собственный) который watch'ит traefik acme.json и каждый раз когда меняется (i.e. cert ротировался) — извлекает PEM-bundle на disk + триггерит docker exec <db> kill -HUP 1 для reload. Каждый DB получает auto-renewed LE cert. Это deferred — см. follow-up task в vds-kzntsv.
Где применено
4 shared DBs на vds-kzntsv: postgres / mariadb / mongo / redis. Self-signed 10-year certs в /opt/stacks/databases/<engine>/certs/. Strong random hex32 пароли в ~/projects/.common/secrets/vds-kzntsv.env.