infra/docs/DECISIONS.md
jack 1e638055c8
Some checks failed
CI/CD / syntax-check (push) Successful in 1m23s
CI/CD / deploy (push) Has been cancelled
feat(mail): rename mail→mx, webmail→mail.csrx.ru + reliability
Rename:
- docker-mailserver: hostname mail → mx, OVERRIDE_HOSTNAME → mx.csrx.ru
- Traefik route: webmail/domain_webmail → mail/domain_mail
- domain_webmail removed, domain_mail + domain_mx added to main.yml
- certbot cert: mail.csrx.ru → mx.csrx.ru

Email reliability improvements:
- certbot renewal cron (03:15 + 15:15 daily)
- deploy-hook: auto-reload Postfix+Dovecot after cert renewal
- POSTFIX_MESSAGE_SIZE_LIMIT=26214400 (25 MB)
- SPF hardened: ~all → -all
- DMARC hardened: p=none → p=quarantine, added ruf + fo=1 + adkim/aspf strict
- autodiscover/autoconfig CNAME records for mail client setup
- dns-zone.zone fully updated with architecture comments

Docs:
- STATUS.md: full mail architecture section, client settings, DNS table
- BACKLOG.md: rDNS task + DNS migration steps
- DECISIONS.md: mx/mail split rationale

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-22 20:07:59 +07:00

116 lines
7.5 KiB
Markdown
Raw 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.

