Skip to content

Инфраструктура: api.conf.example, харднинг контейнеров, гигиена compose - #84

Merged
friench merged 1 commit into
mainfrom
infra/deployment-hardening
Jul 7, 2026
Merged

Инфраструктура: api.conf.example, харднинг контейнеров, гигиена compose#84
friench merged 1 commit into
mainfrom
infra/deployment-hardening

Conversation

@friench

@friench friench commented Jul 7, 2026

Copy link
Copy Markdown
Owner

Второй заход по итогам ревью — инфраструктура/деплой. Только конфиги и доки, кода нет.

#68 — недостающие, но задокументированные части

  • nginx/user_conf.d/api.conf.example — харднутый reverse-proxy vhost панели/API (TLS 1.2/1.3, HSTS, OCSP stapling, security-заголовки, client_max_body_size 30m под вложения, forwarded-заголовки для TRUST_PROXY=1). nginx грузит только *.conf, поэтому .example инертен до копирования. Закрывает «нет HTTPS-входа к панели из коробки».
  • nginx/nginx-certbot.env.example — файл, который deployment.md велел копировать, но которого не было.
  • Правки доков (deployment.md, CLAUDE.md), ссылавшихся на закоммиченный api.conf.

#69 — сузить blast-radius Docker-доступа

  • mail-api: no-new-privileges + cap_drop: ALL (и так USER node).
  • Dokploy-compose: сырой mount /var/run/docker.sock + group_add заменён на встроенный docker-socket-proxy (inspect/exec/restart), как в основном стеке feat(security): доступ к Docker через docker-socket-proxy #59. Убран более не нужный DOCKER_GID из env и доков.
  • architecture.md: честно задокументирован остаточный риск — EXEC = root внутри существующих контейнеров (mailserver/nginx), лучше сырого сокета, но это потолок прокси.

#70 — гигиена compose

  • Пин docker-socket-proxy (самый security-sensitive образ) с :latest:0.3.0.
  • Общий x-logging anchor кап-лимитит json-file логи на всех сервисах (было только у mailserver).
  • mem_limit на предсказуемых сервисах (nginx/mail-api/socket-proxy); mailserver оставлен без кэпа с пояснением (слишком низкий кэп OOM-killит доставку почты).
  • Healthcheck для nginx через новый /healthz в fallback-vhost.
  • chmod 600 для секретных .env в доках.

depends_on оставлен как start-order (не service_healthy): docker-mailserver намеренно unhealthy на первом бутстрапе, пока не создан первый ящик — а он создаётся через mail-api. Health-гейт устроил бы дедлок бутстрапа.

Проверки

Оба compose-файла проходят YAML-парс. Docker CLI в этой среде недоступен — docker compose config не гонял; при мерже стоит глянуть docker compose config -q на хосте перед деплоем. Кода/тестов в PR нет.

Что осталось по #70 (не закрываю)

Closes #68
Closes #69
Refs #70

🤖 Generated with Claude Code

…ygiene

Deployment-layer hardening from the code review.

#68 — commit the missing, documented control-plane pieces:
- nginx/user_conf.d/api.conf.example: hardened reverse-proxy vhost for the
  dashboard/API (TLS 1.2/1.3, HSTS, OCSP stapling, security headers,
  client_max_body_size 30m for attachment sends, forwarded headers for
  TRUST_PROXY=1). nginx only loads *.conf, so the template is inert until copied.
- nginx/nginx-certbot.env.example: the file deployment.md already told operators
  to copy but that didn't exist.
- Fix docs (deployment.md, CLAUDE.md) that referenced a committed api.conf.

#69 — shrink the Docker-access blast radius:
- mail-api: no-new-privileges + cap_drop ALL (runs as USER node already).
- Dokploy compose: replace the raw /var/run/docker.sock mount + group_add with a
  bundled docker-socket-proxy (inspect/exec/restart only), mirroring the default
  stack. Drops the now-unused DOCKER_GID from env + docs.
- architecture.md: document the residual risk honestly (EXEC = root inside the
  existing containers; better than a raw socket but that's the ceiling).

#70 — compose hygiene:
- Pin docker-socket-proxy (the most security-sensitive image) off :latest to 0.3.0.
- Shared x-logging anchor caps json-file logs on every service (was mailserver-only).
- mem_limit on the predictable services (nginx/mail-api/socket-proxy); mailserver
  left uncapped with guidance (a too-low cap OOM-kills mail).
- nginx healthcheck via a new /healthz in the fallback vhost.
- Document chmod 600 for the secret-bearing .env files.

depends_on kept as start-order (not service_healthy): docker-mailserver is
intentionally unhealthy on first boot until the first mailbox exists, and that
mailbox is created through mail-api — a health gate would deadlock bootstrap.

Closes #68
Closes #69
Refs #70

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@friench
friench merged commit d53445d into main Jul 7, 2026
1 check passed
@friench
friench deleted the infra/deployment-hardening branch July 7, 2026 23:12
friench added a commit that referenced this pull request Jul 8, 2026
Regression from #84: pinning docker-socket-proxy to 0.3.0 (haproxy-based)
together with read_only:true + tmpfs:/run left it unable to write its generated
config to /usr/local/etc/haproxy/haproxy.cfg (Read-only file system), so the
proxy crash-looped and mail-api could not reach the Docker API — every
provisioning call (create/update/delete mailbox, password change, DKIM, DMS<->DB
sync) returned HTTP 500 with "getaddrinfo ENOTFOUND docker-socket-proxy".

Add /usr/local/etc/haproxy to the tmpfs list in both the default and Dokploy
compose files, keeping the read-only rootfs hardening intact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant