¿Cómo crear un usuario SSH restringido para el reenvío de puertos?

106

ændrük sugirió una conexión inversa para obtener una conexión SSH fácil con otra persona (para ayuda remota). Para que eso funcione, se necesita un usuario adicional para aceptar la conexión. Este usuario debe poder reenviar su puerto a través del servidor (el servidor actúa como proxy).

¿Cómo creo un usuario restringido que no puede hacer nada más que lo descrito anteriormente?

El nuevo usuario no debe poder:

  • ejecutar comandos de shell
  • acceder a archivos o subir archivos al servidor
  • usar el servidor como proxy (por ejemplo, webproxy)
  • acceder a servicios locales que de otro modo no serían accesibles públicamente debido a un firewall
  • matar al servidor

Resumido, ¿cómo creo un usuario SSH restringido que solo puede conectarse al servidor SSH sin privilegios, para poder conectarme a través de esa conexión con su computadora?

Lekensteyn
fuente

Respuestas:

166

TL; DR: vaya al final de la respuesta, "Aplicación de las restricciones"

Agregar un usuario restringido consta de dos partes: 1. Crear el usuario 2. Configurar el demonio SSH (sshd)

Configurando sshd

El mejor lugar para conocer las posibilidades de SSH es leer las páginas del manual relacionadas:

¿Dónde puede el cliente SSH realizar acciones?

Antes de poder restringir algo, debe conocer las características de SSH. Escupir a través de las páginas del manual produce:

  • Ejecución de comandos de shell
  • Carga de archivos a través de sftp
  • Reenvío de puertos
    • El cliente reenvía un puerto (no) usado al servidor
    • El servidor reenvía su puerto al cliente
    • El servidor reenvía un puerto de otro host al cliente (proxy-ish)
  • Reenvío X11 (reenvío de pantalla)
  • Reenvío de agente de autenticación
  • Reenvío de un dispositivo de túnel

Desde la sección Autenticación de la página del manual de sshd (8) :

Si el cliente se autentica con éxito, se ingresa un diálogo para preparar la sesión. En este momento, el cliente puede solicitar cosas como asignar un pseudo-tty, reenviar conexiones X11, reenviar conexiones TCP o reenviar la conexión del agente de autenticación a través del canal seguro.

Después de esto, el cliente solicita un shell o la ejecución de un comando . Los lados luego ingresan al modo de sesión. En este modo, cualquier lado puede enviar datos en cualquier momento, y dichos datos se envían a / desde el shell o comando en el lado del servidor, y el terminal de usuario en el lado del cliente.

Opciones para restringir las funciones SSH

Los archivos y sus opciones que alteran el comportamiento son:

  • ~/.ssh/authorized_keys - contiene claves que pueden conectarse, a las que se pueden dar opciones:
    • command="command"- Se ignora el comando proporcionado por el usuario (si lo hay). Tenga en cuenta que el cliente puede especificar el reenvío TCP y / o X11 a menos que estén explícitamente prohibidos . Tenga en cuenta que esta opción se aplica a la ejecución de shell, comando o subsistema.
    • no-agent-forwarding - Prohíbe el reenvío del agente de autenticación cuando esta clave se usa para la autenticación.
    • no-port-forwarding - Prohíbe el reenvío de TCP cuando esta clave se usa para la autenticación
    • no-X11-forwarding - "Prohibe el reenvío de X11 cuando esta clave se utiliza para la autenticación".
    • permitopen="host:port" - Limite el reenvío de puerto local 'ssh -L' de modo que solo pueda conectarse al host y puerto especificados.
  • ~/.ssh/environment- Este archivo se lee en el entorno al iniciar sesión (si existe). El procesamiento del entorno está deshabilitado de forma predeterminada y se controla mediante la opción PermitUserEnvironment
  • ~/.ssh/rc - Contiene rutinas de inicialización que se ejecutarán antes de que el directorio de inicio del usuario sea accesible.
  • /etc/ssh/sshd_config - el archivo de configuración de todo el sistema
    • AllowAgentForwarding - Especifica si se permite el reenvío ssh-agent (1).
    • AllowTcpForwarding
    • ForceCommand - "Fuerza la ejecución del comando especificado por ForceCommand, ignorando cualquier comando proporcionado por el cliente y ~ / .ssh / rc si está presente. El comando se invoca utilizando el shell de inicio de sesión del usuario con la opción -c".
    • GatewayPorts - "Especifica si los hosts remotos pueden conectarse a los puertos reenviados para el cliente. De forma predeterminada, sshd (8) enlaza los reenvíos de puertos remotos a la dirección de bucle invertido. Esto evita que otros hosts remotos se conecten a los puertos reenviados. GatewayPorts se puede usar para especificar ese sshd debería permitir que los reenvíos de puertos remotos se unan a direcciones que no son de bucle invertido, permitiendo así que otros hosts se conecten ".
    • PermitOpen:

      Especifica los destinos a los que se permite el reenvío de puertos TCP. La especificación de reenvío debe ser una de las siguientes formas:

      PermitOpen host:port
      PermitOpen IPv4_addr:port
      PermitOpen [IPv6_addr]:port
      

      Se pueden especificar varios reenvíos separándolos con espacios en blanco. Se puede usar un argumento de 'cualquiera' para eliminar todas las restricciones y permitir cualquier solicitud de reenvío. Por defecto, todas las solicitudes de reenvío de puertos están permitidas.

    • PermitTunnel- Especifica si se permite el reenvío de dispositivos tun (4). El valor predeterminado es 'no'
    • X11Forwarding- Especifica si se permite el reenvío X11. El valor predeterminado es 'no'

