feat(xray-proxy): добавлен управляемый прокси для CLI
Зачем: - отделить переносимый прокси-интерфейс от общих Bash-функций - запускать агентские CLI через явный внешний или управляемый прокси Что: - добавлен stow-пакет xray-proxy с CLI, Bash-модулем и примерами - реализованы строгий разбор VLESS и управляемый запуск Xray - добавлены проверки процесса, порта, сигналов и отсутствия утечек - сохранены спецификация и очередь следующих тикетов Проверка: - python3 tests/test_xray_proxy.py — 22/22 - python3 tests/test_xray_proxy_managed.py — 16/16 - bash tests/test_proxy_launchers.sh — 9/9 - bash tests/test_install.sh - bash -n, Python 3.8 grammar, git diff --check
This commit is contained in:
1 parent
fdf744e578
commit
e69a4bb7df
20 files changed
+3224
-170
No files matched your search
@@ -0,0 +1,68 @@
|
||||
# 01 — Внешний прокси и переносимый Bash-интерфейс
|
||||
|
||||
**What to build:** Самостоятельный пользовательский пакет прокси, через который
|
||||
можно безопасно запускать команды с уже работающим внешним прокси, например
|
||||
Hiddify. Привычные Bash-команды переходят на единый CLI-контракт, но прежний
|
||||
сценарий без машинного конфига продолжает работать.
|
||||
|
||||
**Blocked by:** None — can start immediately.
|
||||
|
||||
**Status:** done
|
||||
|
||||
- [x] CLI работает на Python 3.8+ без сторонних библиотек, ничего не устанавливает
|
||||
и не запускает без явной подкоманды и выдаёт понятную ошибку на более старом
|
||||
Python.
|
||||
- [x] Машинный INI явно различает типы `external` и `xray`, проверяет имя, адрес,
|
||||
порт и добавку к `NO_PROXY`; отсутствие файла сохраняет внешний прокси
|
||||
`hproxy.local:12334` без неявного переключения на другой тип.
|
||||
- [x] Канонический машинный INI содержит единственную секцию `[proxy]` и ровно
|
||||
по одному ключу `type`, `name`, `address`, `port`, `no_proxy_extra`:
|
||||
|
||||
```ini
|
||||
[proxy]
|
||||
type = external
|
||||
name = Hiddify
|
||||
address = hproxy.local
|
||||
port = 12334
|
||||
no_proxy_extra = .example.internal,git.example.internal
|
||||
```
|
||||
|
||||
Все ключи обязательны при наличии файла; `no_proxy_extra` может быть пустым.
|
||||
Неизвестные секции и ключи, повторы, пропуски, многострочные или содержащие
|
||||
NUL значения, пустые имя или адрес и порт вне диапазона 1–65535 отклоняются.
|
||||
Добавки к `NO_PROXY` задаются через запятую, окружающие пробелы удаляются,
|
||||
пустые элементы отклоняются.
|
||||
- [x] `address` принимает IPv4, IPv6 без квадратных скобок либо ASCII DNS-имя
|
||||
из корректных непустых меток; схема URL, встроенный порт, скобки, zone ID,
|
||||
пробелы, `/`, `@` и другие URL-разделители отклоняются. При формировании
|
||||
proxy-URL CLI сам заключает IPv6-литерал в квадратные скобки.
|
||||
- [x] Для типа `external` команды `status`, `env` и `run -- COMMAND ARG...`
|
||||
проверяют только настроенный адрес и порт; недоступный прокси запрещает запуск
|
||||
дочерней команды с понятным сообщением.
|
||||
- [x] До реализации управляемого процесса конфиг с `type=xray` обрабатывается
|
||||
безопасным отказом: `status`, `env`, `run` и `start` не считают один открытый
|
||||
порт доказательством управляемого Xray и не запускают дочернюю команду.
|
||||
- [x] `env` при успехе печатает только восемь разрешённых proxy-переменных в двух
|
||||
регистрах, а при ошибке не печатает частичный результат; базовый `NO_PROXY`
|
||||
включает loopback и узел прокси, затем дополняется машинной настройкой.
|
||||
- [x] Документированный `PROXY_PREFLIGHT_TIMEOUT` сохраняется как положительное
|
||||
число секунд с прежним значением по умолчанию `2` для проверок доступности из
|
||||
`status`, `env` и `run`; ошибочное значение даёт понятный отказ.
|
||||
- [x] `run` без искажений сохраняет аргументы и код возврата команды, передаёт
|
||||
proxy-переменные только дочернему процессу и возвращает отдельную понятную
|
||||
ошибку, если команда отсутствует в `PATH`.
|
||||
- [x] Переносимый Bash-модуль становится единственным владельцем `proxy_on`,
|
||||
`proxy_off`, `pclaude`, `pcodex`, `myip` и внутренних proxy-помощников;
|
||||
WSL-функция обновления `hproxy.local` остаётся независимой.
|
||||
- [x] Обычный запуск dotfiles подключает общий Bash-файл и переносимый
|
||||
proxy-модуль независимо: после установки доступны все пять proxy-команд и
|
||||
`update_hproxy`; подключение одного переносимого модуля без остальных
|
||||
dotfiles также предоставляет все пять proxy-команд.
|
||||
- [x] Bash-модуль принимает вывод `env` атомарно, проверяет белый список и
|
||||
наличие каждого из восьми ключей ровно один раз, не использует `eval` и при
|
||||
любом неверном выводе оставляет прежние восемь переменных без изменений;
|
||||
`proxy_on` и `proxy_off` больше не делают автоматический запрос внешнего IP.
|
||||
- [x] Автоматические тесты подтверждают прежний внешний Hiddify-сценарий,
|
||||
fail-fast, оба регистра переменных, настраиваемый `NO_PROXY`, отсутствие
|
||||
протечки окружения, обычное и самостоятельное подключение Bash-модуля,
|
||||
безопасный отказ для `type=xray` и атомарный разбор полного вывода `env`.
|
||||
@@ -0,0 +1,72 @@
|
||||
# 02 — Управляемый Xray: полный штатный путь
|
||||
|
||||
**What to build:** Пользователь с уже установленным поддерживаемым Xray кладёт
|
||||
одну VLESS-ссылку в закрытый файл и получает полный штатный сценарий локального
|
||||
прокси: проверку профиля, запуск на переднем плане, диагностику, запуск команд
|
||||
через прокси и штатную остановку.
|
||||
|
||||
**Blocked by:** 01 — Внешний прокси и переносимый Bash-интерфейс.
|
||||
|
||||
**Status:** done
|
||||
|
||||
- [x] `start` разрешён только при явном типе `xray`, требует loopback-адрес и
|
||||
согласованный локальный порт, никогда не скачивает бинарник и выбирает первый
|
||||
`xray` из `PATH`. Один разрешённый путь используется для `version`,
|
||||
`run -test` и основного процесса и показывается перед запуском.
|
||||
- [x] Успешный `xray version` разбирается только по первой строке в форме
|
||||
`Xray VERSION` с концом строки либо ASCII-пробелом после полного токена
|
||||
версии. Принимаются только `25.12.8` и `26.3.27`; ведущий мусор, похожий
|
||||
префикс вроде `26.3.270`, пустой, неверный или завершившийся ошибкой вывод
|
||||
запрещает преобразование и запуск. Запрошенная или указанная в профиле версия
|
||||
не подменяет фактическую версию выбранного бинарника.
|
||||
- [x] Профиль берётся из предусмотренного файла по умолчанию либо из явно
|
||||
переданного `-f`, содержит ровно одну VLESS-ссылку, а конечный файл после
|
||||
разрешения симлинка недоступен группе и остальным пользователям.
|
||||
- [x] Строгий преобразователь поддерживает TCP/RAW с REALITY и XHTTP с TLS,
|
||||
сохраняет все предусмотренные спецификацией поля, отклоняет повторы,
|
||||
любой неизвестный query-параметр, отсутствующие обязательные поля и
|
||||
неподдерживаемые сочетания до запуска Xray.
|
||||
- [x] Для Xray 26.3.27 корректно обрабатываются `pcs`/`vcn`, а устаревшие
|
||||
`allowInsecure`/`insecure` не переносятся и дают предупреждение; для явно
|
||||
выбранного Xray 25.12.8 сохраняется совместимое insecure-поведение, а новые
|
||||
поля отклоняются без автоматического отката версии.
|
||||
- [x] `allowInsecure` и `insecure` принимают только точные значения `true` или
|
||||
`false`; при одновременном присутствии значения должны совпадать. Для
|
||||
25.12.8 только согласованное истинное значение включает `allowInsecure` с
|
||||
предупреждением, а для 26.3.27 поле никогда не попадает в JSON и истинное
|
||||
значение предупреждает об обычной проверке сертификата.
|
||||
- [x] Созданный клиентский JSON слушает только loopback, предоставляет HTTP и
|
||||
SOCKS на одном порту, отключает журнал доступа и проходит `xray run -test`
|
||||
до запуска основного процесса.
|
||||
- [x] `start` остаётся на переднем плане как супервизор одного дочернего Xray;
|
||||
после запуска процесс жив и порт принимает соединения, а сбой отдельной
|
||||
проверки внешнего IP остаётся предупреждением.
|
||||
- [x] Штатный `start` создаёт закрытое состояние с PID супервизора и дочернего
|
||||
Xray, разрешённым путём к бинарнику, портом и безопасным отпечатком профиля.
|
||||
`status`, `env`, `run` и `stop` требуют это состояние, живой супервизор,
|
||||
ожидаемый дочерний процесс и настроенный порт; один чужой listener или PID
|
||||
никогда не считается управляемым Xray и не затрагивается.
|
||||
- [x] `status`, `env`, `run` и `stop` обеспечивают штатный путь управляемого
|
||||
Xray; `run` никогда не запускает его скрыто, а `stop` может завершить
|
||||
управляемый процесс после переключения машинного типа на `external`.
|
||||
Успешный `stop` посылает сигнал супервизору, ограниченно ждёт завершения
|
||||
супервизора и Xray, очищает штатное состояние и возвращает исходный
|
||||
foreground-вызов `start` в терминал.
|
||||
- [x] Диагностика внешнего IP действительно делает запрос через настроенный
|
||||
локальный HTTP-прокси. Её сбой явно показан как предупреждение и не отменяет
|
||||
уже успешные проверки процесса и порта.
|
||||
- [x] Безопасная сводка показывает только полезные несекретные свойства
|
||||
профиля; XHTTP `path` показывается только как скрытое значение с длиной, а
|
||||
UUID, ключи, short ID и другие секреты — без фрагментов исходного значения.
|
||||
Полная VLESS-ссылка и уникальные секреты отсутствуют в stdout, stderr и argv
|
||||
дочерних процессов при успехе, любом отказе разбора и сбое `run -test`.
|
||||
- [x] Модульные и интеграционные тесты покрывают обе семьи ссылок, варианты
|
||||
ALPN, типичную ссылку Hiddify XHTTP/TLS, обе поддержанные версии Xray и
|
||||
понятные отказы на неподдерживаемом вводе.
|
||||
- [x] Чёрный ящик для каждой поддержанной версии доказывает полный порядок:
|
||||
профиль по умолчанию и `-f`, `version`, успешный `run -test` до основного
|
||||
запуска, живое управляемое состояние и порт, `status`, `env`, дочернюю
|
||||
команду через `run`, проксированную IP-диагностику, `stop` и ограниченное
|
||||
завершение исходного `start`. Через один настроенный локальный порт реально
|
||||
завершаются отдельный HTTP-proxy-запрос и отдельный SOCKS-proxy-запрос.
|
||||
Подмена порта или состояния чужим процессом даёт безопасный отказ.
|
||||
@@ -0,0 +1,32 @@
|
||||
# 03 — Безопасный жизненный цикл управляемого процесса
|
||||
|
||||
**What to build:** Управляемый Xray предсказуемо переживает повторные команды,
|
||||
сигналы, устаревшее состояние и аварийное исчезновение супервизора, не затрагивая
|
||||
чужие процессы и не оставляя секретные или временные данные.
|
||||
|
||||
**Blocked by:** 02 — Управляемый Xray: полный штатный путь.
|
||||
|
||||
**Status:** ready-for-agent
|
||||
|
||||
- [ ] Супервизор владеет пользовательской блокировкой, допускает только один
|
||||
управляемый экземпляр и не заменяет запущенный профиль другим без явных
|
||||
`stop` и нового `start`.
|
||||
- [ ] Состояние с правами `0600` хранит PID и время старта супервизора и Xray,
|
||||
путь к бинарнику, порт и безопасный отпечаток профиля, но не ссылку или её
|
||||
секреты; служебные каталоги имеют права `0700`.
|
||||
- [ ] `status` и `stop` сверяют PID со временем старта и ожидаемым исполняемым
|
||||
файлом, поэтому чужой, устаревший и повторно использованный PID никогда не
|
||||
считается управляемым процессом.
|
||||
- [ ] SIGINT, SIGTERM и SIGHUP передаются дочернему Xray; штатное завершение
|
||||
удаляет состояние, сгенерированный JSON и временные файлы.
|
||||
- [ ] Если супервизор аварийно исчез, `stop` завершает оставшийся Xray только
|
||||
после полной проверки его личности и затем безопасно очищает состояние.
|
||||
- [ ] Повторный запуск, смена профиля, конфликт локального порта и несогласованный
|
||||
машинный адрес завершаются понятным отказом без повреждения уже работающего
|
||||
экземпляра.
|
||||
- [ ] Полная VLESS-ссылка и её секреты отсутствуют в stdout, stderr, служебных
|
||||
файлах и аргументах дочерних процессов; временная конфигурация существует
|
||||
только внутри закрытого служебного каталога.
|
||||
- [ ] Автоматические тесты покрывают блокировку, повторный запуск, сигналы,
|
||||
остановку, очистку, смену профиля, конфликт порта, чужой, устаревший и
|
||||
повторно использованный PID, а также исчезнувший супервизор.
|
||||
@@ -0,0 +1,28 @@
|
||||
# 04 — Проверяемая пользовательская установка Xray
|
||||
|
||||
**What to build:** Пользователь без root устанавливает официальный Xray одной
|
||||
явной командой и получает проверенный бинарник нужной версии без скрытой замены
|
||||
существующей установки или записи за пределами пользовательских каталогов.
|
||||
|
||||
**Blocked by:** 01 — Внешний прокси и переносимый Bash-интерфейс.
|
||||
|
||||
**Status:** ready-for-agent
|
||||
|
||||
- [ ] `install` поддерживает Linux x86_64 и aarch64, по умолчанию выбирает
|
||||
зафиксированную версию 26.3.27 и принимает другую только через явный параметр
|
||||
версии, не объявляя произвольный релиз поддержанным.
|
||||
- [ ] Архив и файл контрольной суммы скачиваются из официального релиза, после
|
||||
чего SHA-256 проверяется до распаковки и установки; несовпадение прекращает
|
||||
операцию без изменения установленного бинарника.
|
||||
- [ ] Работа ведётся во временном пользовательском каталоге, извлечённый бинарник
|
||||
сначала проходит проверку `version`, а затем атомарно устанавливается в
|
||||
пользовательский каталог исполняемых файлов.
|
||||
- [ ] Существующий бинарник другой версии не перезаписывается без `--replace`;
|
||||
ошибка, отказ или прерывание не оставляют частично установленный файл.
|
||||
- [ ] После установки `command -v xray` обязан указывать на установленный файл;
|
||||
иначе команда завершается с понятной подсказкой исправить `PATH`.
|
||||
- [ ] Установщик не использует sudo, systemd или системные каталоги, а команды
|
||||
`start` и запуск без подкоманды никогда не начинают скачивание.
|
||||
- [ ] Автоматические тесты проверяют обе архитектуры, корректную и повреждённую
|
||||
контрольную сумму, существующую версию, `--replace`, атомарность и отсутствие
|
||||
записи вне изолированного пользовательского каталога.
|
||||
@@ -0,0 +1,37 @@
|
||||
# 05 — Доставка пакета и итоговая приёмка
|
||||
|
||||
**What to build:** Завершённая прокси-подсистема устанавливается отдельно или
|
||||
вместе с dotfiles, снабжена безопасными шаблонами и понятной инструкцией, а её
|
||||
заявленные сценарии подтверждены автоматическими и доступными системными
|
||||
проверками.
|
||||
|
||||
**Blocked by:** 03 — Безопасный жизненный цикл управляемого процесса; 04 —
|
||||
Проверяемая пользовательская установка Xray.
|
||||
|
||||
**Status:** ready-for-agent
|
||||
|
||||
- [ ] Самостоятельный stow-совместимый пакет доставляет CLI, Bash-модуль и
|
||||
вымышленные шаблоны машинного INI и VLESS-профиля; реальные пользовательские
|
||||
настройки, UUID, ссылки и ключи не входят в репозиторий.
|
||||
- [ ] Пакет можно подключить отдельно через stow, эквивалентные ручные симлинки
|
||||
или копирование; общий установщик использует тот же пакет без отдельной
|
||||
реализации и сохраняет повторяемость установки.
|
||||
- [ ] Основная инструкция объясняет внешний Hiddify и управляемый Xray,
|
||||
подготовку двух файлов настройки, все команды управления, `proxy_on/off`,
|
||||
`pclaude/pcodex`, `NO_PROXY`, ограничения CIDR и диагностику.
|
||||
- [ ] Инструкция фиксирует две поддержанные семьи VLESS, современную проверку
|
||||
сертификата, явный запасной Xray 25.12.8 и отсутствие systemd, автозапуска и
|
||||
автоматического перехода между типами прокси.
|
||||
- [ ] Корневой обзор кратко описывает подсистему, показывает подключение пакета
|
||||
и направляет к полной инструкции, не дублируя её.
|
||||
- [ ] Полный автоматический прогон подтверждает Python 3.8-совместимый синтаксис,
|
||||
внешний Hiddify-сценарий, обе семьи VLESS, обе версии Xray, установку,
|
||||
жизненный цикл, Bash-интеграцию и отсутствие утечки секретов.
|
||||
- [ ] На доступной Ubuntu с Python 3.10 проверены установка, `xray run -test` и
|
||||
работа HTTP и SOCKS через один локальный вход для заявленных совместимых
|
||||
сочетаний Xray 26.3.27 и 25.12.8.
|
||||
- [ ] На целевой удалённой системе выполнен тот же пользовательский сценарий;
|
||||
версия Python в РЕД ОС проверена до заявления поддержки, а недоступная среда
|
||||
явно записана как неподтверждённая вместо неподкреплённого обещания.
|
||||
- [ ] После приёмки статус исходной спецификации и документация соответствуют
|
||||
фактически подтверждённому объёму, включая все оставшиеся ограничения.
|
||||
Reference in new issue
Block a user