Como definir o bash readline para o modo vi automaticamente após o login em um sistema?


9

Minha equipe é responsável por milhares de máquinas Linux / Unix; portanto, a conta raiz é "compartilhada" entre os administradores. Eu prefiro o modo vi, outros preferem o modo emacs.

Como posso definir a linha de leitura do bash para o modo vi no login SSH em qualquer máquina, sem forçar todo mundo a usar o modo vi também?

Em essência, gostaria de ter o efeito de set -o viapós o login sem precisar digitá-lo todas as vezes e sem forçá-lo a todos os outros (tanto quanto o modo emacs é irritante para mim, o modo vi é irritante para eles).

Eu sei que isso não seria um problema se todos usassem suas próprias contas com o sudo para executar comandos privilegiados, mas devido a circunstâncias fora do meu controle, isso infelizmente não é uma opção.


1
Isso não é fácil. Uma maneira é analisar o arquivo de log do sshd e ver qual chave foi usada para efetuar login. Eu esperava uma solução do lado do cliente, por exemplo, uma maneira de passar minha configuração local de readline para o lado remoto ou algo assim, ou alguma mágica escura do OpenSSH que é executada silenciosamente set -o viantes de me dar o controle do shell.
Patrick Patrick

1
Talvez você possa usar um script Expect no cliente que esteja com ssh no servidor, envie o set -o vicomando e depois mude para o modo interativo.
Barmar 27/01

1
Pelo menos o OpenSSH sshdconfigura várias variáveis ​​de ambiente que podem ajudá-lo a determinar quem está do outro lado. Por exemplo, SSH_CLIENTcontém o endereço IP de conexão (e também a porta de saída / entrada do cliente). Brincar com isso ~/.bashrcpode permitir que você faça as coisas apenas para você .
Sami Laine

1
A recompensa diz que você está procurando uma "solução do lado do cliente" - o que é isso? ssh em um host Unix? Putty? Beija Flor? Java SSH? Cygwin?
Jeff Schaller

1
Você menciona dezenas de milhares de máquinas. Deseja iniciar a partir de uma máquina e acessá-las ou deseja pular de máquina em máquina para máquina carregando o modo vi com você?
icarus 15/02

Respostas:


3

Aqui está uma maneira boba de fazer isso, que realmente funciona bem com autenticação de chave pública:

Primeiro, verifique se a sua máquina local está ncnela.

Segundo, ainda na sua máquina local, crie um script (eu o chamarei connect-to-server) e coloque-o em um local que você ${PATH}conheça *:

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

Em seguida, modifique o .bashrcsistema remoto para incluir em algum lugar:

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

Por fim, de volta à sua máquina local, edite ~/.ssh/configpara adicionar:

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

Desvantagens dessa abordagem (e por que eu a chamo de boba):

  • Se houver um comando de proxy real necessário, ele se tornará mais complicado.
  • Se outra pessoa efetuar login ao mesmo tempo que você, é possível que o .yournamearquivo ainda não tenha sido excluído. Nesse caso, eles também receberão set -o vi.
  • O mais importante, se você o fizer ssh serverNickname command, commandserá executado, mas (como .bashrcnunca é originado) o .yournamearquivo permanecerá; portanto, seria educado ter um segundo alias na sua configuração ssh que não use o pseudo-proxy.

De fato, a única vantagem dessa abordagem é que seu sshcomando não precisa receber argumentos extras.


* Se você não deseja alterar nada em sistemas remotos, aqui está um pseudo-proxy alternativo que cria um temporário .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

Isso tem as mesmas desvantagens do outro método, portanto, você ainda deseja um segundo alias em sua sshconfiguração que não invoque o pseudo-proxy.


Muito criativo. Obrigado pela sugestão. Eu já uso o ProxyCommand extensivamente (algumas redes / hosts exigem mais de 5 saltos para chegar) e isso resultaria rapidamente em um pesadelo de configuração. Edit: Olhando para todas as respostas, acho que a sua se aproxima mais.
1011 Patrick

4

Eu iria para:

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

