Come impostare bash readline in modalità vi automaticamente all'accesso a un sistema?


9

Il mio team è responsabile di migliaia di macchine Linux / Unix, quindi naturalmente l'account di root è "condiviso" tra gli amministratori. Preferisco la modalità vi, altri preferiscono la modalità emacs.

Come posso impostare readline di bash in modalità vi al login SSH su qualsiasi macchina, senza costringere tutti gli altri a usare anche la modalità vi?

In sostanza, vorrebbe avere l'effetto di set -o vidopo il login senza doverlo digitare ogni volta e senza forzarlo su tutti gli altri (per quanto la modalità emacs sia fastidiosa per me, la modalità vi è fastidiosa per loro).

So che questo non sarebbe un problema se tutti usassero i propri account con sudo per eseguire comandi privilegiati, ma a causa di circostanze al di fuori del mio controllo, purtroppo non è un'opzione.


1
Non è facile Un modo è analizzare il file di registro di sshd e vedere quale chiave è stata utilizzata per accedere. Speravo in una soluzione lato client, ad esempio un modo per passare la mia configurazione readline locale al lato remoto o qualcosa del genere, oppure della magia oscura di OpenSSH che si esegue silenziosamente set -o viprima di darmi il controllo della shell.
Patrick,

1
Forse potresti usare uno script Expect sul client che viene inviato al server, inviare il set -o vicomando, quindi passa alla modalità interattiva.
Barmar,

1
Almeno OpenSSH sshdimposta diverse variabili di ambiente che potrebbero aiutarti a determinare chi si trova dall'altra parte. Ad esempio, SSH_CLIENTcontiene l'indirizzo IP di connessione (e anche la porta in uscita / in entrata del client). Giocherellare con questo in ~/.bashrcpotrebbe consentirti di fare le cose solo per te .
Sami Laine,

1
La taglia dice che stai cercando una "soluzione lato client" - che cos'è? ssh su un host Unix? Putty? Colibrì? Java SSH? Cygwin?
Jeff Schaller

1
Citi decine di migliaia di macchine. Vuoi iniziare da una macchina e arrivare a queste macchine o vuoi essere in grado di saltare da una macchina all'altra portando la modalità vi con te?
Icaro,

Risposte:


3

Ecco un modo stupido per farlo, che funziona davvero solo con l'autenticazione a chiave pubblica:

Innanzitutto, assicurati che la tua macchina locale sia ncpresente.

In secondo luogo, sempre sul tuo computer locale, crea uno script (lo chiamerò connect-to-server) e mettilo in un posto che ${PATH}conosci *:

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

Successivamente, modifica il .bashrcsul sistema remoto per includerlo da qualche parte:

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

Infine, nel tuo computer locale, modifica ~/.ssh/configper aggiungere:

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

Gli aspetti negativi di questo approccio (e perché lo chiamo sciocco):

  • Se è necessario un comando proxy effettivo, diventa più complicato.
  • Se qualcun altro accede contemporaneamente a te, c'è la possibilità che il .yournamefile non sia stato ancora cancellato, nel qual caso ottengono set -o vianche il file.
  • Soprattutto, se lo fai ssh serverNickname command, commandverrà eseguito, ma (poiché .bashrcnon viene mai fornito) il .yournamefile rimane, quindi sarebbe educato avere un secondo alias nella tua configurazione ssh che non usi lo pseudo-proxy.

In effetti, l' unico lato positivo di questo approccio è che sshnon è necessario fornire ulteriori argomenti al comando.


* Se non vuoi cambiare nulla sui sistemi remoti, ecco uno pseudo-proxy alternativo che crea un temporaneo .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

Ciò ha gli stessi svantaggi dell'altro metodo, quindi si vorrebbe comunque un secondo alias nella sshconfigurazione che non invochi lo pseudo-proxy.


Molto creativo. Grazie per il suggerimento Uso già ProxyCommand ampiamente (alcune reti / host richiedono 5+ hop per raggiungere) e questo si tradurrebbe rapidamente in un incubo di configurazione. Modifica: guardando tutte le risposte penso che le tue si avvicinino di più.
Patrick,

4

Vorrei andare per:

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

