Cómo restringir una clave SSH a un solo comando o a una dirección

Pon opciones delante de la clave en authorized_keys: restrict,command="/usr/local/bin/backup",from="203.0.113.10" ssh-ed25519 AAAA.... Con esa línea, la clave ejecuta siempre ese único comando, pida lo que pida el cliente, solo desde esa dirección, sin terminal y sin reenvío. Los límites se aplican solo a esa clave, no a otras claves ni a contraseñas de la cuenta.

Actualizada el . Todas las opciones, el script envoltorio, rrsync y SFTP se probaron con OpenSSH 10.0 en Debian 13, en un contenedor desechable.

Las opciones

OpciónQué haceQué ve el otro lado
restrictSin reenvío de ningún tipo, sin terminal, sin ~/.ssh/rcPTY allocation request failed on channel 0
command="..."Ejecuta siempre este comandoLa salida de ese comando
from="..."Solo desde estas direcciones o nombresPermission denied
permitopen="host:port"Limita ssh -L y -W a este destinostdio forwarding failed o open failed
permitlisten="[host:]port"Limita ssh -R a este puertoremote port forwarding failed

Empieza con restrict y añade solo lo que la clave necesita. Además cubre cualquier restricción que OpenSSH añada en el futuro, algo que una lista de no-pty,no-port-forwarding y similares no hace. Las opciones van en una sola línea, separadas por comas y sin espacios entre ellas.

Solo un comando

restrict,command="/usr/local/bin/backup" ssh-ed25519 AAAA... backup-job

Ahora tanto ssh user@server como ssh user@server rm -rf / ejecutan la copia de seguridad y nada más.

Unos pocos comandos permitidos, con SSH_ORIGINAL_COMMAND

Lo que pidió el cliente está en la variable SSH_ORIGINAL_COMMAND, y un script pequeño puede comprobarlo:

#!/bin/sh
# /usr/local/bin/ssh-allow
case "$SSH_ORIGINAL_COMMAND" in
  "uptime"|"df -h")
    exec $SSH_ORIGINAL_COMMAND
    ;;
  *)
    echo "Not allowed: $SSH_ORIGINAL_COMMAND" >&2
    exit 1
    ;;
esac
restrict,command="/usr/local/bin/ssh-allow" ssh-ed25519 AAAA... monitoring

En nuestra prueba, ssh user@server uptime funcionó, y ssh user@server "uptime; id" respondió Not allowed: uptime; id. Compara cadenas completas, como arriba. Un patrón como uptime* deja pasar cualquier cosa que empiece por esa palabra, y nunca pases la variable a sh -c ni a eval.

Elige también con cuidado los comandos permitidos. Un comando que pueda escribir archivos o abrir un shell, como un editor, anula la restricción.

Limitar por dirección

from="203.0.113.10,198.51.100.0/24" ssh-ed25519 AAAA... office

Funcionan tanto los patrones como los rangos CIDR, y ! excluye una dirección. Usa direcciones IP en lugar de nombres de host, ya que un nombre de host depende del DNS inverso. Cuando la dirección no coincide, el cliente ve Permission denied y el registro del servidor lo explica:

Authentication tried for user with correct key but not from a permitted host (host=198.51.100.7, ip=198.51.100.7, required=203.0.113.10).

Una clave solo para un túnel

restrict,port-forwarding,permitopen="10.0.1.10:5432",command="/usr/sbin/nologin" ssh-ed25519 AAAA... db-tunnel

Esta clave puede llegar a la base de datos en esa dirección y ese puerto. Un reenvío a otro puerto falla, y un comando solo recibe This account is currently not available. Para usarla, añade -N para que ssh no pida ningún comando:

ssh -N -L 5432:10.0.1.10:5432 user@server

Un nombre de host en permitopen debe coincidir exactamente con lo que envía el cliente, sin ninguna resolución. localhost y 127.0.0.1 cuentan como distintos.

Ojo: esto sigue permitiendo ssh -R

port-forwarding activa ambas direcciones, y permitopen limita solo una. En nuestra prueba, la clave de arriba aún podía abrir un reenvío remoto con ssh -R. permitlisten="none" no es una salida, porque entonces la línea no es válida y la clave deja de funcionar por completo. Haz una de estas dos cosas:

  • Pon en permitlisten un único puerto que nada use, por ejemplo permitlisten="127.0.0.1:9". Comprobamos que un reenvío en cualquier otro puerto es rechazado.
  • Limita la cuenta en la configuración del servidor, lo que se aplica a todas sus claves:
# /etc/ssh/sshd_config.d/10-tunnel.conf
Match User tunnel
    AllowTcpForwarding local

Para saber qué hacen -L, -R y -D, consulta túneles SSH locales, remotos y dinámicos.

Claves para transferir archivos

restrict por sí solo no impide la transferencia de archivos. En nuestra prueba, sftp seguía funcionando con él, porque SFTP no necesita terminal ni reenvío. Para permitir SFTP y nada más:

restrict,command="internal-sftp" ssh-ed25519 AAAA... files

Para permitir rsync en una sola carpeta, usa rrsync, que viene con rsync. En Debian está en /usr/bin/rrsync, y otros sistemas pueden variar. Con -ro permite solo lectura, y confirmamos que las subidas fallan:

restrict,command="/usr/bin/rrsync -ro /srv/backup" ssh-ed25519 AAAA... backup-read

Lo que estas opciones no hacen

  • Solo atan a esa clave. Otra clave, o una contraseña, en la misma cuenta no tiene ninguno de estos límites. Da a cada tarea su propia cuenta cuando puedas, y desactiva las contraseñas.
  • El usuario puede editar el archivo. Si la cuenta puede escribir en su propio authorized_keys, un comando que escriba archivos puede quitar los límites. Para una frontera firme, deja el archivo como propiedad de root. Consulta authorized_keys explicado.
  • El orden de las líneas importa. Si la misma clave aparece también en una línea sin restricciones más arriba, gana esa línea.

Preguntas

¿command= en authorized_keys bloquea SFTP?

Sí, si command= apunta a algo distinto de internal-sftp, porque SFTP ejecuta entonces ese comando en su lugar. restrict por sí solo no bloquea SFTP.

¿Qué es SSH_ORIGINAL_COMMAND?

Una variable de entorno con el comando que pidió el cliente cuando se ejecuta un comando forzado. Un script puede comprobarla y permitir solo unos pocos comandos exactos.

¿Puedo usar un nombre de host en from=?

Sí, pero depende del DNS inverso, que la otra parte puede controlar. Las direcciones IP y los rangos CIDR son más seguros.

Fuentes

Más en Proteger un servidor

Todas las guías