107 lines
14 KiB
Markdown
107 lines
14 KiB
Markdown
# Lightweight-server: S1–S10, 23.10.2026
|
||
|
||
Технический результат: **fixed**, S8 — **no_change** (защита уже была в исходном main). Релизная приёмка и merge остаются отдельным gate; автор ветку не вливает. База: `ef83559f7de19fd8649c78b22b05df397650cf46`. Ветка: `codex/lightweight-security-20261023`.
|
||
|
||
Задание: `agents/codex/inbox/2026-10-23-lightweight-security.md`. Изолированный worktree существующего репозитория, без clone. На хосте и реальных RU/DE/FI/DK серверах установщики и root-команды не запускались. Создана собственная одноразовая KVM Ubuntu 24.04; после проверок остановлена, её диски, seed и временные SSH-ключи удалены.
|
||
|
||
## Граница исправления
|
||
|
||
Установщики сами создавали публичные правила и wildcard-слушатели. Исправлена вся цепочка этапов 2/3/6, включая сохранённые управляемые правила и первый запуск upstream-установщика. Частичная правка одного списка портов оставляла бы прежние разрешения и другие этапы, открывающие их снова.
|
||
|
||
| Пункт | Изменение и подтверждение |
|
||
| --- | --- |
|
||
| S1 | По умолчанию только текущий/будущий SSH и TCP 443. Нет автоматического 80/2096/40000/63636. Старые управляемые правила требуют явного сохранения; при отказе удаляются реальные UFW-правила IPv4/IPv6 и лишь затем обновляется файл состояния. Проверены реальные правила в VM. |
|
||
| S2 | `webListen=127.0.0.1`, адрес проверяется через `ss`. Перед первым upstream start/restart временная обёртка systemctl применяет loopback к панели и отключает подписки; ошибка не допускает старт. Итоговая конфигурация проверяется до снятия защиты и после запуска. |
|
||
| S3 | Режимы панели и подписок независимы. Подписки: loopback / выключены / явно публичные со случайным путём. Прежний валидный приватный путь сохраняется; достаточно длинный секретный публичный путь сохраняется по умолчанию. Ротация запрашивает подтверждение и предупреждает о поломке старых URL. Путь в журнал не выводится. |
|
||
| S4 | Закреплены 3x-ui v3.10.2, Xray-install `05aa47266c94e6fb5ca48cad96afdb4becb5891f`, Xray-core v26.10.21 и SHA-256 всех используемых исполняемых payload amd64/arm64. Локальный helper проверяет их перед запуском; неизвестные транзитивные загрузки 3x-ui блокируются. GitHub либо HTTPS Forgejo mirror, без fallback. В README загрузка самого проекта по полному проверенному SHA и доверенной сумме, без mutable main. |
|
||
| S5 | После подтверждённого оператором нового входа удаляются правила 22/tcp и запись в управляемом списке. Если UFW всё ещё разрешает IPv4/IPv6 22 либо статус не проверен, скрипт не объявляет успех и сохраняет запись. Без подтверждения, при SSH=22 или MANAGE_UFW=no очистка не выполняется. |
|
||
| S6 | Только PAM open_session. Отправка через фиксированный `/usr/bin/setsid`, отдельный процесс, stdin/stdout/stderr закрыты. Производственные пути конфигурации и журнала фиксированы, без тестовых environment overrides. Тест: close не отправляет, open запускает одну отправку, возврат <500ms при curl, задержанном на 1s. |
|
||
| S7 | WARP — отдельный opt-in, по умолчанию выключен. |
|
||
| S8 | Уже присутствовали `--proto '=https' --tlsv1.2`; исходный файл не изменён, регрессия закреплена новым тестом. |
|
||
| S9 | Кавычки и обратные слэши экранируются в curl config, управляющие символы отклоняются до вызова curl. Проверены newline-инъекция и большой URL с vertical-tab, без pipefail/grep-q обхода. Секретная ссылка не передаётся в argv curl. |
|
||
| S10 | Опциональный age encrypt streaming без временного plaintext-архива; SCP с BatchMode и строгим заранее проверенным known_hosts. Ошибка выгрузки — ошибка операции. Повтор initial проверяет checksum и повторяет выгрузку; несовместимый старый plaintext при выбранном age явно отклоняется без удаления снимка. В VM настоящий age/zstd/tar round-trip совпал с исходным fixture. |
|
||
|
||
Файлы: этапы `2`, `user-3`, `tg-4`, `vpn-5`, `panel-warp-6`, `backup-7`; новый `ochenstarik-server-supply-chain.sh` и справочный `...pins.txt`; README EN/RU, CI workflow и четыре новых теста. Исходные восемь тестов не изменены.
|
||
|
||
## Проверки по порядку
|
||
|
||
1. `find . -type f -name '*.sh' -print0 | xargs -0 -n1 bash -n` — PASS.
|
||
2. Новые поведенческие проверки:
|
||
- `tests/test-firewall-panel-security.sh`: defaults, миграция старых UFW-правил, IPv6 residual error, sudo без SSH_CONNECTION, независимые режимы/сохранение путей, startup guard, ожидание слушателя, подтверждение удаления 22;
|
||
- `tests/test-download-security.sh`: escaping/контрольные символы, cache shim lifecycle и запрет неизвестного installer URL, реальные вызовы helper из этапов 5/6, отсутствие upstream main, отсутствие fallback при ошибке mirror;
|
||
- `tests/test-notification-backup-security.sh`: PAM open/close, detached delivery, строгие SSH options и ошибка offsite;
|
||
- `tests/test-backup-initial-retry.sh`: реальный early-return путь initial, ошибка SCP → повтор, неизменность snapshot/checksum, age policy с прежним plaintext.
|
||
3. `for test in tests/*.sh; do bash "$test"; done` — **12/12 PASS**; `git diff --check` — PASS; diff исходных `tests/` пуст.
|
||
4. Одноразовая Ubuntu 24.04 amd64, настоящий systemd/UFW/3x-ui: этап 2 (полная установка), миграция старых правил, этап 6 fresh install и повтор с публичными подписками — exit 0.
|
||
5. Тот же VM: helper `prepare-xray` → `install-xray` — exit 0, реальный Xray **26.10.21**; age-backup round-trip — PASS.
|
||
|
||
## Настоящие UFW и TCP-слушатели
|
||
|
||
Ubuntu image SHA-256: `04e8f2461a16a44e78a370284471f29616cdd9baf1893df25807b71f985b6c94`, проверен по официальному SHA256SUMS cloud-images. SSH в тесте оставлен на 22; WARP выключен.
|
||
|
||
После полного этапа 2 и после удаления прежних 80/2096/40000/63636:
|
||
|
||
```text
|
||
ufw status verbose
|
||
Status: active
|
||
Logging: on (low)
|
||
Default: deny (incoming), allow (outgoing), disabled (routed)
|
||
New profiles: skip
|
||
22/tcp ALLOW IN Anywhere
|
||
443/tcp ALLOW IN Anywhere
|
||
22/tcp (v6) ALLOW IN Anywhere (v6)
|
||
443/tcp (v6) ALLOW IN Anywhere (v6)
|
||
```
|
||
|
||
После fresh этапа 6 с приватной панелью и приватными подписками UFW остаётся тем же. Полный `ss -ltn` (сокращены только столбцы очередей):
|
||
|
||
```text
|
||
Local Address:Port Peer Address:Port
|
||
127.0.0.1:2096 0.0.0.0:*
|
||
127.0.0.1:2053 0.0.0.0:*
|
||
0.0.0.0:22 0.0.0.0:*
|
||
127.0.0.54:53 0.0.0.0:*
|
||
127.0.0.1:62789 0.0.0.0:*
|
||
127.0.0.53%lo:53 0.0.0.0:*
|
||
127.0.0.1:11111 0.0.0.0:*
|
||
[::]:22 [::]:*
|
||
```
|
||
|
||
DNS/внутренние Xray-порты остаются локальными. Разрешение UFW 443 не означает, что скрипт создаёт на 443 отдельный HTTPS-сервис. Во время sampled fresh bootstrap наблюдались только `127.0.0.1:49587` (временный upstream порт), `127.0.0.1:2053`, `127.0.0.1:2096`; sampling дополняет pre-start enforcement, не доказывает отсутствие любого мгновенного слушателя сам по себе.
|
||
|
||
При явном публичном доступе к подпискам UFW дополнительно содержит:
|
||
|
||
```text
|
||
2096/tcp ALLOW IN Anywhere
|
||
2096/tcp (v6) ALLOW IN Anywhere (v6)
|
||
```
|
||
|
||
`ss -ltn`: панель **127.0.0.1:2053**, подписки **\*:2096**. DB: `webListen=127.0.0.1`, `subListen=''`, `subEnable=true`. Секретный путь не выгружался в журнал.
|
||
|
||
## Два аудитора и собственная проверка
|
||
|
||
Дешёвые исполнители: `lightweight_firewall_panel`, `lightweight_downloads_notifications_backup`. До патча независимый boundary investigator; после кандидата два свежих read-only Codex auditor: `lightweight_security_audit_one`, `lightweight_security_audit_two`. Один цикл аудита, без самооценки авторского кода как релизного approval.
|
||
|
||
Подтверждены и исправлены найденные аудиторами: сохранённые UFW entries, игнорирование ошибки удаления 22, IPv6 статус, потеря/ротация прежнего subscription path, initial/offsite retry и несовместимая age policy, неточное описание pin manifest. Root дополнительно воспроизвёл реальные ошибки: sudo очищает SSH_CONNECTION, повторный mkdir shim и readiness race после systemctl start. После исправления повторил focused, все repository checks и настоящий VM-прогон. Неподтверждённое замечание об обязательной root escalation через PAM аудитор отозвал; тестовые env overrides всё равно удалены из производства.
|
||
|
||
## Evidence и пределы
|
||
|
||
Сырые обезличенные результаты сохранены в managed Codex Security artifact storage, target `lightweight-server`, bundle `artifacts-827d7430ed6521a319c17103be0c53cc1d742842b3e097b3a5f63693cead15d9`, каталог `artifacts/lightweight-20261023/`:
|
||
|
||
| Файл | SHA-256 |
|
||
| --- | --- |
|
||
| final-checks.log | 57be6a409fbce5488611bbc853eaecaacecdde8fa7c40656b87fb2ec925970f2 |
|
||
| ubuntu24-step2.log | 6e37718bf82ef1bf061449dee8782d79855946dca2ba5776e6bc9bada3dbec37 |
|
||
| ubuntu24-migration.log | 0257a0c97a8dd592c3492d886ad6fb42af2fedff7139eb70ea16387a8b2e2bab |
|
||
| ubuntu24-private.log | 4de0099004bcb79822b5a915d1e5914ea3b447e8b41e67471c730f75f39553c8 |
|
||
| ubuntu24-public.log | f0d75dd045e89ca9f8066542e4806b20c10724165e07f56f659b35c83728274f |
|
||
| ubuntu24-age.log | 737740b39ceba68214737f21283fea1d37a5f448c5bb3addcb6eef1d39fbcc09 |
|
||
| ubuntu24-xray.log | 270af1adae421035c3d97d4b0779c2d2f922e72208988036a3e534cff27c2a14 |
|
||
|
||
Полная VPN-маршрутизация реального сервера, WARP opt-in, arm64 execution и реальный SCP на чужой хост не запускались; вне требуемого Ubuntu amd64 прогона и разрешений. Mirror проверен на fail-closed отказ; готовый публичный mirror релиз не опубликован, нужны файлы с указанными суммами. Блок README требует реально одобренные SHA+checksum и не выдумывает готовый release.
|
||
|
||
По последнему правилу владельца новый PR ждёт Grok на точном SHA либо Claude по прямому запросу владельца. Два технических Codex-аудита этот release gate не заменяют. CI удалённой ветки проверяется отдельно после публикации; этот отчёт фиксирует локальные/VM результаты.
|
||
|
||
## Поправка CI после публикации
|
||
|
||
Первоначальный SHA `9f06756cefb56be41fb815edcf526f2eef924ef4`, автоматические push/PR runs 1/2 (tasks 7364/7365): FAIL **до checkout**, не отказ тестов. Forgejo 9.0.3, runner v6.4.0 отклонил `runs.using=node24` существующего checkout@v6. Workflow закреплён на совместимом Node20 checkout v4.2.2, точный upstream/mirror SHA `11bd71901bbe5b1630ceea73d27597364c9af683`; `action.yml` проверен из Forgejo mirror. Все 12 проверок сохранены. Результат автоматического CI нового head фиксируется в PR и AI-Memory; повторный dispatch не выполнялся.
|