mas se você é um administrador, pode tentar algo mais limpo. Por exemplo, você pode usar a SendEnvopção ssh no lado do cliente para transmitir uma variável específica, usar AcceptEnvna sshdconfiguração (lado do servidor) para aceitá-la e, com base nisso, modificar o .bashrcarquivo raiz para ajustar o comportamento de acordo com o valor da variável.

Isso implica alterar a sshdconfiguração em todos os hosts, assim como nos deles .bashrc. Não é exatamente uma maneira "independente" de fazer, no entanto ...


3

Para uma solução fácil para o cliente:

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

Isto irá falhar se scripts de inicialização do shell da raiz explicitamente usos set -o emacsou conjuntos EDITORpara emacs, ou se da raiz .initrcinvoca arquivo emacsatalhos de teclado.

O restante desta resposta diz respeito a soluções do lado do servidor.


Isso funciona quando você está sshinserindo na máquina e depois usa sudo -i:

Para o seu /root/.bashrc:

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

Isso permite que você tenha um bashrcarquivo pessoal chamado no /root/.bashrc-patrickqual você pode fazer o que quiser, como set -o vi.

Combinando isso com uma abordagem um tanto ingênua para escolher esse arquivo rc, dependendo 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, isso só funciona se você estiver se conectando do mesmo endereço IP o tempo todo ...

Outra abordagem que usa o campo de comentário da chave SSH específica que você está usando, que funciona se você estiver encaminhando o agente SSH para o servidor:

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

Isso seleciona o campo de comentário da chave que você usou para se conectar ao servidor. O head -n 1que há no caso de acontecer de ter várias de suas chaves no authorized_keysarquivo.

Em seguida, você pode $ssh_commentescolher um arquivo rc para a origem, diretamente como na $SUDO_USERabordagem acima (na qual o comentário $ssh_commentpode precisar passar por uma limpeza, se for o nome do caminho) ou por uma casedeclaração, como na $SSH_CLIENTabordagem.


Correspondência SSH_CLIENTe SUDO_USERé o que eu uso atualmente, mas requer modificação no servidor e não é particularmente confiável. Eu esperava uma solução pura do lado do cliente. Obrigado pela sugestão.
1011 Patrick

3

Se você realmente deseja fazer isso sem modificação no lado do servidor, ou:

1) Execute algo como

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

Não acho que a documentação seja clara demais, mas -o optioné mencionada e parece funcionar.

2) Use esperar:

O expectscript:

$ 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

Torne-o executável e execute:

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

Isso pressupõe que você possa efetuar login sem inserir senhas (para o host remoto ou para suas chaves); caso contrário, o script de espera precisará levar isso em conta. Mas com muitas máquinas, é provável que você já tenha isso. Além disso, esperava um cifrão e um espaço. Edite-o de acordo com sua solicitação: "# "talvez.

Embora se algo impresso antes do prompt incluir esses mesmos caracteres, você precisará incluir algo mais específico na sequência esperada.

Além disso, esse script não suporta fornecer argumentos extras para ssh. Se você deseja dar um comando explícito para executar, provavelmente não precisa do modo vi, mas se precisar dizer tunelamento de porta, isso pode ser um problema.


Mas, em qualquer caso, eu realmente acho que isso deve ser resolvido nos sistemas de destino com contas separadas ( sudoou simplesmente o antigo UID 0). A configuração personalizada também seria útil para muitos outros casos e, em geral, você teria vários arquivos de configuração e variáveis ​​de ambiente que gostaria de definir. (Considere que os administradores podem não concordar com o valor $EDITORou o conteúdo vircou o que for.)

Também remover usuários seria mais fácil com contas separadas.

Qualquer maneira de sincronizar arquivos em todos os hosts também resolveria isso trivialmente, permitindo que você efetue login com algo como ssh -t user@host 'patricks_shell.sh'ou ssh -t user@host 'bash --rcfile patrick.rc'.


Pensei em usar o expect, mas como você já apontou na sua pergunta, o problema está correspondendo a algo único para o prompt iniciar interact. Não existe, os prompts podem ser muito diferentes, assim como os motds / output dos perfis. Obrigado pela sugestão embora.
1011 Patrick

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.