¿Cómo restringir a los usuarios de SSH a un conjunto predefinido de comandos después de iniciar sesión?

85

Esta es una idea de seguridad. Nuestros empleados tendrán acceso a algunos comandos en un servidor Linux, pero no a todos. Por ejemplo, deberán tener la posibilidad de acceder a un archivo de registro ( less logfile) o iniciar diferentes comandos ( shutdown.sh/ run.sh).

Información de contexto:

Todos los empleados acceden al servidor con el mismo nombre de usuario: nuestro producto se ejecuta con permisos de usuario "normales", no se necesita "instalación". Simplemente descomprímalo en su directorio de usuario y ejecútelo. Gestionamos varios servidores donde está "instalada" nuestra aplicación. En cada máquina hay un usuario johndoe. A veces, nuestros empleados necesitan acceder a la aplicación en la línea de comandos para acceder y comprobar los archivos de registro o para reiniciar la aplicación manualmente. Solo algunas personas tendrán acceso completo a la línea de comandos.

Estamos usando la autenticación ppk en el servidor.

Sería genial si employee1 solo pudiera acceder al archivo de registro y employee2 también pudiera hacer X, etc.

Solución: como solución, usaré la commandopción como se indica en la respuesta aceptada . Haré mi propio pequeño script de shell que será el único archivo que se puede ejecutar para algunos empleados. El script ofrecerá varios comandos que se pueden ejecutar, pero ningún otro. Usaré los siguientes parámetros authorized_keyscomo se indica aquí :

command="/bin/myscript.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding,no-pty
ssh-dss AAAAB3....o9M9qz4xqGCqGXoJw= user@host

Esto es suficiente seguridad para nosotros. ¡Gracias, comunidad!

Marcel
fuente
¿No es suficiente la seguridad basada en permisos de ACL de Linux estándar? ¿Qué funciones adicionales necesitas?
James Brady
22
Es una idea terrible, Marcel.
Vinko Vrsalovic
13
@Vinko, @PEZ: He agregado información de fondo. En lugar de decir "idea estúpida", podría aportar comentarios valiosos. ¿Cuál es en su opinión una mejor idea?
Marcel
8
Todavía no veo ninguna excusa para que varios usuarios compartan el mismo nombre de usuario.
Eldelshell
3
¿"Mi propio pequeño script de shell"? Suena bastante peligroso. A menos que sea un experto, probablemente haya muchas formas de escapar de él al caparazón completo. Prefiero confiar en un programa bien escrito, depurado y mantenido (se han mencionado varios).
bortzmeyer

Respuestas:

68

También puede restringir las claves a los comandos permitidos (en el archivo Authorized_keys).

Es decir, el usuario no iniciaría sesión a través de ssh y luego tendría un conjunto restringido de comandos, sino que solo se le permitiría ejecutar esos comandos a través de ssh (por ejemplo, "ssh somehost bin / showlogfile")

HD.
fuente
Eso parece interesante. ¿Es posible definir varios comandos?
Marcel
12
Este artículo le brinda algunas opciones para múltiples comandos usando el archivo Authorized_keys
Bash
@ rd834 ...: Muchas gracias. Creo que esto me dio una "buena" solución ... (agregado a la pregunta). Aceptaré esta respuesta como "correcta".
Marcel
11
El libro SSH de O'Reilly tiene una excelente explicación de cómo hacer esto, incluyendo permitir múltiples comandos configurando un script que mira la variable de entorno SSH_ORIGINAL_COMMAND. oreilly.com/catalog/sshtdg/chapter/ch08.html
Alex Dupuy
5
esta respuesta por defecto del servidor tiene un buen consejo sobre cómo permitir múltiples comandos usando la variable SSH_ORIGINAL_COMMAND exportada.
MattBianco
32

sshsigue la rshtradición al usar el programa de shell del usuario del archivo de contraseña para ejecutar comandos.

Esto significa que podemos resolver esto sin involucrar la sshconfiguración de ninguna manera.

