Jak ustawić bash readline na tryb vi automatycznie po zalogowaniu się do systemu?

9

Mój zespół jest odpowiedzialny za tysiące komputerów z systemem Linux / Unix, więc oczywiście konto root jest „współdzielone” przez administratorów. Wolę tryb vi, inni wolą tryb emacs.

Jak mogę ustawić readline basha na tryb vi po zalogowaniu SSH na dowolnym komputerze, bez zmuszania wszystkich innych do używania trybu vi?

Zasadniczo chciałbym mieć efekt set -o vipo zalogowaniu bez konieczności wpisywania go za każdym razem i bez narzucania go wszystkim innym (o ile tryb emacs jest dla mnie denerwujący, tryb vi jest denerwujący dla nich).

Wiem, że nie byłoby problemu, gdyby wszyscy używali własnych kont w sudo do wykonywania uprzywilejowanych poleceń, ale z powodu okoliczności poza moją kontrolą niestety nie jest to możliwe.

Patrick
źródło
1
To nie jest łatwe. Jednym ze sposobów jest parsowanie pliku dziennika sshd i sprawdzenie, który klucz został użyty do zalogowania. Miałem nadzieję na rozwiązanie po stronie klienta, na przykład sposób przekazania mojej lokalnej konfiguracji readline na stronę zdalną lub coś w tym rodzaju, lub trochę mrocznej magii OpenSSH, która cicho wykonuje się, set -o vizanim daje mi kontrolę nad powłoką.
Patrick
1
Może mógłbyś użyć skryptu Expect na kliencie, który ssh na serwerze, wysłać set -o vipolecenie, a następnie przejść do trybu interaktywnego.
Barmar
1
Przynajmniej OpenSSH sshdkonfiguruje kilka zmiennych środowiskowych, które mogą pomóc ci ustalić, kto jest po drugiej stronie. Na przykład SSH_CLIENTzawiera łączący adres IP (a także port wychodzący / przychodzący klienta). Zajmowanie się tym ~/.bashrcmoże pozwolić ci robić rzeczy tylko dla ciebie .
Sami Laine
1
Nagroda mówi, że szukasz „rozwiązania po stronie klienta” - co to jest? ssh na hoście Unix? Kit? Koliber? Java SSH? Cygwin?
Jeff Schaller
1
Wspominasz o dziesiątkach tysięcy maszyn. Czy chcesz zacząć od jednej maszyny i dostać się na te maszyny, czy też chcesz móc przeskakiwać z maszyny na maszynę na maszynę, przenosząc tryb vi ze sobą?
icarus

Odpowiedzi:

3

Oto głupi sposób, aby to zrobić, który naprawdę działa dobrze tylko z uwierzytelnianiem za pomocą klucza publicznego:

Najpierw upewnij się, że masz na nim lokalną maszynę nc.

Po drugie, wciąż na komputerze lokalnym, stwórz skrypt (zadzwonię connect-to-server) i umieść go w miejscu, ${PATH}o którym wiesz *:

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

Następnie zmodyfikuj .bashrcw systemie zdalnym, aby zawierał gdzieś:

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

Na koniec w lokalnej maszynie edytuj, ~/.ssh/configaby dodać:

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

Wady tego podejścia (i dlaczego nazywam to głupotą):

  • Jeśli potrzebne jest rzeczywiste polecenie proxy, staje się bardziej skomplikowane.
  • Jeśli ktoś zaloguje się w tym samym czasie, co Ty, istnieje szansa, że .yournameplik nie zostanie jeszcze usunięty, w którym to przypadku również otrzyma set -o vi.
  • Co najważniejsze, jeśli to zrobisz ssh serverNickname command, commanduruchomi się, ale (ponieważ .bashrcnigdy nie jest pozyskiwany) .yournameplik pozostaje, więc byłoby grzecznie mieć drugi alias w konfiguracji ssh, który nie używa pseudo-proxy.

W rzeczywistości jedyną zaletą tego podejścia jest to, że twoje sshpolecenie nie wymaga dodatkowych argumentów.


* Jeśli nie chcesz nic zmieniać na zdalnych systemach, oto alternatywny pseudo-proxy, który tworzy tymczasowe .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

Ma to te same wady co druga metoda, więc nadal potrzebujesz drugiego aliasu w sshkonfiguracji, który nie wywołuje pseudo-proxy.

Lis
źródło
Bardzo kreatywne. Dziękuję za sugestię. Używam już ProxyCommand bardzo intensywnie (niektóre sieci / hosty wymagają ponad 5 przeskoków, aby osiągnąć), co szybko spowodowałoby koszmar konfiguracji. Edycja: patrząc na wszystkie odpowiedzi, które moim zdaniem są najbliższe.
Patrick,
4

Wybrałbym:

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

