Para: cualquiera que llegue a producción por SSH desde un entorno efímero — un pod de GPU-cloud, un runner de CI, una VM desechable. Sin pasos de ataque — solo hacer las claves de privilegio mínimo para que una fuga cause menos daño. Para lo básico de separar claves, ver la lista de comprobación de la base.
La visión de este sitio: trata los entornos efímeros como no confiables
El cómputo desechable a menudo omite el endurecimiento — es un sitio que puede comprometerse. Pon ahí una clave de root de producción y comprometer ese entorno equivale a comprometer producción. Este sitio usa una clave distinta por servidor, acota cada una a un propósito, y quita las claves en cuanto dejan de usarse (→ separa las claves para minimizar el daño). Tratar una clave no como "una comodidad" sino como "el activo más crítico, que da acceso a todos los servidores si se filtra" es, al final, el enfoque más seguro.
✗ clave de root en un entorno efímero
entorno comprometido → root en producción con esa clave → alcanza archivos, secretos y otros servicios — todo.
○ clave no-root restringida a un comando
aunque se filtre la misma clave, solo puede ejecutar el único comando especificado. Sin shell, sin reenvío — el daño queda contenido a una operación.
Por qué es peligrosa una clave de root en un entorno efímero
Los entornos efímeros tienden a omitir el endurecimiento ("lo borraremos pronto"), lo que los convierte en un objetivo fácil y en un punto de paso cómodo para atacar producción. Una clave de root de producción ahí hace que el daño sea el mayor posible.
Supón que levantas un pod para cómputo y, para trabajar en producción desde él, registras una clave en el authorized_keys de root. Práctico — pero si ese pod se compromete, el atacante entra a producción como root con esa clave. Empiece el ataque con un RCE o con una fuga de clave, el resultado final es el mismo: producción tomada con una clave robada (relacionado: una clave robada vía RCE y facturada por fraude).
Tres pasos hacia claves de privilegio mínimo
Cambia de "concede todo el poder porque es cómodo" a "limita lo que puede hacer al mínimo".
Quita la clave de root del entorno efímero
~/.ssh/authorized_keys de producción las claves que ya no uses. Si están inactivas, quitarlas no tiene impacto. Primero, no dejes claves sin usar en su sitio.Aunque se necesite de nuevo, no vuelvas a root
Restringe la clave a una sola operación
command="..." y restrict a la línea de la clave en authorized_keys. Incluso un inicio de sesión exitoso solo puede ejecutar el único comando especificado, con el reenvío de puertos y el shell deshabilitados. Una fuga queda contenida a esa única operación.# Register a key restricted to ONE operation in a non-root user's authorized_keys.
# Logging in with it can run only the given command; forwarding and PTY are off.
command="/usr/local/bin/deploy-pull",restrict ssh-ed25519 AAAA...rest-of-public-key deploy@cirestrict deshabilita el reenvío, el reenvío de agente, la PTY, X11 y más de una sola vez; command="..." fija la acción ejecutable a una sola cosa. La jugada básica es una clave acotada a un único propósito — solo hacer pull de un despliegue, solo ejecutar un script específico — creada por caso de uso.
Un montaje habitual (peligroso)
- registrar una clave en el root de producción desde un pod/CI
- dejar en su sitio las claves terminadas
- reutilizar una clave en muchos servidores
- esparcir claves que conceden un shell completo
El montaje de privilegio mínimo
- llegar a producción solo como usuario no-root
- las claves están limitadas a una operación (restringidas a un comando)
- separar claves por servidor y propósito
- quitar las claves de authorized_keys en el momento en que dejan de usarse
Trata una clave reutilizada como tu activo más crítico
Piensa en una clave como "una comodidad" y la reutilizarás y olvidarás de borrarla. En lugar de eso, trátala como el activo más crítico, que da acceso a todos los servidores si se filtra. Separa por servidor y propósito, mantén las claves de producción fuera de los entornos efímeros, y quítalas siempre tras usarlas. Solo eso evita que una fuga se extienda a todo.
Las principales restricciones que puedes poner en una línea de clave
En OpenSSH, las opciones escritas al principio de una línea de clave en authorized_keys acotan lo que esa clave puede hacer. Las habituales:
| Opción | Qué restringe | Cuándo usarla |
|---|---|---|
command="..." | Iniciar sesión con la clave ejecuta solo el comando especificado; se ignora el comando que haya enviado el cliente | Claves que deben hacer un único trabajo fijo, como hacer pull de un despliegue o sacar una copia de seguridad |
restrict | Deshabilita de una vez el reenvío de puertos, de agente y de X11, la asignación de PTY y la ejecución de ~/.ssh/rc (OpenSSH 7.2+) | Casi cualquier clave de automatización; vuelve a activar solo lo que necesites, p. ej. pty |
from="..." | Además de la clave, el nombre de host o la dirección IP del cliente debe coincidir con la lista | CI o servidores de trabajo con una IP de origen fija |
expiry-time="..." | La clave deja de aceptarse a partir de la fecha y hora indicadas (OpenSSH 7.7+) | Claves pensadas para funcionar solo durante un trabajo temporal |
PermitRootLogin (en sshd_config) | Si se permite el inicio de sesión directo como root; forced-commands-only permite root solo a claves con command= | no, salvo que de verdad necesites acceso directo como root |
Las notas de versión de OpenSSH describen restrict como la opción que incluye todas las restricciones de clave actuales y futuras. En lugar de enumerar no-pty, no-port-forwarding, etc. una por una, añade restrict y vuelve a activar solo lo que necesites, para que nada se cuele cuando aparezcan funciones nuevas.
# Also narrowing the source and the lifetime (the IP is a documentation address)
from="192.0.2.10",expiry-time="20261231",command="/usr/local/bin/deploy-pull",restrict ssh-ed25519 AAAA...rest-of-public-key deploy@ci-2026-10Si gestionas accesos con caducidad en muchas claves, los certificados de OpenSSH son otra opción (firma con ssh-keygen -s, fija la validez con -V y restringe con force-command o source-address). Para un montaje pequeño, las opciones en la línea de clave bastan para empezar.
Cómo comprobar tu propio entorno
Estas comprobaciones solo leen la configuración y los registros de servidores que gestionas tú. Cuando cambies la configuración, deja abierta una sesión SSH, prueba una conexión nueva desde otro terminal y cierra la sesión antigua solo cuando sepas que no te has quedado fuera.
1. Lista las claves registradas
# Show the fingerprint and comment of each key registered for root and each user
sudo ssh-keygen -lf /root/.ssh/authorized_keys
sudo ssh-keygen -lf /home/deploy/.ssh/authorized_keysCada clave imprime una línea como 256 SHA256:... deploy@ci (ED25519). Usa el comentario (la parte deploy@ci del final) para localizar las claves de origen desconocido, las de entornos efímeros con los que ya has terminado y las registradas en root. El nombre de archivo antiguo authorized_keys2 también se lee por defecto, así que, si existe, revísalo igual.
2. Comprueba la configuración que sshd usa de verdad
# Check the syntax, then print the settings actually in effect
sudo sshd -t
sudo sshd -T | grep -iE 'permitrootlogin|authorizedkeysfile|pubkeyauthentication|passwordauthentication'sshd -T muestra los valores efectivos tras leer los archivos de configuración. Si permitrootlogin es yes o prohibit-password, cualquiera con una clave puede entrar como root directamente. Si authorizedkeysfile apunta a un sitio distinto del predeterminado, añade esa ubicación al paso 1.
3. Mira qué claves se usan de verdad
Tras un inicio de sesión correcto con clave, sshd suele registrar una línea como Accepted publickey for deploy from 192.0.2.10 port 50022 ssh2: ED25519 SHA256:.... Léela en /var/log/auth.log en Debian y Ubuntu, en /var/log/secure en sistemas de la familia Red Hat, o con journalctl -u ssh en hosts con systemd (-u sshd en la familia Red Hat).
Cruzar estas huellas con la lista del paso 1 muestra qué claves no se usan. El informe del NIST sobre gestión de claves SSH (NISTIR 7966) sugiere vigilar los registros durante unos 6 a 12 meses antes de quitar las claves sin uso, para no quitar por error claves que se usan muy poco. Las claves que sabes que ya no sirven, como la de un entorno efímero, pueden quitarse enseguida.
4. Revisa el lado efímero
- ¿Hay una clave privada que llega a producción guardada en los secretos de CI o en el
~/.sshde un pod de GPU? - ¿Te conectas al entorno efímero con
ssh -A(reenvío de agente) de camino a producción? - ¿Se comparte una misma clave entre varios sistemas de CI o servidores?
Errores frecuentes y cómo corregirlos
- Borrar la clave privada en el lado efímero y quedarse ahí: si la clave privada ya se copió, sigue funcionando mientras la clave pública siga en el servidor. Quita siempre la línea correspondiente del
authorized_keysde producción. - Ejecutar tal cual el comando del cliente (la variable de entorno
SSH_ORIGINAL_COMMAND) dentro del script que llamacommand=: es lo mismo que no tener restricción. Usa el valor solo cuando coincida exactamente con una de un conjunto fijo de opciones. - Pasar a un usuario no-root pero permitirle sudo sin restricciones: si ese usuario puede hacer sudo de cualquier cosa sin contraseña, en la práctica es root. Si hace falta sudo, limítalo a un comando en sudoers.
- Entrar en un entorno efímero con
ssh -A: cualquiera con privilegios suficientes allí puede usar las claves cargadas en tu agente local a través de la conexión reenviada (el manual de OpenSSH lo advierte). Si necesitas pasar por un host, usassh -J(conexión a través de un host de salto). - Dejar claves sin comentario: en una revisión no sabrás de dónde vienen. Añade el propósito y la fecha al crearlas, p. ej.
ssh-keygen -C "deploy@ci-2026-10". - Compartir una clave entre varias personas o entornos: NISTIR 7966 también pide prohibir compartir claves privadas o copiarlas a otros sistemas. No puedes rastrear quién la usó y dónde, y una sola fuga obliga a sustituirla en todas partes.
Un caso real: las claves SSH estaban entre lo que recogió una herramienta de CI comprometida
En marzo de 2026 apareció una versión maliciosa del escáner de vulnerabilidades de código abierto Trivy, y se reescribieron sus etiquetas de GitHub Actions. Según el desarrollador, Aqua Security, el código malicioso recogía credenciales de los runners de CI y las enviaba fuera, y entre sus objetivos estaban las claves privadas de ~/.ssh, authorized_keys, known_hosts y las claves de host SSH. Aqua pide a quien pueda haber ejecutado una versión afectada que trate como expuesto todo secreto que ese pipeline pudiera leer, claves SSH incluidas, y lo rote de inmediato (detalles: el compromiso de la cadena de suministro de Trivy (marzo de 2026), en inglés).
En el caso de Codecov de 2021, un script que se ejecutaba en CI también fue modificado en su origen y, durante unos dos meses, se enviaron fuera secretos de los entornos de CI de los clientes (→ el ataque a la cadena de suministro de Codecov).
Ambos muestran que las claves que hay dentro de CI pueden robarse aunque tu propio código esté bien. Si una clave robada está registrada en el root de producción, en ese momento se entrega producción. Con un usuario no-root dedicado, command= más restrict, y el origen acotado con from=, la misma clave filtrada solo puede hacer el único trabajo que definiste, y se rechazan las conexiones desde cualquier otro sitio.
Qué hace este sitio
Este sitio usa una clave dedicada por servidor y nunca reutiliza claves. Los despliegues aterrizan en un usuario no-root, creado a propósito, y producción es unidireccional — "recibe, no sale a buscar" (→ producción solo recibe). No deja claves permanentes desde el cómputo efímero hacia producción; se conecta solo cuando hace falta, con privilegio mínimo. La razón es justo la de este artículo: mantener pequeño por adelantado lo que puede hacer cada clave, de modo que un compromiso no pueda extenderse a todo.
Sigue leyendo
- Una vulnerabilidad nueva: OpenSSH 10.6p2 corrige una elusión de la autenticación por contraseña en sshd en Linux con musl sin PAM (CVE-2026-123158)
- Lo básico: la lista de comprobación de la base de seguridad (privilegio mínimo, proteger las cuentas de administrador)
- Operaciones: separa las claves para minimizar el daño / producción solo recibe · el manual de respuesta a vulnerabilidades
- Casos: el compromiso de la cadena de suministro de Trivy (claves SSH en el punto de mira en CI) (en inglés) · el ataque a la cadena de suministro de Codecov
- Incidente: una clave robada vía RCE y facturada por fraude · la fuga de datos de Benesse (privilegio excesivo y abuso interno)
Fuentes
- Manual de OpenBSD, "sshd(8)", AUTHORIZED_KEYS FILE FORMAT (command, restrict, from, expiry-time, SSH_ORIGINAL_COMMAND): man.openbsd.org
- Manual de OpenBSD, "sshd_config(5)", PermitRootLogin y AuthorizedKeysFile: man.openbsd.org
- Manual de OpenBSD, "ssh(1)", -A (advertencia sobre el reenvío de agente) y -J: man.openbsd.org
- Manual de OpenBSD, "ssh-keygen(1)", -l, -C y opciones de certificado: man.openbsd.org
- Notas de versión de OpenSSH 7.2 (se añade restrict): openssh.org
- Notas de versión de OpenSSH 7.7 (se añade expiry-time): openssh.org
- NIST, "NISTIR 7966: Security of Interactive and Automated Access Management Using Secure Shell (SSH)" (octubre de 2015): csrc.nist.gov
- Wikibooks, "OpenSSH/Logging and Troubleshooting" (huellas de clave en las líneas de registro de inicio de sesión correcto): en.wikibooks.org
- Aqua Security, "Trivy Supply Chain Attack: What You Need to Know" (2026): aquasec.com
- Codecov, "Bash Uploader Security Update" (2021): about.codecov.io