خطای check_hostname در pip پشت پروکسی: pip یا تونل؟
دو خرابی، همان پیام نام میزبان pip را پشت پروکسی چاپ میکنند. کلاینت کهنه را از پروکسیای که ترافیک را دوباره امضا میکند جدا کنید و لایه درست را تعمیر کنید.
دو خرابی متفاوت همان ردپای pip را چاپ میکنند و تعمیری که به یکی میخورد دیگری را بدتر میکند. کلاینتی که برای نام بردن یک میزبان تونلشده خیلی قدیمی است به ارتقا نیاز دارد. پروکسیای که ترافیک را دوباره امضا میکند به گواهی ریشه مورد اعتماد نیاز دارد، نه به تنزل نسخه. پیش از ویرایش یک متغیر پروکسی مشخص کنید کدامیک از این دو را دارید، چون مسیرها در جهتهای مخالف میروند.
پیش از دست زدن به تنظیمات، فریمهای آخر را بخوانید
جایی که ردپا پایان مییابد میگوید کدام لایه تسلیم شده است. اگر فریمهای پایانی درون کد TLS کتابخانه urllib3 همراه pip باشند و متن از نام میزبان شکایت کند، کتابخانه کلاینت نتوانسته یک مرحله بررسی را تمام کند و این بسیار بیشتر مشکل نسخه است تا مشکل گواهی. اگر متن بهجای آن از اتصال ردشده، نام پروکسی حلنشده یا خطای 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 |