31 – CA para SSH

Te doy la bienvenida al episodio 31 de deployandome, el podcast de tecnología para sysadmins y devops. Soy Rodolfo Pilas y estoy grabando el 14 de agosto de 2018.

Cuando instalamos un nuevo servidor, nos preocupamos de dejarlo actualizado, de configurar el idioma y la sincronización de hora, de instalar las aplicaciones y librerías que requiere para su uso, de hacer el hardening correspondiente para darle las condiciones básicas de seguridad, de configurar el sistema de backup y de inscribirlo en el sistema de monitoreo de nuestra infraestructura. Entre todo esto, nos tomamos muy en serio los accesos para quiénes tendrán que hacer login: con qué usuario ingresarán, si ese usuario tendrá permisos en el sudoers y recolectamos e inscribimos con mucho cuidado los certificados públicos SSH en el authorized_keys para que ingresen.

Ahora, te pregunto una cosa ¿no sientes que la administración de todos estos certificados ssh es algo frustrante?

Te cuento lo que me suele pasar a mi: a medida que va pasando el tiempo los servidores empiezan a tener certificados que no se sabe bien a quién pertenecen, a veces de algún desarrollador que necesitó entrar para una ayuda puntual, a veces de alguna proveedor que necesitó algún acceso.

También se vuelve bastante difícil distribuir los certificados para los nuevos administradores y quitar aquellos de los que no están más afectados al servidor y ya no deberían tener acceso, algunas veces puedo manejar esto con Ansible, algunas veces es una tarea manual.

Como que siento que en la carrera de mantener y administrar los accesos con certificados SSH estoy atrás.

Pero existe una luz sobre este problema, una solución bastante simple pues desde la versión de OpenSSH 5.4 es posible utilizar certificados tanto para servidores, como para usuarios, basados en la confianza dada por una Autoridad de Certificación y de esto, vamos a conversar en esta edición de deployandome.

Add support for certificate authentication of users and hosts
using a new, minimal OpenSSH certificate format (not X.509).
Certificates contain a public key, identity information and
some validity constraints and are signed with a standard SSH
public key using ssh-keygen(1). CA keys may be marked as
trusted in authorized_key or via a TrustedUserCAKeys option 
in sshd_config(5) (for user authentication), or in known_hosts
(for host authentication).

No es la primera vez que en deployandome tratamos el tema de OpenSSH: en la edicione 4 conversamos sobre sshuttle para armar VPN con SSH, en la edición 5 de sshfs para montar por SSH filesystems de servidores remotos, o por ejemplo, en la edicion 20 de MOSH para mantener un shell permanente y performante validando por SSH.

Pero más allá de esto, explicar el funcionamiento de la validación que hace OpenSSH con los certificados siempre me ha resultado un desafío interesante; cuando lo hago en el marco de los cursos que dicto en la Universidad termino con unos gráficos en el pizarrón, unas cuantas flechas, recuadros y hasta colores que me ayudan en la explicación.

Ahora voy a tratar de explicártelo tipo cuento, acercándote a los conceptos y, si necesitas profundizar, siempre puedes recurrir al blog deployando.me donde dejo comandos y referencias a documentación adicional.

Para una conexión por SSH tenemos básicamente dos tipos de validación donde intervienen certificados

Primero una validación del host al que nos estamos conectando, donde nos atiende un servidor OpenSSH y nos envía un paquete firmado que verificamos con el certificado público que tenemos guardado en el archivo local known_hosts, que es el llavero donde tenemos los certificados de los hosts en los cuales confiamos. Si no tenemos el certificado público nos pregunta si queremos incorporar el certificado a nuestro llavero y contestamos el famoso yes que precede toda conexión SSH que hacemos por primera vez.

La segunda validación es la de usuario y sirve para habilitar el acceso al host remoto. En este caso el certificado público del usuario de origen debe estar inscripto en el archivo authorized_keys del usuario con el que voy a acceder en el host de destino.

Esto, que es la configuración normal en que solemos tener un entorno de conexiones SSH, conlleva el problema que debemos gestionar manual la primer conexión a los servidores (para incorporar el certificado del host al known_host) y debemos mantener los certificados para acceder en archivos authorized_keys que están distribuidos en todos los host y en los usuarios de esos hosts.

Pero con un sistema de confianza basados en certificados públicos firmados podemos simplificar, automatizar y controlar más eficazmente nuestro entorno de conexiones SSH.

La utilidad principal es ssh-keygen que vamos a utilizar para crear, firmar y revisar certificados que hemos firmado.

