VPN är uppe, den interna instrumentpanelen öppnas i en webbläsare, och Python-skriptet kastar fortfarande ett proxyfel. Det är inget fel på tunneln i den bilden: tolken läser en proxyadress från sin egen miljö och skickar begäran dit. Den här runbooken läser miljön som processen faktiskt ser, tillämpar den smalaste uteslutningen och överväger först därefter en uttrycklig sessionspolicy. Varje steg slutar med ett kommando vars utdata avgör om du fortsätter.

Läs miljön som processen faktiskt ser

Proxyinställningar på skrivbordet, shell-exporter och Environment=-raderna i en systemd-enhet är tre olika källor, och Requests läser bara den sista som den ärver. Kör diagnostiken nedan i samma shell, container eller enhet som kör det misslyckade skriptet, inte i din interaktiva terminal. En variabel som exporteras i ditt inloggningsskal är osynlig för en containerprocess, och den luckan förklarar många rapporter om en proxy som dyker upp från ingenstans. De två kommandona skriver ut samma data på olika detaljnivåer.

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()]"

Båda stavningarna av varje variabel läses, och dubbletter tillämpas i den ordning processen tog emot dem, så en inaktuell HTTP_PROXY kan skriva över värdet du just exporterade. CA-bundle-variablerna spelar roll här också, eftersom de ändrar vad verify löses upp till utan att dyka upp någonstans i koden. Om utdata är tomt är miljön inte ditt problem och du kan sluta läsa det här avsnittet. Om den listar en proxy du inte känner igen, hitta filen eller enheten som ställer in den innan du ändrar något.

Fråga Requests vad den skulle göra med en specifik URL. Det här anropet visar de sammanslagna proxy-, verify- och cert-värdena för den begäran utan att skicka ett paket.

python3 -c "import requests;s=requests.Session();print(s.merge_environment_settings('https://api.corp.example/status',{},None,None,None))"

En icke-tom proxies-post för den privata värden är ditt svar, och det utskrivna verify-värdet talar om vilket CA-bundle begäran skulle lita på. Om båda ser korrekta ut ligger felet längre ner i stacken och du bör hoppa till verifieringsavsnittet. Gissa inte från koden: sammanslagningen sker per begäran, vilket är varför en session kan bete sig olika för två värdar.

Uteslut den privata värden innan du inaktiverar något

När proxyn verkligen behövs för andra destinationer är den smala rättelsen en uteslutning för den här värden. Lägg till i den befintliga listan i stället för att ersätta den, eftersom radering av NO_PROXY bryter varje annan intern destination som förlitade sig på den. Kommandona nedan lägger till posten och frågar sedan Requests om kringgåendet gäller.

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))"

Matchningen sker efter värdens suffix, så .corp.example täcker hela domänen medan api.corp.example täcker ett enda namn. En bar stjärna inaktiverar proxy för varje värd och är nästan aldrig vad ett produktionsjobb vill ha. Om en CIDR-post respekteras beror på Requests- och urllib3-versionerna i imagen, så testa den exakta posten i stället för att anta att den fungerar. Kontrollera resultatet av should_bypass_proxies för den riktiga URL:en, inte för ett värdnamn du skrev från minnet.

Håll de två lagren åtskilda i huvudet. Att kringgå HTTP-proxyn kringgår inte VPN:en, eftersom proxybeslutet fattas i biblioteket och tunnelbeslutet i kärnans routingtabell.

Inaktivera trust_env bara som en medveten sessionspolicy

Om hela jobbet ska ignorera den omgivande konfigurationen, bygg sessionen uttryckligen och registrera beslutet i koden. Det är en policy, inte en kringgång, och den hör hemma i en diff som en granskare kan se. Exemplet nedan behåller en timeout, en uttrycklig CA-sökväg och en statuskontroll.

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()

Att stänga av trust_env ändrar tre beteenden på en gång, och bara ett av dem handlar om proxyservrar. Requests slutar läsa .netrc-uppgifter, och variablerna REQUESTS_CA_BUNDLE och CURL_CA_BUNDLE slutar påverka verify, som då faller tillbaka på det medföljande certifikatförrådet. Om din organisation avslutar TLS med sin egen CA ger den återgången ett verifieringsfel som verkar orelaterat till ändringen du just gjorde. Det är den uttryckliga överföringen av verify som håller de två effekterna åtskilda.

Det du inte bör göra är att sätta verify=False för att tysta det resulterande felet. Det byter en felaktig proxykonfiguration mot en overifierad anslutning, och det döljer certifikatproblemet som uteslutningen skulle avslöja.

Verifiera lagret som misslyckas och behåll en återställning

Tre fel ser likadana ut i en logg och har inget gemensamt: en proxy som inte når destinationen kastar ProxyError eller returnerar 502, ett förtroendeproblem kastar SSLError, och ett avslag från applikationen returnerar 401 eller 403. Återskapa begäran utanför Requests för att se vilket du har. Kommandona nedan tar bort proxyn, testar certifikatkedjan och upprepar sedan anropet genom biblioteket.

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)"

Om curl-anropet lyckas och Python-anropet misslyckas är miljön fortfarande skillnaden. Om båda misslyckas i TLS-fasen var proxyn aldrig problemet och certifikatkedjan är det. Om båda returnerar 401 nådde begäran applikationen och det som återstår är autentisering. Att anteckna vilket av de tre du observerade sparar nästa person en timme.

Återställning är en rad i båda riktningarna: ta bort uteslutningen eller vänd trust_env tillbaka. Spara det tidigare NO_PROXY-värdet innan du lägger till något, och föredra ett omslagsskript eller en Environment=-post på enhetsnivå framför en export som bara lever i din terminalhistorik. Båda ändringarna syns i processens miljö, så en omstart brukar föra dig tillbaka till det gamla beteendet.

Avsluta med att bekräfta fyra saker: de sammanslagna inställningarna för den privata URL:en visar ingen proxy, certifikatet valideras mot den avsedda CA:n, begäran returnerar den status du förväntar dig, och ändringen är committad på en plats som kan återskapas. Ett grönt skript med en oförklarad miljö är en rättelse som går sönder igen på nästa maskin. Kommandona i den här runbooken är de du ska klistra in i ärendet.

Relaterade artiklar