Sửa lỗi gRPC: x509: certificate signed by unknown authority

intermediate🔒 SSL/TLS2026-07-12| Dịch vụ gRPC Go (Golang), Docker container, Kubernetes, Linux/macOS/Windows, sử dụng chứng chỉ SSL/TLS tự ký.

Error Message

rpc error: code = Unavailable desc = connection error: desc = "transport: authentication handshake failed: x509: certificate signed by unknown authority"
#grpc#go#tls#x509#handshake#security

Thông báo lỗi

Bạn đã thiết lập gRPC server, bật TLS và mọi thứ có vẻ đã sẵn sàng. Sau đó, bạn chạy client và gặp phải chướng ngại này:

rpc error: code = Unavailable desc = connection error: desc = "transport: authentication handshake failed: x509: certificate signed by unknown authority"

Tại sao lỗi này xảy ra

Lỗi này xuất hiện trong quá trình bắt tay (handshake) TLS. gRPC client của bạn đang cố gắng xác minh danh tính của server, nhưng nó không nhận diện được Certificate Authority (CA) đã ký chứng chỉ của server. Nếu CA không nằm trong trust store của hệ thống hoặc không được định nghĩa rõ ràng trong mã nguồn, kết nối sẽ bị ngắt ngay lập tức để đảm bảo an toàn.

Bạn thường sẽ gặp phải tình trạng này trong ba trường hợp sau:

  • Phát triển cục bộ (Local Development): Bạn đang sử dụng chứng chỉ tự ký (self-signed certificates) được tạo qua OpenSSL.
  • CA nội bộ (Private CAs): Tổ chức của bạn sử dụng một CA nội bộ chưa được thêm vào Docker container hoặc trust store của hệ điều hành.
  • Chuỗi chứng chỉ bị hỏng (Broken Chains): Server cung cấp chứng chỉ của nó nhưng không gửi được các chứng chỉ trung gian (intermediate certificates) cần thiết để kết nối tới Root CA.

Hướng dẫn sửa lỗi từng bước

Cách 1: Truyền Root CA vào gRPC Client (Khuyên dùng)

Cách khắc phục đáng tin cậy nhất cho các microservices nội bộ là truyền trực tiếp chứng chỉ CA cho client. Thay vì phụ thuộc vào store toàn cục của hệ điều hành, bạn chỉ định chính xác chứng chỉ nào mà client nên tin tưởng.

1. Xác định vị trí chứng chỉ CA của bạn. Bạn cần file ca.crt (khóa công khai của CA) sẵn có cho mã nguồn client.

2. Tải file trong Go client:

import (
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
    "log"
)

func main() {
    // Đường dẫn đến chứng chỉ CA đã ký chứng chỉ của server
    caFile := "certs/ca.crt"

    // Khởi tạo thông tin xác thực TLS cho client từ file
    creds, err := credentials.NewClientTLSFromFile(caFile, "")
    if err != nil {
        log.Fatalf("Failed to load credentials: %v", err)
    }

    // Kết nối bằng thông tin xác thực cụ thể
    conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(creds))
    if err != nil {
        log.Fatalf("Connection failed: %v", err)
    }
    defer conn.Close()

    // Tiếp tục với các cuộc gọi gRPC...
}

Cách 2: Xử lý sai lệch Hostname

Ngay cả với một CA đáng tin cậy, quá trình bắt tay vẫn thất bại nếu hostname không khớp với chứng chỉ. Nếu chứng chỉ của bạn được cấp cho api.internal.com nhưng bạn đang kết nối tới localhost, Go sẽ từ chối nó. Bạn có thể ghi đè mong đợi này trong client:

// Đối số thứ hai ghi đè tên server mong đợi (ServerNameOverride)
creds, err := credentials.NewClientTLSFromFile(caFile, "api.internal.com")

Cách 3: Bỏ qua xác minh (Chỉ dành cho thử nghiệm)

Nếu bạn đang trong tình huống cấp bách và cần debug vấn đề kết nối, bạn có thể yêu cầu client bỏ qua tất cả các lỗi chứng chỉ. Tuyệt đối không sử dụng cách này trong môi trường production vì nó khiến bạn dễ bị tấn công Man-in-the-Middle (MITM) attacks.

import (
    "crypto/tls"
    "google.golang.org/grpc"
    "google.golang.org/grpc/credentials"
)

config := &tls.Config{
    InsecureSkipVerify: true,
}

creds := credentials.NewTLS(config)
conn, err := grpc.Dial("localhost:50051", grpc.WithTransportCredentials(creds))

Xác minh bản sửa lỗi

Sau khi cập nhật mã nguồn, hãy kiểm tra kết nối bằng grpcurl. Đây là một cách tuyệt vời để xác nhận server đang hoạt động đúng trước khi bạn quay lại với mã nguồn Go:

grpcurl -cacert ca.crt localhost:50051 list

Một lệnh thành công sẽ trả về danh sách các dịch vụ. Nếu bạn vẫn thấy lỗi x509, vấn đề có khả năng nằm ở chính file chứng chỉ.

Mẹo chuyên nghiệp cho môi trường Production

Những lỗi nhỏ trong các file chứng chỉ có thể dẫn đến hàng giờ bực bội. Nếu một file chứng chỉ bị cắt xén hoặc bị hỏng trong quá trình triển khai CI/CD, thông báo lỗi sẽ không cho bạn biết lý do tại sao. Tôi luôn xác minh checksum SHA-256 của file ca.crt trên cả máy cục bộ và server để đảm bảo chúng giống hệt nhau.

Để kiểm tra nhanh, tôi sử dụng Hash Generator của ToolCraft. Nó chạy cục bộ trong trình duyệt của bạn, vì vậy dữ liệu chứng chỉ của bạn luôn được riêng tư. Việc so sánh các mã hash là một công việc chỉ mất 10 giây nhưng giúp tránh được rất nhiều rắc rối.

Finally, avoid hardcoding file paths like /Users/dev/certs. Use environment variables such as SSL_CERT_PATH. This makes your service portable, whether it's running in a local Docker container or a production Kubernetes cluster.

Related Error Notes