Mensaje de error
curl: (60) SSL certificate problem: self-signed certificate
También se muestra como
- curl: (60) SSL certificate problem: self signed certificate
- curl: (60) SSL certificate OpenSSL verify result: self-signed certificate (18)
OpenSSL 3.0 y posteriores imprimen «self-signed certificate»; OpenSSL 1.1.1 imprime «self signed certificate». Las versiones más recientes de curl (8.22.0 en nuestra prueba) lo formulan como «SSL certificate OpenSSL verify result: self-signed certificate (18)». No lo confundas con «self-signed certificate in certificate chain», donde existe una cadena de CA que termina en una raíz que no es de confianza.
curl (OpenSSL backend) — CURLE_PEER_FAILED_VERIFICATION, X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT (18)
Qué significa
El servidor presentó un único certificado firmado con su propia clave (el emisor es igual al sujeto), y curl no tiene ese certificado exacto en su almacén de confianza. No hay ninguna CA detrás que curl pueda buscar, así que la verificación se detiene en la profundidad 0. Es normal en servidores de desarrollo y dispositivos dedicados, y nunca se soluciona instalando un intermedio.
Por qué ocurre
Un servidor de desarrollo o de pruebas con un certificado generado
frecuenteLos servidores HTTPS locales, los entornos de docker-compose y los fixtures de pruebas suelen generar un certificado autofirmado al arrancar (por ejemplo, con openssl req -x509). Cada regeneración produce un certificado nuevo, así que una copia en la que se confiaba ayer puede no coincidir hoy.
Un dispositivo o producto sigue usando su certificado de fábrica
frecuenteLos routers, los NAS, las impresoras, las interfaces iDRAC/iLO y las instalaciones de Proxmox, Jenkins o Elasticsearch vienen con un certificado autofirmado hasta que alguien instala uno adecuado.
Un proxy o un ingress sirve su certificado predeterminado
ocasionalCuando ningún certificado coincide con el nombre solicitado, ingress-nginx sirve «Kubernetes Ingress Controller Fake Certificate» y Traefik sirve «TRAEFIK DEFAULT CERT», ambos autofirmados. En ese caso, el problema real es el enrutamiento o el SNI, no la confianza.
El servidor apunta al archivo equivocado
raraUna configuración que hace referencia a un certificado autofirmado temporal (snakeoil.pem en Debian, localhost.crt de un paquete) en lugar del emitido produce este error aunque haya un certificado real en el disco.
Cómo solucionarlo
- 1.
Confiar exactamente en este certificado, después de comprobarlo
Para un único endpoint interno, guarda el certificado, confirma su huella digital SHA-256 por un canal de confianza (la consola del dispositivo, el administrador que lo creó) y pásalo con --cacert. Así, curl acepta este certificado y ningún otro en esa llamada.
bash # Fetch the certificate once, compare its fingerprint out of band, then trust exactly that file openssl s_client -connect api.example.com:8443 -servername api.example.com </dev/null 2>/dev/null \ | openssl x509 -out api.example.com.pem openssl x509 -in api.example.com.pem -noout -fingerprint -sha256 curl --cacert api.example.com.pem https://api.example.com:8443/ - 2.
Desarrollo: emitir certificados desde una CA local
Una CA local (mkcert, step-ca o una raíz creada a mano) te permite confiar una sola vez en una raíz y emitir certificados para tantos hosts de desarrollo como quieras. Los certificados ya no son autofirmados, así que el error desaparece en todos los equipos donde esté instalada la raíz.
bash # mkcert creates a local CA, installs it into the system and browser stores, # and issues certificates signed by it (no longer self-signed) mkcert -install mkcert api.example.test localhost 127.0.0.1 - 3.
Hosts públicos: instalar un certificado de una CA pública
Todo aquello a lo que accedan otras personas o clientes de terceros debería presentar un certificado de una CA de confianza pública; los clientes ACME como certbot, lego o Caddy obtienen y renuevan uno automáticamente.
bash # Public names: get a certificate from a public CA, e.g. with certbot (ACME) sudo certbot certonly --nginx -d api.example.com - 4.
Diagnóstico temporal: comprobar el servicio con la verificación desactivada
-k hace que curl omita las comprobaciones de certificados, así que un 200 confirma que solo falta la confianza. No lo dejes puesto: un certificado autofirmado aceptado sin comprobarlo puede sustituirse en cualquier momento por el de un atacante, y curl no lo notaría.
bash # Diagnostic only: confirms the service answers when certificate checks are skipped curl -sS -k -o /dev/null -w '%{http_code}\n' https://api.example.com:8443/
Errores relacionados
curl: (60) SSL certificate problem: self-signed certificate in certificate chaincurl (OpenSSL backend)curl: (60) SSL certificate problem: unable to get local issuer certificatecurl (OpenSSL backend)verify error:num=18:self-signed certificateOpenSSL CLI (openssl s_client / openssl verify)code: 'DEPTH_ZERO_SELF_SIGNED_CERT'Node.js tls / https / fetch