# Журнал архитектурных решений
> Фиксируй здесь: что решили, почему, какие альтернативы отвергли.
> Формат: дата | решение | причина | альтернативы
---
## Инфраструктура
### Два сервера (main + tools)
**Дата:** начало проекта
**Решение:** Разделить сервисы на два VPS: main для ядра, tools для вспомогательных.
**Причина:** main (1 vCPU / 2 GB RAM) перегружен — Plane + Forgejo + мониторинг занимают почти всю память. tools позволяет разгрузить.
**Итог:** Outline, n8n, mail работают на tools. Traefik на main проксирует к tools через IP:PORT.
### Traefik вместо nginx/caddy
**Дата:** начало проекта
**Решение:** Traefik v3 как единственный точка входа.
**Причина:** Автоматический TLS через Let's Encrypt, file provider для маршрутов, удобная интеграция с Docker.
**Альтернативы:** nginx + certbot (ручной renewal), Caddy (проще но меньше гибкости).
### Cloudflare proxy + UFW whitelist
**Дата:** начало проекта
**Решение:** Порты 80/443 открыты только для IP Cloudflare.
**Причина:** Скрывает реальный IP сервера, защита от DDoS на уровне Cloudflare, бесплатный WAF.
**Важно:** Некоторые сервисы (wiki, vault, webmail) должны быть DNS-only (оранжевое облако off) — когда Cloudflare не поддерживает нужные порты или протоколы.
### Wildcard TLS через DNS-01
**Дата:** начало проекта
**Решение:** Один сертификат `*.csrx.ru` вместо отдельных на каждый поддомен.
**Причина:** Новый сервис — ноль ожидания сертификата. Cloudflare DNS-01 работает без HTTP-challenge.
**Конфиг:** `roles/services/templates/traefik/traefik.yml.j2` — certresolver `letsencrypt`.
---
## Безопасность
### Authelia для 2FA
**Дата:** начало проекта
**Решение:** Authelia + TOTP защищает admin-панели (Traefik dashboard, Plane god-mode).
**Причина:** Одна точка SSO, не нужно настраивать auth в каждом сервисе.
**Альтернативы:** Keycloak (тяжелее, требует больше ресурсов), Authentik (сложнее).
### CrowdSec community edition
**Дата:** начало проекта
**Решение:** CrowdSec без платного API ключа.
**Причина:** Бесплатная tier достаточна. Анализирует логи Traefik + auth.log.
**Ограничение:** Нет расширенных threat feeds — только community reputation.
### Syncthing — удалён
**Дата:** 2026-03
**Решение:** Удалить Syncthing полностью из инфраструктуры.
**Причина:** Не используется. Открытые порты (22000, 21027) без сервиса — лишняя attack surface.
**Что убрали:** UFW rules, docker image, domain_sync, переменную vault, Authelia правило.
---
## Сервисы
### Outline wiki — email magic link
**Дата:** 2026-02
**Решение:** Авторизация через email magic link (SMTP → docker-mailserver).
**Причина:** Outline не поддерживает простую login/password аутентификацию без SSO.
**Проблемы решённые:**
- `guestSignin=true` в БД обязателен для email auth
- `jwtSecret` в БД хранится в зашифрованном виде — при смене `SECRET_KEY` нужно пересоздать
### docker-mailserver вместо внешнего SMTP
**Дата:** 2026-03
**Решение:** Self-hosted Postfix + Dovecot на tools-сервере.
**Причина:** Полный контроль, аккаунты @csrx.ru для приёма и отправки, нет зависимости от внешних SMTP.
**Конфиг:** SSL_TYPE=letsencrypt через certbot с Cloudflare DNS-01. DKIM через opendkim.
**Альтернативы:** Mailgun/SendGrid (только отправка, без приёма), Maddy (менее зрелый).
### Бэкап в Timeweb S3 cold
**Дата:** 2026-03
**Решение:** Hourly backup → S3 cold storage `visual-backup/data/`, 7 дней хранения.
**Причина:** Дешевле горячего хранилища, бэкапы не нужны быстро — приемлемая задержка восстановления.
**Команда:** `aws s3 cp ... --storage-class COLD`
### SnappyMail вместо Roundcube
**Дата:** 2026-03
**Решение:** djmaze/snappymail как веб-клиент почты на `mail.csrx.ru`.
**Причина:** Лёгкий, современный UI, простой Docker образ.
**Альтернативы:** Roundcube (тяжелее, требует MySQL), Rainloop (заброшен).
### mx.csrx.ru вместо mail.csrx.ru для MX-сервера
**Дата:** 2026-03
**Решение:** docker-mailserver hostname = `mx.csrx.ru`, веб-клиент = `mail.csrx.ru`.
**Причина:** Стандарт индустрии — MX-хост называется `mx`, пользовательский интерфейс — `mail`.
Раньше оба указывали на разные вещи под одним именем, что запутывало.
**DNS:** `mx.csrx.ru` → 85.193.83.9 (DNS-only, прямые порты SMTP/IMAP).
`mail.csrx.ru` → 87.249.49.32 (Cloudflare proxied, HTTPS вебмейл через Traefik).
**SPF:** `v=spf1 mx -all``-all` жёстче чем `~all`, явно запрещает чужие отправители.
**DMARC:** `p=quarantine` — подозрительные письма в спам (было `p=none` — только мониторинг).
---
## CI/CD
### Forgejo Actions
**Дата:** начало проекта
**Решение:** Forgejo Actions + act_runner для CI/CD деплоя.
**Причина:** Self-hosted, интегрировано с Forgejo, совместимо с GitHub Actions синтаксисом.
**Принцип:** Push в master → `ansible-playbook deploy.yml` + `tools.yml`. Никаких ручных правок на сервере.
---
## Известные компромиссы
| Компромисс | Почему так | Что улучшить |
|-----------|-----------|-------------|
| act_runner монтирует `/var/run/docker.sock` | Нужен для запуска job-контейнеров | Rootless Docker или ограниченный API |
| Plane использует `:stable` тег | Plane не публикует версионные теги | Мониторить GitHub releases |
| Мониторинг на main (не на отдельном сервере) | mon-сервер пока не задеплоен | Создать `playbooks/mon.yml` |
| Нет бэкапа tools-сервера | Бэкап роль подключена только к main | Добавить в `tools.yml` |