Ошибка check_hostname в pip за прокси: pip или туннель?
Два разных сбоя печатают одну и ту же ошибку имени хоста в pip за прокси. Отличите устаревший клиент от прокси, который заново подписывает трафик, и исправьте нужный слой.
Два разных сбоя печатают один и тот же трейсбек pip, и исправление, подходящее одному, ухудшает другой. Клиент, слишком старый, чтобы назвать туннелированный хост, требует обновления. Прокси, который заново подписывает трафик, требует доверенного корневого сертификата, а не понижения версии. Определите, какой из двух у вас, прежде чем править переменную прокси, потому что пути ведут в противоположные стороны.
Прочитайте последние кадры, прежде чем трогать настройки
То, где заканчивается трейсбек, говорит, какой слой сдался. Если замыкающие кадры находятся в поставляемом с pip коде TLS urllib3 и текст жалуется на имя хоста, клиентская библиотека не смогла завершить шаг проверки, и это проблема версии гораздо чаще, чем проблема сертификата. Если текст вместо этого сообщает об отказе в соединении, неразрешённом имени прокси или ошибке HTTP 407, виновата конечная точка туннеля, и никакое обновление этого не изменит.
Зафиксируйте версию pip и переменные прокси в той же оболочке, которая падает. Оболочка, открытая до поднятия туннеля, несёт другие значения, чем открытая после, и разница между ними часто и есть весь диагноз.
python3 -m pip --version
env | grep -i -E 'proxy|no_proxy'
Когда клиент старше туннеля
Более старые выпуски pip включали стек HTTPS, который терял имя целевого хоста, как только сокет оборачивался туннелем. Сообщение check_hostname — это то, что такой стек выдаёт, дойдя до проверки без имени для сравнения. Обновление убирает неисправность, потому что более новый клиент сохраняет имя на протяжении рукопожатия, и изменение достаточно дёшево, чтобы попробовать его первым всякий раз, когда замыкающие кадры указывают на встроенный код TLS.
Если индекс недоступен с падающей машины, загрузите актуальный wheel на хосте, который может это сделать, и установите из файла. Используйте имя файла, который вы действительно скачали, а не версию, скопированную из поста в блоге.
python3 -m pip install --upgrade pip
python3 -m pip install ./pip-24.2-py3-none-any.whl
Когда прокси заново подписывает трафик
Прокси, который завершает TLS и перевыпускает сертификаты под корпоративным корнем, даёт другую ошибку: certificate verify failed, а не жалобу на имя хоста. Это различие и есть подсказка, потому что клиент дошёл до сервера и затем отверг показанную ему идентичность. Исправление — явно доверять этому корню, будь то для одной команды или через переменную PIP_CERT, чтобы клиент мог построить цепочку к тому, что предъявляет прокси. Добавление корня в системное хранилище доверия тоже работает, но меняет доверие для каждой программы на машине, и это большее решение, чем починка одной установки пакета.
Держите --trusted-host как диагностику, никогда как постоянное состояние. Он отключает проверку, которую вы пытались сохранить, поэтому действительно неверный сертификат после этого выглядит здоровым. Если вы использовали его, чтобы установить один пакет, уберите его и почините цепочку до следующего выпуска.
Ваш собственный код на Python полностью игнорирует конфигурацию pip. Вызову requests нужно передать прокси и пакет туда, где этот сеанс их видит, поэтому блок ниже задаёт и то и другое на объекте сеанса.
pip install --cert /usr/local/share/ca-certificates/corp-root.crt requests
export PIP_CERT=/usr/local/share/ca-certificates/corp-root.crt
s = requests.Session()
s.proxies = {"https": "http://10.20.30.40:8080"}
s.verify = "/usr/local/share/ca-certificates/corp-root.crt"
s.get("https://pypi.org/simple/", timeout=15)
Когда конечная точка или порт неверны
Клиент туннеля часто переписывает системный прокси при подключении, из-за чего pip может остаться направленным на хост, который никогда не отвечает на этом порту. Проверьте конечную точку клиентом, который не является pip, прежде чем менять любую настройку pip. Если curl падает с тем же аргументом прокси, неисправность выше pip, и никакая конфигурация внутри pip не поможет.
Флаг команды и переменные окружения говорят одно и то же разным областям: флаг действует для одного вызова, переменные — для каждого запущенного после процесса. Добавьте NO_PROXY для внутреннего индекса, чтобы приватный трафик пакетов пропускал обход через туннель.
curl -x http://proxy.corp.internal:8080 --head https://pypi.org/simple/
pip install --proxy http://proxy.corp.internal:8080 requests
Сохраните настройку и докажите, что путь работает
Храните прокси в собственной конфигурации pip, когда он должен пережить текущую оболочку, затем прочитайте, что pip действительно будет использовать. Вывод debug называет файл и переменную за каждым значением, и это улаживает споры о приоритете быстрее, чем чтение документации.
Проверка намеренно использует двух клиентов. Запрос из стандартной библиотеки доказывает маршрут и цепочку доверия, а загрузка доказывает, что собственная конфигурация pip с ними согласна. Когда первый успешен, а вторая падает, разница внутри pip, а не в сети.
pip config set global.proxy http://proxy.corp.internal:8080
pip config debug
python3 -c "import urllib.request as r; print(r.urlopen('https://pypi.org/simple/').status)"
pip download --no-deps -d /tmp/wheelcheck requests
| What you see | First command | Repair |
|---|---|---|
| hostname message inside vendored TLS frames | python3 -m pip --version | upgrade the client |
| certificate verify failed after the tunnel | curl -x http://proxy.corp.internal:8080 --head https://pypi.org/simple/ | trust the corporate root |
| connection refused or HTTP 407 | env | grep -i -E 'proxy|no_proxy' | correct host, port or credentials |