Saltar al contenido
ecdsa.com
← Todos los artículos

Certificados SSH frente a authorized_keys: CA y principals

Con authorized_keys, escalar significa copiar las claves a cada servidor y no olvidarse nunca de quitarlas. Los certificados OpenSSH lo sustituyen por una sola CA de confianza, claves firmadas de corta duración y nombres en lugar de huellas digitales. Aquí tienes la configuración completa, probada en OpenSSH 10.0, con cada fallo que produce.

Respuesta breve. Con authorized_keys, cada servidor enumera todas las claves públicas con las que se puede iniciar sesión, y el acceso dura hasta que alguien borra la línea. Con los certificados OpenSSH, los servidores confían en una sola CA de usuarios (TrustedUserCAKeys). La clave de cada usuario se firma con ssh-keygen -s, que le añade una identidad (-I), las cuentas en las que puede iniciar sesión (principals, -n) y un período de validez (-V). Los clientes confían en una sola CA de hosts (@cert-authority en known_hosts) en lugar de aceptar la huella digital de cada host en la primera conexión. Los certificados de corta duración hacen que las bajas de personal sean automáticas, y las listas de revocación de claves (RevokedKeys) se encargan del resto. El precio es que ahora tienes que operar una CA y proteger su clave.

Lo que authorized_keys no hace

~/.ssh/authorized_keys es sencillo y no tiene piezas móviles, y por eso está en todas partes. Sus límites aparecen cuando hay más personas y más servidores:

  • El acceso nunca caduca. Una clave añadida para un contratista en 2023 sigue funcionando en 2026, en todos los servidores a los que se copió, salvo que alguien la quite. (Existe expiry-time= por línea, pero alguien tiene que ponerlo.)
  • No hay identidad. La línea es una clave y un comentario. A qué persona, equipo o tarea pertenece consta en una hoja de cálculo, si es que consta en algún sitio.
  • La distribución es cosa tuya. Añadir o quitar a alguien obliga a tocar cada servidor, con herramientas de gestión de configuración o a mano.
  • Las claves de host se basan en la confianza en el primer uso. Cada cliente acepta la huella digital de cada servidor la primera vez, y reconstruir un servidor provoca REMOTE HOST IDENTIFICATION HAS CHANGED en todas partes.

Los certificados OpenSSH, disponibles desde OpenSSH 5.4 (2010), resuelven los cuatro problemas. No son X.509: el formato es propio de OpenSSH, una clave pública más una identidad y unas restricciones, firmada por una clave de CA que, a su vez, es una clave SSH corriente. En 2025, la descripción del formato pasó del archivo PROTOCOL.certkeys del código fuente de OpenSSH a un Internet-Draft, hoy draft-ietf-sshm-cert, en el grupo de trabajo SSHM del IETF.

Dos CA, dos sentidos

Necesitas dos claves de firma separadas, porque responden a preguntas distintas:

  • La CA de usuarios responde a la pregunta «¿puede esta clave iniciar sesión con esta cuenta?». Los servidores confían en ella.
  • La CA de hosts responde a la pregunta «¿de verdad este host es web01?». Los clientes confían en ella.
Creación de las CA (OpenSSH 10.0p2)
$ ssh-keygen -q -t ed25519 -f user_ca -C "Example user CA" -N ""
$ ssh-keygen -q -t ed25519 -f host_ca -C "Example host CA" -N ""

Con -N "" el laboratorio es más sencillo. Una clave de CA real debe estar en un HSM, en un token de hardware o en un servicio de firma (así funcionan el motor de secretos SSH de HashiCorp Vault, smallstep, Teleport y sus equivalentes en los proveedores de nube). Quien tenga la clave de la CA de usuarios puede iniciar sesión como cualquier usuario que admitan los servidores.

Certificados de usuario

Firma la clave pública que Alice ya tiene, con validez de 8 horas, para las cuentas alice y deploy:

