Le VPN est actif, le tableau de bord interne s’ouvre dans un navigateur, et le script Python lève toujours une erreur de proxy. Rien ne cloche avec le tunnel dans ce tableau : l’interpréteur lit une adresse de proxy dans son propre environnement et y envoie la requête. Ce guide lit l’environnement que le processus voit réellement, applique l’exclusion la plus étroite et n’envisage qu’ensuite une politique de session explicite. Chaque étape se termine par une commande dont la sortie décide si vous continuez.

Lisez l’environnement que le processus voit réellement

Les paramètres de proxy du bureau, les exports du shell et les lignes Environment= d’une unité systemd sont trois sources différentes, et Requests ne lit que la dernière qu’il hérite. Exécutez le diagnostic ci-dessous dans le même shell, conteneur ou unité qui lance le script défaillant, pas dans votre terminal interactif. Une variable exportée dans votre shell de connexion est invisible pour un processus de conteneur, et cet écart explique de nombreux signalements de proxy qui surgit de nulle part. Les deux commandes impriment les mêmes données à des niveaux de détail différents.

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

Les deux orthographes de chaque variable sont lues, et les entrées en double sont appliquées dans l’ordre où le processus les a reçues, donc un HTTP_PROXY obsolète peut écraser la valeur que vous venez d’exporter. Les variables de bundle CA comptent aussi ici, car elles changent ce en quoi verify se résout sans apparaître nulle part dans le code. Si la sortie est vide, l’environnement n’est pas votre problème et vous pouvez cesser de lire cette section. Si elle liste un proxy que vous ne reconnaissez pas, trouvez le fichier ou l’unité qui le définit avant de changer quoi que ce soit.

Demandez à Requests ce qu’il ferait avec une URL précise. Cet appel affiche les valeurs fusionnées de proxy, verify et cert pour cette requête sans envoyer de paquet.

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

Une entrée proxies non vide pour l’hôte privé est votre réponse, et la valeur verify imprimée vous dit en quel bundle CA la requête aurait confiance. Si les deux semblent corrects, la défaillance se situe plus loin dans la pile et vous devriez passer à la section de vérification. Ne devinez pas à partir du code : la fusion se fait par requête, c’est pourquoi une session peut se comporter différemment pour deux hôtes.

Excluez l’hôte privé avant de désactiver quoi que ce soit

Quand le proxy est réellement requis pour d’autres destinations, la correction étroite est une exclusion pour cet hôte. Ajoutez à la liste existante au lieu de la remplacer, car effacer NO_PROXY casse toute autre destination interne qui en dépendait. Les commandes ci-dessous ajoutent l’entrée puis demandent à Requests si le contournement s’applique.

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

La correspondance se fait par suffixe d’hôte, donc .corp.example couvre tout le domaine tandis que api.corp.example couvre un seul nom. Un astérisque nu désactive le proxy pour tous les hôtes et n’est presque jamais ce que veut une tâche de production. Qu’une entrée CIDR soit honorée dépend des versions de Requests et d’urllib3 dans l’image, alors testez l’entrée exacte au lieu de supposer qu’elle fonctionne. Vérifiez le résultat de should_bypass_proxies pour l’URL réelle, pas pour un nom d’hôte tapé de mémoire.

Gardez les deux couches séparées dans votre tête. Contourner le proxy HTTP ne contourne pas le VPN, car la décision de proxy se prend dans la bibliothèque et la décision de tunnel dans la table de routage du noyau.

Ne désactivez trust_env que comme politique de session délibérée

Si l’ensemble de la tâche doit ignorer la configuration ambiante, construisez la session explicitement et consignez la décision dans le code. C’est une politique, pas un contournement, et elle appartient à un diff qu’un relecteur peut voir. L’exemple ci-dessous conserve un délai d’attente, un chemin CA explicite et une vérification de statut.

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

Désactiver trust_env change trois comportements à la fois, et un seul concerne les proxies. Requests cesse de lire les identifiants .netrc, et les variables REQUESTS_CA_BUNDLE et CURL_CA_BUNDLE cessent d’affecter verify, qui retombe alors sur le magasin de certificats intégré. Si votre organisation termine TLS avec sa propre CA, ce repli lève une erreur de vérification qui semble sans rapport avec le changement que vous venez de faire. C’est le passage explicite de verify qui garde les deux effets séparés.

Ce que vous ne devez pas faire, c’est définir verify=False pour faire taire l’erreur qui en résulte. Cela échange une mauvaise configuration de proxy contre une connexion non vérifiée, et cela masque le problème de certificat que l’exclusion devait révéler.

Vérifiez la couche défaillante et gardez un retour arrière

Trois pannes se ressemblent dans un journal et n’ont rien en commun : un proxy qui n’atteint pas la destination lève ProxyError ou renvoie 502, un problème de confiance lève SSLError, et un rejet de l’application renvoie 401 ou 403. Reproduisez la requête hors de Requests pour voir laquelle vous avez. Les commandes ci-dessous retirent le proxy, testent la chaîne de certificats, puis répètent l’appel via la bibliothèque.

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

Si l’appel curl réussit et que l’appel Python échoue, l’environnement reste la différence. Si les deux échouent à l’étape TLS, le proxy n’a jamais été le problème et la chaîne de certificats l’est. Si les deux renvoient 401, la requête a atteint l’application et le travail restant est l’authentification. Noter laquelle des trois vous avez observée fait gagner une heure à la personne suivante.

Le retour arrière est une ligne dans les deux sens : retirez l’exclusion, ou remettez trust_env. Enregistrez la valeur précédente de NO_PROXY avant d’y ajouter quoi que ce soit, et préférez un script d’enveloppe ou une entrée Environment= au niveau de l’unité à un export qui ne vit que dans l’historique de votre terminal. Les deux changements sont visibles dans l’environnement du processus, donc un redémarrage vous ramène généralement à l’ancien comportement.

Terminez en confirmant quatre choses : les paramètres fusionnés pour l’URL privée n’affichent aucun proxy, le certificat se valide contre la CA prévue, la requête renvoie le statut attendu, et le changement est commité à un endroit reproductible. Un script vert avec un environnement inexpliqué est une correction qui casse de nouveau sur la machine suivante. Les commandes de ce guide sont celles à coller dans le ticket.

Articles similaires