Sprint 0 / #18: multi-stage Dockerfile и супервизор entrypoint #49

Open
embeddevio wants to merge 10 commits from feat/18-docker-runtime into main
Owner

Refs #18, Refs #15. (Пересоздано: первая ветка feat/18-dockerfile была выведена из ветки #16 и PR тянул её коммиты — теперь ветка отведена от main, старый PR #48 закрыт.)

Что сделано

  • deploy/Dockerfile — multi-stage:
    • stage go-build (golang:1.23-alpine): собирает amneziawg-go и amneziawg-tools (awg), ревизии параметризованы AMNEZIAWG_GO_REF / AMNEZIAWG_TOOLS_REF;
    • stage runtime (alpine:3.21): iptables/ip6tables, ipset, iproute2, dnsmasq, curl, tini, su-exec, python3 + venv из requirements.txt; компилятор удаляется после сборки (apk del .build-deps);
    • пользователь awg (непривилегированный) для API, каталоги /config, /data, /run/awg (VOLUME), EXPOSE 8080, LABEL;
    • HEALTHCHECK на /api/v1/health (эндпоинт появится в #31), ENTRYPOINT через tini для корректных сигналов.
  • deploy/entrypoint.sh — супервизор: preflight (/dev/net/tun, доступ к iptables, наличие awg/ipset) с явным FATAL вместо тихой деградации; подготовка каталогов и прав; запись таблицы 100 awg-vpn; ip_forward; запуск awg-api под su-exec awg с пробросом кода возврата в Docker.
  • deploy/awg0.conf.example — пример клиентского .conf (заглушки, без ключей).

Что НЕ проверено (честно)

  • Сборка образа не выполнена: на хосте TechLead-агента нет бинарника docker. Проверка awg --version / ipset --version / HEALTHCHECK в манифесте / размер ≤ 250 MB / поведение без NET_ADMIN должна выполняться там, где есть Docker (зависит от #17 runner и #21 стенда).
  • Репозитории и ревизии amneziawg-* соответствуют upstream-референсу PoC, но должны быть запинены на тег перед релизом (#45).
  • shellcheck в окружении агента отсутствует — проверен только sh -n (синтаксис).

Гейты

  • AC #18 на стенде с Docker (devops)
  • CI green (#17)
  • Ревью владельца роли
Refs #18, Refs #15. (Пересоздано: первая ветка `feat/18-dockerfile` была выведена из ветки #16 и PR тянул её коммиты — теперь ветка отведена от `main`, старый PR #48 закрыт.) ## Что сделано - **deploy/Dockerfile** — multi-stage: - stage `go-build` (`golang:1.23-alpine`): собирает `amneziawg-go` и `amneziawg-tools` (`awg`), ревизии параметризованы `AMNEZIAWG_GO_REF` / `AMNEZIAWG_TOOLS_REF`; - stage `runtime` (`alpine:3.21`): iptables/ip6tables, ipset, iproute2, dnsmasq, curl, tini, su-exec, python3 + venv из `requirements.txt`; компилятор удаляется после сборки (`apk del .build-deps`); - пользователь `awg` (непривилегированный) для API, каталоги `/config`, `/data`, `/run/awg` (VOLUME), `EXPOSE 8080`, `LABEL`; - `HEALTHCHECK` на `/api/v1/health` (эндпоинт появится в #31), `ENTRYPOINT` через `tini` для корректных сигналов. - **deploy/entrypoint.sh** — супервизор: preflight (`/dev/net/tun`, доступ к iptables, наличие `awg`/`ipset`) с явным FATAL вместо тихой деградации; подготовка каталогов и прав; запись таблицы 100 `awg-vpn`; `ip_forward`; запуск `awg-api` под `su-exec awg` с пробросом кода возврата в Docker. - **deploy/awg0.conf.example** — пример клиентского `.conf` (заглушки, без ключей). ## Что НЕ проверено (честно) - **Сборка образа не выполнена**: на хосте TechLead-агента нет бинарника `docker`. Проверка `awg --version` / `ipset --version` / `HEALTHCHECK` в манифесте / размер ≤ 250 MB / поведение без `NET_ADMIN` должна выполняться там, где есть Docker (зависит от #17 runner и #21 стенда). - Репозитории и ревизии `amneziawg-*` соответствуют upstream-референсу PoC, но **должны быть запинены на тег** перед релизом (#45). - `shellcheck` в окружении агента отсутствует — проверен только `sh -n` (синтаксис). ## Гейты - [ ] AC #18 на стенде с Docker (devops) - [ ] CI green (#17) - [ ] Ревью владельца роли
embeddevio added the devops label 2026-10-21 23:31:11 +03:00
embeddevio added 2 commits 2026-10-21 23:31:12 +03:00
embeddevio added 1 commit 2026-10-21 23:38:50 +03:00
embeddevio added 1 commit 2026-10-21 23:42:45 +03:00
embeddevio added 1 commit 2026-10-21 23:47:18 +03:00
Author
Owner

TechLead — сборка прогнана на реальном Docker-хосте, ветка обновилась тремя фиксами.

Хост: 192.168.1.147 (Docker 29.4.1) — сборка велась из объединённого контекста этого PR и PR #47 (иначе нет requirements.txt и backend/).

Коммиты в этой ветке:

  1. fix(#18): builder на golang:1.25 — amneziawg-go требует go ≥ 1.25 (сборка на 1.23 падала).
  2. fix(#18): amneziawg-tools/src собирает бинарь wg — cp awg падало; копируем wg → awg (+ awg-quick).
  3. fix(#18): создавать /etc/iproute2/rt_tables — в Alpine файла нет, запись таблицы 100 падала с nonexistent directory; заодно уточнено предупреждение про ip_forward.

Проверено на собранном образе (awg-dash:dev, 202 MB, sha256:47b38a6515…): awg --version rc=0, ipset --version rc=0 (с NET_ADMIN), amneziawg-go --version rc=0, HEALTHCHECK в манифесте, без NET_ADMIN — явный FATAL rc=1, с NET_ADMIN preflight проходит. Детали — в комментарии к issue #18.

Гейты: сборка ✓, размер ✓, поведение без NET_ADMIN ✓. Остаётся CI (#17) и ревью владельца роли. Мержить после #47 (PR #49 standalone не собирается).

**TechLead — сборка прогнана на реальном Docker-хосте, ветка обновилась тремя фиксами.** Хост: `192.168.1.147` (Docker 29.4.1) — сборка велась из объединённого контекста этого PR и PR #47 (иначе нет `requirements.txt` и `backend/`). Коммиты в этой ветке: 1. `fix(#18): builder на golang:1.25` — `amneziawg-go` требует go ≥ 1.25 (сборка на 1.23 падала). 2. `fix(#18): amneziawg-tools/src собирает бинарь wg` — `cp awg` падало; копируем `wg` → `awg` (+ `awg-quick`). 3. `fix(#18): создавать /etc/iproute2/rt_tables` — в Alpine файла нет, запись таблицы 100 падала с `nonexistent directory`; заодно уточнено предупреждение про `ip_forward`. Проверено на собранном образе (`awg-dash:dev`, 202 MB, `sha256:47b38a6515…`): `awg --version` rc=0, `ipset --version` rc=0 (с NET_ADMIN), `amneziawg-go --version` rc=0, HEALTHCHECK в манифесте, без NET_ADMIN — явный FATAL rc=1, с NET_ADMIN preflight проходит. Детали — в комментарии к issue #18. Гейты: сборка ✓, размер ✓, поведение без NET_ADMIN ✓. Остаётся CI (#17) и ревью владельца роли. Мержить **после** #47 (PR #49 standalone не собирается).
embeddevio added 1 commit 2026-10-29 21:09:43 +03:00
Merge branch 'main' into feat/18-docker-runtime
ci / lint (push) Successful in 51s
ci / lint (pull_request) Successful in 49s
contract-openapi / openapi-contract (push) Successful in 59s
contract-openapi / ts-client-is-current (push) Successful in 48s
contract-openapi / openapi-contract (pull_request) Successful in 59s
contract-openapi / ts-client-is-current (pull_request) Successful in 43s
ci / unit (push) Successful in 52s
ci / security (push) Successful in 1m21s
ci / unit (pull_request) Successful in 55s
ci / security (pull_request) Successful in 1m20s
ci / build (push) Successful in 36s
ci / build (pull_request) Successful in 36s
ci / image-scan (push) Failing after 3s
ci / image-scan (pull_request) Failing after 3s
ci / push (push) Has been skipped
ci / push (pull_request) Has been skipped
e153143cc1
embeddevio added 1 commit 2026-10-29 21:09:54 +03:00
fix(#61): бамп базовых образов и x/crypto под гейт image-scan
ci / lint (push) Successful in 40s
ci / lint (pull_request) Successful in 40s
contract-openapi / openapi-contract (push) Successful in 51s
contract-openapi / ts-client-is-current (push) Successful in 42s
contract-openapi / openapi-contract (pull_request) Successful in 54s
contract-openapi / ts-client-is-current (pull_request) Successful in 39s
ci / unit (push) Successful in 46s
ci / security (push) Successful in 1m24s
ci / unit (pull_request) Successful in 58s
ci / security (pull_request) Successful in 1m27s
ci / build (push) Successful in 9m44s
ci / build (pull_request) Successful in 35s
ci / image-scan (push) Failing after 7s
ci / image-scan (pull_request) Failing after 3s
ci / push (push) Has been skipped
ci / push (pull_request) Has been skipped
20db51b7a2
- GO_IMAGE golang:1.25-alpine → golang:1.26-alpine (CVE Go stdlib 1.25.14: CVE-2026-56857 CRITICAL, CVE-2026-111943/78667/97031 HIGH)
- golang.org/x/crypto → v0.43.0 в amneziawg-go (CVE-2025-47913 HIGH)
- runtime alpine:3.21 → alpine:3.23 + apk upgrade (HIGH в пакетах базы)

Refs #61, Refs #18, Refs #17
embeddevio added 1 commit 2026-10-29 21:51:42 +03:00
fix(#61): x/crypto v0.58.0 + x/net v0.62.0
ci / lint (pull_request) Successful in 47s
contract-openapi / openapi-contract (push) Successful in 53s
contract-openapi / ts-client-is-current (push) Successful in 36s
contract-openapi / openapi-contract (pull_request) Successful in 58s
contract-openapi / ts-client-is-current (pull_request) Successful in 42s
ci / unit (pull_request) Successful in 53s
ci / security (pull_request) Successful in 1m21s
ci / build (pull_request) Successful in 40s
ci / image-scan (pull_request) Failing after 7s
ci / push (pull_request) Has been skipped
ci / lint (push) Successful in 50s
ci / unit (push) Successful in 52s
ci / security (push) Successful in 1m19s
ci / build (push) Successful in 39s
ci / image-scan (push) Failing after 3s
ci / push (push) Has been skipped
ea63e904b4
После бампа Go 1.26 / alpine 3.23 в образе остались только Go-модули:
  x/crypto v0.43.0 — 5 HIGH (ssh*, фиксы до v0.55.0)
  x/net    v0.45.0 — 16 HIGH (http2/idna/dnsmessage, фиксы до v0.60.0)
Оба подняты на актуальные версии из proxy.golang.org (v0.58.0 / v0.62.0).
В alpine 3.23.7 находок нет (66 пакетов).

Refs #61
embeddevio added 1 commit 2026-10-29 22:16:55 +03:00
fix(#61): пины транзитивных python-зависимостей под гейт image-scan
ci / lint (push) Successful in 51s
ci / lint (pull_request) Successful in 44s
contract-openapi / openapi-contract (push) Successful in 49s
contract-openapi / ts-client-is-current (push) Successful in 43s
contract-openapi / openapi-contract (pull_request) Successful in 58s
contract-openapi / ts-client-is-current (pull_request) Successful in 49s
ci / unit (push) Successful in 51s
ci / security (push) Successful in 1m11s
ci / unit (pull_request) Successful in 56s
ci / security (pull_request) Successful in 2m42s
ci / build (push) Successful in 5m35s
ci / build (pull_request) Failing after 42s
ci / image-scan (push) Failing after 45s
ci / image-scan (pull_request) Has been skipped
ci / push (push) Has been skipped
ci / push (pull_request) Has been skipped
cc3621a12b
После бампа Go/alpine/x-crypto/x-net в образе остались 5 HIGH — все в venv:
  urllib3 2.7.0 → 2.8.0, msgpack 1.1.2 → 1.2.3, setuptools 70.3.0 → 78.1.1

Refs #61
embeddevio added 1 commit 2026-10-29 22:42:00 +03:00
fix(#61): не оставлять pip в runtime-образе
ci / lint (push) Successful in 52s
ci / lint (pull_request) Successful in 44s
contract-openapi / openapi-contract (push) Successful in 50s
contract-openapi / ts-client-is-current (push) Successful in 52s
contract-openapi / openapi-contract (pull_request) Failing after 13m2s
ci / unit (push) Has been cancelled
ci / security (push) Has been cancelled
ci / unit (pull_request) Has been cancelled
ci / security (pull_request) Has been cancelled
contract-openapi / ts-client-is-current (pull_request) Has been cancelled
ci / build (push) Has been cancelled
ci / image-scan (push) Has been cancelled
ci / push (push) Has been cancelled
ci / build (pull_request) Has been cancelled
ci / image-scan (pull_request) Has been cancelled
ci / push (pull_request) Has been cancelled
b69c3b67e2
trivy разбирает pip/_vendor/vendor.txt и репортит вендоренные msgpack 1.1.2 /
urllib3 2.7.0 / setuptools 70.3.0 как установленные пакеты — при том что в venv
реально стоят 1.2.4 / 2.8.0 / 84.2.1 (проверено pip list внутри образа staging-cc3621a1).
py3-pip переведён в .build-deps и удаляется вместе с ними; pip в venv и системный
pip вычищаются — runtime не должен содержать инсталлятора.

Refs #61
Author
Owner

⏸ Работы приостановлены владельцем (29.10.2026 ~22:55 MSK)

Фиксирую фактическое состояние ветки, чтобы возобновление было с нуля догадок.

Что уже в main (влито мной по поручению владельца)

PR Итог
#63 merged — contract-openapi переведён на archive-fallback, все 4 статуса зелёные
#60 merged — в main появились .gitea/workflows/ci.yml + docs/ci/; все 16 статусов зелёные

main = 19f0526. CI-гейты теперь репозиторные (любая новая ветка получает lint/unit+coverage/security).

Состояние этого PR

  • head b69c3b6, база main @ 19f0526, mergeable: true, 4 файла (deploy/Dockerfile, deploy/entrypoint.sh, deploy/awg0.conf.example + requirements.txt).
  • build — зелёный на живом раннере (run 81/89: docker build → staging-<sha>, ~200–227 MB).
  • image-scan — красный, но состав находок сжат с 22 (21 HIGH + 1 CRITICAL) до 5 HIGH и причина каждого шага установлена (см. #61).
  • push — не запускается, пока image-scan красный (fail-closed); нужен репо-секрет REGISTRY_TOKEN (write:package) — действие владельца.
  • Не перепроверено: последний коммит b69c3b6 (удаление pip из runtime) не прогнан — раннер остановлен до возобновления работ.

Что проверить при возобновлении (порядок)

  1. ssh root@192.168.1.147 'docker start gitea-runner-awg-repo' — раннер был остановлен командой владельца.
  2. rerun run для sha b69c3b6 (или push) → ждать build → image-scan.
  3. Ожидание: image-scan зелёный (все 5 находок были вендоренными пакетами pip, pip из образа удалён). Если появились новые — смотреть get_job_log_preview.
  4. Только после зелёного image-scan — мерж PR (DoD: CI green + ревью devops), затем закрытие #18 и карточки t_af973267.

Ветки feat/17-verify, probe/artifact-v4, ci/17-gate-negative не удалял — снимаются после закрытия #17/#62.

## ⏸ Работы приостановлены владельцем (29.10.2026 ~22:55 MSK) Фиксирую фактическое состояние ветки, чтобы возобновление было с нуля догадок. ### Что уже в `main` (влито мной по поручению владельца) | PR | Итог | |---|---| | #63 | merged — `contract-openapi` переведён на archive-fallback, все 4 статуса зелёные | | #60 | merged — в `main` появились `.gitea/workflows/ci.yml` + `docs/ci/`; все 16 статусов зелёные | `main` = `19f0526`. CI-гейты теперь репозиторные (любая новая ветка получает lint/unit+coverage/security). ### Состояние этого PR - head **`b69c3b6`**, база `main` @ `19f0526`, `mergeable: true`, 4 файла (`deploy/Dockerfile`, `deploy/entrypoint.sh`, `deploy/awg0.conf.example` + `requirements.txt`). - `build` — **зелёный на живом раннере** (run 81/89: `docker build` → `staging-<sha>`, ~200–227 MB). - `image-scan` — **красный**, но состав находок сжат с 22 (21 HIGH + 1 CRITICAL) до 5 HIGH и причина каждого шага установлена (см. #61). - `push` — не запускается, пока `image-scan` красный (fail-closed); нужен репо-секрет `REGISTRY_TOKEN` (write:package) — действие владельца. - **Не перепроверено:** последний коммит `b69c3b6` (удаление pip из runtime) не прогнан — раннер остановлен до возобновления работ. ### Что проверить при возобновлении (порядок) 1. `ssh root@192.168.1.147 'docker start gitea-runner-awg-repo'` — раннер был остановлен командой владельца. 2. `rerun` run для sha `b69c3b6` (или push) → ждать `build` → `image-scan`. 3. Ожидание: `image-scan` зелёный (все 5 находок были вендоренными пакетами pip, pip из образа удалён). Если появились новые — смотреть `get_job_log_preview`. 4. Только после зелёного `image-scan` — мерж PR (DoD: CI green + ревью `devops`), затем закрытие #18 и карточки `t_af973267`. Ветки `feat/17-verify`, `probe/artifact-v4`, `ci/17-gate-negative` не удалял — снимаются после закрытия #17/#62.
Some checks are pending
ci / lint (push) Successful in 52s
ci / lint (pull_request) Successful in 44s
contract-openapi / openapi-contract (push) Successful in 50s
contract-openapi / ts-client-is-current (push) Successful in 52s
contract-openapi / openapi-contract (pull_request) Failing after 13m2s
ci / unit (push) Has been cancelled
ci / security (push) Has been cancelled
ci / unit (pull_request) Has been cancelled
ci / security (pull_request) Has been cancelled
contract-openapi / ts-client-is-current (pull_request) Has been cancelled
ci / build (push) Has been cancelled
ci / image-scan (push) Has been cancelled
ci / push (push) Has been cancelled
ci / build (pull_request) Has been cancelled
ci / image-scan (pull_request) Has been cancelled
ci / push (pull_request) Has been cancelled
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin feat/18-docker-runtime:feat/18-docker-runtime
git checkout feat/18-docker-runtime
Sign in to join this conversation.