Cuando el orden de los factores afecta el producto (Certificados)

El problema

Tratando de clonar un repositorio desde una computadora con Ubuntu, me encontré con un error que me tomó mucho tiempo solucionar.
El repositorio era accesible desde varias otras computadoras sin problemas, así que era obvio que el problema no estaba en el servidor, por lo que pasé muchas horas de frustración buscando el problema que causaba que esta nueva computadora no pueda acceder a lo que era claramente accesible a otras computadoras.
El error apuntaba a un error con el certificado.
fatal: unable to access ‘https://xxx.xxx.xxx/xxx.git/’: server certificate verification failed.

Años de experiencia

Los años de experiencia en las lides informáticas, inmediatamente dieron su opinión.  En Windows, cuando una aplicación tiene problemas en establecer una comunicación con un servidor seguro (cifrado con SSL o TLS), la forma más práctica de investigar el problema es intentar abrir una comunicación con un navegador común.  Se hace esto por dos razones; primero, porque si hay un error con las versiones de cifrado, o con los certificados, o autoridades, el navegador nos dará un informe bastante completo de la razón por la cual la comunicación no puede establecerse, y segundo, porque los navegadores también nos dan una opción de incorporar el certificado al almacén de certificados aceptados, en caso que el error haya sido simplemente que hay una pequeña discrepancia en el certificado y podemos asumir el riesgo.  En linux, esto nos es tan “fácil”, puesto que no hay un “almacén” centralizado al que vayan las aplicaciones para verificar los certificados, sino que cada aplicación guarda los certificados internamente.  Sin embargo, pensé, no es mala idea abrir la dirección desde el navegador, por lo menos me dará el error.

Oh sorpresa

Para mi sorpresa, el navegador estableció la comunicación segura sin ningún problema.  Hmmm, ¿y ahora?  Si el navegador puede conectarse, quiere decir que el certificado es bueno y el problema es en la configuración del git.

Actualicémonos

Ok, asegurémonos que la versión del git sea la actual.  Me di cuenta que la versión de git estaba ligeramente desactualizada… pensé… a lo mejor no soporta la versión de cifrado que tiene el servidor (algunos recuerdan que el cifrado ha pasado por versiones SSL 1.0, 2.0, 3.0 y luego cambió de nombre para ser TLS 1.0, 1.1, 1.2 y ya está casi listo el 1.3).  Es recomendable que conforme estos protocolos de seguridad se van mejorando, uno vaya deshabilitando los anteriores.  Por ejemplo, nadie debe tener activo SSL 1.0 o 2.0, se han publicado demasiados problemas con esas versiones.  Version SSL 3.0… ya también debería dejarse de usar, pero algunos sitios todavía lo requieren.  Pensé que a lo mejor el servidor requería una versión de seguridad más alta que el git que vino instalado en Ubuntu, así que … a actualizar:

sudo apt-get update
sudo apt-get install git
git --version

Nop… el mismo error.

Google, al rescate

Google siempre ha sido un compañero fiel, hasta en las circunstancias más difíciles de la vida (2AM y el código no compila con errores de “Encoding::CompatibilityError (incompatible character encodings: UTF-8 and ASCII-8BIT)”, por ejemplo).  Sin embargo, hasta la página 6 de los resultados que encontraba, no importa qué términos usaba para buscar, todas, absolutamente todas las sugerencias encontradas eran para cuando alguien usa un certificado auto-firmado.  Ese no era mi caso, el certificado era legítimo y por lo tanto ninguna de esas “soluciones” se aplicaba.  Tenía que ser algo más, sin embargo, seguí algunas de las sugerencias… por si acaso.

sudo dpkg-reconfigure ca-certificates

also

sudo update-ca-certificates

Como era de esperarse, ninguna de estas sugerencias dio ningún resultado… el certificado es legítimo y es obviamente accesible de otras computadoras y aún desde el navegador de esta misma computadora.