Si no desea que el usuario pueda tener acceso al shell, simplemente reemplace el shell de ese usuario con un script. Si mira /etc/passwd, verá que hay un campo que asigna un intérprete de comandos de shell a cada usuario. El script se utiliza como shell tanto para su inicio de sesión interactivo ssh user@host como para los comandos ssh user@host command arg ....

Aquí hay un ejemplo. Creé un usuario foocuyo shell es un script. El script imprime el mensaje my arguments are:seguido de sus argumentos (cada uno en una línea separada y entre paréntesis angulares) y termina. En el caso de registro, no hay argumentos. Esto es lo que sucede:

webserver:~# ssh foo@localhost
foo@localhost's password:
Linux webserver [ snip ]
[ snip ]
my arguments are:
Connection to localhost closed.

Si el usuario intenta ejecutar un comando, se verá así:

webserver:~# ssh foo@localhost cat /etc/passwd
foo@localhost's password:
my arguments are:
<-c>
<cat /etc/passwd>

Nuestro "caparazón" recibe un -c invocación de estilo, con el comando completo como un argumento, de la misma manera que /bin/shlo recibiría.

Entonces, como puede ver, lo que podemos hacer ahora es desarrollar más el script para que reconozca el caso cuando ha sido invocado con un -cargumento, y luego analiza la cadena (digamos, por coincidencia de patrones). Las cadenas permitidas se pueden pasar al shell real invocando de forma recursiva /bin/bash -c <string>. El caso de rechazo puede imprimir un mensaje de error y terminar (incluido el caso cuando-c falta).

Tienes que tener cuidado al escribir esto. Recomiendo escribir solo coincidencias positivas que permitan solo cosas muy específicas y no permitan todo lo demás.

Nota: si es así root, aún puede iniciar sesión en esta cuenta anulando el shell en elsu comando, así su -s /bin/bash foo. (Shell sustituto de su elección). No root no puede hacer esto.

Aquí hay un script de ejemplo: restrinja al usuario a usar solo sshpara gitacceder a los repositorios bajo /git.

#!/bin/sh

