Commit graph

3 commits

Author SHA1 Message Date
bot-backend
05e82989a8 fix(ops): закрепить рабочий адрес api.telegram.org на новом хосте
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 10s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
Замер 2026-08-23 с Poincare: у api.telegram.org семь публикуемых адресов,
отвечает РОВНО ОДИН — 149.154.167.220 (302 за 0.11 с). Резолвер при этом
отдаёт 149.154.166.110, который мёртв. Скан всего 149.154.167.0/24
подтвердил: живой он один во всём блоке.

Это не блокировка Selectel и не наш файрвол: с Beget отвечают три адреса
из тех же семи, включая тот, что отдаёт DNS там. Telegram частично
фильтруется у ОБОИХ провайдеров, просто Beget попадает на живой адрес.
Через прокси ASocks Telegram не проходит ни с одного из четырёх узлов
(все 000) — узлы российские, путь закрыт.

Без закрепления tradein-tgbot на long-polling резолвит мёртвый адрес,
виснет и умирает МОЛЧА: restart поднимает его заново, он снова виснет,
ни краша, ни строки в логе.

Закреплены все три потребителя Telegram, а не только бот:
- tgbot и backend — extra_hosts в новом docker-compose.selectel.yml.
  backend тоже ходит в Telegram: пересылка алертов GlitchTip
  (api/v1/glitchtip.py) и support-чат (api/v1/support.py).
- хостовые скрипты ops/lib-backup.sh и ops/uptime-healthcheck.sh —
  шаг 11 в selectel-bootstrap.sh кладёт запись в /etc/hosts.
  Им compose не помогает, они идут из cron, не из контейнера.

Отдельный override-файл, а не правка docker-compose.prod.yml: на Beget
закрепление не нужно, и менять поведение действующего прода ради
будущего хоста нельзя. Файл просто не передаётся в -f.

Адрес вынесен в TELEGRAM_API_IP с текущим дефолтом — если он умрёт,
правка в одну переменную окружения без релиза. Шаг bootstrap проверяет
не факт записи, а что Bot API отвечает 401 на фиктивный токен, и при
другом коде печатает команду поиска нового живого адреса.

Проверено: docker compose config даёт ровно две вставки extra_hosts
(tradein-backend, tradein-tgbot) и ничего больше; TELEGRAM_API_IP
подставляется в обе.

Refs #3059, #3057
2026-08-23 23:32:35 +03:00
bot-backend
fb75bce22d fix(ops): hardening sshd молча не применялся из-за приоритета cloud-init
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
Найдено при первом живом запуске на Poincare.

Ubuntu кладёт в /etc/ssh/sshd_config.d/ файл 50-cloud-init.conf с
PasswordAuthentication yes. sshd берёт ПЕРВОЕ встреченное значение директивы,
а не последнее, поэтому наш 60-hardening.conf проигрывал по имени: после
reload парольный вход для root остался бы открытым.

Коварство в том, что `sshd -t` при этом отвечает «конфиг валиден», и скрипт
рапортовал об успехе. Сервер уже под перебором (fail2ban держал 6 адресов),
так что тихий отказ здесь стоил бы дорого.

Три правки:
- файл называется 00-hardening.conf и читается первым; старый 60-* удаляется,
  чтобы повторный запуск не оставил два конфликтующих;
- после записи проверяется не синтаксис, а ИТОГ через `sshd -T`: если
  passwordauthentication != no, скрипт падает и НЕ перезапускает sshd;
- комментарий про версии Docker приведён в соответствие с поведением —
  ставится последняя из репозитория, версии прода служат ориентиром для
  предупреждения (приехали 29.7.2 / 5.5.0 против 29.4.1 / v5.1.3).
2026-08-22 12:56:22 +03:00
bot-backend
6484116079 chore(ops): скрипты аудита и первичной настройки выделенного сервера Selectel
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / backend-tests (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
Сервер Poincare (SPB-4, 188.246.224.93) поднят 22.08.2026. Два скрипта под
переезд (#2989, #3027-#3032).

selectel-audit.sh — только чтение. Отвечает на вопрос, который надо закрыть
до установки чего бы то ни было: собраны ли диски в зеркало. Переставить ОС
можно только пока сервер пустой.

Результат первого прогона: RAID1 на всех трёх разделах ([2/2] [UU] на /boot,
swap и /), память non-ECC подтверждена (Error Correction Type: None),
910G свободно из 924G.

selectel-bootstrap.sh — идемпотентная настройка: пользователь с ключом,
ufw, Docker версий текущего прода (29.4.1 / v5.1.3), лимит журналов
контейнеров, sysctl, unattended-upgrades, fail2ban, ужесточение sshd.

Порядок шагов выбран так, чтобы не потерять доступ: правило для SSH
добавляется ДО включения файрвола, а sshd в конце намеренно НЕ
перезапускается — сначала проверяется вход по ключу в отдельном окне.
При выключенном PasswordAuthentication ошибка означала бы IP-KVM за 1320 руб.

Два умолчания выставлены по факту, а не по привычке:

- часовой пояс Europe/Moscow, а не UTC — оба хоста сейчас на MSK, и смена
  пояса сдвинула бы все шесть cron-задач (бэкапы, геокодинг) на три часа;
- swap не создаётся, если он уже есть — установщик Selectel отдаёт раздел
  4.7G в зеркале, добавлять файл поверх незачем при 62G памяти.

.gitattributes: *.sh с LF — иначе скрипт, отредактированный под Windows,
падает на удалённом хосте с `$'\r': command not found`.
2026-08-22 12:49:09 +03:00