¿Cómo configurar bash readline en modo vi automáticamente al iniciar sesión en un sistema?


9

Mi equipo es responsable de miles de máquinas Linux / Unix, por lo que, naturalmente, la cuenta raíz se "comparte" entre los administradores. Prefiero el modo vi, otros prefieren el modo emacs.

¿Cómo puedo configurar la línea de lectura de bash en modo vi al iniciar sesión SSH en cualquier máquina, sin obligar a todos los demás a usar el modo vi también?

En esencia, me gustaría tener el efecto de set -o videspués de iniciar sesión sin tener que escribirlo todo el tiempo, y sin forzarlo a todos los demás (por mucho que el modo emacs me moleste, el modo vi es molesto para ellos).

Sé que esto no sería un problema si todos usaran sus propias cuentas con sudo para ejecutar comandos privilegiados, pero debido a circunstancias fuera de mi control, lamentablemente no es una opción.


1
Eso no es facil. Una forma es analizar el archivo de registro de sshd y ver qué clave se usó para iniciar sesión. Esperaba una solución del lado del cliente, por ejemplo, una forma de pasar mi configuración de línea de lectura local al lado remoto o algo así, o algo de magia oscura de OpenSSH que se ejecuta en silencio set -o viantes de darme el control del caparazón.
Patrick

1
Tal vez podría usar un script de Expect en el cliente que ssh está en el servidor, enviar el set -o vicomando y luego cambiar al modo interactivo.
Barmar

1
Al menos OpenSSH sshdconfigura varias variables de entorno que pueden ayudarlo a determinar quién está en el otro extremo. Por ejemplo, SSH_CLIENTcontiene la dirección IP de conexión (y el puerto de salida / entrada del cliente también). Jugar con esto ~/.bashrcpodría permitirte hacer cosas solo por ti .
Sami Laine

1
La recompensa dice que está buscando una "solución del lado del cliente". ¿Qué es eso? ssh en un host Unix? ¿Masilla? Colibrí Java SSH? Cygwin?
Jeff Schaller

1
Mencionas decenas de miles de máquinas. ¿Desea comenzar desde una máquina y llegar a estas máquinas, o desea poder saltar de una máquina a otra llevando el modo vi con usted?
icarus

Respuestas:


3

Aquí hay una manera tonta de hacerlo, que realmente solo funciona bien con la autenticación de clave pública:

Primero, asegúrese de que su máquina local tenga ncpuesta.

En segundo lugar, aún en su máquina local, cree un script (lo llamaré connect-to-server) y colóquelo en un lugar que ${PATH}sepa *:

#!/bin/sh
# connect-to-server
ssh -q server-hostname "touch .yourname" </dev/null >/dev/null 2>&1
nc server-hostname 22

A continuación, modifique el .bashrcen el sistema remoto para incluir en alguna parte:

# partial .bashrc
if [ -f "${HOME}/.yourname" ]; then
  rm "${HOME}/.yourname"
  set -o vi
fi

Finalmente, de vuelta en su máquina local, edite ~/.ssh/configpara agregar:

# partial ssh config
Host serverNickname
  Hostname server-hostname
  ProxyCommand connect-to-server

Desventajas de este enfoque (y por qué lo llamo tonto):

  • Si se necesita un comando proxy real, se vuelve más complicado.
  • Si alguien más inicia sesión al mismo tiempo que usted, existe la posibilidad de que el .yournamearchivo aún no se haya eliminado, en cuyo caso también obtendrá el archivo set -o vi.
  • Lo que es más importante, si lo hace ssh serverNickname command, commandse ejecutará, pero (dado .bashrcque nunca se obtiene) el .yournamearchivo permanece, por lo que sería cortés tener un segundo alias en su configuración ssh que no use el pseudo-proxy.

De hecho, el único aspecto positivo de este enfoque es que su sshcomando no necesita ningún argumento adicional.


* Si no desea tener que cambiar nada en sistemas remotos, aquí hay un pseudo-proxy alternativo que crea un temporal .bashrc:

#!/bin/sh
# connect-to-server
ssh -q server-hostname 'ln .bashrc .bashrc.real; cat .bashrc.real <(printf "set -o vi; ln -f .bashrc.real .bashrc\n") >.bashrc.yourname; ln -f .bashrc.yourname .bashrc' </dev/null >/dev/null 2>&1
nc server-hostname 22

Esto tiene las mismas desventajas del otro método, por lo que aún querría un segundo alias en su sshconfiguración que no invoque el pseudo-proxy.


Muy creativo. Gracias por la sugerencia. Ya uso ProxyCommand ampliamente (algunas redes / hosts requieren más de 5 saltos para llegar) y esto rápidamente resultaría en una pesadilla de configuración. Editar: Mirando todas las respuestas, creo que la tuya se acerca más.
Patrick

4

Yo iría por:

ssh server -t "bash --login -o vi"

pero si eres administrador, puedes probar algo más limpio. Por ejemplo, podría usar la SendEnvopción ssh en el lado del cliente para transmitir una variable específica, usar AcceptEnven la sshdconfiguración (lado del servidor) para aceptarla y, en base a esto, modificar el .bashrcarchivo raíz para ajustar el comportamiento de acuerdo con el valor de la variable.

