Utilizar certificados mediante ssh nos puede resultar útil cuando un desarrollador pierde su key y hay que regenerarla, no tendremos que entrar en todos los servidores para instalar la nueva key, simplemente generaremos otra, la firmaremos con la CA, añadiremos la antigua a la lista de revocadas y distribuiremos la lista de revocadas a los servidores.
OpenSSH soporta autenticación mediante certificados desde la versión 5.4 :
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_keys or via a TrustedUserCAKeys option in sshd_config(5) (for user authentication),
or in known_hosts (for host authentication).
Creamos un usuario para gestionar la CA en el servidor CA:
useradd -m -u 3000 -g sshca -G sshca -c “SSH Certificate Authority Signing User” -s /bin/bash -d /home/sshca sshca
su -i -u sshca
Creamos la CA:
mkdir ~/my-ca
cd ~/my-ca
ssh-keygen -C CA -f ca
chmod 0644 ca.pub
Copiamos la ca.pub a los servidores ssh:
Editamos la config en el server ssh para que permita la autenticación mediante pubkey-certificado:
TrustedUserCAKeys /etc/ssh/ca.pub
Reiniciamos el servicio:
Generamos en la CA el certificado del cliente en base a su pubkey, la sintaxis es de la siguiente forma:
Un ejemplo de un certificado válido por una semana para el usuario kr0m sería así:
Signed user key id_rsa-cert.pub: id "kr0m" serial 1 for root valid from 2019-05-20T22:41:00 to 2019-05-27T22:42:20
NOTA: Un intervalo de tiempo válido puede ser desde ahora hasta X tiempo o un rango concreto, desde X hasta Y separados por “;”.
Podemos consultar los datos del certificado con:
id_rsa-cert.pub:
Type: ssh-rsa-cert-v01@openssh.com user certificate
Public key: RSA-CERT SHA256:oV14spSAEcsEWiXREUYvBgVEyEumRMBQGtwbZXXF6ZE
Signing CA: RSA SHA256:xhAgl+pmJ4Y5jZgAM1hGCFgIwbidV7xRKphbxtfDvqE
Key ID: "kr0m"
Serial: 1
Valid: from 2019-05-20T22:41:00 to 2019-05-27T22:42:20
Principals:
root
Critical Options: (none)
Extensions:
permit-X11-forwarding
permit-agent-forwarding
permit-port-forwarding
permit-pty
permit-user-rc
Le pasamos el certificado al cliente:
Probamos a conectar:
ssh root@A.B.C.D -p 22
En los logs del server podemos ver el registro del acceso:
May 20 22:46:04 rsaserver sshd[715]: Accepted publickey for root from x.x.x.x port 50102 ssh2: RSA-CERT ID kr0m (serial 1) CA RSA SHA256:xhAgl+pmJ4Y5jZgAM1hGCFgIwbidV7xRKphbxtfDvqE
Si intentamos conectar después de la semana en los logs veríamos:
May 20 23:00:33 rsaserver sshd[1619]: error: Certificate invalid: expired
La autenticación por certifcados nos permite definir “principals” estos actúan como tokens de acceso, de este modo podemos definir zonas de acceso. Esto resulta útil para restringir el acceso a ciertos servidores.
Configuramos los servidores ssh:
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
Definimos los principals:
echo -e ‘zone-webservers\nroot-everywhere’ > /etc/ssh/auth_principals/root
Reiniciamos el servicio:
La idea es tener un principal genérico que utilizarán los sysadmins(root-everywhere) y uno mas específico para los usuarios.
Para tener acceso a este servidor nuestro certificado debe haber sido emitido con el principal zone-webservers o root-everywhere.
Si intentamos conectar con el certificado sin el principal aparecerá el siguiente error:
May 20 23:02:34 rsaserver sshd[935]: error: Certificate does not contain an authorized principal
Regeneramos el certificado en la CA indicando el principal:
Actualizamos el certificado en el cliente y conectamos:
Podemos hacer que el principal sea dinámico ejecutando en el servidor ssh un script/binario para obtenerlo, por ejemplo consultando un activedirectory:
AuthorizedPrincipalsCommand /usr/bin/adquery user –P %u
AuthorizedPrincipalsCommandUser root
Las variables soportadas en la configuración de los principals son:
AuthorizedPrincipalsCommand accepts the tokens %%, %F, %f, %h, %i, %K, %k, %s, %T, %t, %U, %u.
AuthorizedPrincipalsFile accepts the tokens %%, %h, %U, %u.
%% A literal `%'.
%F The fingerprint of the CA key.
%f The fingerprint of the key or certificate.
%h The home directory of the user.
%i The key ID in the certificate.
%K The base64-encoded CA key.
%k The base64-encoded key or certificate for authentication.
%s The serial number of the certificate.
%T The type of the CA key.
%t The key or certificate type.
%U The numeric user ID of the target user.
%u The username.
Podemos comprobar mediante tcpdump que no hay conexión alguna entre el servidor ssh a la CA en el proceso de login:
En la CA:
Realizamos la conexión al servidor ssh desde el cliente:
En el tcpdump no aparece nada de tráfico, esto es bueno ya que la CA no será un PoF si nos atacan por fuerza bruta el servicio ssh.
Pero por otro lado para revocar certificados tendremos que distribuir el fichero de revoked keys a cada servidor ssh de forma independiente, este fichero se puede distribuir vía pssh, ansible, puppet, chef, cron…
En mi caso la mejor opción es crontabear en cada server un wget del fichero de keys revocadas y si hace falta aplicar la revocación de forma inmediata ejecutar mediante pssh el wget de forma manual.
Para revocar un certificado habrá que configurar los servidores ssh para que consulten la lista de keys revocadas y revocarla en la CA.
En el servidor ssh:
touch /etc/ssh/revoked_keys
Reiniciamos el servicio:
En la CA revocamos la key del usuario:
ssh-keygen -k -u -f revoked_keys id_rsa.pub –> Append al fichero existente
Podemos comprobar si la key está en la lista de revocadas:
id_rsa.pub (kr0m@DirtyCow): REVOKED
NOTA: Si revocamos una key por accidente no tendremos mas remedio que regenerar las keys ssh y el certificado asociado a ellas.
Distribuimos el fichero de keys revocadas a todos los servidores ssh:
Comprobamos que el cliente ya no puede conectar:
En los logs veremos:
May 20 23:49:57 rsaserver sshd[1306]: error: Authentication key RSA SHA256:oV14spSAEcsEWiXREUYvBgVEyEumRMBQGtwbZXXF6ZE revoked by file /etc/ssh/revoked_keys
NOTA: Las keys revocadas son independientes de si utilizamos certs o pubkeys, sin habilitar los certs podemos hacer uso del baneo de keys.
Lo recomendable es generar los certificados con caducidad X y además ir revocando las keys si un desarrollador abandona el proyecto, así en la lista de revocados solo tenemos que mantener los desarrolladores despedidos en los últimos X días.
Si queremos que solo sea posible hacer login mediante pubkeys-certs:
PubkeyAuthentication yes
PasswordAuthentication no
ChallengeResponseAuthentication no
Reiniciamos el servicio:
NOTA: No hay forma de configurarlo solo con certificados y sin autenticación por pubkey.
Los conceptos clave que debemos extraer de este artículo son:
- Distribuir la pubkey de la CA a los servidores ssh.
- Distribuir la lista de keys revocadas cuando un desarrollador abandona el proyecto.
- Cada departamento debe tener un principal diferente, de este modo podemos utilizar la misma CA para todos los certificados de todos los usuarios.
- Crontabear como root un script que compruebe el contenido de los authorized_keys de todos los usuarios del sistema, de este modo si el servidor fuese comprometido lo detectaríamos de forma rápida y sencilla, este script también podría meter las keys encontradas en el fichero de keys revocadas por si algún desarrollador intenta darse acceso mediante authorized_keys
El servidor de la CA es un punto critico de seguridad, un atacante con acceso a la CA podría generarse un certificado con el que acceder a todos los servidores de nuestra infraestructura, la única manera de detectar estos accesos sería revisando los logs de los servidores finales.