ma se sei un amministratore, puoi provare qualcosa di più pulito. Ad esempio, è possibile utilizzare l' SendEnvopzione ssh sul lato client per trasmettere una variabile specifica, utilizzare AcceptEnvnella sshdconfigurazione (lato server) per accettarla e, in base a ciò, modificare il .bashrcfile root per regolare il comportamento in base al valore della variabile.

Ciò implica cambiare la sshdconfigurazione su tutti gli host e sui loro .bashrc. Non esattamente un modo "autonomo" di fare, tuttavia ...


3

Per una soluzione lato client semplice:

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

Questo fallirà se gli script di inizializzazione della shell di root usano esplicitamente set -o emacso si impostano EDITORsu emacs, o se il .initrcfile di root richiama emacsle associazioni di tasti.

Il resto di questa risposta riguarda soluzioni lato server.


Funziona quando si sshentra nella macchina e quindi si utilizza sudo -i:

Per il tuo /root/.bashrc:

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

Questo ti permette di avere un bashrcfile personale chiamato in /root/.bashrc-patrickcui puoi fare quello che vuoi, come set -o vi.

Combinando questo con un approccio un po 'ingenuo per raccogliere quel file rc a seconda di $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

Questo ovviamente funziona solo se ti connetti sempre dallo stesso indirizzo IP ...

Un altro approccio che utilizza il campo dei commenti della particolare chiave SSH che stai utilizzando, che funziona se stai inoltrando l'agente SSH al server:

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

Questo seleziona il campo del commento per la chiave che hai usato per connetterti al server. L' head -n 1è lì nel caso in cui vi capita di avere molti dei vostri chiavi nel authorized_keysfile.

È quindi possibile utilizzare $ssh_commentper selezionare un file rc da sorgente, sia direttamente come con l' $SUDO_USERapproccio sopra (in cui il commento in $ssh_commentpotrebbe dover subire un po 'di pulizia se è un nome di percorso), sia tramite caseun'istruzione come con l' $SSH_CLIENTapproccio.


Corrispondenza SSH_CLIENTed SUDO_USERè quello che uso attualmente, ma richiede modifiche sul lato server e non è particolarmente affidabile. Speravo in una soluzione pura lato client. Grazie per il suggerimento però.
Patrick,

3

Se vuoi davvero farlo senza modifiche sul lato server, o:

1) Esegui qualcosa di simile

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

Non credo che la documentazione sia troppo chiara su questo, ma -o optionè menzionata e sembra funzionare.

2) Utilizzo previsto:

La expectsceneggiatura:

$ 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

Renderlo eseguibile ed esegui:

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

Ciò presuppone che sia possibile accedere senza immettere le password (per l'host remoto o per le chiavi), altrimenti lo script di attesa dovrebbe tenerne conto. Ma con molte macchine probabilmente lo avrai già. Inoltre, mi aspettavo un segno di dollaro e uno spazio, modificalo secondo il tuo prompt: "# "forse.

Tuttavia, se qualcosa stampato prima del prompt include quegli stessi caratteri, dovrai includere qualcosa di più specifico nella stringa prevista.

Inoltre, quello script non supporta fornire ulteriori argomenti a ssh. Se hai intenzione di dare un comando esplicito per l'esecuzione, probabilmente non hai bisogno di vi-mode, ma se hai bisogno di dire port tunneling, questo potrebbe essere un problema.


Ma in ogni caso, penso davvero che questo dovrebbe essere risolto sui sistemi di destinazione con account separati ( sudoo semplicemente vecchio UID 0). La configurazione personalizzata sarebbe utile anche in molti altri casi e in generale avresti un mucchio di file di configurazione e variabili di ambiente che desideri impostare. (Considera che gli amministratori potrebbero non essere d'accordo sul valore $EDITORo sul contenuto virco altro.)

Anche la rimozione degli utenti sarebbe più semplice con account separati.

Qualsiasi modo di sincronizzare i file su tutti gli host lo risolverebbe anche banalmente permettendoti di accedere con qualcosa di simile ssh -t user@host 'patricks_shell.sh'o ssh -t user@host 'bash --rcfile patrick.rc'.


Ho pensato di usare wait, ma come hai già sottolineato nella tua domanda, il problema sta abbinando qualcosa di unico per l'avvio del prompt interact. Non esiste, i prompt possono essere molto diversi, così come i motds / output dei profili. Grazie per il suggerimento però.
Patrick,

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.