Esto implica cambiar la sshdconfiguración en todos los hosts, así como en sus .bashrc. Sin embargo, no es exactamente una forma "independiente" de hacer ...


3

Para una solución fácil del lado del cliente:

alias connect='ssh -t root@server "bash -o vi"'

Esto producirá un error si las secuencias de comandos shell de inicialización de la raíz explícitamente usos set -o emacso conjuntos EDITORa emacs, o si la raíz .initrcinvoca el archivo de emacslas asociaciones de teclas.

El resto de esta respuesta se refiere a soluciones del lado del servidor.


Esto funciona cuando está sshentrando en la máquina y luego usa sudo -i:

Para su /root/.bashrc:

if [[ -n "$SUDO_USER" ]] && [[ -f /root/.bashrc-"$SUDO_USER" ]]; then
  source /root/.bashrc-"$SUDO_USER"
fi

Esto le permite tener un bashrcarchivo personal llamado /root/.bashrc-patricken el que puede hacer lo que quiera, como set -o vi.

Combinando esto con un enfoque algo ingenuo para recoger ese archivo rc dependiendo de $SSH_CLIENT:

if [[ -n "$SUDO_USER" ]]; then
  person="$SUDO_USER"
elif [[ -n "$SSH_CLIENT" ]]; then

  case "$SSH_CLIENT" in
    192.168.216.100*)  person="joe" ;;
    192.168.216.120*)  person="patrick" ;;
    192.168.216.150*)  person="lindsey" ;;
  esac

fi

if [[ -n "$person" ]] && [[ -f /root/.bashrc-"$person" ]]; then
  source /root/.bashrc-"$person"
fi

Obviamente, esto solo funciona si te estás conectando desde la misma dirección IP todo el tiempo ...

Otro enfoque que utiliza el campo de comentarios de la clave SSH particular que está utilizando, que funciona si reenvía el agente SSH al servidor:

ssh_comment="$( ssh-add -L | grep -f /root/.ssh/authorized_keys | awk '{ print $NF '} | head -n 1 )"

Esto selecciona el campo de comentarios para la clave que usó para conectarse al servidor. El head -n 1está allí en caso de que tenga varias de sus claves en el authorized_keysarchivo.

Luego, puede usar $ssh_commentpara elegir un archivo rc a la fuente, ya sea directamente como con el $SUDO_USERenfoque anterior (en el que el comentario $ssh_commentpuede necesitar una limpieza si se trata de un nombre de ruta), o mediante una casedeclaración como con el $SSH_CLIENTenfoque.


Coincidencia SSH_CLIENTy SUDO_USERes lo que uso actualmente, pero requiere modificación del lado del servidor y no es particularmente confiable. Esperaba una solución pura del lado del cliente. Gracias por la sugerencia sin embargo.
Patrick

3

Si realmente desea hacerlo sin modificaciones en el lado del servidor, ya sea:

1) Ejecuta algo como

$ ssh user@host -t 'bash -l -o vi' 

No creo que la documentación sea ​​demasiado clara al respecto, pero -o optionse menciona y parece funcionar.

2) Utilice esperar:

El expectguión:

$ cat bashsetup.expect
#!/usr/bin/expect -f 

set user [lindex $argv 0];
set host [lindex $argv 1];

spawn ssh -l $user $host
expect "$ "
send "set -o vi\n"
interact

Hazlo ejecutable y ejecuta:

$ ./bashsetup.expect user testhost
spawn ssh -l user testhost
[motd, blahblah...]
user@testhost ~$ set -o vi
user@testhost ~$ 

Esto supone que puede iniciar sesión sin ingresar contraseñas (para el host remoto o para sus claves), de lo contrario, el script esperado debería tener eso en cuenta. Pero con muchas máquinas es probable que ya tenga eso. Además, esperaba un signo de dólar y un espacio, edítelo según su solicitud: "# "tal vez.

Aunque si algo impreso antes del aviso incluye esos mismos caracteres, deberá incluir algo más específico en la cadena esperada.

Además, ese script no admite dar argumentos adicionales a ssh. Si va a dar un comando explícito para ejecutarse, probablemente no necesite el modo vi, pero si necesita decir túnel de puerto, eso podría ser un problema.


Pero en cualquier caso, realmente creo que esto debería resolverse en los sistemas de destino con cuentas separadas ( sudoo simplemente UID 0 antiguo). La configuración personalizada también sería útil para muchos otros casos, y en general tendría un montón de archivos de configuración y variables de entorno que le gustaría establecer. (Tenga en cuenta que los administradores podrían no estar de acuerdo con el valor $EDITORo el contenido de virclo que sea).

También eliminar usuarios sería más fácil con cuentas separadas.

Cualquier forma de sincronizar archivos en todos los hosts también resolvería trivialmente esto al permitirle iniciar sesión con algo como ssh -t user@host 'patricks_shell.sh'o ssh -t user@host 'bash --rcfile patrick.rc'.


Pensé en usar esperar, pero como ya señaló en su pregunta, el problema coincide con algo único para que comience el mensaje interact. No existe, las indicaciones pueden ser muy diferentes, al igual que los motds / resultados de los perfiles. Gracias por la sugerencia sin embargo.
Patrick

Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.