Σφάλμα ProxyError στο Requests σε ιδιωτικό API; Ελέγξτε πρώτα το περιβάλλον
Το Requests συνδυάζει ρυθμίσεις proxy, auth και CA από το περιβάλλον. Δείτε τι βλέπει η διεργασία, εξαιρέστε τον ιδιωτικό κεντρικό υπολογιστή και κρατήστε την επαλήθευση TLS ενεργή.
Το VPN είναι ενεργό, ο εσωτερικός πίνακας ανοίγει σε πρόγραμμα περιήγησης και το σκριπτ Python εξακολουθεί να πετά σφάλμα proxy. Δεν υπάρχει τίποτα λάθος με τη σήραγγα σε αυτή την εικόνα: ο διερμηνέας διαβάζει μια διεύθυνση proxy από το δικό του περιβάλλον και στέλνει εκεί το αίτημα. Αυτός ο οδηγός διαβάζει το περιβάλλον που βλέπει πραγματικά η διεργασία, εφαρμόζει τη στενότερη εξαίρεση και μόνο τότε εξετάζει μια ρητή πολιτική συνεδρίας. Κάθε βήμα τελειώνει με μια εντολή της οποίας η έξοδος κρίνει αν συνεχίζετε.
Διαβάστε το περιβάλλον που βλέπει πραγματικά η διεργασία
Οι ρυθμίσεις proxy της επιφάνειας εργασίας, οι εξαγωγές του κελύφους και οι γραμμές Environment= μιας μονάδας systemd είναι τρεις διαφορετικές πηγές, και το Requests διαβάζει μόνο την τελευταία που κληρονομεί. Εκτελέστε τη διάγνωση παρακάτω στο ίδιο κέλυφος, κοντέινερ ή μονάδα που εκτελεί το σκριπτ που αποτυγχάνει, όχι στο διαδραστικό τερματικό σας. Μια μεταβλητή που εξάγεται στο κέλυφος σύνδεσής σας είναι αόρατη σε μια διεργασία κοντέινερ, και αυτό το κενό εξηγεί πολλές αναφορές για proxy που εμφανίζεται από το πουθενά. Οι δύο εντολές εκτυπώνουν τα ίδια δεδομένα σε διαφορετικά επίπεδα λεπτομέρειας.
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 χωρίς να εμφανίζονται πουθενά στον κώδικα. Αν η έξοδος είναι κενή, το περιβάλλον δεν είναι το πρόβλημά σας και μπορείτε να σταματήσετε να διαβάζετε αυτή την ενότητα. Αν αναφέρει ένα proxy που δεν αναγνωρίζετε, βρείτε το αρχείο ή τη μονάδα που το ορίζει πριν αλλάξετε οτιδήποτε.
Ρωτήστε το Requests τι θα έκανε με ένα συγκεκριμένο URL. Αυτή η κλήση δείχνει τις συνδυασμένες τιμές proxy, 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 θα εμπιστευόταν το αίτημα. Αν και τα δύο φαίνονται σωστά, η αποτυχία βρίσκεται αργότερα στη στοίβα και θα πρέπει να μεταβείτε στην ενότητα επαλήθευσης. Μην μαντεύετε από τον κώδικα: η σύντηξη γίνεται ανά αίτημα, γι' αυτό μία συνεδρία μπορεί να συμπεριφέρεται διαφορετικά για δύο κεντρικούς υπολογιστές.
Εξαιρέστε τον ιδιωτικό κεντρικό υπολογιστή προτού απενεργοποιήσετε οτιδήποτε
Όταν το proxy χρειάζεται πραγματικά για άλλους προορισμούς, η στενή διόρθωση είναι μια εξαίρεση για αυτόν τον κεντρικό υπολογιστή. Προσθέστε στην υπάρχουσα λίστα αντί να την αντικαταστήσετε, γιατί η διαγραφή του 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 καλύπτει ένα όνομα. Ένας γυμνός αστερίσκος απενεργοποιεί το proxy για κάθε κεντρικό υπολογιστή και σχεδόν ποτέ δεν είναι αυτό που θέλει μια εργασία παραγωγής. Το αν μια καταχώριση CIDR τηρείται εξαρτάται από τις εκδόσεις Requests και urllib3 στην εικόνα, οπότε δοκιμάστε την ακριβή καταχώριση αντί να υποθέσετε ότι λειτουργεί. Ελέγξτε το αποτέλεσμα του should_bypass_proxies για το πραγματικό URL, όχι για ένα όνομα κεντρικού υπολογιστή που πληκτρολογήσατε από μνήμη.
Κρατήστε τα δύο επίπεδα χωριστά στο μυαλό σας. Η παράκαμψη του proxy HTTP δεν παρακάμπτει το VPN, γιατί η απόφαση για το proxy λαμβάνεται στη βιβλιοθήκη και η απόφαση για τη σήραγγα στον πίνακα δρομολόγησης του πυρήνα.
Απενεργοποιήστε το 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 αλλάζει τρεις συμπεριφορές ταυτόχρονα, και μόνο μία από αυτές αφορά το proxy. Το Requests σταματά να διαβάζει διαπιστευτήρια .netrc, και οι μεταβλητές REQUESTS_CA_BUNDLE και CURL_CA_BUNDLE σταματούν να επηρεάζουν το verify, το οποίο τότε καταφεύγει στο ενσωματωμένο κατάστημα πιστοποιητικών. Αν ο οργανισμός σας τερματίζει TLS με δικό του CA, αυτή η υποχώρηση εγείρει σφάλμα επαλήθευσης που μοιάζει άσχετο με την αλλαγή που μόλις κάνατε. Η ρητή μεταβίβαση του verify είναι αυτό που κρατά τα δύο αποτελέσματα χωριστά.
Αυτό που δεν πρέπει να κάνετε είναι να ορίσετε το verify=False για να σιγάσετε το σφάλμα που προκύπτει. Αυτό ανταλλάσσει μια λανθασμένη διαμόρφωση proxy με μια μη επαληθευμένη σύνδεση και κρύβει το πρόβλημα πιστοποιητικού που η εξαίρεση έπρεπε να αποκαλύψει.
Επαληθεύστε το επίπεδο που αποτυγχάνει και κρατήστε δυνατότητα επαναφοράς
Τρεις αστοχίες μοιάζουν ίδιες σε ένα αρχείο καταγραφής και δεν έχουν τίποτα κοινό: ένα proxy που δεν φτάνει στον προορισμό πετά ProxyError ή επιστρέφει 502, ένα πρόβλημα εμπιστοσύνης πετά SSLError, και μια απόρριψη εφαρμογής επιστρέφει 401 ή 403. Αναπαράγετε το αίτημα εκτός του Requests για να δείτε ποια έχετε. Οι εντολές παρακάτω αφαιρούν το proxy, δοκιμάζουν την αλυσίδα πιστοποιητικών και μετά επαναλαμβάνουν την κλήση μέσω της βιβλιοθήκης.
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, το proxy δεν ήταν ποτέ το πρόβλημα και είναι η αλυσίδα πιστοποιητικών. Αν και οι δύο επιστρέφουν 401, το αίτημα έφτασε στην εφαρμογή και η υπόλοιπη δουλειά είναι η ταυτοποίηση. Η καταγραφή ποιας από τις τρεις παρατηρήσατε γλιτώνει μια ώρα από τον επόμενο άνθρωπο.
Η επαναφορά είναι μία γραμμή προς οποιαδήποτε κατεύθυνση: αφαιρέστε την εξαίρεση ή γυρίστε το trust_env πίσω. Αποθηκεύστε την προηγούμενη τιμή NO_PROXY πριν προσθέσετε σε αυτήν, και προτιμήστε ένα σκριπτ περιτυλίγματος ή μια καταχώριση Environment= σε επίπεδο μονάδας αντί για μια εξαγωγή που ζει μόνο στο ιστορικό του τερματικού σας. Και οι δύο αλλαγές είναι ορατές στο περιβάλλον της διεργασίας, οπότε μια επανεκκίνηση συνήθως σας επιστρέφει στην παλιά συμπεριφορά.
Ολοκληρώστε επιβεβαιώνοντας τέσσερα πράγματα: οι συνδυασμένες ρυθμίσεις για το ιδιωτικό URL δεν δείχνουν proxy, το πιστοποιητικό επικυρώνεται με το προβλεπόμενο CA, το αίτημα επιστρέφει την κατάσταση που περιμένετε και η αλλαγή έχει δεσμευτεί κάπου αναπαραγώγιμα. Ένα πράσινο σκριπτ με ανεξήγητο περιβάλλον είναι μια διόρθωση που σπάει ξανά στο επόμενο μηχάνημα. Οι εντολές σε αυτόν τον οδηγό είναι αυτές που θα επικολλήσετε στο δελτίο.