Hora de ensuciarse

Bueno, cuando todo lo demás falla, hay que ponerse a trabajar.  Afortunadamente, hay herramientas hoy en día que nos permiten mantenernos ligeramente por encima de la superficie y no tenemos que observar los bytes con Wireshark para encontrar los errores.

En linux, openssl es la mejor herramienta para investigar estos problemas.

openssl s_client -connect xxx.xxx.xxx:443

probablemente el comando más útil… sin embargo, al correrlo, el comando tuvo éxito en establecer la conexión.  Un dato importante que te da el openssl, es la cadena de certificados.  Muy comúnmente, personas ponen un certificado en el servidor, sin poner la cadena total, y algunos navegadores protestan por eso.  Sin embargo, al examinar la cadena de certificados pude comprobar que todos los certificados necesarios estaban presentes.

Certificate chain

 0 s:/C=US/postalCode=xxx/ST=xx/L=xxx/street=xxx/O=xxx/OU=xxx/CN=xxx.xxx.xxx
   i:/C=US/ST=MI/L=Ann Arbor/O=Internet2/OU=InCommon/CN=InCommon RSA Server CA
 1 s:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
   i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
 2 s:/C=US/ST=New Jersey/L=Jersey City/O=The USERTRUST Network/CN=USERTrust RSA Certification Authority
   i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root
3 s:/C=US/ST=MI/L=Ann Arbor/O=Internet2/OU=InCommon/CN=InCommon RSA Server CA
   i:/C=US/ST=New Jersey/L=Jersey City/O=The USERTRUST Network/CN=USERTrust RSA Certification Authority
 
---

Yo realmente esperaba que este comando iba a fallar y darme alguna información útil que me ayudaría a entender por qué la conexión era rechazada por git. A buscar por otro lado.

Google sugirió una alternativa a openssl, gnutls-cli, que está basada en la librería gnutls que algunas aplicaciones usan en lugar de la librería openssl.

Como esa herramienta no está disponible por defecto, hay que instalarla.

sudo apt-get install gnutls-bin

Distracciones

¿Ya nos vamos? La dulce Jenny me trajo a la realidad que habíamos quedado en encontrarnos con unos amigos para cenar a las 7.30PM.  No sé qué voy a hacer, le dije, tengo un problema y no sé cómo solucionarlo.  Vamos a comer y relájate, ya verás que lo solucionas como todos los otros problemas a las 3AM.  Sonreí, era una broma interna… con demasiada frecuencia me acuesto con problemas irresueltos, sólo para despertarme en medio de la noche con la solución.  Fue un lindo tiempo, amigos de décadas, juntos después de años… comida peruana… todo contribuyó a una linda y relajante noche.  Lo cual no impidió que durante la cena y conversación de sobremesa haga un par de búsquedas en google sobre el problema en cuestión.

Casi al final de la cena, recibí un mensaje de nuestro hijo menor que necesitaba que lo recojan de la escuela.  Acababa de regresar de Orlando, FL, donde fue con el coro de la escuela a participar en una competencia en Disney, y donde ellos ganaron el oro!!!.  Afortunadamente, nuestra otra hija pudo recogerlo, y cuando llegamos le di un gran abrazo.  Lo extrañé mucho, la casa se siente demasiado silenciosa sin él.

Ahora sí

Okay, ya todos están dormidos, veamos qué podemos hacer con este problemita.

gnutls-cli -p 443 xxx.xxx.xxx

ohhhh, el mismo error… qué coincidencia.  Investigué y encontré que el git para ubuntu está compilado con la librería gnutls en lugar de openssl en casi todos los otros casos!!!! De razón había una diferencia en el comportamiento de esta máquina.  grrrrrrr… eso clarificaba por qué fallaba con git en esta computadora y no en las otras, pero no me daba ninguna pista del por qué fallaba.

gnutls vs openssl