ssh-keygen -s: emisión de un certificado de usuario
$ ssh-keygen -s user_ca -I alice@example.test -n alice,deploy -V -5m:+8h -z 1001 id_ed25519.pub
Signed user key /home/alice-laptop/id_ed25519-cert.pub: id "alice@example.test" serial 1001 for alice,deploy valid from 2026-10-20T13:14:08 to 2026-10-20T21:19:08
  • -I es el ID de clave. sshd lo anota en el registro en cada inicio de sesión, así que pon ahí una persona o una tarea, no un nombre de host.
  • -n enumera los principals, los nombres con los que puede actuar este certificado. El manual de ssh-keygen dice que los certificados sin principals son «válidos para todos los usuarios o hosts», pero sshd 10.0 rechaza un certificado de usuario así cuando la confianza viene de TrustedUserCAKeys (Certificate lacks principal list). Indica siempre -n.
  • -V -5m:+8h fija la validez desde hace 5 minutos (por el desfase de reloj) hasta dentro de 8 horas.
  • -z asigna un número de serie, al que puede hacer referencia una revocación.

ssh-keygen -L muestra lo firmado:

ssh-keygen -L -f id_ed25519-cert.pub
/home/alice-laptop/id_ed25519-cert.pub:
        Type: ssh-ed25519-cert-v01@openssh.com user certificate
        Public key: ED25519-CERT SHA256:9Mr4hhu4+c6LnVoj+n8tVj4AKD/AP037BQ5pyEAv07s
        Signing CA: ED25519 SHA256:aG9VR0NrG4he7YusGsggg/Gt3WviRbg3Anbfz4HJe60 (using ssh-ed25519)
        Key ID: "alice@example.test"
        Serial: 1001
        Valid: from 2026-10-20T13:14:08 to 2026-10-20T21:19:08
        Principals: 
                alice
                deploy
        Critical Options: (none)
        Extensions: 
                permit-X11-forwarding
                permit-agent-forwarding
                permit-port-forwarding
                permit-pty
                permit-user-rc

De forma predeterminada, un certificado de usuario concede las cinco extensiones. -O clear las quita, y después puedes volver a añadir solo lo que necesites, por ejemplo -O clear -O permit-pty. Las opciones críticas restringen el certificado. force-command sustituye cualquier comando que pida el cliente, y source-address limita desde dónde se puede usar. Con source-address=10.0.0.0/8, se rechaza un inicio de sesión desde 127.0.0.1. Desde un origen permitido, el comando forzado se ejecuta pida lo que pida el cliente:

Opciones críticas en la práctica (líneas del registro de sshd con LogLevel VERBOSE)
        Critical Options: 
                force-command echo backup only
                source-address 10.0.0.0/8
$ ssh deploy@web01.example.test
deploy@web01.example.test: Permission denied (publickey).
cert: Authentication tried for deploy with valid certificate but not from a permitted source address (127.0.0.1).
Refused by certificate options
...
$ ssh deploy@web01.example.test "rm -rf /tmp/anything"
backup only
Starting session: forced-command (key-option) 'echo backup only' for deploy from 127.0.0.1 port 48976 id 0

El cliente ssh encuentra el certificado automáticamente cuando está junto a la clave privada como id_ed25519-cert.pub. Si no, indícalo con CertificateFile. ssh-add carga la clave y el certificado en el agente.

Configuración de sshd

Todo lo que hay que hacer del lado del servidor cabe en unas pocas líneas. Este archivo de configuración adicional superó sshd -t y se usó en todas las pruebas de esta página:

/etc/ssh/sshd_config.d/ca.conf
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub
TrustedUserCAKeys /etc/ssh/user_ca.pub
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
RevokedKeys /etc/ssh/revoked_keys
PasswordAuthentication no
LogLevel VERBOSE
  • TrustedUserCAKeys contiene una o varias claves públicas de CA. Durante una rotación de la CA, incluye las dos.
  • AuthorizedPrincipalsFile asocia cuentas con principals. Aquí /etc/ssh/auth_principals/deploy contiene una sola línea, deploy, así que solo los certificados que incluyen el principal deploy pueden iniciar sesión como deploy. Sin esta opción, sshd exige que el propio nombre de la cuenta figure entre los principals. La asociación es más flexible: un principal como oncall puede admitirse en muchas cuentas, y AuthorizedPrincipalsCommand puede buscarlo en un servicio de directorio.
  • RevokedKeys apunta a una lista de revocación (consulta más abajo).
  • LogLevel VERBOSE registra el ID de clave y el número de serie del certificado en cada inicio de sesión. Es el registro de auditoría que authorized_keys nunca tuvo.

