Tình Huống
Playbook của bạn đang tải file hoặc gọi API nội bộ qua get_url hoặc uri. Mọi thứ trông ổn — cho đến khi dòng này xuất hiện trên terminal:
FAILED! => {
"msg": "Status code was -1 and not [200]: Request failed: "
}
Status code -1 nghĩa là kết nối chưa bao giờ thành công. Các nguyên nhân phổ biến:
- Server nội bộ đang dùng self-signed certificate
- Proxy SSL inspection của công ty (Zscaler, Forcepoint, Squid với SSL bump)
- Môi trường staging hoặc dev dùng cert được ký bởi CA nội bộ mà Python không biết đến
- Cert server đã hết hạn mà không ai phát hiện — cho đến khi Ansible bắt được
Tại Sao Python/Ansible Từ Chối Cert
Các module uri và get_url của Ansible sử dụng urllib của Python bên trong. Python xác thực chứng chỉ SSL dựa vào CA bundle của hệ thống — hoặc bundle của gói certifi nếu gói đó được cài. Khi cert của server không được ký bởi CA nào có trong bundle đó, Python ngắt kết nối và trả về status -1. Không có thông báo, không có cảnh báo. Chỉ là dừng cứng.
Trình duyệt xử lý khác — chúng có trust store riêng và hiển thị cảnh báo để người dùng bấm qua. Python không có tùy chọn đó.
Cách Sửa Nhanh — Tắt Xác Thực (Dùng Cẩn Thận)
Cần chạy được ngay trên mạng nội bộ có kiểm soát? Thêm validate_certs: false:
- name: Download file from internal server
get_url:
url: "https://internal.example.com/files/package.tar.gz"
dest: /tmp/package.tar.gz
validate_certs: false
- name: Hit internal API
uri:
url: "https://internal-api.example.com/health"
method: GET
validate_certs: false
register: api_response
Cách này tắt hoàn toàn xác thực SSL. Chấp nhận được cho tooling nội bộ sau firewall. Không chấp nhận được cho bất cứ thứ gì kết nối internet công cộng hoặc dữ liệu nhạy cảm — bạn đang mở toàn bộ cho tấn công MITM.
Cách Sửa Vĩnh Viễn — Thêm CA Certificate Của Bạn
Cách đúng là dạy Python tin tưởng CA nội bộ của bạn. Ba tùy chọn, tùy theo thiết lập của bạn.
Tùy Chọn 1: Trỏ Ansible Đến CA Bundle Cụ Thể
Tham số ca_path cho phép bạn chỉ định chính xác cert nào cần tin tưởng, theo từng task:
- name: Download with custom CA
get_url:
url: "https://internal.example.com/files/package.tar.gz"
dest: /tmp/package.tar.gz
ca_path: /etc/ssl/certs/internal-ca.crt
- name: URI with custom CA
uri:
url: "https://internal-api.example.com/status"
ca_path: /etc/ssl/certs/internal-ca.crt
Lấy CA cert từ đội infra hoặc bảo mật của bạn. Với self-signed server cert, kéo trực tiếp từ control node:
# Lấy cert từ server
openssl s_client -connect internal.example.com:443 -showcerts </dev/null 2>/dev/null | \
openssl x509 -outform PEM > /etc/ssl/certs/internal-ca.crt
Tùy Chọn 2: Thêm CA Vào System Trust Store
Thêm một lần, và mọi thứ — Ansible, curl, Python scripts — đều tự động nhận. Lựa chọn tốt nhất cho mục đích dùng thường xuyên.
Trên Ubuntu/Debian:
sudo cp internal-ca.crt /usr/local/share/ca-certificates/internal-ca.crt
sudo update-ca-certificates
Trên RHEL/CentOS/Rocky:
sudo cp internal-ca.crt /etc/pki/ca-trust/source/anchors/internal-ca.crt
sudo update-ca-trust
Sau khi xong, bỏ validate_certs: false hoặc ca_path khỏi các task — system trust store xử lý từ đây.
Tùy Chọn 3: Đặt CA Bundle Qua Biến Môi Trường
Không có quyền root? Chạy trong container? Đặt REQUESTS_CA_BUNDLE trong block environment của playbook:
- name: Download with CA via env var
get_url:
url: "https://internal.example.com/files/package.tar.gz"
dest: /tmp/package.tar.gz
environment:
REQUESTS_CA_BUNDLE: /path/to/internal-ca.crt
Để thiết lập lâu dài, export các biến này trong shell profile khởi động Ansible (ví dụ ~/.bashrc hoặc /etc/profile.d/ansible-ca.sh):
export REQUESTS_CA_BUNDLE=/etc/ssl/certs/internal-ca.crt
export SSL_CERT_FILE=/etc/ssl/certs/internal-ca.crt
Proxy Công Ty (SSL Inspection)
Zscaler và các proxy tương tự chặn traffic HTTPS và ký lại mọi kết nối bằng CA của proxy. Python của bạn thấy cert proxy đó, không tin tưởng nó, và từ chối.
Cách sửa: lấy root CA của proxy từ IT và thêm vào theo Tùy chọn 2 ở trên. Khi CA đó đã có trong trust store, mọi HTTPS qua proxy đều hoạt động — không cần tắt xác thực.
Trước tiên, xác nhận proxy có thực sự là thủ phạm không:
# Kiểm tra cert bạn thực sự đang nhận được
openssl s_client -connect internal.example.com:443 </dev/null 2>/dev/null | openssl x509 -text -noout | grep -E "Issuer|Subject"
Nếu Issuer hiển thị nhà cung cấp proxy của bạn (ví dụ như Zscaler Inc) thay vì CA thực của server, đó là vấn đề của bạn.
Xác Nhận Bản Sửa
Kiểm tra trên control node trước khi chạy toàn bộ playbook:
# Kiểm tra nhanh bằng Python
python3 -c "
import urllib.request
response = urllib.request.urlopen('https://internal.example.com')
print(response.status)
"
# Hoặc kiểm tra chuỗi cert bằng curl
curl -v https://internal.example.com 2>&1 | grep -E "SSL|certificate|verify"
Sau đó chạy một playbook tối giản để xác nhận Ansible hoạt động ổn:
- name: Verify SSL fix
hosts: localhost
gather_facts: false
tasks:
- name: Check endpoint
uri:
url: "https://internal.example.com/health"
method: GET
register: result
- debug:
msg: "Status: {{ result.status }}"
Status: 200 mà không cần validate_certs: false — bạn đã xong.
Danh Sách Kiểm Tra
- Lấy CA cert thực sự — đừng chỉ tắt xác thực
- Trên Ubuntu:
update-ca-certificates; trên RHEL:update-ca-trust - Dùng
ca_pathđể kiểm soát cấp task, dùng system trust store để sửa toàn cục - Đứng sau proxy công ty? Lấy proxy root CA từ IT — không phải cert của server
- Chạy
openssl s_clienttrước để xem chính xác chuỗi cert bạn đang xử lý