ale jeśli jesteś administratorem, możesz spróbować czegoś czystszego. Na przykład można użyć SendEnvopcji ssh po stronie klienta, aby przesłać określoną zmienną, użyć AcceptEnvw sshdkonfiguracji (po stronie serwera), aby ją zaakceptować, i na tej podstawie zmodyfikować .bashrcplik root, aby dostosować zachowanie zgodnie z wartością zmiennej.

Oznacza to zmianę sshdkonfiguracji na wszystkich hostach, a także ich .bashrc. Jednak nie do końca „samodzielny” sposób ...

Uriel
źródło
3

Dla łatwego rozwiązania po stronie klienta:

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

To nie powieść, jeśli skrypty inicjalizacji powłoki korzeniem jest wyraźnie zastosowania set -o emacslub zestawy EDITORdo emacs, lub jeśli root .initrcwywołuje plik emacskluczy powiązań.

Pozostała część odpowiedzi dotyczy rozwiązań po stronie serwera.


Działa to, gdy wchodzisz sshdo komputera, a następnie używasz sudo -i:

Dla twojego /root/.bashrc:

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

Dzięki temu możesz mieć osobisty bashrcplik o nazwie, w /root/.bashrc-patrickktórym możesz robić, co chcesz, jak chcesz set -o vi.

Łącząc to z nieco naiwnym podejściem do pobierania tego pliku rc w zależności od $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

To oczywiście działa tylko wtedy, gdy cały czas łączysz się z tym samym adresem IP ...

Inne podejście, które wykorzystuje pole komentarza konkretnego używanego klucza SSH, które działa, jeśli przekazujesz agenta SSH na serwer:

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

Spowoduje to wybranie pola komentarza dla klucza użytego do połączenia z serwerem. Występuje head -n 1na wypadek, gdybyś miał kilka kluczy w authorized_keyspliku.

Możesz następnie użyć $ssh_commentdo wybrania pliku rc do źródła, albo bezpośrednio, jak w $SUDO_USERpowyższym podejściu (w którym komentarz $ssh_commentmoże wymagać pewnych porządków, jeśli jest to nazwa ścieżki), lub za pomocą caseinstrukcji, jak w przypadku $SSH_CLIENTpodejścia.

Kusalananda
źródło
Obecnie pasuję SSH_CLIENTi SUDO_USERużywam go, ale wymaga modyfikacji po stronie serwera i nie jest szczególnie niezawodny. Miałem nadzieję na rozwiązanie wyłącznie po stronie klienta. Dziękuję za sugestię.
Patrick,
3

Jeśli naprawdę chcesz to zrobić bez modyfikacji po stronie serwera:

1) Uruchom coś takiego

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

Nie sądzę, żeby dokumentacja była na to zbyt jasna, ale -o optionjest wspomniana i wydaje się, że działa.

2) Użyj spodziewać:

expectSkrypt:

$ 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

Spraw, by był wykonywalny i uruchom:

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

Zakłada się, że możesz się zalogować bez wprowadzania haseł (dla zdalnego hosta lub dla swoich kluczy), w przeciwnym razie skrypt oczekiwań musiałby to wziąć pod uwagę. Ale przy wielu maszynach prawdopodobnie już to masz. Ponadto, oczekiwano na znak dolara i przestrzeni, edytować, że zgodnie z własnymi monit: "# "być może.

Chociaż jeśli coś wydrukowane przed pytaniem zawiera te same znaki, musisz dołączyć coś bardziej szczegółowego do oczekiwanego ciągu.

Ponadto ten skrypt nie obsługuje przekazywania dodatkowych argumentów ssh. Jeśli zamierzasz wydać jednoznaczne polecenie, prawdopodobnie nie potrzebujesz trybu vi, ale jeśli potrzebujesz powiedzieć tunelowanie portów, może to być problem.


Ale w każdym razie naprawdę uważam, że należy to rozwiązać w systemach docelowych z osobnymi kontami ( sudolub zwykłym starym identyfikatorem UID 0). Spersonalizowana konfiguracja przydałaby się również w wielu innych przypadkach, i ogólnie miałbyś wiele plików konfiguracyjnych i zmiennych środowiskowych, które chciałbyś ustawić. (Weź pod uwagę, że administratorzy mogą nie zgadzać się co do wartości $EDITOR, zawartości virclub jakiejkolwiek innej).

Również usuwanie użytkowników byłoby łatwiejsze dzięki oddzielnym kontom.

Każdy sposób synchronizacji plików na wszystkich hostach rozwiązałby to w trywialny sposób, umożliwiając zalogowanie się za pomocą czegoś takiego jak ssh -t user@host 'patricks_shell.sh'lub ssh -t user@host 'bash --rcfile patrick.rc'.

ilkkachu
źródło
Myślałem o użyciu funkcji expect, ale, jak już wskazałeś w swoim pytaniu, problem polega na dopasowaniu czegoś wyjątkowego, aby uruchomić monit interact. Nie istnieje, monity mogą być zupełnie inne, podobnie jak motds / wyjście z profili. Dziękuję za sugestię.
Patrick,