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:
Dmitry Dementiev committed 2026-07-27 20:53:44 +03:00
commit 2d4af081b8
9 files changed
+111 -10

No files matched your search

+6
View File
@@ -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
На всех платформах фиксируются настройки для предсказуемого поведения: На всех платформах фиксируются настройки для предсказуемого поведения:
+2 -2
View File
@@ -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
+5 -2
View File
@@ -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 -2
View File
@@ -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
+1 -1
View File
@@ -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 |
+7 -1
View File
@@ -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))
+2 -1
View File
@@ -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`.
### Поддерживаемые профили ### Поддерживаемые профили
+81
View File
@@ -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()
+3 -1
View File
@@ -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"