Comencé a leer sobre la diferencia entre estas dos librerías.  Son básicamente dos implementaciones de la misma funcionalidad.  Pero entre búsqueda y pestañeo encontré que openssl es perdonador con respecto al orden los certificados, en cambio gnutls no.  Hmmm, déjame mirar.

Eureka

Efectivamente, esta información había estado disponible desde muy temprano, pero, al no saber que openssl y gnutls tienen diferente comportamiento, ignoré este detalle tan valioso.  Es cierto que todos los certificados estaban en la cadena, y openssl, simplemente se asegura que todos los certificados estén presentes.  Por el contrario, gnutls, mira el primer certificado de la cadena, y se asegura que corresponda al nombre del sitio.  Luego, mira al segundo certificado y se asegura que corresponda con la autoridad del primero.  Si no corresponde, vomita.  Luego mira al tercero y se asegura que corresponda con la autoridad del segundo… y así por el estilo.

En otras palabras, la librería gnutls se asegura que la cadena de certificados efectivamente forme una cadena. Al inspeccionar la cadena nuevamente pude ver el error.  El primer certificado era correcto, pero los tres siguientes estaban en orden reverso… es decir, 0321, en lugar de 0123.
El servidor originalmente decía:

XXX.XXX.XXX => InCommon RSA Server CA
AddTrust External CA Root => AddTrust External CA Root
USERTrust RSA Certification Authority => AddTrust External CA Root
InCommon RSA Server CA => USERTrust RSA Certification Authority

Pero debía decir:

XXX.XXX.XXX => InCommon RSA Server CA
InCommon RSA Server CA => USERTrust RSA Certification Authority
USERTrust RSA Certification Authority => AddTrust External CA Root
AddTrust External CA Root => AddTrust External CA Root

Malinformado

Todo el tiempo había estado tratando de solucionar el problema equivocado.  El hecho de que todas las demás computadoras se podían conectar y un programa de esta computadora no lo podía hacer me llevó a asumir que este programa estaba mal (instalado o configurado).  Sin embargo, el error nunca estuvo en este programa, el error estaba en el servidor, que estaba sirviendo los certificados en orden incorrecto, y este programa era el único que estaba bien escrito, y, por lo tanto, impedía que me conecte.

Editar el certificado en formato crt no es nada difícil, cualquier editor de texto lo puede hacer. Es básicamente un archivo de texto y se ve algo así…

-----BEGIN CERTIFICATE-----
MIIFxTCCBFWgAwIBAgIRALPs2ZL4g5aeNMpqd5hBfVIwDQYJKoZIhvcNAQELBQAw
djELMdkGA0UEBhMCVVMxCzAJBgNVBAgTAk1JMRIwEAYDVQQHEwlBbm4gQXJib3Ix
...==
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIF+TCCA+GgAwIBAgIQRyDQ+oVGcn4XoWQCkYRjdDANBgkqhkiG9w0BAQwFADCB
iDELMAkGA1UEBhMCVVMxEzARBgNVBAgTCk5ldyBKZXJzZXkxFDASBgNVBAcTC0pl
...
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIFdzCCBF+gAwIBAgIQE+oocFv0740MNmMJgGFDNjANBgkqhkiG9w0BAQwFADBv
MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk
...=
-----END CERTIFICATE-----
-----BEGIN CERTIFICATE-----
MIIENjCCAx6gAwIBAgIBATANBgkqhqiG9w0BAQUFADBvMQswCQYDVQQGEwJTRTEU
MBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0IEV4dGVybmFs
...=
-----END CERTIFICATE-----

Moví el cuarto certificado a la segunda posición, y el de la segunda al cuarto.  Reempecé el sitio y voilá, todo funcionó a la perfección.

Moraleja

La solución estuvo a mi vista desde casi el primer momento… el error decía que era un problema del certificado, pero el hecho que todo lo demás funcionaba me condujo a una inferencia equivocada.  Nunca asumas nada, verifica todo.