Aplicando las restricciones

La modificación del archivo de configuración de todo el sistema /etc/ssh/sshd_configpermite que la configuración se aplique incluso si se aplica la autenticación basada en contraseña o si las restricciones ~/.ssh/authorized_keysse eliminan accidentalmente. Si ha modificado los valores predeterminados globales, debe descomentar las opciones en consecuencia.

Match User limited-user
   #AllowTcpForwarding yes
   #X11Forwarding no
   #PermitTunnel no
   #GatewayPorts no
   AllowAgentForwarding no
   PermitOpen localhost:62222
   ForceCommand echo 'This account can only be used for [reason]'

Ahora agregue un usuario:

sudo useradd -m limited-user

La opción ForceCommandse puede omitir si el shell está configurado en un no-shell como /bin/false(o /bin/true) ya /bin/false -c [command]que no hará nada.

Ahora el cliente solo puede conectarse al puerto 62222 en la dirección de bucle invertido del servidor a través de SSH (no escuchará en la dirección IP pública)

La desactivación AllowTcpForwardingtambién prohibiría el uso de -R, por lo tanto, anularía el uso de una cuenta tan restringida para reenviar un solo puerto. PermitOpen localhost:62222asume que el puerto 62222 en el servidor nunca está en uso porque el cliente puede conectarse felizmente y escucharlo también.

Si se permite el reenvío TCP en la configuración de todo el sistema y la autenticación deshabilitada basada en contraseña, también puede usar la configuración por clave. Edite ~/.ssh/authorized_keysy agregue las siguientes opciones antes de ssh-(con un espacio entre las opciones y ssh-):

command="echo 'This account can only be used for [reason]'",no-agent-forwarding,no-X11-forwarding,permitopen="localhost:62222"

Verificar

Para asegurarse de que funciona como se esperaba, es necesario ejecutar algunos casos de prueba. En los comandos a continuación, hostdebe reemplazarse por el inicio de sesión real si no está configurado ~/.ssh/config. Detrás del comando, se muestra un comando que debe ejecutarse en el cliente o el servidor (como se especifica).

# connection closed:
ssh host
# connection closed (/bin/date is not executed):
ssh host /bin/date
# administratively prohibited (2x):
ssh host -N -D 62222 # client: curl -I --socks5 localhost:62222 example.com
ssh host -N -L 8080:example.com:80 # client: curl -I localhost:8080
sftp host
# should be possible because the client should forward his SSH server
ssh host -N -R 8080:example.com:80 # server: curl -I localhost:8080
# This works, it forwards the client SSH to the server
ssh host -N -R 62222:localhost:22
# unfortunately, the client can listen on that port too. Not a big issue
ssh host -N -L 1234:localhost:62222

Conclusión

Lista de verificación: el usuario SSH no debería poder:

  • ejecutar comandos de shell - hecho
  • acceder a archivos o subir archivos al servidor - hecho
  • usar el servidor como proxy (por ejemplo, webproxy) - hecho
  • acceder a servicios locales que de otro modo no serían accesibles públicamente debido a un firewall; en parte , el cliente no puede acceder a otros puertos que no sean 62222, pero puede escuchar y conectarse al puerto 62222 en el servidor
  • matar el servidor: listo (tenga en cuenta que estas comprobaciones se limitan al servidor SSH. Si tiene otro servicio vulnerable en la máquina, podría permitir que un posible atacante ejecute comandos, mate el servidor, etc.)
