feat(proxy): Gitea добавлен в обход прокси
- Зачем: - домашний Gitea должен подключаться напрямую без ошибок TLS через прокси. - Что: - dementev.space добавлен во все списки исключений прокси. - списки NO_PROXY выровнены и защищены тестом от расхождения. - Проверка: - python3 tests/test_proxy_env_sync.py. - python3 tests/test_proxy_client.py. - bash tests/test_proxy_launchers.sh.
This commit is contained in:
commit
2d4af081b8
9 files changed
+111
-10
No files matched your search
@@ -122,6 +122,12 @@ Windows не меняется. Если Codex уже запущен, его ну
|
|||||||
Если Claude уже запущен обычным способом, его нужно полностью закрыть перед
|
Если Claude уже запущен обычным способом, его нужно полностью закрыть перед
|
||||||
запуском через `Claude Proxy`.
|
запуском через `Claude Proxy`.
|
||||||
|
|
||||||
|
Список хостов, которые идут мимо прокси, продублирован в профилях Codex,
|
||||||
|
PowerShell и VS Code: одного общего файла у Windows и WSL нет. Эталон —
|
||||||
|
`DEFAULT_NO_PROXY_EXTRA` в `proxy-client/.local/bin/proxy-client`; меняя его,
|
||||||
|
поправь и копии. Расхождение ловит `tests/test_proxy_env_sync.py` — раньше
|
||||||
|
копии разъезжались, и один и тот же хост то шёл мимо прокси, то нет.
|
||||||
|
|
||||||
### Git baseline
|
### Git baseline
|
||||||
|
|
||||||
На всех платформах фиксируются настройки для предсказуемого поведения:
|
На всех платформах фиксируются настройки для предсказуемого поведения:
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
HTTP_PROXY=http://hproxy.local:12334
|
HTTP_PROXY=http://hproxy.local:12334
|
||||||
HTTPS_PROXY=http://hproxy.local:12334
|
HTTPS_PROXY=http://hproxy.local:12334
|
||||||
ALL_PROXY=socks5://hproxy.local:12334
|
ALL_PROXY=socks5://hproxy.local:12334
|
||||||
NO_PROXY=hproxy.local,localhost,127.0.0.1,::1,[::1],.concept-software.ru
|
NO_PROXY=hproxy.local,localhost,127.0.0.1,::1,[::1],.concept-software.ru,dementev.space,.dementev.space
|
||||||
|
|
||||||
http_proxy=http://hproxy.local:12334
|
http_proxy=http://hproxy.local:12334
|
||||||
https_proxy=http://hproxy.local:12334
|
https_proxy=http://hproxy.local:12334
|
||||||
all_proxy=socks5://hproxy.local:12334
|
all_proxy=socks5://hproxy.local:12334
|
||||||
no_proxy=hproxy.local,localhost,127.0.0.1,::1,[::1],.concept-software.ru
|
no_proxy=hproxy.local,localhost,127.0.0.1,::1,[::1],.concept-software.ru,dementev.space,.dementev.space
|
||||||
|
|
||||||
WSLENV=HTTP_PROXY/u:HTTPS_PROXY/u:ALL_PROXY/u:NO_PROXY/u:http_proxy/u:https_proxy/u:all_proxy/u:no_proxy/u
|
WSLENV=HTTP_PROXY/u:HTTPS_PROXY/u:ALL_PROXY/u:NO_PROXY/u:http_proxy/u:https_proxy/u:all_proxy/u:no_proxy/u
|
||||||
NODE_USE_ENV_PROXY=1
|
NODE_USE_ENV_PROXY=1
|
||||||
@@ -75,12 +75,15 @@ function Get-PublicIp {
|
|||||||
catch { return $null }
|
catch { return $null }
|
||||||
}
|
}
|
||||||
|
|
||||||
# Единый источник правды для прокси-эндпоинта.
|
# Единый источник правды для прокси-эндпоинта в PowerShell.
|
||||||
|
# Список NoProxy обязан совпадать с тем, что выдаёт proxy-client
|
||||||
|
# (см. DEFAULT_NO_PROXY_EXTRA в proxy-client/.local/bin/proxy-client);
|
||||||
|
# расхождение ловит tests/test_proxy_env_sync.py.
|
||||||
function Get-ProxyEndpoint {
|
function Get-ProxyEndpoint {
|
||||||
@{
|
@{
|
||||||
ProxyHost = 'hproxy.local'
|
ProxyHost = 'hproxy.local'
|
||||||
Port = 12334
|
Port = 12334
|
||||||
NoProxy = 'hproxy.local,localhost,127.0.0.1,.concept-software.ru'
|
NoProxy = 'hproxy.local,localhost,127.0.0.1,::1,[::1],.concept-software.ru,dementev.space,.dementev.space'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,8 +4,10 @@ $ProxyHostName = 'hproxy.local'
|
|||||||
$ProxyPort = 12334
|
$ProxyPort = 12334
|
||||||
$HttpProxy = "http://${ProxyHostName}:${ProxyPort}"
|
$HttpProxy = "http://${ProxyHostName}:${ProxyPort}"
|
||||||
$SocksProxy = "socks5://${ProxyHostName}:${ProxyPort}"
|
$SocksProxy = "socks5://${ProxyHostName}:${ProxyPort}"
|
||||||
$NoProxy = "${ProxyHostName},localhost,127.0.0.1,::1,[::1],.concept-software.ru"
|
# Список обязан совпадать с выдачей proxy-client (DEFAULT_NO_PROXY_EXTRA);
|
||||||
$ProxyBypassList = "${ProxyHostName};localhost;127.0.0.1;[::1];*.concept-software.ru"
|
# расхождение ловит tests/test_proxy_env_sync.py.
|
||||||
|
$NoProxy = "${ProxyHostName},localhost,127.0.0.1,::1,[::1],.concept-software.ru,dementev.space,.dementev.space"
|
||||||
|
$ProxyBypassList = "${ProxyHostName};localhost;127.0.0.1;[::1];*.concept-software.ru;dementev.space;*.dementev.space"
|
||||||
|
|
||||||
function Set-ProxyEnvironment {
|
function Set-ProxyEnvironment {
|
||||||
$env:HTTP_PROXY = $HttpProxy
|
$env:HTTP_PROXY = $HttpProxy
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ $ErrorActionPreference = 'Stop'
|
|||||||
$ProxyHostName = 'hproxy.local'
|
$ProxyHostName = 'hproxy.local'
|
||||||
$ProxyPort = 12334
|
$ProxyPort = 12334
|
||||||
$HttpProxy = "http://${ProxyHostName}:${ProxyPort}"
|
$HttpProxy = "http://${ProxyHostName}:${ProxyPort}"
|
||||||
$ProxyBypassList = "${ProxyHostName};localhost;127.0.0.1;[::1];*.concept-software.ru"
|
$ProxyBypassList = "${ProxyHostName};localhost;127.0.0.1;[::1];*.concept-software.ru;dementev.space;*.dementev.space"
|
||||||
|
|
||||||
function Get-CodexApplication {
|
function Get-CodexApplication {
|
||||||
$Package = Get-AppxPackage -Name 'OpenAI.Codex' -ErrorAction SilentlyContinue |
|
$Package = Get-AppxPackage -Name 'OpenAI.Codex' -ErrorAction SilentlyContinue |
|
||||||
|
|||||||
@@ -38,7 +38,13 @@ CONFIG_KEYS = {"type", "name", "address", "port", "no_proxy_extra"}
|
|||||||
DEFAULT_NAME = "Hiddify"
|
DEFAULT_NAME = "Hiddify"
|
||||||
DEFAULT_ADDRESS = "hproxy.local"
|
DEFAULT_ADDRESS = "hproxy.local"
|
||||||
DEFAULT_PORT = 12334
|
DEFAULT_PORT = 12334
|
||||||
DEFAULT_NO_PROXY_EXTRA = (".concept-software.ru",)
|
DEFAULT_NO_PROXY_EXTRA = (
|
||||||
|
".concept-software.ru",
|
||||||
|
# Свой Gitea: домен пишем в двух видах, потому что программы читают список
|
||||||
|
# по-разному — где-то запись с точкой покрывает только поддомены.
|
||||||
|
"dementev.space",
|
||||||
|
".dementev.space",
|
||||||
|
)
|
||||||
XRAY_VERSION = "26.3.27"
|
XRAY_VERSION = "26.3.27"
|
||||||
VERSION_LINE = re.compile(
|
VERSION_LINE = re.compile(
|
||||||
r"^Xray ({0})(?: .*)?$".format(re.escape(XRAY_VERSION))
|
r"^Xray ({0})(?: .*)?$".format(re.escape(XRAY_VERSION))
|
||||||
|
|||||||
@@ -334,7 +334,8 @@ source ~/.config/proxy-client/shell.bash
|
|||||||
|
|
||||||
Поддержка CIDR в `NO_PROXY` различается между программами, поэтому надёжнее
|
Поддержка CIDR в `NO_PROXY` различается между программами, поэтому надёжнее
|
||||||
указывать точные IP и домены. Без машинного файла внешний режим дополнительно
|
указывать точные IP и домены. Без машинного файла внешний режим дополнительно
|
||||||
включает `.concept-software.ru` в `NO_PROXY`. Для `type = xray` адрес обязан
|
включает `.concept-software.ru`, `dementev.space` и `.dementev.space` в
|
||||||
|
`NO_PROXY`. Для `type = xray` адрес обязан
|
||||||
быть адресом обратной петли, например `127.0.0.1` или `::1`.
|
быть адресом обратной петли, например `127.0.0.1` или `::1`.
|
||||||
|
|
||||||
### Поддерживаемые профили
|
### Поддерживаемые профили
|
||||||
|
|||||||
@@ -0,0 +1,81 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
"""Список исключений прокси лежит в нескольких профилях (Bash, PowerShell,
|
||||||
|
Codex, VS Code). Раньше копии расходились, и один и тот же хост то шёл мимо
|
||||||
|
прокси, то нет. Тест сверяет каждую копию с эталоном из proxy-client."""
|
||||||
|
import re
|
||||||
|
import runpy
|
||||||
|
import unittest
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
REPO_ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
CLI = REPO_ROOT / "proxy-client/.local/bin/proxy-client"
|
||||||
|
|
||||||
|
# Где список продублирован: файл -> (регулярное выражение, начало строки,
|
||||||
|
# которое в файле подставляется переменной и потому не попадает в захват).
|
||||||
|
COPIES = (
|
||||||
|
("chezmoi/dot_codex/dot_env", re.compile(r"^NO_PROXY=(.+)$", re.M), ""),
|
||||||
|
("chezmoi/dot_codex/dot_env", re.compile(r"^no_proxy=(.+)$", re.M), ""),
|
||||||
|
(
|
||||||
|
"chezmoi/dot_config/powershell/profile.ps1",
|
||||||
|
re.compile(r"^\s*NoProxy\s*=\s*'(.+)'\s*$", re.M),
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"chezmoi/dot_local/bin/start-claude-proxy.ps1",
|
||||||
|
re.compile(r'^\$NoProxy = "\$\{ProxyHostName\},(.+)"\s*$', re.M),
|
||||||
|
"hproxy.local,",
|
||||||
|
),
|
||||||
|
(
|
||||||
|
"vscode/.vscode-server/server-env-setup",
|
||||||
|
re.compile(r'^\s*export NO_PROXY="(.+)"\s*$', re.M),
|
||||||
|
"",
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
# Списки обхода для Windows: тот же смысл, другой синтаксис (точка с запятой,
|
||||||
|
# поддомены звёздочкой), поэтому сверяем их только между собой.
|
||||||
|
BYPASS_FILES = (
|
||||||
|
"chezmoi/dot_local/bin/start-claude-proxy.ps1",
|
||||||
|
"chezmoi/dot_local/bin/start-codex-proxy.ps1",
|
||||||
|
)
|
||||||
|
BYPASS_RE = re.compile(r'^\$ProxyBypassList = "\$\{ProxyHostName\};(.+)"\s*$', re.M)
|
||||||
|
|
||||||
|
|
||||||
|
def expected_no_proxy():
|
||||||
|
"""Эталон — то, что proxy-client выдаёт в режиме по умолчанию."""
|
||||||
|
module = runpy.run_path(str(CLI))
|
||||||
|
items = [module["DEFAULT_ADDRESS"], "localhost", "127.0.0.1", "::1", "[::1]"]
|
||||||
|
items.extend(module["DEFAULT_NO_PROXY_EXTRA"])
|
||||||
|
return ",".join(dict.fromkeys(items))
|
||||||
|
|
||||||
|
|
||||||
|
class NoProxySyncTest(unittest.TestCase):
|
||||||
|
def test_copies_match_proxy_client(self):
|
||||||
|
expected = expected_no_proxy()
|
||||||
|
for relative_path, pattern, prefix in COPIES:
|
||||||
|
text = (REPO_ROOT / relative_path).read_text(encoding="utf-8")
|
||||||
|
match = pattern.search(text)
|
||||||
|
self.assertIsNotNone(
|
||||||
|
match, "{}: не найден список исключений".format(relative_path)
|
||||||
|
)
|
||||||
|
self.assertEqual(
|
||||||
|
expected,
|
||||||
|
prefix + match.group(1),
|
||||||
|
"{}: список разошёлся".format(relative_path),
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_windows_bypass_lists_match_each_other(self):
|
||||||
|
values = []
|
||||||
|
for relative_path in BYPASS_FILES:
|
||||||
|
text = (REPO_ROOT / relative_path).read_text(encoding="utf-8")
|
||||||
|
match = BYPASS_RE.search(text)
|
||||||
|
self.assertIsNotNone(
|
||||||
|
match, "{}: не найден ProxyBypassList".format(relative_path)
|
||||||
|
)
|
||||||
|
values.append(match.group(1))
|
||||||
|
self.assertEqual(values[0], values[1], "ProxyBypassList разошёлся")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
unittest.main()
|
||||||
@@ -25,7 +25,9 @@ if getent hosts hproxy.local >/dev/null 2>&1; then
|
|||||||
export HTTP_PROXY="$proxy_url"
|
export HTTP_PROXY="$proxy_url"
|
||||||
export HTTPS_PROXY="$proxy_url"
|
export HTTPS_PROXY="$proxy_url"
|
||||||
export ALL_PROXY="socks5://hproxy.local:12334"
|
export ALL_PROXY="socks5://hproxy.local:12334"
|
||||||
export NO_PROXY="hproxy.local,localhost,127.0.0.1,.concept-software.ru"
|
# Список обязан совпадать с выдачей proxy-client (DEFAULT_NO_PROXY_EXTRA);
|
||||||
|
# расхождение ловит tests/test_proxy_env_sync.py.
|
||||||
|
export NO_PROXY="hproxy.local,localhost,127.0.0.1,::1,[::1],.concept-software.ru,dementev.space,.dementev.space"
|
||||||
|
|
||||||
# Дублируем в нижнем регистре для лучшей совместимости с инструментами
|
# Дублируем в нижнем регистре для лучшей совместимости с инструментами
|
||||||
export http_proxy="$HTTP_PROXY"
|
export http_proxy="$HTTP_PROXY"
|
||||||
|
|||||||
Reference in new issue
Block a user