refactor(proxy-client): переименован интерфейс прокси
- Зачем: - прежнее имя выпячивало Xray, хотя клиент поддерживает также внешний прокси, а два каталога конфигурации были неочевидны. - Что: - пакет, CLI, конфигурация, состояние, тесты и документация переименованы в proxy-client. - машинный конфиг, профиль и два явных примера собраны в одном пользовательском каталоге. - свежая установка защищена от сворачивания каталога конфигурации внутрь репозитория. - Проверка: - bash tests/test_proxy_client_full.sh. - bash tests/test_install.sh. - ручные start, status и xray run -test; незавершённая сетевая приёмка записана в handoff.
This commit is contained in:
1 parent
e5323284a8
commit
6e431d875e
25 files changed
+310
-227
No files matched your search
@@ -0,0 +1,6 @@
|
||||
[proxy]
|
||||
type = external
|
||||
name = Hiddify
|
||||
address = hproxy.local
|
||||
port = 12334
|
||||
no_proxy_extra = .example.internal,git.example.internal
|
||||
@@ -0,0 +1,6 @@
|
||||
[proxy]
|
||||
type = xray
|
||||
name = Local Xray
|
||||
address = 127.0.0.1
|
||||
port = 12334
|
||||
no_proxy_extra = .example.internal,git.example.internal
|
||||
@@ -0,0 +1 @@
|
||||
vless://11111111-2222-4333-8444-555555555555@example.invalid:443?type=tcp&security=reality&encryption=none&flow=xtls-rprx-vision&sni=front.example.invalid&fp=chrome&pbk=SYNTHETIC_PUBLIC_KEY&sid=SYNTHETIC_SHORT_ID&headerType=none#Synthetic-example
|
||||
@@ -0,0 +1,91 @@
|
||||
# Переносимый Bash-интерфейс proxy-client. Файл можно подключать отдельно от
|
||||
# остальных dotfiles: source ~/.config/proxy-client/shell.bash
|
||||
|
||||
_proxy_cli() {
|
||||
"${PROXY_CLIENT_BIN:-proxy-client}" "$@"
|
||||
}
|
||||
|
||||
_proxy_apply_env_file() {
|
||||
local output_file="$1" line key value
|
||||
local -A proxy_values=()
|
||||
local -a allowed_keys=(
|
||||
HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY
|
||||
http_proxy https_proxy all_proxy no_proxy
|
||||
)
|
||||
|
||||
while IFS= read -r line || [ -n "$line" ]; do
|
||||
if [[ "$line" != *=* ]]; then
|
||||
echo "Ошибка: proxy-client env вернул строку без '='" >&2
|
||||
return 1
|
||||
fi
|
||||
key="${line%%=*}"
|
||||
value="${line#*=}"
|
||||
case "$key" in
|
||||
HTTP_PROXY|HTTPS_PROXY|ALL_PROXY|NO_PROXY|http_proxy|https_proxy|all_proxy|no_proxy) ;;
|
||||
*)
|
||||
echo "Ошибка: proxy-client env вернул неизвестный ключ: $key" >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
proxy_values["$key"]="$value"
|
||||
done < "$output_file"
|
||||
|
||||
for key in "${allowed_keys[@]}"; do
|
||||
if [[ -n "${proxy_values[$key]+set}" ]]; then
|
||||
printf -v "$key" '%s' "${proxy_values[$key]}"
|
||||
export "$key"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
proxy_on() {
|
||||
local output_file old_umask
|
||||
old_umask="$(umask)"
|
||||
umask 077
|
||||
if ! output_file="$(mktemp "${TMPDIR:-/tmp}/proxy-client-env.XXXXXX")"; then
|
||||
umask "$old_umask"
|
||||
echo "Ошибка: не удалось создать временный файл для proxy-env" >&2
|
||||
return 1
|
||||
fi
|
||||
umask "$old_umask"
|
||||
if ! _proxy_cli env > "$output_file"; then
|
||||
rm -f "$output_file"
|
||||
echo "Ошибка: прокси не включён" >&2
|
||||
return 1
|
||||
fi
|
||||
if ! _proxy_apply_env_file "$output_file"; then
|
||||
rm -f "$output_file"
|
||||
echo "Ошибка: прокси не включён" >&2
|
||||
return 1
|
||||
fi
|
||||
rm -f "$output_file"
|
||||
echo "Прокси включён"
|
||||
}
|
||||
|
||||
proxy_off() {
|
||||
unset HTTP_PROXY HTTPS_PROXY ALL_PROXY NO_PROXY
|
||||
unset http_proxy https_proxy all_proxy no_proxy
|
||||
echo "Прокси выключен"
|
||||
}
|
||||
|
||||
_proxy_run() {
|
||||
local command_name="${1:-}"
|
||||
if [ -z "$command_name" ]; then
|
||||
echo "Ошибка: не указана команда" >&2
|
||||
return 2
|
||||
fi
|
||||
shift
|
||||
_proxy_cli run -- "$command_name" "$@"
|
||||
}
|
||||
|
||||
pclaude() { _proxy_run claude "$@"; }
|
||||
pcodex() { _proxy_run codex "$@"; }
|
||||
|
||||
myip() {
|
||||
local address
|
||||
if ! address="$(curl -fsS --max-time 3 https://ifconfig.me)"; then
|
||||
echo "Ошибка: не удалось определить внешний IP" >&2
|
||||
return 1
|
||||
fi
|
||||
printf 'IP: %s\n' "$address"
|
||||
}
|
||||
Executable
+1552
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1 @@
|
||||
^/README\.md$
|
||||
@@ -0,0 +1,175 @@
|
||||
# proxy-client
|
||||
|
||||
Переносимый Linux/Bash-интерфейс для безопасного запуска команд через явно
|
||||
настроенный внешний прокси или через управляемый пользовательский Xray.
|
||||
|
||||
CLI требует Python 3.8+ и использует только стандартную библиотеку. Он ничего не
|
||||
устанавливает и не запускает без явной подкоманды. `start` использует первый
|
||||
`xray` из `PATH`; скачивания и установки при запуске нет.
|
||||
|
||||
## Подключение
|
||||
|
||||
В составе dotfiles:
|
||||
|
||||
```bash
|
||||
cd ~/dotfiles
|
||||
mkdir -p ~/.config/proxy-client
|
||||
stow proxy-client
|
||||
source ~/.config/proxy-client/shell.bash
|
||||
```
|
||||
|
||||
Полный `./install.sh` выполняет тот же `stow` вместе с остальными пакетами, а
|
||||
`.bash_custom` подключает модуль автоматически.
|
||||
|
||||
Без остальных dotfiles можно создать две ссылки:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.local/bin ~/.config/proxy-client
|
||||
ln -s /path/to/proxy-client/.local/bin/proxy-client ~/.local/bin/proxy-client
|
||||
ln -s /path/to/proxy-client/.config/proxy-client/shell.bash \
|
||||
~/.config/proxy-client/shell.bash
|
||||
printf '%s\n' 'source ~/.config/proxy-client/shell.bash' >> ~/.bashrc
|
||||
```
|
||||
|
||||
Вместо ссылок файлы можно скопировать в те же пути. Исполняемому файлу нужен
|
||||
режим `chmod 755`. Каталог `~/.local/bin` должен входить в `PATH`.
|
||||
|
||||
## Машинный конфиг
|
||||
|
||||
Скопируйте пример нужного режима и отредактируйте его:
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.config/proxy-client
|
||||
cp ~/.config/proxy-client/examples/config.xray ~/.config/proxy-client/config
|
||||
# или для внешнего Hiddify:
|
||||
cp ~/.config/proxy-client/examples/config.external ~/.config/proxy-client/config
|
||||
```
|
||||
|
||||
Формат строгий: одна секция `[proxy]`, пять обязательных ключей без повторов и
|
||||
неизвестных полей.
|
||||
|
||||
```ini
|
||||
[proxy]
|
||||
type = external
|
||||
name = Hiddify
|
||||
address = hproxy.local
|
||||
port = 12334
|
||||
no_proxy_extra = .example.internal,git.example.internal
|
||||
```
|
||||
|
||||
`address` — IPv4, IPv6 без квадратных скобок или ASCII DNS-имя без схемы и
|
||||
порта. `no_proxy_extra` — список через запятую; значение может быть пустым.
|
||||
Поддержка CIDR в `NO_PROXY` различается между программами, поэтому надёжнее
|
||||
указывать точные IP и домены.
|
||||
|
||||
Если файла нет, используется прежний внешний Hiddify на
|
||||
`hproxy.local:12334`, включая `.concept-software.ru` в `NO_PROXY`. Это не
|
||||
переключает тип прокси автоматически.
|
||||
|
||||
Для управляемого Xray задайте явный тип, loopback IP и нужный локальный порт:
|
||||
|
||||
```ini
|
||||
[proxy]
|
||||
type = xray
|
||||
name = Local Xray
|
||||
address = 127.0.0.1
|
||||
port = 12334
|
||||
no_proxy_extra = .example.internal,git.example.internal
|
||||
```
|
||||
|
||||
Секретный профиль хранится отдельно. Скопируйте синтетический пример, замените
|
||||
его одной настоящей VLESS-ссылкой и закройте права конечного файла:
|
||||
|
||||
```bash
|
||||
cp ~/.config/proxy-client/examples/default.url ~/.config/proxy-client/default.url
|
||||
chmod 600 ~/.config/proxy-client/default.url
|
||||
```
|
||||
|
||||
Файл должен содержать ровно одну непустую строку. Можно передать другой файл
|
||||
через `proxy-client start -f /path/to/profile.url`. Если это симлинк, проверяются
|
||||
права конечного файла.
|
||||
|
||||
## Команды
|
||||
|
||||
```bash
|
||||
proxy-client install [--replace]
|
||||
proxy-client status
|
||||
proxy-client start [-f PROFILE]
|
||||
proxy-client stop
|
||||
proxy-client env
|
||||
proxy-client run -- COMMAND ARG...
|
||||
proxy_on
|
||||
proxy_off
|
||||
pclaude [ARG...]
|
||||
pcodex [ARG...]
|
||||
myip
|
||||
```
|
||||
|
||||
`install` без root устанавливает единственную поддержанную версию Xray
|
||||
`26.3.27` в `~/.local/bin/xray`. Для `x86_64`/`amd64` используется официальный
|
||||
`Xray-linux-64.zip`, для `aarch64`/`arm64` — `Xray-linux-arm64-v8a.zip`.
|
||||
Остальные архитектуры отклоняются. CLI скачивает рядом файл `.dgst`, проверяет
|
||||
единственную запись `SHA2-256= <64 lowercase hex>` в официальном
|
||||
четырёхстрочном формате, извлекает `xray` в закрытый временный каталог внутри
|
||||
`~/.local/bin`, проверяет `xray version` и только затем атомарно меняет
|
||||
назначение. Дубликаты, uppercase, пропущенные и лишние записи отклоняются.
|
||||
Архив и извлечённый бинарник ограничены 64 MiB, `.dgst` — 4 KiB; объявленный
|
||||
`Content-Length` должен точно совпадать с фактически прочитанным размером.
|
||||
|
||||
Существующая корректная версия `26.3.27` даёт успешный no-op без сети. Другой,
|
||||
битый, неисполняемый файл или симлинк не заменяется без `--replace`; каталог не
|
||||
заменяется никогда. Если после установки `PATH` выбирает не этот файл, команда
|
||||
завершается с ошибкой и предлагает поставить `~/.local/bin` в начало `PATH`,
|
||||
но проверенная установка сохраняется. Родительский симлинк принимается, только
|
||||
если разрешённый каталог остаётся внутри `HOME`. Во время живого или
|
||||
неподтверждённого managed-экземпляра установка запрещена: сначала выполните
|
||||
`proxy-client stop`.
|
||||
|
||||
Для `external` команды `status`, `env` и `run` проверяют настроенный адрес и
|
||||
порт. Для `xray` они дополнительно требуют закрытое состояние именно того
|
||||
супервизора и дочернего Xray, который запустил CLI. Один открытый порт или чужой
|
||||
PID не принимается. `run` никогда не запускает Xray скрыто.
|
||||
|
||||
`start` поддерживает только Xray `26.3.27`, сначала выполняет
|
||||
`xray version` и `xray run -test`, затем остаётся на переднем плане. Используйте
|
||||
отдельную панель tmux. После готовности один loopback-порт принимает HTTP и
|
||||
SOCKS. `stop` ограниченно ждёт завершения супервизора и Xray, очищает служебные
|
||||
файлы и возвращает исходный `start` в терминал. Команда `stop` работает и после
|
||||
явного переключения машинного типа на `external`. CLI не создаёт systemd-службу,
|
||||
не запускает Xray автоматически и не переключается между типами прокси.
|
||||
|
||||
`run` передаёт восемь proxy-переменных только дочернему процессу и сохраняет
|
||||
аргументы и код возврата. `proxy_on` атомарно применяет те же восемь переменных
|
||||
к текущей Bash-сессии; `proxy_off` удаляет их.
|
||||
|
||||
Таймаут проверки задаётся положительным числом секунд:
|
||||
|
||||
```bash
|
||||
export PROXY_PREFLIGHT_TIMEOUT=2
|
||||
```
|
||||
|
||||
Значение по умолчанию — 2 секунды. `env` при успехе печатает только восемь строк
|
||||
`KEY=value`; для обычной диагностики используйте `status` или `myip`.
|
||||
|
||||
## Поддерживаемые профили
|
||||
|
||||
Поддерживаются только VLESS TCP/RAW с REALITY и VLESS XHTTP с TLS. Неизвестные,
|
||||
повторные и несовместимые query-параметры отклоняются. XHTTP поддерживает ALPN
|
||||
`http/1.1`, `h2` и `h3`; `extra` должен быть JSON-объектом без повторных ключей.
|
||||
|
||||
Xray 26.3.27 использует `pcs` и `vcn`, а устаревшие `allowInsecure` и
|
||||
`insecure` не переносит. Истинное legacy-значение даёт предупреждение, но
|
||||
обычная проверка сертификата остаётся включённой. Автоматического перехода
|
||||
между версиями или типами прокси нет.
|
||||
|
||||
Для справки при ручном конфигурировании: последняя версия Xray, где
|
||||
`allowInsecure` ещё применяется, —
|
||||
[25.12.8](https://github.com/XTLS/Xray-core/releases/tag/v25.12.8); опция
|
||||
удалена в [26.2.6](https://github.com/XTLS/Xray-core/releases/tag/v26.2.6).
|
||||
CLI такие версии не поддерживает и не устанавливает.
|
||||
|
||||
Сводка скрывает UUID и ключи. Содержимое XHTTP `path` не показывается — только
|
||||
его длина. Сгенерированный JSON и состояние лежат в закрытом каталоге
|
||||
`$XDG_STATE_HOME/proxy-client` либо `~/.local/state/proxy-client` и удаляются после
|
||||
штатной остановки. Сбой диагностики внешнего IP через локальный HTTP-прокси
|
||||
показывается как предупреждение и не останавливает уже готовый Xray.
|
||||
Reference in new issue
Block a user