Un inicio de sesión con el certificado de Alice, visto desde los dos lados:

ssh -v (cliente) y registro de sshd (servidor), abreviado
Server host certificate: ssh-ed25519-cert-v01@openssh.com SHA256:IuzvI4QsLmSx1+6p3yrvlRx2wdl+LUCY1zXqSzssLAc, serial 0 ID "web01" CA ssh-ed25519 SHA256:+iie/YHPulJReHN2GQ8UvrorBVrtayD2T1MGxeg/QCQ valid from 2026-10-20T13:14:08 to 2027-10-19T13:19:08
Host 'web01.example.test' is known and matches the ED25519-CERT host certificate.
Offering public key: /home/alice-laptop/id_ed25519 ED25519-CERT SHA256:9Mr4hhu4+c6LnVoj+n8tVj4AKD/AP037BQ5pyEAv07s explicit
Server accepts key: /home/alice-laptop/id_ed25519 ED25519-CERT SHA256:9Mr4hhu4+c6LnVoj+n8tVj4AKD/AP037BQ5pyEAv07s explicit
Authenticated to web01.example.test ([127.0.0.1]:22) using "publickey".

Accepted publickey for deploy from 127.0.0.1 port 57432 ssh2: ED25519-CERT SHA256:9Mr4hhu4+c6LnVoj+n8tVj4AKD/AP037BQ5pyEAv07s ID alice@example.test (serial 1001) CA ED25519 SHA256:aG9VR0NrG4he7YusGsggg/Gt3WviRbg3Anbfz4HJe60

Si no puedes editar sshd_config, una sola cuenta puede confiar en una CA desde su propio authorized_keys. La opción principals= funciona como el archivo de principals:

~deploy/.ssh/authorized_keys: confianza en una CA para una sola cuenta (clave abreviada)
cert-authority,principals="deploy-team" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIK63...
$ ssh deploy@web01.example.test 'echo logged in as $(whoami)'
logged in as deploy

Certificados de host

Firma la clave de host de cada servidor con la CA de hosts. -h la convierte en un certificado de host, y los principals son los nombres que usan los clientes para conectarse:

Firma de una clave de host
$ ssh-keygen -s host_ca -I web01 -h -n web01.example.test,web01 -V -5m:+52w /etc/ssh/ssh_host_ed25519_key.pub
Signed host key /etc/ssh/ssh_host_ed25519_key-cert.pub: id "web01" serial 0 for web01.example.test,web01 valid from 2026-10-20T13:14:08 to 2027-10-19T13:19:08

Después apunta HostCertificate al resultado (como en el archivo de sshd de arriba) y da a los clientes una sola línea en known_hosts (o en /etc/ssh/ssh_known_hosts) en lugar de una huella digital por host:

known_hosts: confiar en la CA de hosts para un dominio
@cert-authority *.example.test ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIBtSsqoqxwcl31DTJg3kTPsv9eLuuw5gU1BcvYybODlu Example host CA

A partir de ahí, los servidores nuevos y los reconstruidos son de confianza desde la primera conexión, sin pedir confirmación, siempre que su certificado incluya el nombre que usó el cliente. Esa última condición es la sorpresa habitual. Si te conectas con un nombre que no es un principal, el cliente recurre a las claves de host simples y, con la comprobación estricta, se detiene:

Conexión como db01.example.test a un host certificado solo como web01
$ ssh deploy@db01.example.test
Certificate invalid: name is not a listed principal
No ED25519 host key is known for db01.example.test and you have requested strict checking.
Host key verification failed.

Incluye todos los nombres que usan los clientes, también los nombres cortos, los CNAME, las direcciones IP si la gente se conecta por IP y los nombres de los balanceadores de carga. ssh-keyscan -c obtiene el certificado de un servidor para inspeccionarlo, y las líneas @revoked de known_hosts rechazan una clave de host o una CA concretas.