Lekensteyn
fuente
3
Para que esto funcione, la cuenta agregada por useradddebe estar desbloqueada. Eso se puede hacer reemplazando la contraseña /etc/shadowpor un asterico ( *) o estableciendo una contraseña usando sudo passwd limited-user.
Lekensteyn
2
SFTP funciona porque el servidor SSH ejecuta el sftp-servercomando. Con ForceCommandeste comando ya no se ejecutará.
Lekensteyn
2
También es útil la from=opción en authorized_keys. Le permite limitar el uso de la clave a fuentes con una dirección IP o nombre de host en particular. Ejemplo, from="1.1.1.1"o from="10.0.0.?,*.example.com". Consulte la sección "FORMATO DE ARCHIVO DE LLAVES AUTORIZADAS" en la página de manual ( linux.die.net/man/8/sshd ) para ver todas las opciones de claves autorizadas.
Donn Lee
2
"AllowTcpForwarding local" prohíbe el reenvío remoto. (No sé si existió cuando se escribió esta respuesta.)
Simon Sapin el
1
Tenga en cuenta que esto se rompe por las conexiones ssh persistentes, por lo que no debería tener algo como Host * ControlMaster autoen su ~/.ssh/configarchivo al conectarse ssh -N. Si lo necesita, usessh -N -o ControlMaster=no
nh2
3

Estoy seguro de que hay muchas soluciones para esto, y muchas más robustas que la que propongo. Sin embargo, esto puede ser suficiente para sus necesidades. Para hacerlo, supongo que el usuario puede realizar la autenticación basada en la clave ssh (la masilla o cualquier unix ssh deberían admitir esto).

  • Agregue un usuario como lo haría normalmente ('adduser' o cualquier otra herramienta)

  • Cree los usuarios .ssh dir y .ssh / Authorized_keys

your_user $ sudo -Hu ssh_forwarder /bin/bash

ssh_forwarder $ cd ~
ssh_forwarder $ mkdir .ssh
ssh_forwarder $ ( umask 066 && cat > .ssh/authorized_keys ) <<EOF
no-agent-forwarding,no-X11-forwarding,command="read a; exit" ssh-rsa AAAB3NzaC1y....2cD/VN3NtHw== smoser@brickies
EOF
  • Desactiva el acceso con contraseña a esta cuenta.
your_user $ sudo usermod --lock ssh_forwarder

Ahora, la única forma en que el usuario puede ingresar a su sistema es mediante el acceso a la clave ssh adecuada, y ssh ejecutará "/ bin / bash -c 'read a'" para ellos, sin importar lo que intenten ejecutar. 'leer a' simplemente leerá hasta una nueva línea, y luego el shell saldrá, por lo que el usuario solo tiene que presionar 'enter' para cerrar la conexión.

Hay muchas otras cosas que podrías hacer en 'command ='. Consulte man authorized_keysy busque 'comando' para obtener más información.

Si no le gusta el hecho de que presionar enter mata la conexión, puede usar algo como lo siguiente para la entrada 'command =':

command="f=./.fifo.$$ && mkfifo $f && trap \"rm -f $f\" EXIT && read a <$f && echo $a; exit;"

Esto solo crea un fifo temporal en el directorio de inicio de los usuarios y luego trata de leerlo. No se escribirá nada en ese archivo, por lo que esto se bloqueará indefinidamente. Además, si desea terminar esa conexión a la fuerza, puede hacer algo como:

 your_user$ echo GOODBYE | sudo tee ~ssh_forwarder/.fifo.*

Esto debería usar muy pocos recursos y nada debería salir mal en ese script que no terminaría en la terminación del shell.

sleep 1h; echo You have been here too long. Good bye.

No vi a mano cómo podría permitir que el usuario remita hacia adelante ( ssh -R) pero limite ( ssh -L). Quizás podría usarse 'permitopen'. Buscar en Google no fue muy útil. Parecería que algo como 'reenvío sin puerto, permitremoteopen = 10001' sería útil para permitir ssh -R 6901:localhost:6901.

Esta es una solucion. Definitivamente se puede mejorar, y cualquier apertura de puertos remotos debe ser examinada. Si mi objetivo era permitir que mi abuela se conectara a mi LAN para poder usar vnc para ver su pantalla, y el acceso a esas teclas se limitara a ella, personalmente me sentiría razonablemente seguro. Si esto fuera para una empresa, sería necesaria una investigación más exhaustiva. Una cosa a tener en cuenta es ssh -Nque no solicita un shell en absoluto, por lo que el código 'command =' no se ejecuta.

Otros mecanismos, posiblemente más seguros, pueden incluir la creación de un shell personalizado para el usuario e incluso bloquearlo con apparmour.

smoser
fuente
3
Esto parece muy hack, ni siquiera es necesario tener un shell porque no se necesita ejecutar ningún comando. Vea la página del manual dessh para la -Nopción. Debe haber una forma más limpia de lograr esto.
Lekensteyn
Es verdad. Espero ver una solución más limpia también. Creo que las claves autorizadas junto con el reenvío sin puerto sería una buena solución si hubiera una opción 'permitremoteopen'.
smoser
Investigué un poco (ver arriba). Como ssh -Nno ejecuta un comando en absoluto, no hay problema que command="something"cierre la sesión.
Lekensteyn
¿Me estoy perdiendo de algo? Parecía que eso chsh ssh_forwarder -s /bin/false es todo lo que se necesita.
Typelogic