🏗️ Архитектура — evrotrans-mailer

SMTP-релей с DKIM (evrotrans.net) + центральный cron-оркестратор парка + FTP-точка
Хост: vm-evrotrans-yandex-mailer-1750877542270
Дата: 2026-06-06
ОС: Ubuntu 20.04.6 LTS · ядро 5.4.0-216
Docker: 24.0.2 / Compose v2.18.1
Внешний IP: 31.44.10.106 · mail.evrotrans.net
Платформа: Yandex Cloud + Яндекс Cyber Backup
Содержание
  1. Назначение сервера
  2. Аппаратура и платформа
  3. Сетевая топология
  4. Стек контейнеров
  5. Каталог стека
  6. Потоки данных
  7. Доставляемость почты (DKIM/SPF/DMARC/PTR)
  8. Сервисы хоста
  9. Технологический стек
  10. Узкие места и заметки

1. Назначение сервера

Сервер выполняет три независимые функции:

📧 1. Почтовый релей (send-only)
Postfix + OpenDKIM

Принимает почту от приложений по внутренней сети, подписывает DKIM-ключом evrotrans.net и отправляет наружу. Локальных ящиков нет — транзитный отправитель. Активно работает.

⏱️ 2. Cron-оркестратор
контейнер crontab → SSH на хост

По расписанию заходит по SSH обратно на хост (как admin/user) и запускает обслуживающие скрипты: бэкапы, certbot, рестарт nginx/php/node. Сейчас в простое (задач нет).

📁 3. FTP-точка
vsftpd, chroot per-user

Доступ к домашним каталогам пользователей. Фактически не используется (лог пуст).

Плюс инфраструктурные агенты: резервное копирование (Яндекс/Acronis), мониторинг (Zabbix), гостевой агент Yandex Cloud.

2. Аппаратное обеспечение и платформа

ПараметрЗначение
ПлатформаYandex Cloud + backup от Яндекс.Облака
CPU2 vCPU, Intel Xeon (Icelake)
RAM964 MiB (🟠 в обрез: ~81 MiB free, активный своп ~460 MiB)
Swap1.5 GiB zram (zram0+zram1) + /swapfile 1 ГБ
Диск16 GB (/dev/vda), / = 15 GB ext4, занято 74%
Внешний IP31.44.10.106 (Ставрополь), домен mail.evrotrans.net
Uptime343 дня
🟠 При 1 GB RAM и постоянном свопе машина работает на пределе памяти; диск на 74% и растёт. Это узкое место для роста.

3. Сетевая топология

