ci: гейт против публикации занятых на VPS портов в workflow (#2757) #2759

Merged
bot-backend merged 1 commit from fix/2757-ci-port-guard into main 2026-08-06 21:40:05 +00:00
Collaborator

Проблема

Раннер запускает и job, и сервис-контейнеры с --network host — проверено на проде:

docker inspect FORGEJO-ACTIONS-TASK-..._JOB-changes -f '{{.HostConfig.NetworkMode}}' → host

Значит services: postgres + ports: 5432:5432 не поднимает тестовую БД, а уводит сборку в боевой Postgres: порт контейнер занять не может, а localhost:5432 из job'а — это прод. В #2745 отказ произошёл только из-за несовпадения пароля.

Что сделано

  1. scripts/check-workflow-ports.py — роняет сборку, если в .forgejo/workflows/*.yml публикуется порт, занятый на VPS. Ловит все формы: блок ports:, инлайн-список ports: ["..."], docker run -p / --publish. Комментарии пропускаются (в них живут анти-примеры). --selftest — asserts на позитив и на ложные срабатывания (- 5432 без публикации, 15432:5432, postgis:16-3.4, "22:00" в тексте, DSN localhost:5432/test).
  2. Шаг в changes-job ci.yml — этот job и так бежит на каждом PR и уже сделал checkout, отдельный слот дефицитного раннера не занимается (~1с). python3 в образе раннера есть (catthehacker/ubuntu:act-latest → 3.12.3, проверено).
  3. Снят капкан в шапке ci.yml: комментарий «FUTURE: добавить service-контейнер — см. .github/workflows/ci.yml как образец service-блока» указывал ровно на анти-пример (ports: - 5432:5432), безвредный там лишь потому, что GitHub Actions у нас не исполняется. Теперь указывает на рабочий способ из ci-tradein.yml (#2745).

Занятые порты (ss -ltnp на bot-server, 2026-08-06)

Порт Кто держит
22 sshd хоста
53 systemd-resolved (127.0.0.53)
80 / 443 gendesign-caddy-1 (0.0.0.0)
2222 forgejo, git-over-ssh
3000 gendesign-frontend-1 (127.0.0.1)
5432 gendesign-postgres-1 — боевая БД
8000 gendesign-backend-1 — боевой API (пароля, в отличие от БД, тут вообще нет)

Опровергнутая предпосылка issue

«На VPS живут Redis, CouchDB, GlitchTip — та же ловушка» — нет. Ни один из них на хосте не слушает: в docker ps у них 6379/tcp, 5984/tcp, 8000/tcp без ->, они в bridge-сетях. Сервис-контейнер с их портами ни с чем не столкнётся, и внесение их в запрет ронял бы сборку на ровном месте. В списке только то, что реально в LISTEN.

Зато в списке нашёлся порт, которого в issue не было: 8000 — боевой API gendesign-backend. Ловушка та же, но там нет даже пароля, который спас в случае с БД.

Границы

  • deploy-tradein.yml не тронут (его правит открытый #2680).
  • Пункт 3 issue (нужен ли боевому Postgres публикуемый порт вообще) — вопрос владельцу, задан комментарием в #2757, правки нет.

Test plan

  • --selftest зелёный (позитив + ложные срабатывания)
  • end-to-end на подложном workflow с services: postgres + ports: 5432:5432 → exit 1 и ::error file=...,line=8
  • на текущих 6 workflow — зелёный

Refs #2757, #2745

## Проблема Раннер запускает и job, и сервис-контейнеры с `--network host` — проверено на проде: ``` docker inspect FORGEJO-ACTIONS-TASK-..._JOB-changes -f '{{.HostConfig.NetworkMode}}' → host ``` Значит `services: postgres` + `ports: 5432:5432` не поднимает тестовую БД, а уводит сборку в **боевой** Postgres: порт контейнер занять не может, а `localhost:5432` из job'а — это прод. В #2745 отказ произошёл только из-за несовпадения пароля. ## Что сделано 1. **`scripts/check-workflow-ports.py`** — роняет сборку, если в `.forgejo/workflows/*.yml` публикуется порт, занятый на VPS. Ловит все формы: блок `ports:`, инлайн-список `ports: ["..."]`, `docker run -p` / `--publish`. Комментарии пропускаются (в них живут анти-примеры). `--selftest` — asserts на позитив и на ложные срабатывания (`- 5432` без публикации, `15432:5432`, `postgis:16-3.4`, `"22:00"` в тексте, DSN `localhost:5432/test`). 2. **Шаг в `changes`-job `ci.yml`** — этот job и так бежит на каждом PR и уже сделал checkout, отдельный слот дефицитного раннера не занимается (~1с). python3 в образе раннера есть (`catthehacker/ubuntu:act-latest` → 3.12.3, проверено). 3. **Снят капкан в шапке `ci.yml`**: комментарий «FUTURE: добавить service-контейнер — см. `.github/workflows/ci.yml` как образец service-блока» указывал ровно на анти-пример (`ports: - 5432:5432`), безвредный там лишь потому, что GitHub Actions у нас не исполняется. Теперь указывает на рабочий способ из `ci-tradein.yml` (#2745). ## Занятые порты (`ss -ltnp` на bot-server, 2026-08-06) | Порт | Кто держит | |---|---| | 22 | sshd хоста | | 53 | systemd-resolved (127.0.0.53) | | 80 / 443 | gendesign-caddy-1 (0.0.0.0) | | 2222 | forgejo, git-over-ssh | | 3000 | gendesign-frontend-1 (127.0.0.1) | | 5432 | gendesign-postgres-1 — **боевая БД** | | 8000 | gendesign-backend-1 — **боевой API** (пароля, в отличие от БД, тут вообще нет) | ## Опровергнутая предпосылка issue «На VPS живут Redis, CouchDB, GlitchTip — та же ловушка» — **нет**. Ни один из них на хосте не слушает: в `docker ps` у них `6379/tcp`, `5984/tcp`, `8000/tcp` **без** `->`, они в bridge-сетях. Сервис-контейнер с их портами ни с чем не столкнётся, и внесение их в запрет ронял бы сборку на ровном месте. В списке только то, что реально в `LISTEN`. Зато в списке нашёлся порт, которого в issue не было: **8000** — боевой API gendesign-backend. Ловушка та же, но там нет даже пароля, который спас в случае с БД. ## Границы - `deploy-tradein.yml` не тронут (его правит открытый #2680). - Пункт 3 issue (нужен ли боевому Postgres публикуемый порт вообще) — вопрос владельцу, задан комментарием в #2757, правки нет. ## Test plan - [x] `--selftest` зелёный (позитив + ложные срабатывания) - [x] end-to-end на подложном workflow с `services: postgres` + `ports: 5432:5432` → exit 1 и `::error file=...,line=8` - [x] на текущих 6 workflow — зелёный Refs #2757, #2745
bot-backend added 1 commit 2026-08-06 21:24:01 +00:00
ci: гейт против публикации занятых на VPS портов в workflow (#2757)
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI / changes (pull_request) Successful in 8s
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 / frontend-tests (pull_request) Successful in 1m22s
CI / openapi-codegen-check (pull_request) Successful in 2m30s
CI / backend-tests (pull_request) Successful in 15m29s
c485fc1d9e
Раннер запускает и job, и сервис-контейнеры с `--network host` (проверено:
NetworkMode=host у job-контейнеров), а на хосте слушают боевые сервисы.
Поэтому `services: postgres` + `ports: 5432:5432` не поднимает тестовую БД,
а уводит сборку в БОЕВОЙ Postgres: занять порт контейнер не может, и
`localhost:5432` из job'а — прод. В #2745 спасло несовпадение пароля.

scripts/check-workflow-ports.py ловит публикацию занятого порта в любой форме
(`ports:` блоком, инлайн-списком, `docker run -p/--publish`) и роняет сборку.
Список портов — замер `ss -ltnp` на bot-server 2026-08-06, не догадки:
22, 53, 80, 443, 2222, 3000, 5432, 8000. Redis/CouchDB/GlitchTip в список НЕ
попали: они на хосте не слушают (bridge-сети, порты без публикации), запрет
ронял бы сборку на ровном месте.

Шаг встроен в changes-job ci.yml — тот и так бежит на каждом PR и уже сделал
checkout, отдельный слот дефицитного раннера не занимается.

Заодно снят капкан в шапке ci.yml: комментарий «FUTURE: добавить service —
см. .github/workflows/ci.yml как образец» указывал ровно на анти-пример
(`ports: 5432:5432`), безвредный там лишь потому, что GitHub Actions у нас не
исполняется. Теперь он указывает на рабочий способ из ci-tradein.yml.

Refs #2757, #2745
bot-backend merged commit d605f0b938 into main 2026-08-06 21:40:05 +00:00
bot-backend deleted branch fix/2757-ci-port-guard 2026-08-06 21:40:06 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#2759
No description provided.