Lỗi Gặp Phải
Bạn đang pull image từ private registry, thực hiện API call bên trong pod, hoặc cấu hình webhook — và gặp lỗi này:
x509: certificate signed by unknown authority
Đôi khi lỗi xuất hiện kèm thêm thông tin chi tiết:
Error response from daemon: Get "https://registry.internal/v2/": x509: certificate signed by unknown authority
Post "https://webhook.internal/validate": x509: certificate signed by unknown authority
Nguyên nhân cốt lõi luôn giống nhau: TLS stack của Go (Docker, kubectl, kubelet — tất cả đều viết bằng Go) không thể xác minh chuỗi certificate. CA đã ký certificate đơn giản là không có trong system trust store. Chỉ vậy thôi.
Chẩn Đoán
Trước khi chỉnh sửa bất kỳ cấu hình nào, hãy xác nhận certificate mà server đang thực sự phục vụ:
# Kiểm tra cert mà server đang phục vụ
openssl s_client -connect registry.internal:443 -showcerts </dev/null 2>&1 | openssl x509 -noout -issuer -subject
# Hoặc dùng curl để xem toàn bộ chain
curl -v https://registry.internal/v2/ 2>&1 | grep -A5 "SSL connection"
Thấy issuer như CN=My Corp CA hoặc CN=registry.internal? Đó là cert private hoặc self-signed. Không có public CA nào ký nó, nên bạn cần tự thêm CA đó vào trust store.
Cách Sửa 1: Thêm CA cert vào Docker daemon (private registry)
Pull từ internal registry với cert self-signed hoặc private CA? Cách sửa này xử lý được. Docker có thư mục certificate riêng cho từng registry, tách biệt với system store.
# Tạo thư mục cho registry
sudo mkdir -p /etc/docker/certs.d/registry.internal:5000
# Sao chép CA cert của bạn (lấy từ đội infra hoặc trích xuất từ server)
sudo cp ca.crt /etc/docker/certs.d/registry.internal:5000/ca.crt
# Khởi động lại Docker
sudo systemctl restart docker
Tên thư mục phải khớp chính xác: /etc/docker/certs.d/<host>:<port>/ca.crt. Với port 443, bạn có thể bỏ phần port đi.
Kiểm tra ngay kết quả:
docker pull registry.internal:5000/myimage:latest
Cách Sửa 2: Thêm CA vào system trust store (Linux)
kubelet, kubectl plugins, custom operators — bất kỳ Go binary nào trên host đều đọc system trust store, không phải thư mục cert của Docker. Đây là nơi cách sửa này phát huy tác dụng.
# Ubuntu/Debian
sudo cp ca.crt /usr/local/share/ca-certificates/my-corp-ca.crt
sudo update-ca-certificates
# RHEL/CentOS/Fedora
sudo cp ca.crt /etc/pki/ca-trust/source/anchors/my-corp-ca.crt
sudo update-ca-trust extract
# Xác nhận đã được thêm vào
certutil -L -d /etc/pki/nssdb | grep "My Corp" # RHEL
# hoặc đơn giản test với curl
curl https://registry.internal/v2/
Cách Sửa 3: Kubernetes — thêm CA vào containerd (cấp node)
Pod không pull được image, hoặc kubelet đang ghi lỗi này? Cách sửa nằm ở từng node, trong config của containerd — không phải trong Kubernetes.
# Trên mỗi node, chỉnh sửa config containerd
sudo mkdir -p /etc/containerd/certs.d/registry.internal:5000
cat <<EOF | sudo tee /etc/containerd/certs.d/registry.internal:5000/hosts.toml
[host."https://registry.internal:5000"]
ca = "/etc/ssl/certs/my-corp-ca.crt"
EOF
# Sao chép CA cert
sudo cp ca.crt /etc/ssl/certs/my-corp-ca.crt
# Khởi động lại containerd
sudo systemctl restart containerd
Với managed cluster (EKS, GKE, AKS), bạn không thể tự do SSH vào các node. Hãy dùng DaemonSet hoặc nhúng CA vào bootstrap script của node pool thay thế.
Cách Sửa 4: Kubernetes — inject CA qua ConfigMap vào pod
Lỗi xảy ra bên trong pod đang chạy — ứng dụng của bạn đang gọi internal HTTPS service và Go HTTP client từ chối cert. Mount CA dưới dạng volume và trỏ Go đến nó qua biến môi trường.
apiVersion: v1
kind: ConfigMap
metadata:
name: corp-ca-bundle
data:
ca.crt: |
-----BEGIN CERTIFICATE-----
MIIBxTCCAW+gAwIBAgIJA...
-----END CERTIFICATE-----
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: myapp
spec:
template:
spec:
volumes:
- name: ca-bundle
configMap:
name: corp-ca-bundle
containers:
- name: myapp
image: myapp:latest
volumeMounts:
- name: ca-bundle
mountPath: /etc/ssl/certs/corp-ca.crt
subPath: ca.crt
env:
- name: SSL_CERT_FILE
value: /etc/ssl/certs/corp-ca.crt
SSL_CERT_FILE là biến môi trường đặc thù của Go, dùng để ghi đè đường dẫn CA bundle mặc định. Cách khác: mount cert vào thư mục bundle mặc định và chạy update-ca-certificates trong init container trước khi ứng dụng khởi động.
Cách Sửa 5: Kubernetes admission webhooks
Thấy lỗi này trong log của kube-apiserver khi webhook kích hoạt? Trường caBundle trong MutatingWebhookConfiguration hoặc ValidatingWebhookConfiguration của bạn bị sai hoặc thiếu hoàn toàn. API server dùng bundle đó để xác minh certificate của webhook server.
# Kiểm tra CA bundle mà webhook hiện đang dùng
kubectl get mutatingwebhookconfiguration my-webhook -o jsonpath='{.webhooks[0].clientConfig.caBundle}' | base64 -d | openssl x509 -noout -text
# Patch CA đúng vào
CA_BUNDLE=$(cat ca.crt | base64 -w0)
kubectl patch mutatingwebhookconfiguration my-webhook \
--type='json' \
-p="[{'op': 'replace', 'path': '/webhooks/0/clientConfig/caBundle', 'value': '${CA_BUNDLE}'}]"
Đang dùng cert-manager? Thêm annotation cert-manager.io/inject-ca-from và nó sẽ tự xử lý rotation — không cần patch thủ công nữa.
Lấy CA Certificate
Không có sẵn file CA cert? Kéo trực tiếp từ server. Cách an toàn nhất là lưu toàn bộ chain và dùng nó làm CA bundle:
# Lưu toàn bộ chain và dùng làm CA bundle
openssl s_client -connect registry.internal:443 -showcerts </dev/null 2>&1 \
| sed -n '/BEGIN CERTIFICATE/,/END CERTIFICATE/p' > chain.crt
Đoạn one-liner trích xuất chain bạn thường thấy online (với các lệnh openssl lồng nhau và offset awk) rất dễ hỏng và bị lỗi với chain dài hơn hai cert. Đừng dùng trong script. Hãy nhờ đội infra cung cấp trực tiếp CA cert — đó là cách đúng nhất.
Xác Minh
# Docker registry
docker pull registry.internal:5000/myimage:latest
# curl với system trust store
curl https://registry.internal/v2/
# Từ bên trong pod
kubectl run -it --rm debug --image=alpine --restart=Never -- wget -O- https://internal-service.default.svc/health
# Go binary tuân theo SSL_CERT_FILE
SSL_CERT_FILE=/path/to/ca.crt your-go-binary
Tham Khảo Nhanh
- Docker pull thất bại →
/etc/docker/certs.d/<registry>/ca.crt - kubelet/containerd pull thất bại →
hosts.tomlcủa containerd trên node - Ứng dụng trong pod thất bại → mount CA qua ConfigMap +
SSL_CERT_FILE - Webhook validation thất bại → sửa
caBundletrong webhook config - CLI tools thất bại → thêm CA vào system trust store +
update-ca-certificates