flowchart TB
  NET["🌐 Интернет
31.44.10.106 (NAT)"] subgraph HOST["ХОСТ vm-evrotrans-yandex-mailer"] direction TB subgraph IF["Сетевые интерфейсы"] direction TB ETH0["eth0 — 10.128.0.20/24
основной (default, metric 100)
• SSH 48008 (админы)
• FTP 48080
• Yandex Cloud metadata 169.254.169.254
• external-IP, исходящий бэкап"] ETH1["eth1 — 10.150.0.20/24
сервисный/почтовый VPC (metric 200)
• Postfix 25 (только тут!)
сюда ходят app-серверы за почтой"] end subgraph BR["Docker bridges"] direction TB BRIDGE["br-2b94d6a4a048 — 172.18.0.0/16"] PF["postfix
172.18.0.3"] AH["autoheal
172.18.0.2"] DOCKER0["docker0 — 172.17.0.0/16
не используется (linkdown)"] CRON["crontab-контейнер
network_mode=host
(делит стек хоста)"] end end NET --> ETH0 NET --> ETH1 ETH1 -- "порт 25" --> BRIDGE BRIDGE --> PF BRIDGE --> AH class NET ext class ETH0,ETH1,BRIDGE,DOCKER0,IF,BR net class PF,AH mail class CRON svc class DOCKER0 ext classDef mail fill:#241a0c,stroke:#ffb454,color:#dbe4ee classDef svc fill:#10242b,stroke:#3fb6c9,color:#cfeef5 classDef net fill:#11202e,stroke:#4ea1ff,color:#dbe4ee classDef ext fill:#12161c,stroke:#5a6675,color:#cfd8e2
Рис. 1 — Сетевая топология: внешний NAT, интерфейсы eth0/eth1 и Docker-мосты.
ИнтерфейсПодсетьНазначение
eth010.128.0.0/24Управление: SSH, FTP, внешний IP (NAT), метаданные Yandex Cloud, исходящий бэкап
eth110.150.0.0/24Сервисная сеть: только сюда проброшен порт 25 Postfix
br-2b94d6a4a048172.18.0.0/16Внутренняя сеть Docker-стека (postfix ↔ autoheal)
docker0172.17.0.0/16linkdown, не задействован

DNS: systemd-resolved (127.0.0.53), search-домены ru-central1.internal, auto.internal (Яндекс.Облако).

4. Стек контейнеров (Compose, проект stable)

flowchart TB
  subgraph HOST["ХОСТ vm-evrotrans-yandex-mailer"]
    direction LR
    PF["postfix
catatnight/postfix (2.11)
:25 → 10.150.0.20
+ OpenDKIM milter :12301
net: bridge .18"] AH["autoheal
willfarrell/autoheal 1.2.0
следит за health контейнеров,
перезапускает нездоровые
⚠️ docker.sock:rw"] CRON["crontab
ddan9/crontab
network_mode: host
планировщик (dormant)
→ SSH назад на хост
keys → /root/.ssh · host → /var/host"] end AH -. "управляет (docker.sock:rw)" .-> PF AH -. "управляет (docker.sock:rw)" .-> CRON class PF mail class AH svc class AH warn class CRON svc classDef mail fill:#241a0c,stroke:#ffb454,color:#dbe4ee classDef svc fill:#10242b,stroke:#3fb6c9,color:#cfeef5 classDef warn fill:#2a1416,stroke:#ff5c5c,color:#ffd9d9
Рис. 2 — Стек контейнеров Compose: postfix, autoheal и dormant-планировщик crontab.

Три сервиса, restart: always, у всех no-new-privileges + apparmor=docker-default, privileged: false.

4.1 postfix — SMTP-релей с DKIM

4.2 autoheal — авто-восстановление

Следит за контейнерами с healthcheck (интервал 60 с) и перезапускает нездоровые. Для этого монтирует /var/run/docker.sock:rw (управляет Docker'ом хоста).

4.3 crontab — планировщик/оркестратор

Образ ddan9/crontab, network_mode: host. Обёртки в crontab/host/ заходят по SSH на localhost:48008:

СкриптДействие
cron_admin.shsshpass ssh r3ddan9@localhost "echo PASS | sudo -S bash -c '<cmd> &'" — команда под root
cron_user.shто же, но как evrotrans без sudo
*_key.shварианты по SSH-ключу (без пароля)
initialize_keys.shгенерит ключ контейнера и прогревает known_hosts
🟠 Архитектурная заметка: механизм передаёт пароль через sshpass и echo PASS | sudo -S, пароли — из .env (plaintext) и захардкожены в скриптах. Правильнее — SSH-ключи + sudoers NOPASSWD на конкретные команды. См. AUDIT_REPORT_mailer.

5. Каталог стека /home/evrotrans/stable/

stable/
├── .env                       # конфиг и СЕКРЕТЫ (пароли, токены) — см. аудит
├── docker-compose.yml         # описание 3 сервисов
├── postfix/
│   ├── config/main.cf
│   └── domainkeys/            # DKIM-ключи evrotrans.net (+ .bak)
├── crontab/
│   ├── config/                # расписания cron  ← ПУСТО (dormant)
│   ├── host/                  # обёртки SSH-запуска
│   ├── keys/                  # SSH-ключи контейнера ← пусто
│   └── logs/cron.log          # 9.9 MB спама "wakeup"
├── host/                      # ★ БИБЛИОТЕКА ОБСЛУЖИВАЮЩИХ СКРИПТОВ (с web-сервера)
│   ├── cron_backup_catcher_*.sh / cron_home_backuper_*.sh / cron_backup_cleaner_*.sh
│   ├── cron_certbot_renew.sh
│   ├── bash_restart_{php,node,containters}.sh
│   └── .*.bak                 # ~30 архивных заданий (nginx, mysql, openvpn, api3, gds2 ...)
└── backup/config/
🔎 Скрипты host/ оперируют путями /home/etrans/stable/{web,mysql,nginx,...} и контейнерами (stable_mysql_1, stable-certbot-1), которых на mailer нет — это общая библиотека, скопированная с web-сервера, здесь в основном инертна. config/ пуст, cron логирует только wakeup → автоматизация dormant.

🔴 Скрипты содержат захардкоженные секреты (MySQL root vsyvzoajoa, ключ шифрования, SSH-пароль V2LaYJMAc0 к 77.222.52.97) — см. AUDIT_REPORT_mailer §2-bis.

6. Потоки данных

6.1 Отправка почты (основной поток)

flowchart TB
  APP["App-серверы евротранса"]
  PORT["10.150.0.20:25"]
  PF["[postfix]"]
  DKIM["OpenDKIM
подпись (evrotrans.net)"] RCPT["🌐 Интернет
почтовые серверы получателей
(Gmail, Yandex, …)"] APP -- "SMTP + SASL-auth (webmaster), внутр. сеть" --> PORT PORT --> PF PF --> DKIM DKIM -- "прямая отправка по MX (relayhost пуст), TLS=may" --> RCPT class APP ext class PORT net class PF,DKIM mail class RCPT ext classDef mail fill:#241a0c,stroke:#ffb454,color:#dbe4ee classDef net fill:#11202e,stroke:#4ea1ff,color:#dbe4ee classDef ext fill:#12161c,stroke:#5a6675,color:#cfd8e2
Рис. 3 — Поток 6.1: отправка почты от приложений через Postfix и DKIM наружу.

6.2 Плановое обслуживание (оркестрация) — на исходном web-сервере

flowchart TB
  CRON["[crontab контейнер]"]
  WRAP["cron_admin.sh / cron_user.sh
sshpass/ssh → localhost:48008"] SSHD["[sshd]"] HOST["скрипты host/
(как r3ddan9 sudo / evrotrans)"] HB["🏠 home_backuper
tar --lzma → openssl aes-256-cbc
(-k w7yNyNnJVx4N) → backup/{config,db,web}
database: mysqldump -p\"vsyvzoajoa\""] BC["📥 backup_catcher
sshpass scp -P 8008
etrans@77.222.52.97:…/backup_*.tar.gz"] CB["🔐 certbot renew
docker exec stable-certbot-1 certbot renew"] RC["♻️ restart_containers
compose down + system prune -af + up --build
(агрессивно!)"] CL["🧹 cleaner
удаление старых дампов/логов"] R77["🌐 бэкап-узел
77.222.52.97:8008"] CRON -- "по расписанию (config/ пусто = dormant)" --> WRAP WRAP --> SSHD SSHD -- "как r3ddan9 (sudo) / evrotrans" --> HOST HOST --> HB HOST --> BC HOST --> CB HOST --> RC HOST --> CL BC -. "scp" .-> R77 class CRON,WRAP svc class CRON warn class SSHD,HOST svc class WRAP warn class HB,BC,CB,CL svc class RC warn class R77 ext classDef svc fill:#10242b,stroke:#3fb6c9,color:#cfeef5 classDef ext fill:#12161c,stroke:#5a6675,color:#cfd8e2 classDef warn fill:#2a1416,stroke:#ff5c5c,color:#ffd9d9
Рис. 4 — Поток 6.2: оркестрация обслуживания через SSH с хоста (бэкапы, certbot, рестарты).
⚠️ Модель бэкапа: tar(lzma) → AES-256-CBC → файл; «catcher» по scp стягивает архивы с 77.222.52.97:8008. Ключ шифрования зашит в скриптах и равен .env-ключу — шифрование декоративное (ключ рядом с данными).

6.3 Инфраструктурные потоки (активные соединения)

6-bis. Доставляемость почты (DKIM / SPF / DMARC / PTR)

Сервер реально отправляет уведомительную почту от webmaster@evrotrans.net. Аутентификационная обвязка домена согласована:

МеханизмВ DNSСостояние
DKIM default._domainkeyv=DKIM1; k=rsa; p=MIGf… совпадает с локальным ключом — подпись валидна
SPFv=spf1 ip4:…31.44.10.106… include:spf.mail.ru ~all IP сервера в списке (+ 4 IP парка)
PTR 31.44.10.106evrotrans.net совпадает с HELO/myhostname — важно для антиспама
DMARCv=DMARC1; p=none; …🟠 p=none — только мониторинг, без принуждения
MX домена10 emx.mail.ru, beget, 50 mail.evrotrans.netвходящая на mail.ru/beget; этот хост — MX 50 (резерв), по сути исходящий
✅ Триада SPF+DKIM+PTR выровнена — письма имеют хорошие шансы пройти антиспам. Слабое место — DMARC p=none (поднять до quarantine/reject после анализа rua-отчётов).
🔎 Часть писем в логе — deferred с кодом 452-4.2.2 out of storage space (ящики получателей переполнены) и битые адреса (опечатка yndex.ru). Не проблема сервера, но признак устаревших списков рассылки.

7. Сервисы хоста (вне Docker)

СервисПортНазначение
sshd48008Доступ админов и канал оркестрации cron-контейнера
vsftpd48080FTP к домашним каталогам (не используется)
zabbix-agent210050Мониторинг
Яндекс/Acronis Backuplocalhost + 37845Резервное копирование, антивирус (mms, aakore, …)
google-guest-agentУправление ключами/пользователями из метаданных Yandex Cloud
unattended-upgradesАвтообновления безопасности

8. Технологический стек

КомпонентВерсияЗаметка
ОСUbuntu 20.04.6 LTSядро 5.4.0-216 (актуальное)
Docker Engine24.0.2доступно обновление до 28
Docker Composev2.18.1проект stable, 3/3 running
Postfix2.11.0🟠 очень старый (образ ~11 лет)
autoheal1.2.0образ ~4 года
crontabddan9/crontabкастомный образ, ~3 года

9. Узкие места и заметки

Сильные стороны дизайна:
🟠 Узкие места / технический долг:
Риски безопасности этой архитектуры (секреты в .env и в скриптах, FTP cleartext, zabbix в группе docker, docker.sock в autoheal, NOPASSWD-orphan) подробно разобраны в аудите безопасности.