Qué aspecto tienen los fallos

Ante cualquier problema con un certificado de usuario, el cliente solo dice Permission denied (publickey). El motivo está en el registro del servidor, con LogLevel VERBOSE:

SituaciónRegistro de sshd (OpenSSH 10.0p2)
La cuenta no está entre los principals del certificado (después de la asociación)Certificate does not contain an authorized principal
Certificado emitido sin -n, sin AuthorizedPrincipalsFileCertificate lacks principal list
Certificado cuya ventana -V ya terminóCertificate invalid: expired
Número de serie o clave en la KRLAuthentication key ED25519-CERT SHA256:... revoked by file /etc/ssh/revoked_keys
source-address no coincidecert: Authentication tried for deploy with valid certificate but not from a permitted source address (127.0.0.1).

Dos cosas que parecen problemas de certificados pero no lo son:

  • Desfase de reloj. La validez se comprueba con el reloj del servidor. Emite certificados válidos desde unos minutos antes (-V -5m:...) y mantén los servidores sincronizados por NTP.
  • Penalizaciones por fallos repetidos. Desde OpenSSH 9.8, sshd penaliza las direcciones que fallan la autenticación una y otra vez (PerSourcePenalties, activado de forma predeterminada). Al probar certificados desde una misma máquina, un intento fallido hizo que la siguiente conexión se cortara antes de la autenticación:
sshd 10.0p2 tras un inicio de sesión fallido con certificado
srclimit_penalise: 127.0.0.1/32: activating ipv4 penalty of 19 seconds for penalty: failed authentication
$ ssh deploy@web01.example.test
Connection closed by 127.0.0.1 port 22
drop connection #0 from [127.0.0.1]:55080 on [127.0.0.1]:22 penalty: failed authentication

Si ssh muestra Connection closed justo después de una serie de fallos, espera a que termine la penalización antes de volver a cambiar la configuración.

Revocación con KRL

La validez corta es el principal mecanismo de revocación: un certificado de 8 horas deja de valer por sí solo. Para lo demás, OpenSSH tiene las listas de revocación de claves (KRL, desde OpenSSH 6.2), un formato binario compacto que permite revocar por número de serie, por ID de clave o por clave. Revocación del certificado de Alice con número de serie 1001:

ssh-keygen -k: crear una KRL y comprobar un certificado con ella
$ printf 'serial: 1001\n' > /tmp/krl-spec
$ ssh-keygen -k -f /etc/ssh/revoked_keys -s user_ca.pub /tmp/krl-spec
Revoking from /tmp/krl-spec
$ ssh-keygen -Q -f /etc/ssh/revoked_keys id_ed25519-cert.pub
/home/alice-laptop/id_ed25519-cert.pub (alice@laptop): REVOKED

sshd lee RevokedKeys en cada autenticación, así que el siguiente inicio de sesión falla sin reiniciar nada. Añade -u para actualizar una KRL existente en lugar de sustituirla. Distribuye la KRL con las mismas herramientas que la clave de la CA. Un servidor con una KRL desactualizada acepta lo que no sabe que está revocado. Es un motivo más para que los certificados sean de corta duración y para tratar la KRL como una red de seguridad para las horas que quedan.

Comparación directa

authorized_keysCertificados
Conceder accesoCopiar una clave a cada servidorFirmar una clave una vez
Retirar accesoBorrarla en cada servidorDejar que caduque o añadirla a la KRL
CaducidadOpcional por línea (expiry-time=)Todo certificado tiene un período de validez
Identidad en los registrosHuella digital de la claveID de clave y número de serie
Restriccionesfrom=, command=, restrict por líneasource-address, force-command, extensiones por certificado
Confianza en los hostsHuella digital por host (TOFU)Una línea @cert-authority
Secreto críticoNinguno centralizadoLa clave privada de la CA
Piezas móvilesNingunaEmisión, protección de la CA, distribución de la KRL

Las restricciones por línea de authorized_keys funcionan de verdad y se probaron en el mismo servidor:

Opciones de authorized_keys, OpenSSH 10.0p2 (claves abreviadas)
restrict,from="10.0.0.0/8",command="echo backup only" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAA...
$ ssh deploy@web01.example.test id
deploy@web01.example.test: Permission denied (publickey).
/home/deploy/.ssh/authorized_keys:1: Authentication tried for deploy with correct key but not from a permitted host (host=127.0.0.1, ip=127.0.0.1, required=10.0.0.0/8).

expiry-time="20261001" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAI...
$ ssh deploy@web01.example.test id
deploy@web01.example.test: Permission denied (publickey).

Para un puñado de servidores y personas, authorized_keys con restrict y expiry-time es suficiente. Los certificados compensan cuando las altas, las bajas y las reconstrucciones de hosts son semanales, o cuando necesitas saber quién inició sesión, no solo con qué clave.

Migración desde authorized_keys

No hace falta cambiarlo todo el mismo día. sshd comprueba authorized_keys y las CA de confianza a la vez, así que los dos tipos de inicio de sesión funcionan durante la transición:

  1. Despliega primero la confianza en la CA. Distribuye TrustedUserCAKeys, los archivos de principals y una KRL vacía a todos los servidores, y deja authorized_keys como está.
  2. Emite certificados para un primer equipo y deja que sus miembros los usen durante unas semanas. Cada inicio de sesión aparece ahora en el registro con ID ... (serial ...).
  3. Localiza a los rezagados. Un inicio de sesión mediante authorized_keys se registra sin ID de certificado, por ejemplo Accepted publickey for deploy from 127.0.0.1 port 58770 ssh2: ED25519 SHA256:tT5X32Wds5InYaLSh7mV+QRqAtxA4KXwlKV3hkf0YkI. Suelen ser automatizaciones: ejecutores de CI, tareas de copia de seguridad, monitorización. Dale a cada una su propio certificado, de corta duración o con restricciones estrictas.
  4. Desactiva los archivos de claves con AuthorizedKeysFile none cuando ya nada legítimo los use. Mantén una vía de acceso de emergencia, como el acceso por consola o una CA de emergencia aparte guardada sin conexión, para el día en que el servicio de firma no funcione.
  5. Haz la parte de los hosts por separado. Firma las claves de host y añade la línea @cert-authority al archivo global de los clientes, /etc/ssh/ssh_known_hosts. Las entradas antiguas por host de known_hosts pueden quedarse. Siguen funcionando hasta que cambien las claves de host.

En la práctica

  • Emite los certificados desde tu proveedor de identidad. Un servicio de firma comprueba el inicio de sesión SSO del usuario (OIDC) y devuelve un certificado con sus principals, válido durante unas pocas horas. Nadie maneja la clave de la CA, y quien deja la empresa pierde el acceso SSH como muy tarde al día siguiente, sin tocar ningún servidor.
  • Guarda la clave del usuario en hardware si puedes. ssh-keygen -t ed25519-sk crea una clave respaldada por FIDO2. Los certificados funcionan con ella igual que con cualquier otra clave.
  • Rota la CA con un período de solapamiento. Añade la nueva CA de usuarios como segunda línea en TrustedUserCAKeys, o la nueva CA de hosts como segunda línea @cert-authority. Emite desde la nueva CA y después retira la antigua.
  • Vigila también el lado de los hosts. Vuelve a firmar las claves de host antes de que caduquen sus certificados. Un certificado de host caducado hace que los clientes recurran a las claves de host simples, que normalmente no tienen:
ssh a un host cuyo certificado caducó ayer
Certificate invalid: expired
No ED25519 host key is known for web01.example.test and you have requested strict checking.
Host key verification failed.

Para inspeccionar un archivo de clave o de certificado sin ejecutar nada, el inspector de claves SSH decodifica claves OpenSSH y muestra su tipo, su tamaño y su huella digital. La auditoría de servidores SSH comprueba lo que ofrece un servidor, incluidos sus algoritmos de clave de host y de certificado y el intercambio de claves poscuántico.

Fuentes

Toda la salida de esta página procede de OpenSSH 10.0p2 (paquetes de Debian 13) en debian:trixie-20261005-slim, el 20 de octubre de 2026, con claves de prueba generadas en el contenedor.