Mensaje de error
x509: certificate signed by unknown authority
Go crypto/x509 — returned by Certificate.Verify and TLS dials
Qué significa
Go intentó construir una cadena desde el certificado del servidor hasta una raíz de confianza y no lo consiguió: ninguna ruta termina en un certificado del pool de raíces configurado. Puede que el certificado sea perfectamente válido; simplemente, Go no tiene motivos para confiar en su emisor. Es el error TLS más frecuente en los despliegues de Go.
Por qué ocurre
El servidor no envía el certificado intermedio
frecuenteLas raíces de los almacenes de confianza firman los intermedios, y los intermedios firman los certificados hoja. Un servidor configurado solo con el certificado hoja (cert.pem en lugar de fullchain.pem) deja a Go sin forma de unir hoja → raíz. Los navegadores suelen disimularlo con intermedios en caché; Go no.
CA interna o privada que no está en el pool
frecuenteLas CA corporativas, los certificados de desarrollo autofirmados y los certificados emitidos por la malla de servicios son, por diseño, desconocidos para el almacén del sistema. Hay que indicárselos al cliente de forma explícita.
Imagen de contenedor mínima sin certificados de CA
frecuenteLas imágenes scratch y slim no incluyen /etc/ssl/certs. Entonces, cualquier conexión TLS falla con este error, por muy público y válido que sea el certificado del destino.
Un proxy de interceptación vuelve a firmar el tráfico
ocasionalLos dispositivos intermedios corporativos de inspección TLS sustituyen los certificados por otros firmados por la CA de la empresa. Los equipos que no tienen esa CA instalada ven todos los sitios externos como no fiables.
Cómo solucionarlo
- 1.
Añadir la CA al pool de raíces del cliente
Amplía el pool del sistema en lugar de sustituirlo, para que los sitios públicos sigan funcionando junto con tu CA interna.
go roots, err := x509.SystemCertPool() if err != nil { roots = x509.NewCertPool() } pem, _ := os.ReadFile("internal-ca.pem") roots.AppendCertsFromPEM(pem) client := &http.Client{Transport: &http.Transport{ TLSClientConfig: &tls.Config{RootCAs: roots}, }} - 2.
Corregir el servidor: servir la cadena completa
Comprueba qué envía realmente el servidor; si solo aparece un certificado, configura el servidor para que use el archivo de cadena completa (por ejemplo, el fullchain.pem de Let's Encrypt).
bash openssl s_client -connect api.example.com:443 -showcerts </dev/null \ | grep -c "BEGIN CERTIFICATE" # 1 = leaf only (broken), 2+ = chain present - 3.
Incluir los certificados de CA en la imagen
En las imágenes mínimas, copia un bundle de CA durante la compilación. Nunca lo «resuelvas» con InsecureSkipVerify: eso desactiva la verificación por completo.
docker FROM alpine AS certs RUN apk add --no-cache ca-certificates FROM scratch COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/