Памылка ProxyError у Requests на прыватным API? Спачатку праверце асяроддзе
Requests аб'ядноўвае налады проксі, аўтэнтыфікацыі і CA з асяроддзя. Прачытайце, што бачыць працэс, выключыце прыватны хост і пакіньце праверку TLS уключанай.
VPN працуе, унутраная панэль адкрываецца ў браўзеры, а скрыпт Python усё роўна выдае памылку проксі. У гэтай карціне з тунэлем усё ў парадку: інтэрпрэтатар чытае адрас проксі з уласнага асяроддзя і накіроўвае запыт туды. Гэтае кіраўніцтва чытае асяроддзе, якое працэс бачыць насамрэч, ужывае самае вузкае выключэнне і толькі пасля гэтага разглядае яўную палітыку сеансу. Кожны крок завяршаецца камандай, вынік якой вырашае, ці працягваць.
Прачытайце асяроддзе, якое працэс бачыць насамрэч
Налады проксі на працоўным стале, экспарты абалонкі і радкі Environment= у юніце systemd — гэта тры розныя крыніцы, і Requests чытае толькі апошнюю з тых, што атрымлівае ў спадчыну. Запусціце дыягностыку ніжэй у той жа абалонцы, кантэйнеры ці юніце, што запускае збойны скрыпт, а не ў сваім інтэрактыўным тэрмінале. Зменная, экспартаваная ў вашай абалонцы ўваходу, нябачная для працэсу кантэйнера, і менавіта гэтая прага тлумачыць шмат паведамленняў пра проксі, які з'яўляецца ніадкуль. Дзве каманды друкуюць адныя і тыя ж даныя з рознай ступенню дэталізацыі.
env | grep -i -E 'proxy|requests_ca_bundle|curl_ca_bundle'
python3 -c "import os;[print(k,'=',v) for k,v in sorted(os.environ.items()) if 'proxy' in k.lower()]"
Чытаюцца абодва напісанні кожнай зменнай, а дублікаты прымяняюцца ў тым парадку, у якім працэс іх атрымаў, таму састарэлая HTTP_PROXY можа перакрыць значэнне, якое вы толькі што экспартавалі. Зменныя CA-bundle тут таксама важныя, бо яны змяняюць тое, у што разгортваецца verify, не з'яўляючыся нідзе ў кодзе. Калі вывад пусты, асяроддзе — не ваша праблема, і вы можаце спыніць чытанне гэтага раздзела. Калі ў ім пералічаны проксі, якога вы не пазнаеце, знайдзіце файл ці юніт, які яго задае, перш чым што-небудзь змяняць.
Спытайце ў Requests, што ён зробіць з пэўным URL. Гэты выклік паказвае аб'яднаныя значэнні проксі, verify і cert для гэтага запыту, не адпраўляючы пакет.
python3 -c "import requests;s=requests.Session();print(s.merge_environment_settings('https://api.corp.example/status',{},None,None,None))"
Непусты запіс proxies для прыватнага хоста — гэта ваш адказ, а надрукаванае значэнне verify паведамляе, якому CA-bundle даверыцца запыт. Калі абодва выглядаюць слушна, збой знаходзіцца далей у стэку, і вам варта перайсці да раздзела праверкі. Не здагадвайцеся па кодзе: аб'яднанне адбываецца для кожнага запыту, таму адзін сеанс можа паводзіць сябе па-рознаму для двух хасцёў.
Выключыце прыватны хост, перш чым нешта адключаць
Калі проксі сапраўды патрэбны для іншых прызначэнняў, вузкім рашэннем будзе выключэнне для гэтага хоста. Дадавайце да існуючага спісу, а не замяняйце яго, бо сціранне NO_PROXY ламае ўсе іншыя ўнутраныя прызначэнні, якія на яго спадзяваліся. Каманды ніжэй дадаюць запіс і затым пытаюцца ў Requests, ці дзейнічае абыход.
export NO_PROXY="${NO_PROXY:+$NO_PROXY,}api.corp.example,.corp.example"
python3 -c "import requests;print(requests.utils.should_bypass_proxies('https://api.corp.example/status',None))"
Супадзенне ідзе па суфіксе хоста, таму .corp.example пакрывае ўвесь дамен, а api.corp.example — адно імя. Голы зорачка адключае проксі для кожнага хоста і амаль ніколі не тое, чаго хоча працоўная задача. Ці будзе ўлічаны запіс CIDR, залежыць ад версій Requests і urllib3 у вобразе, таму праверце дакладны запіс, а не мяркуйце, што ён працуе. Праверце вынік should_bypass_proxies для рэальнага URL, а не для імя хоста, якое вы набралі па памяці.
Трымайце два ўзроўні асобна ў галаве. Абыход проксі HTTP не абыходзіць VPN, бо рашэнне пра проксі прымаецца ў бібліятэцы, а рашэнне пра тунэль — у табліцы маршрутызацыі ядра.
Адключайце trust_env толькі як свядома выбраную палітыку сеансу
Калі ўся задача павінна ігнараваць навакольную канфігурацыю, пабудуйце сеанс яўна і зафіксуйце рашэнне ў кодзе. Гэта палітыка, а не абыходны шлях, і ёй месца ў diff, які бачыць рэцэнзент. Прыклад ніжэй захоўвае тайм-аўт, яўны шлях да CA і праверку статусу.
import requests
with requests.Session() as session:
session.trust_env = False
response = session.get(
"https://api.corp.example/status",
timeout=(5, 20),
verify="/etc/ssl/certs/corp-ca.pem",
)
response.raise_for_status()
Адключэнне trust_env змяняе тры паводзіны адразу, і толькі адна з іх датычыцца проксі. Requests перастае чытаць уліковыя даныя .netrc, а зменныя REQUESTS_CA_BUNDLE і CURL_CA_BUNDLE перастаюць уплываць на verify, які тады звяртаецца да ўбудаванага сховішча сертыфікатаў. Калі ваша арганізацыя завяршае TLS сваім CA, гэтае вяртанне выдае памылку праверкі, якая выглядае не звязанай са зменаю, якую вы толькі што зрабілі. Менавіта яўная перадача verify трымае два эфекты асобна.
Чаго рабіць не варта — гэта ставіць verify=False, каб заглушыць памылку, якая ўзнікла. Гэта мяняе няправільную канфігурацыю проксі на неправеранае злучэнне і хавае праблему сертыфіката, якую павінна была выявіць выключэнне.
Праверце збойны ўзровень і захавайце магчымасць адкату
Тры збоі выглядаюць у журнале аднолькава і не маюць нічога агульнага: проксі, які не дасягае прызначэння, выдае ProxyError або вяртае 502, праблема даверу выдае SSLError, а адмова праграмы вяртае 401 або 403. Узнавіце запыт па-за Requests, каб убачыць, які з іх у вас. Каманды ніжэй выдаляюць проксі, правяраюць ланцужок сертыфікатаў і затым паўтараюць выклік праз бібліятэку.
curl -sv --noproxy '*' https://api.corp.example/status -o /dev/null
openssl s_client -connect api.corp.example:443 -servername api.corp.example </dev/null
python3 -c "import requests;r=requests.get('https://api.corp.example/status',timeout=10);print(r.status_code)"
Калі выклік curl паспяховы, а выклік Python збойны, розніца ўсё яшчэ ў асяроддзі. Калі абодва збойныя на этапе TLS, проксі ніколі не быў праблемай, а праблема — у ланцужку сертыфікатаў. Калі абодва вяртаюць 401, запыт дасягнуў праграмы, і астатняя праца — аўтэнтыфікацыя. Запіс таго, які з трох вы назіралі, зэканоміць наступнаму чалавеку гадзіну.
Адкат — гэта адзін радок у любым кірунку: прыбярыце выключэнне або вярніце trust_env назад. Захавайце папярэдняе значэнне NO_PROXY, перш чым дадаваць да яго, і аддавайце перавагу скрыпту-абгортцы або запісу Environment= на ўзроўні юніта замест экспарту, які жыве толькі ў гісторыі вашага тэрмінала. Абедзве змены бачныя ў асяроддзі працэсу, таму перазапуск звычайна вяртае вас да старой паводзіны.
Завяршыце, пацвердзіўшы чатыры рэчы: аб'яднаныя налады для прыватнага URL не паказваюць проксі, сертыфікат правяраецца супраць патрэбнага CA, запыт вяртае чаканы статус, а змяненне зафіксавана ў месцы, якое можна ўзнавіць. Зялёны скрыпт з непатлумачаным асяроддзем — гэта выпраўленне, якое зноў зламаецца на наступнай машыне. Каманды з гэтага кіраўніцтва — тыя, што варта ўставіць у тыкет.