if [ $# -ne 2 ] || [ "$1" != "-c" ] ; then
  printf "interactive login not permitted\n"
  exit 1
fi

set -- $2

if [ $# != 2 ] ; then
  printf "wrong number of arguments\n"
  exit 1
fi

case "$1" in
  ( git-upload-pack | git-receive-pack )
    ;; # continue execution
  ( * )
    printf "command not allowed\n"
    exit 1
    ;;
esac

# Canonicalize the path name: we don't want escape out of
# git via ../ path components.

gitpath=$(readlink -f "$2")  # GNU Coreutils specific

case "$gitpath" in
  ( /git/* )
     ;; # continue execution
  ( * )
    printf "access denied outside of /git\n"
    exit 1
    ;;
esac

if ! [ -e "$gitpath" ] ; then
   printf "that git repo doesn't exist\n"
   exit 1
fi

"$1" "$gitpath"

Por supuesto, estamos confiando en que estos programas de Git git-upload-packy git-receive-packno tienen agujeros o trampillas de evacuación que le darán a los usuarios el acceso al sistema.

Eso es inherente a este tipo de esquema de restricción. El usuario está autenticado para ejecutar código en un determinado dominio de seguridad, y estamos imponiendo una restricción para limitar ese dominio a un subdominio. Por ejemplo, si permite que un usuario ejecute el vimcomando en un archivo específico para editarlo, el usuario puede obtener un shell con :!sh[Enter].

Kaz
fuente
6
En serio, esto es MUY PELIGROSO. ¿Qué me impedirá ejecutar git-receive-pack '/git/';dd if=/dev/urandom of=/dev/sda?
Yeti
@Yeti ¿Tenemos un agujero de inyección de comando aquí? Eso necesita ser abordado. Además, el abuso de patrones de archivo que perpetré en el caseno me parece correcto.
Kaz
1
Sí, /bin/bash -c "$2"es inseguro (similar a cómo funciona la inyección SQL). Puede filtrar la cadena con "comillas mágicas" como PHP. Pero la manera más fácil de garantizar la seguridad es llamar a un comando manualmente y luego pasar los parámetros entre comillas dobles. Porque la seguridad de todo depende de que ese comando sea el enlace más débil (más difícil de verificar). Lo más interesante es ver cómo tu respuesta tiene 22 votos a favor, pero nadie se dio cuenta de esto;) ¿Actualizarás tu propia respuesta?
Yeti
1
@Yeti Sí; Lo acabo de hacer. Reemplacé el guión con uno que rompe el argumento -c sety luego toma solo las dos primeras palabras. El primero debe ser un git-comando permitido y el segundo debe ser una ruta que se canonicalice, se verifique con el prefijo permitido y también se verifique su existencia. ¿Puedes pensar en alguna forma de romper esto?
Kaz
1
A menos que alguien cree un alias (o anule un comando) para cualquiera de los comandos dados, entonces no, las comillas dobles de Bash deberían ser seguras (y si no, esto es un error en Bash).
Yeti
15

Lo que está buscando se llama Shell restringido . Bash proporciona un modo en el que los usuarios solo pueden ejecutar comandos presentes en sus directorios de inicio (y no pueden moverse a otros directorios), lo que podría ser lo suficientemente bueno para usted.

Encontré este hilo muy ilustrativo, aunque un poco anticuado.

Vinko Vrsalovic
fuente
1
¿Qué pasa si el usuario hace "! / Bin / sh" o algo así desde el indicador less?
PEZ
3
@Ubersoldat: Por favor, crezca y baje el tono de la agresión en todas sus publicaciones. Estaba preguntando si la restricción solo se aplica a bash o procesos secundarios también (y para responder a su pregunta, resulta que no).
Un problema importante con el shell restringido es que para protegerlo, debe usar .profile o .bashrc para restringir el PATH, deshabilitar los elementos internos, etc., pero esos archivos solo se invocan para los shells interactivos. Si un usuario usa ssh para ejecutar un comando remoto, no se invoca. Esto permite que un usuario con acceso ssh a una cuenta con SHELL = / bin / rbash haga algo como "ssh remotehost bash" para obtener un shell no interactivo pero sin restricciones. Necesita comandos forzados SSH como sugirió HD, pero esto puede proteger contra escapes de shell como PEZ solicitó (una vez que PATH está bloqueado, incluye / bin: / usr / bin por defecto).
Alex Dupuy
2
Retiro lo dicho, fiesta será invocar .bashrc (no .profile) cuando se ejecuta de forma no interactiva bajo sshd; sin embargo, debe asegurarse de establecer PATH explícitamente y deshabilitar los alias y los elementos internos en .bashrc; cambiar a un subdirectorio que no esté en / por encima de PATH o .ssh / o .bashrc también es una buena idea. Tener cualquier comando que pueda escribir en archivos arbitrarios creará un problema; estos no siempre son obvios, por ejemplo, el comando sed 'w' podría usarse para sobrescribir .bashrc y romper. Una cárcel chroot siempre será más segura si puede usarla, y los comandos forzados de ssh serán más restringidos.
Alex Dupuy
4

Debería adquirir `rssh ', el shell restringido

Puede seguir las guías de restricción mencionadas anteriormente, todas se explican por sí mismas y son fáciles de seguir. Comprenda los términos "jail chroot" y cómo implementar de manera efectiva las configuraciones de sshd / terminal, y así sucesivamente.

Dado que la mayoría de sus usuarios acceden a sus terminales a través de sshd, probablemente también debería buscar en sshd_conifg, el archivo de configuración del demonio SSH, para aplicar ciertas restricciones a través de SSH. Sin embargo, tenga cuidado. Comprenda correctamente lo que intenta implementar, ya que las ramificaciones de configuraciones incorrectas probablemente sean bastante espantosas.

Chuah
fuente
1
Tenga en cuenta que pizzashack.org/rssh es una excelente (posiblemente la mejor) solución para el caso especial en el que desea permitir scp / sftp / rdist / rsync / cvs (y no necesita acceso a nada fuera de una cárcel chroot); sin embargo , no resuelve la cuestión general del autor original, que quería que los usuarios pudieran ver los archivos de registro y ejecutar ciertos scripts de ejecución / apagado.
Alex Dupuy
2
rsshfue eliminado de Debian Buster. Creo que el nuevo atractivo es GNU rush .
Thomas
1
@Thomas Creo que rssh ahora no se mantiene y tiene un problema de seguridad conocido: sourceforge.net/p/rssh/mailman/message/36519118
Max Barraclough
4

¿Por qué no escribe su propio shell de inicio de sesión? Sería bastante simple usar Bash para esto, pero puede usar cualquier idioma.

Ejemplo en Bash

Utilice su editor favorito para crear el archivo /root/rbash.sh(puede ser cualquier nombre o ruta, pero debería ser chown root:rooty chmod 700):

#!/bin/bash

commands=("man" "pwd" "ls" "whoami")
timestamp(){ date +'%Y-%m-%s %H:%M:%S'; }
log(){ echo -e "$(timestamp)\t$1\t$(whoami)\t$2" > /var/log/rbash.log; }
trycmd()
{
    # Provide an option to exit the shell
    if [[ "$ln" == "exit" ]] || [[ "$ln" == "q" ]]
    then
        exit
        
    # You can do exact string matching for some alias:
    elif [[ "$ln" == "help" ]]
    then
        echo "Type exit or q to quit."
        echo "Commands you can use:"
        echo "  help"
        echo "  echo"
        echo "${commands[@]}" | tr ' ' '\n' | awk '{print "  " $0}'
    
    # You can use custom regular expression matching:
    elif [[ "$ln" =~ ^echo\ .*$ ]]
    then
        ln="${ln:5}"
        echo "$ln" # Beware, these double quotes are important to prevent malicious injection
        
        # For example, optionally you can log this command
        log COMMAND "echo $ln"
    
    # Or you could even check an array of commands:
    else
        ok=false
        for cmd in "${commands[@]}"
        do
            if [[ "$cmd" == "$ln" ]]
            then
                ok=true
            fi
        done
        if $ok
        then
            $ln
        else
            log DENIED "$cmd"
        fi
    fi
}

# Optionally show a friendly welcome-message with instructions since it is a custom shell
echo "$(timestamp) Welcome, $(whoami). Type 'help' for information."

# Optionally log the login
log LOGIN "$@"

# Optionally log the logout
trap "trap=\"\";log LOGOUT;exit" EXIT

# Optionally check for '-c custom_command' arguments passed directly to shell
# Then you can also use ssh user@host custom_command, which will execute /root/rbash.sh
if [[ "$1" == "-c" ]]
then
    shift
    trycmd "$@"
else
    while echo -n "> " && read ln
    do
        trycmd "$ln"
    done
fi

Todo lo que tiene que hacer es configurar este ejecutable como su shell de inicio de sesión. Por ejemplo, edite su /etc/passwdarchivo y reemplace su shell de inicio de sesión actual de ese usuario /bin/bashcon /root/rbash.sh.

Este es solo un ejemplo simple, pero puede hacerlo tan avanzado como desee, la idea está ahí. Tenga cuidado de no bloquearse cambiando el shell de inicio de sesión de su propio usuario. Y siempre pruebe los símbolos y comandos extraños para ver si es realmente seguro.

Puede probarlo con: su -s /root/rbash.sh.

¡Cuidado , asegúrese de hacer coincidir todo el comando y tenga cuidado con los comodines! Mejor excluye Bash-símbolos tales como ;, &, &&, ||, $, y acentos abiertos para estar seguro.

Dependiendo de la libertad que le dé al usuario, no será mucho más seguro que esto. Descubrí que a menudo solo necesitaba crear un usuario que tuviera acceso a solo unos pocos comandos relevantes, y en ese caso, esta es realmente la mejor solución. Sin embargo, si desea dar más libertad, una cárcel y permisos podrían ser más apropiados. Los errores se cometen fácilmente y solo se notan cuando ya es demasiado tarde.

Yeti
fuente
Realmente me gustó este enfoque. ¿Tiene alguna idea de cómo podemos manejar comandos de varias palabras como "service httpd restart"?
Farzan
2
@Farzan Puede ejecutar comandos en una variable, por ejemplo ln="service httpd restart", con: ${ln[@]}. Aún así, asegúrese de pensar en los problemas de seguridad, una lista blanca solo para caracteres alfanuméricos y espacios en blanco sería útil en tal caso. Pero también puede simplemente hacer: if [[ "$ln" == "restart-httpd"];then service httpd restart;fiy trabajar con comandos simples como ese.
Yeti
/root/rbash.sh: Permission denied Probé chmod 777 también
Christian
@Christian, ¿ha agregado /root/rbash.sha /etc/shells? Depende de la distribución. También puede intentar deshabilitar temporalmente selinuxy verificar los mensajes de registro en busca de pistas (mire la marca de tiempo) en archivos en formato /var/log.
Yeti
@yeti no, se explicó para agregar a / root :) Lo intentaré ahora.
Christian
1

Es posible que desee considerar la instalación de una cárcel .

tmeisenh
fuente
La pregunta está etiquetada como 'linux'. ¿No son las cárceles específicas de FreeBSD?
Max Barraclough
1

GNU Rush puede ser la forma más flexible y segura de lograr esto:

GNU Rush es un Shell de usuario restringido, diseñado para sitios que brindan acceso remoto limitado a sus recursos, como repositorios svn o git, scp o similares. Utilizando un archivo de configuración sofisticado, GNU Rush le brinda un control completo sobre las líneas de comando que ejecutan los usuarios, así como sobre el uso de los recursos del sistema, como la memoria virtual, el tiempo de CPU, etc.

Thomas
fuente
0

Otra forma de ver esto es usando POSIX ACL, necesita ser compatible con su sistema de archivos, sin embargo, puede tener un ajuste detallado de todos los comandos en Linux de la misma manera que tiene el mismo control en Windows (solo que sin la interfaz de usuario más agradable ). enlace

Otra cosa a considerar es PolicyKit .

Tendrá que buscar bastante en Google para que todo funcione, ya que definitivamente no es una fortaleza de Linux en este momento.

Bryan Rehbein
fuente
0

[Divulgación: escribí sshdo que se describe a continuación]

Si desea que el inicio de sesión sea interactivo, configurar un shell restringido probablemente sea la respuesta correcta. Pero si hay un conjunto real de comandos que desea permitir (y nada más) y está bien que estos comandos se ejecuten individualmente a través de ssh (por ejemplo, ssh user @ host cmd arg blah blah), entonces un control genérico de lista blanca de comandos para ssh podría ser lo que necesita. Esto es útil cuando los comandos están programados de alguna manera en el extremo del cliente y no requiere que el usuario escriba realmente el comando ssh.

Hay un programa llamado sshdo para hacer esto. Controla qué comandos se pueden ejecutar a través de conexiones ssh entrantes. Está disponible para descargar en:

http://raf.org/sshdo/ (lea las páginas del manual aquí) https://github.com/raforg/sshdo/

Tiene un modo de entrenamiento para permitir todos los comandos que se intentan y una opción --learn para producir la configuración necesaria para permitir los comandos aprendidos de forma permanente. Entonces el modo de entrenamiento se puede desactivar y no se ejecutará ningún otro comando.

También tiene una opción --unlearn para dejar de permitir comandos que ya no están en uso para mantener el mínimo privilegio estricto a medida que los requisitos cambian con el tiempo.

Es muy quisquilloso con lo que permite. No permitirá un comando con argumentos. Solo se pueden permitir comandos de shell completos.

Pero admite patrones simples para representar comandos similares que varían solo en los dígitos que aparecen en la línea de comando (por ejemplo, números de secuencia o sellos de fecha / hora).

Es como un firewall o un control de lista blanca para comandos ssh.

Y admite diferentes comandos permitidos para diferentes usuarios.

raf
fuente