Lỗi ProxyError của Requests trên API riêng? Kiểm tra môi trường trước
Requests hợp nhất cài đặt proxy, xác thực và CA từ môi trường. Đọc những gì tiến trình thấy, loại trừ máy chủ riêng và giữ bật xác minh TLS.
VPN đang bật, bảng điều khiển nội bộ mở trong trình duyệt, và tập lệnh Python vẫn ném lỗi proxy. Trong bức tranh đó không có gì sai với đường hầm: trình thông dịch đọc địa chỉ proxy từ môi trường của chính nó và gửi yêu cầu đến đó. Hướng dẫn này đọc môi trường mà tiến trình thực sự thấy, áp dụng loại trừ hẹp nhất và chỉ sau đó mới xem xét một chính sách phiên rõ ràng. Mỗi bước kết thúc bằng một lệnh mà đầu ra quyết định bạn có tiếp tục hay không.
Đọc môi trường mà tiến trình thực sự thấy
Cài đặt proxy trên máy tính để bàn, biến xuất trong shell và các dòng Environment= của một đơn vị systemd là ba nguồn khác nhau, và Requests chỉ đọc nguồn cuối cùng mà nó kế thừa. Chạy chẩn đoán bên dưới trong cùng shell, container hoặc đơn vị chạy tập lệnh bị lỗi, không phải trong terminal tương tác của bạn. Một biến được xuất trong shell đăng nhập của bạn là vô hình với tiến trình container, và khoảng trống đó giải thích nhiều báo cáo về một proxy xuất hiện từ hư không. Hai lệnh in cùng dữ liệu ở các mức chi tiết khác nhau.
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()]"
Cả hai cách viết của mỗi biến đều được đọc, và các mục trùng lặp được áp dụng theo thứ tự tiến trình nhận được, vì vậy một HTTP_PROXY cũ có thể ghi đè giá trị bạn vừa xuất. Các biến gói CA cũng quan trọng ở đây, vì chúng thay đổi verify phân giải thành gì mà không xuất hiện ở đâu trong mã. Nếu đầu ra trống, môi trường không phải vấn đề của bạn và bạn có thể ngừng đọc phần này. Nếu nó liệt kê một proxy bạn không nhận ra, hãy tìm tệp hoặc đơn vị đặt nó trước khi thay đổi bất cứ điều gì.
Hỏi Requests xem nó sẽ làm gì với một URL cụ thể. Lệnh gọi này hiển thị các giá trị proxy, verify và cert đã hợp nhất cho yêu cầu đó mà không gửi gói tin.
python3 -c "import requests;s=requests.Session();print(s.merge_environment_settings('https://api.corp.example/status',{},None,None,None))"
Một mục proxies không trống cho máy chủ riêng là câu trả lời của bạn, và giá trị verify được in cho biết yêu cầu sẽ tin tưởng gói CA nào. Nếu cả hai có vẻ đúng, lỗi nằm sâu hơn trong ngăn xếp và bạn nên chuyển sang phần xác minh. Đừng đoán từ mã: việc hợp nhất diễn ra theo từng yêu cầu, đó là lý do một phiên có thể hành xử khác nhau với hai máy chủ.
Loại trừ máy chủ riêng trước khi bạn tắt bất cứ thứ gì
Khi proxy thực sự cần thiết cho các đích khác, cách sửa hẹp là loại trừ máy chủ này. Thêm vào danh sách hiện có thay vì thay thế nó, vì xóa NO_PROXY sẽ phá vỡ mọi đích nội bộ khác dựa vào nó. Các lệnh bên dưới thêm mục rồi hỏi Requests xem việc bỏ qua có áp dụng không.
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))"
Việc khớp dựa trên hậu tố máy chủ, vì vậy .corp.example bao phủ toàn bộ miền trong khi api.corp.example chỉ bao phủ một tên. Một dấu hoa thị trần sẽ tắt proxy cho mọi máy chủ và gần như không bao giờ là điều một công việc sản xuất muốn. Việc mục CIDR có được tôn trọng hay không phụ thuộc vào phiên bản Requests và urllib3 trong ảnh, vì vậy hãy thử mục chính xác thay vì cho rằng nó hoạt động. Kiểm tra kết quả của should_bypass_proxies cho URL thật, không phải cho tên máy chủ bạn gõ từ trí nhớ.
Giữ hai lớp tách biệt trong đầu. Bỏ qua proxy HTTP không bỏ qua VPN, vì quyết định proxy diễn ra trong thư viện và quyết định đường hầm diễn ra trong bảng định tuyến của nhân.
Chỉ tắt trust_env như một chính sách phiên có chủ đích
Nếu toàn bộ công việc phải bỏ qua cấu hình môi trường, hãy xây dựng phiên một cách rõ ràng và ghi lại quyết định trong mã. Đó là chính sách, không phải cách lách, và nó thuộc về một diff mà người đánh giá có thể thấy. Ví dụ bên dưới giữ thời gian chờ, đường dẫn CA rõ ràng và kiểm tra trạng thái.
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()
Tắt trust_env thay đổi ba hành vi cùng lúc, và chỉ một trong số đó liên quan đến proxy. Requests ngừng đọc thông tin xác thực .netrc, và các biến REQUESTS_CA_BUNDLE cùng CURL_CA_BUNDLE ngừng ảnh hưởng đến verify, thứ sau đó quay về kho chứng chỉ đi kèm. Nếu tổ chức của bạn kết thúc TLS bằng CA riêng, việc quay lui đó tạo ra lỗi xác minh có vẻ không liên quan đến thay đổi bạn vừa thực hiện. Chính việc truyền verify một cách rõ ràng giữ hai hiệu ứng tách biệt.
Điều bạn không nên làm là đặt verify=False để làm im lặng lỗi phát sinh. Điều đó đánh đổi một cấu hình proxy sai lấy một kết nối chưa được xác minh, và che giấu vấn đề chứng chỉ mà việc loại trừ lẽ ra phải phơi bày.
Xác minh lớp bị lỗi và giữ khả năng hoàn tác
Ba lỗi trông giống nhau trong nhật ký và không có gì chung: proxy không đến được đích ném ProxyError hoặc trả về 502, sự cố tin cậy ném SSLError, và việc ứng dụng từ chối trả về 401 hoặc 403. Tái hiện yêu cầu bên ngoài Requests để xem bạn gặp trường hợp nào. Các lệnh bên dưới gỡ proxy, kiểm tra chuỗi chứng chỉ rồi lặp lại lệnh gọi qua thư viện.
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)"
Nếu lệnh gọi curl thành công và lệnh gọi Python thất bại, môi trường vẫn là khác biệt. Nếu cả hai thất bại ở giai đoạn TLS, proxy chưa bao giờ là vấn đề và chuỗi chứng chỉ mới là. Nếu cả hai trả về 401, yêu cầu đã đến được ứng dụng và công việc còn lại là xác thực. Ghi lại bạn đã quan sát trường hợp nào trong ba trường hợp giúp người tiếp theo tiết kiệm một giờ.
Hoàn tác là một dòng theo cả hai hướng: gỡ bỏ loại trừ, hoặc lật trust_env trở lại. Lưu giá trị NO_PROXY trước đó trước khi thêm vào, và ưu tiên một tập lệnh bao hoặc mục Environment= ở cấp đơn vị hơn là một biến xuất chỉ sống trong lịch sử terminal của bạn. Cả hai thay đổi đều hiển thị trong môi trường tiến trình, vì vậy khởi động lại thường đưa bạn về hành vi cũ.
Kết thúc bằng việc xác nhận bốn điều: cài đặt hợp nhất cho URL riêng không hiển thị proxy nào, chứng chỉ xác thực với CA dự định, yêu cầu trả về trạng thái bạn mong đợi, và thay đổi được commit ở nơi có thể tái tạo. Một tập lệnh xanh với môi trường không giải thích được là một bản sửa sẽ hỏng lại trên máy tiếp theo. Các lệnh trong hướng dẫn này là những gì cần dán vào phiếu.