feat(ci): деплой проверяет, к тому ли хосту подключился #3079
No reviewers
Labels
No labels
Fable 5 ревью
GG-форсайт
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
feedback/max
generative
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
ИРД
вторичка
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#3079
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "chore/3029-ci-access-perimeter"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Закрывает часть приёмки #3029. Решение владельца 24.08: сейчас только добавляем, удаляем после переезда — отзыв старого ключа и сужение правил вынесены в #3075.
Дыра, которая закрывается
Подлинность хоста не проверяется нигде:
deploy-tradein.ymlявно ставил-o StrictHostKeyChecking=no;appleboy/ssh-actionне задан входfingerprint.Пока Forgejo, раннеры и прод жили на одной машине, цена была почти нулевой. После 30.08
DEPLOY_HOSTуказывает на Selectel, и тот же канал идёт через интернет — а по нему черезenvs:едутGHCR_PAT,OPENAI_API_KEY,OBJECTIVE_API_KEY,GLITCHTIP_BACKEND_DSNи самDEPLOY_SSH_KEY.Вызовов оказалось пять, а не четыре — в исходном разборе был пропущен
deploy-obsidian.yml, который тоже ходит наDEPLOY_HOSTсDEPLOY_SSH_KEY. Отпечаток добавлен во все пять.Почему мерж безопасен — проверено по исходникам, а не по README
Цепочка пиннинга прослежена до кода:
appleboy/ssh-action@v1.0.3→Dockerfile: FROM ghcr.io/appleboy/drone-ssh:1.7.3→go.mod: easyssh-proxy v1.5.0→easyssh.go:Незаданный секрет в Actions разворачивается в пустую строку →
InsecureIgnoreHostKey()→ поведение до PR бит в бит. Условная конструкция не нужна: механизм инертен по устройству самого действия. Неверное значение падает громко, а не тихо.Инертность безопасна для мержа и ОПАСНА для окна
Это главная правка по итогам ревью. Если 30.08 секрет забудут завести — или заведут пустым, сняв
ssh-keyscan -t ecdsaс хоста без ECDSA-ключа — деплой проедет зелёным через интернет вообще без проверки. Защита молча не применилась бы ровно тогда, когда впервые нужна.Поэтому перед каждым из пяти ssh-шагов стоит преамбула: при пустом секрете печатает
::warningс перечислением того, что именно едет по этому каналу; при заданном — одну строку подтверждения. Warning, а не отказ — секрета сегодня нет ни у кого, отказ сломал бы деплой в момент мержа, что прямо запрещено требованием стадийности.Грабля: нужен ECDSA-отпечаток, а не ed25519
Клиент не задаёт
HostKeyAlgorithms, берётся дефолтx/crypto v0.17.0, гдеsupportedHostKeyAlgosставитecdsa-sha2-nistp256вышеssh-ed25519. Со стоковым OpenSSH согласуется ECDSA — привычный ed25519-отпечаток просто остановит деплой.Сравнение идёт побайтово и без trim — прилипший при копипасте перевод строки делает значение непустым, проверка включается, и все пять шагов падают с
host key fingerprint mismatch. Предупреждение об этом добавлено в шапку каждого файла.Новые секреты (задавать НЕ сейчас, а в окно)
DEPLOY_SSH_FINGERPRINTdeploy.yml×2,deploy-tradein.yml,deploy-obsidian.ymlINFRA_DEPLOY_SSH_FINGERPRINTdeploy-infra.ymlDEPLOY_KNOWN_HOSTSResolve deployed base SHAВ
deploy-infra.ymlнамеренно нет фолбэка наDEPLOY_SSH_FINGERPRINT, хотя у соседнихusername/key/portон есть: отпечаток — идентичность конкретной машины, и фолбэк означал бы сверку ключа Beget с отпечатком Selectel, то есть гарантированный отказ ровно у того workflow, который чинит остающийся хост.Ручной ssh — отдельный случай
deploy-tradein.yml, шагResolve deployed base SHAчитает маркер с прода обычным openssh-клиентом, которому отпечаток не скормить. Схема: секрет задан →StrictHostKeyChecking=yes+UserKnownHostsFile; не задан → прежнее поведение плюс баннер.Заодно расхождение ключа перестало быть неотличимым от недоступного хоста: раньше
2>/dev/nullглоталHost key verification failed, и печаталось обычное «фолбэк на полную пересборку». Теперь причина называется отдельно. Fail-safe сохранён: при любой ошибкеRAW_SHAпустеет и включается build-all, так что цена ошибки вknown_hosts— максимум лишняя полная пересборка, а не сорванный деплой.ops/selectel-ci-access.sh
Аддитивная выдача CI-доступа, 483 строки. Ставит отдельный ключ робота (не переиспользуя админский — отзыв одного не должен бить по другому) с ограничением
from="<адрес раннера>", добавляет узкое правило ufw, не трогая общее, и печатает план сужения, который сам не выполняет.Ревью подтвердило мандат построчно: sshd не перезапускается, default policy ufw не трогается,
ufw deleteвстречается только внутри печатаемого плана, вauthorized_keysидёт исключительно>>— включая дозапись недостающего финального перевода строки, иначе склеились бы две строки. Порт берётся из живогоsshd -T, а не предполагается 22. Пустой адрес без явногоCI_ALLOW_ANY_SOURCE=yes— отказ, чтобы не получитьfrom="".Пользователь намеренно не создаётся: отдельная учётка тянет docker-группу, права на
/opt/gendesignи сменуDEPLOY_USER, то есть перенастройку живого конвейера.Замечание вне scope
deploy-obsidian.ymlходит наDEPLOY_HOST, но CouchDB по плану остаётся на Beget вместе с Forgejo и GlitchTip. После 30.08DEPLOY_HOSTбудет указывать на Selectel — тот же класс, что #3059: молчаливо не тот адресат. Не трогал (правка про подлинность хоста, не про маршрутизацию), но пометил комментарием в файле.Test plan
on:на месте у каждого, число jobs не изменилосьwith.fingerprint×5, новые шаги ×5 и одинrun; триггеры,paths, docker-команды и healthcheck'и идентичны;ci*.yml,perimeter-smoke,stale-claims— без единого измененияset -euo pipefailне падают,exit 0в обеих веткахResolve deployed base SHAпрогнан с подставнымsshв обоих режимах: без секрета аргументы побайтово как сегодня, с секретом —StrictHostKeyChecking=yes; временные файлы вычищаютсяbash -nнаops/selectel-ci-access.shиops/selectel-audit.shops/selectel-ci-access.sh, проверить деплой end-to-end Beget → SelectelRefs #3029, #3075, #3057, #2989