서로 다른 두 장애가 동일한 pip 트레이스백을 출력하며, 한쪽에 맞는 수리가 다른 쪽을 악화시킵니다. 터널로 감싼 호스트를 지목하기에 너무 오래된 클라이언트에는 업그레이드가 필요합니다. 트래픽에 다시 서명하는 프록시에는 버전을 낮추는 것이 아니라 신뢰할 수 있는 루트 인증서가 필요합니다. 경로가 반대 방향으로 이어지므로, 프록시 변수를 편집하기 전에 둘 중 어느 것인지 판별하십시오.

설정을 건드리기 전에 마지막 프레임을 읽으십시오

트레이스백이 끝나는 지점이 어느 계층이 포기했는지 알려 줍니다. 마지막 프레임이 pip에 함께 들어 있는 urllib3 TLS 코드 안에 있고 본문이 호스트 이름을 문제 삼는다면, 클라이언트 라이브러리가 검증 단계를 끝내지 못한 것이며 이는 인증서 문제보다 버전 문제일 때가 훨씬 많습니다. 본문이 대신 거부된 연결, 확인되지 않은 프록시 이름 또는 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 seeFirst commandRepair
hostname message inside vendored TLS framespython3 -m pip --versionupgrade the client
certificate verify failed after the tunnelcurl -x http://proxy.corp.internal:8080 --head https://pypi.org/simple/trust the corporate root
connection refused or HTTP 407env | grep -i -E 'proxy|no_proxy'correct host, port or credentials

관련 글