From 1cee142631bb905669cf6dfdf2b8930852fc517b Mon Sep 17 00:00:00 2001 From: estorozhenko Date: Sun, 13 Sep 2026 07:19:17 +0000 Subject: [PATCH] fix(smtp): GTS_SMTP_FROM_ADDRESS -> GTS_SMTP_FROM (empty From header fixed); openspec: smtp-email spec --- STATUS.md | 42 +++++++++++ docker-compose.yml | 11 ++- openspec/specs/gotosocial/smtp-email/spec.md | 74 ++++++++++++++++++++ 3 files changed, 125 insertions(+), 2 deletions(-) create mode 100644 openspec/specs/gotosocial/smtp-email/spec.md diff --git a/STATUS.md b/STATUS.md index 2d832aa..beab7c7 100644 --- a/STATUS.md +++ b/STATUS.md @@ -46,10 +46,52 @@ Split-domain работает: аккаунты `@user@dedinit.ru`, хост soc - Кастомный page_footer.tmpl (без 🏳️🌈🏳️⚧️🦥 в подвале), смонтирован volume `./web/template/page_footer.tmpl:ro` - Проверка при обновлении GtS: `./scripts/check-pride.sh` (python3-детект юникода, нормализация; grep-эмодзи ловил ложные срабатывания) +### Федеративная лента анонимно (2026-09-11) +- Пустая федальная лента = GtS 0.22.1 по умолчанию отдаёт 401 на /api/v1/timelines/public без OAuth-токена + (`instance-expose-public-timeline: false`). Phanpy открывает ленту до логина → пусто. +- ФИКС: `GTS_INSTANCE_EXPOSE_PUBLIC_TIMELINE: "true"` в docker-compose.yml + recreate. + Проверено: анонимный GET → 200, статусы есть и снаружи (https://social.dedinit.ru). +- **Тренды пустые — НЕ баг конфига**: в GtS 0.22.1 (последний релиз) `/api/v1/trends/{tags,statuses,links}` + захардкожены на `EmptyJSONArray` («NOT FULLY IMPLEMENTED») — исходники internal/api/client/trends/*. + Никакой env-опцией не включаются. Ждать реализации в новых версиях GtS. + +### Почта: причина найдена и исправлена (2026-09-13) +- Симптом: письмо подтверждения приходило В СПАМ и с ПУСТЫМ заголовком `From:` + (пользователь: «в поле отправителя ничего не заполнено, поэтому письмо полностью + было заблокировано», X-Yandex-Spam: 4). +- **Первопричина**: в docker-compose.yml была `GTS_SMTP_FROM_ADDRESS` — НЕСУЩЕСТВУЮЩИЙ ключ. + GtS ждёт `smtp-from` → env `GTS_SMTP_FROM` (internal/config + vendored viper: + дефисы→подчёркивания, префикс GTS_, UPPER). `GetSMTPFrom()` возвращал "" → заголовок + `From: ` пустой при корректном envelope (`Authenticated sender: social@dedinit.ru`). +- **ФИКС**: `GTS_SMTP_FROM: "social@dedinit.ru"` в compose + перезапуск контейнера. + Проверено: env в контейнере `GTS_SMTP_FROM=social@dedinit.ru`, инстанс жив (HTTPS 200), + пользователь kunikov подтверждён вручную (`admin account confirm`), письмо из спама достано. +- SMTP Jino при этом был рабочим с самого начала: login OK, RCPT OK, `250 Ok: queued`. + RealSender (не Noop): GTS_SMTP_HOST непустой → `email.NewSender()`. +- ОСТАЁТСЯ: у dedinit.ru НЕТ SPF и DMARC (DKIM `default` есть). Яндекс кладёт письма в спам + без SPF/DMARC независимо от From. Нужно: SPF TXT `v=spf1 a mx include:jino.ru ~all` + (или из панели Jino) + DMARC `_dmarc.dedinit.ru`. + +### Системное: systemd1 D-Bus завис → dockerd переведён на cgroupfs (2026-09-13) +- При перезапуске gotosocial `docker compose up` упал: `Failed to activate service + 'org.freedesktop.systemd1': timed out` — systemd1 не отвечал по D-Bus (25s таймаут), + системные вызовы systemctl/dbus-send тоже зависали. Причина не выяснена + (фоновый шум mount.davfs «open files exceed max cache size by 3400 MiB» в журнале). +- ВАЖНО: cgroup-драйвер docker был `systemd` → каждый старт контейнера шёл через systemd1. +- **ФИКС**: `/etc/docker/daemon.json` → `"exec-opts": ["native.cgroupdriver=cgroupfs"]`, + dockerd перезапущен вручную (nohup dockerd --containerd=... ). Все контейнеры живы, + метрики/инстанс работают. +- **ДОЛГ**: после ПЕРЕЗАГРУЗКИ сервера dockerd поднимется через systemd; если systemd1 снова + зависнет — контейнеры не стартуют. Проверить после рестарта сервера. +- Побочный эффект: с хоста 127.0.0.1:8082 curl висит (docker-proxy НЕ отвечает), но внешний + доступ social.dedinit.ru (Caddy vps02 → 10.8.0.2:8082) работает — инстанс полностью жив. + ## В работе / Следующие шаги - [x] Регистрация открыта (10/день) + apex-webfinger динамический (change fix-apex-webfinger-and-registration, архив) - [x] Веб-клиент Phanpy + чистый подвал (change add-webclient-and-clean-footer) +- [x] **Отладка почты** (13.09): причина найдена — `GTS_SMTP_FROM_ADDRESS` вместо `GTS_SMTP_FROM` → пустой From; исправлено, kunikov подтверждён +- [ ] SPF/DMARC для dedinit.ru (Jino) — критично для доставки писем GtS на внешние ящики - [ ] Мониторинг: пробросить :9464 (метрики уже слушают), job в prometheus.yml, дашборд Grafana (инструкция по метрикам GtS получена от пользователя 2026-09-06) - [ ] Логи в json-формате при желании (log-format: json) — по умолчанию logfmt diff --git a/docker-compose.yml b/docker-compose.yml index 4fcd142..3e8ba88 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -30,6 +30,8 @@ services: GTS_INSTANCE_TITLE: "dedinit()" GTS_INSTANCE_DESCRIPTION: "Нода для обычных людей" GTS_INSTANCE_CONTACT_EMAIL: "kpa39l@yandex.ru" + # Перезапуск: федальная лента анонимно (иначе /api/v1/timelines/public → 401) + GTS_INSTANCE_EXPOSE_PUBLIC_TIMELINE: "true" # Регистрация открыта: 10 новых аккаунтов в сутки (лимит очереди одобрения — дефолт 20) GTS_ACCOUNTS_REGISTRATION_OPEN: "true" GTS_ACCOUNTS_REGISTRATION_DAILY_LIMIT: "10" @@ -37,7 +39,7 @@ services: GTS_SMTP_HOST: "smtp.jino.ru" GTS_SMTP_PORT: "587" GTS_SMTP_USERNAME: "social@dedinit.ru" - GTS_SMTP_FROM_ADDRESS: "social@dedinit.ru" + GTS_SMTP_FROM: "social@dedinit.ru" # Метрики Prometheus (OTEL) GTS_METRICS_ENABLED: "true" GTS_METRICS_AUTH_ENABLED: "false" @@ -79,7 +81,12 @@ services: - "8383:8080" networks: - gotosocial: + default: + driver: bridge + driver_opts: + # MTU 1500 не проходит через WG-туннель (wg0 mtu 1420) → таймауты исходящих + # соединений (фрагментация теряется, DF-бит). 1380 безопасно ниже 1420. + com.docker.network.driver.mtu: "1380" ipam: config: # Выделенная подсеть (172.29.0.0/16 свободна на bigbox) diff --git a/openspec/specs/gotosocial/smtp-email/spec.md b/openspec/specs/gotosocial/smtp-email/spec.md new file mode 100644 index 0000000..23cf44f --- /dev/null +++ b/openspec/specs/gotosocial/smtp-email/spec.md @@ -0,0 +1,74 @@ +# gotosocial/smtp-email Specification + +## Purpose +Обеспечить доставляемость исходящих писем GoToSocial (подтверждение email, +системные уведомления) через SMTP Jino, гарантируя корректный, непустой +заголовок `From` и валидную аутентификацию. Исправление применено 2026-09-13 +(пустой From из-за неверной env-переменной `GTS_SMTP_FROM_ADDRESS`). + +## Requirements + +### Requirement: SMTP-конфигурация через корректные env-ключи GtS + +- **MUST**: Исходящая почта настраивается через env-переменные, которые + GtS 0.22.x реально читает (префикс `GTS_`, ключи конфига `smtp-*`): + `GTS_SMTP_HOST`, `GTS_SMTP_PORT`, `GTS_SMTP_USERNAME`, + `GTS_SMTP_PASSWORD`, `GTS_SMTP_FROM`. +- **MUST NOT**: НЕ используется `GTS_SMTP_FROM_ADDRESS` — такого ключа GtS + не знает, он молча игнорируется, и заголовок `From` в письмах становится + пустым. +- **MUST**: `GTS_SMTP_FROM` и `GTS_SMTP_USERNAME` совпадают + (`social@dedinit.ru`) — envelope-отправитель и заголовок From согласованы. + +#### Scenario: From заполнен после фикса + +- **GIVEN** docker-compose.yml содержит `GTS_SMTP_FROM: "social@dedinit.ru"` + и контейнер пересоздан +- **WHEN** выполнить + `docker inspect -f '{{.Config.Env}}' gotosocial | tr ' ' '\n' | grep GTS_SMTP_FROM` +- **THEN** вывод содержит `GTS_SMTP_FROM=social@dedinit.ru` + +#### Scenario: Неверный ключ отсутствует + +- **GIVEN** конфигурация в docker-compose.yml +- **WHEN** выполнить `grep -c GTS_SMTP_FROM_ADDRESS docker-compose.yml` +- **THEN** результат равен `0` (несуществующий ключ больше не используется) + +### Requirement: Регистрационные письма приходят с заполненным From + +- **MUST**: Письмо подтверждения email (шаблон `email_confirm`) отправляется + с непустым заголовком `From`, равным `social@dedinit.ru` (значение + `GTS_SMTP_FROM` → `GetSMTPFrom()` не пуст). +- **SHOULD**: Периодически проверять доставляемость вручную (тестовое письмо + через SMTP Jino). + +#### Scenario: Письмо подтверждения после регистрации + +- **GIVEN** настроен SMTP (host/port/username/password/from) +- **WHEN** пользователь регистрируется на инстансе + (`https://social.dedinit.ru/signup`) +- **THEN** в письме подтверждения поле `From` непустое и равно + `social@dedinit.ru`, а `confirmation_sent_at`/`last_emailed_at` у + пользователя в БД проставлены + +#### Scenario: SMTP-доступность Jino + +- **GIVEN** SMTP smtp.jino.ru:587 +- **WHEN** проверить ESMTP-баннер и аутентификацию `social@dedinit.ru` +- **THEN** баннер `220 smtp-out*.jino.ru ESMTP Postfix`, логин проходит, + RCPT TO принимается, DATA → `250 Ok: queued` + +### Requirement: Осведомлённость о репутации домена (SPF/DMARC) + +- **SHOULD**: Для полной доставляемости на внешние ящики (Яндекс и др.) у + домена dedinit.ru настроены SPF (`include:jino.ru`) и DMARC; без них + письма могут попадать в спам даже при корректном From. +- **SHOULD**: Задача настройки SPF/DMARC отслеживается отдельным трекером + и не блокирует данный фикс. + +#### Scenario: Проверка наличия SPF + +- **GIVEN** домен dedinit.ru +- **WHEN** выполнить `dig +short TXT dedinit.ru` +- **THEN** среди записей есть строка, начинающаяся с `v=spf1` (включает + jino.ru), либо задача открыта в STATUS.md