Guías de seguridad

Privilegio mínimo en claves SSH: mantén las claves de root de producción fuera de entornos efímeros que pueden ser comprometidos

Una clave de root desde un entorno efímero (pod de GPU, runner de CI) da root en producción si se filtra. Usa un usuario no-root y claves de un solo comando.

Publicado 2026-06-11 Actualizado 2026-10-06 14 min de lectura
En esta página

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.

Mantén pequeño desde el principio lo que puede hacer una clave. Eso cambia el daño cuando se filtra.

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.

root→todo
una clave filtrada da el control total de producción
desechable
los entornos efímeros omiten el endurecimiento
reutilizada
una fuga expone cada servidor
priv mínimo
limita una clave a una operación y el daño también queda limitado

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".

1

Quita la clave de root del entorno efímero

Borra de ~/.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.
2

Aunque se necesite de nuevo, no vuelvas a root

Cuando reregistres para trabajo en producción, crea un usuario no-root dedicado y registra la clave en él. Aunque se filtre, el alcance queda limitado a los privilegios de ese usuario.
3

Restringe la clave a una sola operación

Añade 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@ci

restrict 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ónQué restringeCuándo usarla
command="..."Iniciar sesión con la clave ejecuta solo el comando especificado; se ignora el comando que haya enviado el clienteClaves que deben hacer un único trabajo fijo, como hacer pull de un despliegue o sacar una copia de seguridad
restrictDeshabilita 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 listaCI 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-10

Si 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_keys

Cada 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 ~/.ssh de 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_keys de producción.
  • Ejecutar tal cual el comando del cliente (la variable de entorno SSH_ORIGINAL_COMMAND) dentro del script que llama command=: 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, usa ssh -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

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

FAQ

Q¿Por qué es peligroso poner una clave de root en un entorno efímero?
A

Los entornos efímeros como los pods de GPU-cloud o los runners de CI son desechables, así que a menudo se omite el endurecimiento y pueden comprometerse. Pon ahí una clave de root a producción, y en el momento en que ese entorno se compromete, el atacante obtiene root en producción junto con la clave. Trata los entornos efímeros como no confiables y mantén las claves de root de producción fuera de ellos.

Q¿Y si aun así necesito llegar a producción desde un entorno efímero?
A

No vuelvas a root. Crea un usuario no-root dedicado y registra una clave que limite lo que puede hacer a una sola operación. En authorized_keys, una clave restringida a un comando y con la marca restrict significa que incluso un inicio de sesión exitoso solo puede ejecutar ese único comando. Una fuga queda entonces contenida a esa única operación.

Q¿Cuál es el principio clave para la gestión de claves?
A

Trata una clave reutilizada como tu activo más crítico y nunca construyas un montaje en el que una fuga exponga todo. Separa las claves por servidor y por propósito, y quita las claves de authorized_keys cuando ya no se necesiten. Eso evita que una sola fuga se extienda a todo.

Q¿Cómo compruebo qué claves SSH están registradas en un servidor?
A

Ejecuta ssh-keygen -lf sobre el ~/.ssh/authorized_keys de cada usuario para listar todas las claves registradas con su huella (SHA256:...) y su comentario. Revisa todas las cuentas, root incluida, y localiza las claves cuyo origen no sepas explicar y las de entornos efímeros que ya no usas. Para ver qué claves se usan de verdad, cruza esas huellas con las líneas de inicio de sesión correcto que sshd escribe en su registro.

Q¿Qué valor debe tener PermitRootLogin?
A

El valor por defecto de OpenSSH es prohibit-password: root no puede entrar con contraseña, pero cualquiera con una clave puede entrar como root directamente. Si no necesitas inicios de sesión directos como root, ponlo en no y trabaja desde un usuario no-root. Si algo como una copia de seguridad necesita de verdad una clave de root, forced-commands-only permite root solo a las claves que llevan la opción command="...".

Q¿Cómo despliego por SSH desde CI o GitHub Actions de forma segura?
A

Los runners de CI son el ejemplo típico de entorno efímero, y hay casos reales en los que una herramienta usada en CI fue comprometida y se recogieron secretos, claves SSH incluidas. Registra la clave de despliegue en un usuario no-root dedicado y limítala a la única acción de pull/despliegue con command="..." y restrict. Si la IP de origen es fija, acótala con from="...". Usa una clave distinta por pipeline y por entorno, y cuando se anuncie un compromiso, quita primero las claves antiguas y después registra las nuevas.