Requests ProxyError na prywatnym API? Najpierw sprawdź środowisko
Requests łączy ustawienia serwera proxy, uwierzytelniania i CA ze środowiska. Przeczytaj, co widzi proces, wyklucz prywatnego hosta i pozostaw włączoną weryfikację TLS.
VPN działa, wewnętrzny panel otwiera się w przeglądarce, a skrypt w Pythonie nadal zgłasza błąd serwera proxy. W tym obrazie z tunelem wszystko jest w porządku: interpreter czyta adres serwera proxy z własnego środowiska i tam wysyła żądanie. Ten przewodnik czyta środowisko, które proces naprawdę widzi, stosuje najwęższe wykluczenie i dopiero potem rozważa jawną politykę sesji. Każdy krok kończy się poleceniem, którego wynik decyduje, czy kontynuować.
Przeczytaj środowisko, które proces naprawdę widzi
Ustawienia serwera proxy w systemie, eksporty w powłoce i wiersze Environment= jednostki systemd to trzy różne źródła, a Requests czyta tylko ostatnie, które dziedziczy. Uruchom poniższą diagnostykę w tej samej powłoce, kontenerze lub jednostce, która uruchamia zawodny skrypt, a nie w swoim terminalu interaktywnym. Zmienna wyeksportowana w powłoce logowania jest niewidoczna dla procesu w kontenerze, i ta luka wyjaśnia wiele zgłoszeń o serwerze proxy, który pojawia się znikąd. Oba polecenia wypisują te same dane na różnych poziomach szczegółowości.
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()]"
Odczytywane są obie pisownie każdej zmiennej, a zduplikowane wpisy są stosowane w kolejności, w jakiej proces je otrzymał, więc nieaktualny HTTP_PROXY może przesłonić wartość, którą właśnie wyeksportowano. Zmienne pakietu CA też mają tu znaczenie, bo zmieniają, na co rozwiązuje się verify, nie pojawiając się nigdzie w kodzie. Jeśli dane wyjściowe są puste, środowisko nie jest twoim problemem i możesz przestać czytać tę sekcję. Jeśli wymienia serwer proxy, którego nie rozpoznajesz, znajdź plik lub jednostkę, która go ustawia, zanim cokolwiek zmienisz.
Zapytaj Requests, co zrobiłby z konkretnym adresem URL. To wywołanie pokazuje połączone wartości proxy, verify i cert dla tego żądania bez wysyłania pakietu.
python3 -c "import requests;s=requests.Session();print(s.merge_environment_settings('https://api.corp.example/status',{},None,None,None))"
Niepusty wpis proxies dla prywatnego hosta to twoja odpowiedź, a wydrukowana wartość verify mówi, któremu pakietowi CA żądanie by zaufało. Jeśli oba wyglądają poprawnie, awaria jest dalej w stosie i powinieneś przejść do sekcji weryfikacji. Nie zgaduj z kodu: łączenie odbywa się dla każdego żądania, dlatego jedna sesja może zachowywać się inaczej wobec dwóch hostów.
Wyklucz prywatnego hosta, zanim cokolwiek wyłączysz
Gdy serwer proxy jest naprawdę potrzebny dla innych celów, wąskim rozwiązaniem jest wykluczenie tego hosta. Dopisuj do istniejącej listy, zamiast ją zastępować, bo usunięcie NO_PROXY psuje każdy inny cel wewnętrzny, który na nim polegał. Poniższe polecenia dopisują wpis i następnie pytają Requests, czy obejście obowiązuje.
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))"
Dopasowanie odbywa się po sufiksie hosta, więc .corp.example obejmuje całą domenę, a api.corp.example jedno imię. Samotna gwiazdka wyłącza serwer proxy dla każdego hosta i prawie nigdy nie jest tym, czego chce zadanie produkcyjne. To, czy wpis CIDR zostanie uwzględniony, zależy od wersji Requests i urllib3 w obrazie, więc przetestuj dokładny wpis, zamiast zakładać, że działa. Sprawdź wynik should_bypass_proxies dla prawdziwego adresu URL, a nie dla nazwy hosta wpisanej z pamięci.
Trzymaj obie warstwy osobno w głowie. Obejście serwera proxy HTTP nie omija VPN, bo decyzja o proxy zapada w bibliotece, a decyzja o tunelu w tablicy routingu jądra.
Wyłączaj trust_env tylko jako świadomą politykę sesji
Jeśli całe zadanie ma ignorować konfigurację otoczenia, zbuduj sesję jawnie i zapisz decyzję w kodzie. To jest polityka, a nie obejście, i należy do diffu, który recenzent może zobaczyć. Poniższy przykład zachowuje limit czasu, jawną ścieżkę CA i sprawdzenie statusu.
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()
Wyłączenie trust_env zmienia trzy zachowania naraz, a tylko jedno z nich dotyczy serwera proxy. Requests przestaje czytać poświadczenia .netrc, a zmienne REQUESTS_CA_BUNDLE i CURL_CA_BUNDLE przestają wpływać na verify, który wtedy wraca do wbudowanego magazynu certyfikatów. Jeśli twoja organizacja kończy TLS własnym CA, ten powrót daje błąd weryfikacji, który wydaje się niezwiązany z właśnie wprowadzoną zmianą. To jawne przekazanie verify utrzymuje oba efekty osobno.
Czego nie należy robić, to ustawienie verify=False, aby uciszyć wynikowy błąd. To zamienia błędną konfigurację serwera proxy na niezweryfikowane połączenie i ukrywa problem z certyfikatem, który wykluczenie miało ujawnić.
Zweryfikuj warstwę, która zawodzi, i zachowaj możliwość wycofania
Trzy awarie wyglądają w logu tak samo i nie mają nic wspólnego: serwer proxy, który nie dociera do celu, zgłasza ProxyError albo zwraca 502, problem z zaufaniem zgłasza SSLError, a odrzucenie przez aplikację zwraca 401 lub 403. Odtwórz żądanie poza Requests, aby zobaczyć, który z nich masz. Poniższe polecenia usuwają serwer proxy, testują łańcuch certyfikatów, a następnie powtarzają wywołanie przez bibliotekę.
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)"
Jeśli wywołanie curl się udaje, a wywołanie w Pythonie zawodzi, różnicą nadal jest środowisko. Jeśli oba zawodzą na etapie TLS, serwer proxy nigdy nie był problemem i problemem jest łańcuch certyfikatów. Jeśli oba zwracają 401, żądanie dotarło do aplikacji, a pozostała praca to uwierzytelnianie. Zapisanie, który z trzech przypadków zaobserwowano, oszczędza następnej osobie godzinę.
Wycofanie to jedna linia w obu kierunkach: usuń wykluczenie albo przywróć trust_env. Zapisz poprzednią wartość NO_PROXY, zanim coś dodasz, i preferuj skrypt opakowujący lub wpis Environment= na poziomie jednostki zamiast eksportu, który żyje tylko w historii twojego terminala. Obie zmiany są widoczne w środowisku procesu, więc restart zwykle przywraca stare zachowanie.
Zakończ, potwierdzając cztery rzeczy: połączone ustawienia dla prywatnego adresu URL nie pokazują serwera proxy, certyfikat jest weryfikowany wobec zamierzonego CA, żądanie zwraca oczekiwany status, a zmiana jest zatwierdzona w miejscu, które można odtworzyć. Zielony skrypt z niewyjaśnionym środowiskiem to poprawka, która znów się zepsuje na następnej maszynie. Polecenia z tego przewodnika to te, które należy wkleić do zgłoszenia.