El funcionamiento de firma es simple:

Generamos dos pares de llaves SSH estándar que usaremos para firmar las llaves públicas de los host y de los usuarios. Te lo repito: siempre movemos, copiamos y firmamos la parte pública de las llaves SSH, la parte privada queda en los servidores y usuarios que las generaron.

Entonces para empezar generamos estos dos pares de llaves que podemos llamar server-CA y user-CA, una para firmar llaves de hosts o servidores y otra de usuarios.

Para firmar usaremos ssh-keygen seguido de -s de sign. Cuando firmamos un certificado de un host también pondremos -h host y cuando firmemos un certificado de un usuario no necesitamos ningún otro switch.

Al firmar, ssh-keygen nos generará un nuevo archivo con el mismo nombre que el archivo de la llave pública que termina en .pub, pero ahora terminado en cert.pub

Estas llaves públicas firmadas podemos consultarlas con ssh-keygen -L para listar información de validez, autoridad de certificación y demás datos del certificado.

Obviamente que te estoy simplificando los comandos, pero creo que te vas haciendo una idea por donde viene la mano del proceso de firmas.

Nuevamente veamos los dos tipos de validación, de host y de usuario, pero ahora utilizando estos certificados firmados.

Para la validación de host, la llave firmada de los host tenemos que llevarla a cada host e inscribirla en el archivo sshd_config con la directiva HostCertificate

En los archivos known_host de los clientes voy a registrar solamente la llave pública de la autoridad de certificación que llamamos server-CA.pub en una linea que inicia con la directiva @cert-authority

Esto va a provocar que al conectar por primera vez por SSH puedo verificar que el certificado ofrecido está firmado por la autoridad de certificación en la que confío y validar la identidad del host automáticamente, sin necesidad de ningun yes ni incorporar nuevos certificados a mi llavero.

La ventaja a nivel de aplicaciones automatizadas es notoria ya que inmediatamente todos nuestros servidores que tengan su certificado firmado por esa autoridad de certificación serán confiables para establecer conexión y utilizar todas las funciones de OpenSSH.

Para la validación de usuario funciona un poco diferente.

Ahora quién hará la validación para otorgar acceso será el propio servidor OpenSSH al que me estoy conectando y ya no dependerá de los usuarios de destino.

En el servidor de destino, modificamos el archivo sshd_config pero esta vez usaremos la directiva TrustedUserCAKeys seguido un archivo que concatena la llave pública de la autoridad de certificación, o sea la user-ca.pub.

Los usuarios que inician conexión tienen las tres llaves: su privada, la pública y la pública firmada. Cuando establezcan conexión SSH será el propio servidor que validará la llave firmada por la autoridad de certificación y si el usuario al que quiere conectar existe, habilitará el acceso.

Si necesitamos separar los accesos, es decir que un usuario no quede con acceso a todos los usuarios de todos los servidores, podemos tener diferentes certificados para firmar, es decir, un certificados para databases, otro para webs, otro storage, y así. Y para evitar el acceso como root tenemos la directiva PermitRootLogin del servicio SSH.


Con el uso de certificados OpenSSH firmados podemos armar un esquema de control de las validaciones de host y accesos de usuarios en nuestra infraestructura. OpenSSH implementa además la posibilidad de crear y disponibilizar listas de revocación para tener una validación inmediata de los certificados que no están más autorizados; pero esto sería tema para otra edición de deployandome.

Por un lado, te reconozco que puede no sea una solución ideal, que no estamos hablando de un esquema X.509, ni de un sistema de autenticación centralizado. Pero, por otro lado, es un método simple que tenemos a nuestra disposición sin mayor esfuerzo, pues no necesitamos instalar servicios adicionales ni tener un servidor online para validación de certificados de la autoridad de certificación.

Y a la hora de manejar una infraestructura dinámica y elástica utilizar certificados SSH firmados nos va a solucionar unos cuantos workarrounds que hacemos generalmente para que SSH fluya sin trabas.

Te recuerdo que en blogpost de este podcast dejo información adicional.

Soy Rodolfo Pilas, en twitter me puedes seguir por @pilasguru y te dejo un saludo. Confío que este podcast te haya aportado para mejorar y, como siempre, espero tus inquietudes y sugerencias comentando en deployando.me

Hasta la próxima edición.

sshd_config man page

What the -I certificate_identity ssh-keygen option is for?

https://framkant.org/2016/10/setting-up-a-ssh-certificate-authority-ca/