Wie wird die Bash-Readline beim Anmelden bei einem System automatisch in den vi-Modus versetzt?

9

Mein Team ist für Tausende von Linux / Unix-Computern verantwortlich, daher wird das Root-Konto natürlich von den Administratoren "geteilt". Ich bevorzuge den vi-Modus, andere bevorzugen den emacs-Modus.

Wie kann ich die Readline von bash bei der SSH-Anmeldung an einem beliebigen Computer auf den vi-Modus setzen, ohne alle anderen dazu zu zwingen, auch den vi-Modus zu verwenden?

Im Wesentlichen möchte ich die Wirkung set -o vinach dem Anmelden haben, ohne es jedes Mal eingeben zu müssen und ohne es allen anderen aufzuzwingen (so sehr der Emacs-Modus für mich ärgerlich ist, ist der vi-Modus für sie ärgerlich).

Ich weiß, dass dies kein Problem wäre, wenn jeder seine eigenen Konten bei sudo verwenden würde, um privilegierte Befehle auszuführen, aber aufgrund von Umständen, die außerhalb meiner Kontrolle liegen, ist dies leider keine Option.

Patrick
quelle
1
Das ist nicht einfach Eine Möglichkeit besteht darin, die Protokolldatei von sshd zu analysieren und festzustellen, welcher Schlüssel zum Anmelden verwendet wurde. Ich hatte auf eine clientseitige Lösung gehofft, beispielsweise eine Möglichkeit, meine lokale Readline-Konfiguration an die Remote-Seite oder ähnliches weiterzugeben, oder etwas dunkle OpenSSH-Magie, die still ausgeführt wird, set -o vibevor ich die Kontrolle über die Shell habe.
Patrick
1
Möglicherweise können Sie ein Expect-Skript auf dem Client verwenden, das ssh auf dem Server installiert, den set -o viBefehl senden und dann in den interaktiven Modus wechseln.
Barmar
1
Zumindest sshdrichtet der OpenSSH mehrere Umgebungsvariablen ein, anhand derer Sie feststellen können, wer sich am anderen Ende befindet. Beispielsweise SSH_CLIENTenthält die Verbindungs IP-Adresse (und den ausgehend / eingehend Port des Clients als auch). Wenn ~/.bashrcSie damit herumspielen , können Sie möglicherweise Dinge nur für Sie tun .
Sami Laine
1
Das Kopfgeld besagt, dass Sie nach einer "clientseitigen Lösung" suchen - was ist das? ssh auf einem Unix-Host? Kitt? Kolibri? Java SSH? Cygwin?
Jeff Schaller
1
Sie erwähnen Zehntausende von Maschinen. Möchten Sie von einer Maschine aus starten und zu diesen Maschinen gelangen, oder möchten Sie in der Lage sein, von Maschine zu Maschine zu Maschine zu springen und den vi-Modus mit sich zu führen?
Ikarus

Antworten:

3

Hier ist eine dumme Methode, die wirklich nur mit der Authentifizierung mit öffentlichem Schlüssel gut funktioniert:

Stellen Sie zunächst sicher, dass Ihr lokaler Computer installiert ist nc.

Zweitens, noch auf Ihrem lokalen Computer, erstellen Sie ein Skript (ich werde es nennen connect-to-server) und platzieren Sie es an einem Ort, den Sie ${PATH}kennen *:

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

Ändern Sie als Nächstes das .bashrcauf dem Remote-System, um Folgendes einzuschließen:

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

Bearbeiten Sie schließlich auf Ihrem lokalen Computer Folgendes ~/.ssh/config:

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

Nachteile dieses Ansatzes (und warum ich es albern nenne):

  • Wenn ein tatsächlicher Proxy-Befehl benötigt wird, wird dieser komplizierter.
  • Wenn sich jemand anderes zur gleichen Zeit wie Sie anmeldet, besteht die Möglichkeit, dass die .yournameDatei noch nicht gelöscht wurde. In diesem Fall erhält er auch die Datei set -o vi.
  • Wenn Sie dies tun ssh serverNickname command, commandwird .bashrcdie .yournameDatei am wichtigsten ausgeführt, aber (da sie nie bezogen wird) bleibt die Datei erhalten. Daher wäre es höflich, einen zweiten Alias ​​in Ihrer SSH-Konfiguration zu haben, der den Pseudo-Proxy nicht verwendet.

Tatsächlich besteht der einzige Vorteil dieses Ansatzes darin, dass Ihrem sshBefehl keine zusätzlichen Argumente gegeben werden müssen.


* Wenn Sie auf Remote-Systemen nichts ändern müssen, finden Sie hier einen alternativen Pseudo-Proxy, der eine temporäre Datei erstellt .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

Dies hat dieselben Nachteile wie die andere Methode, sodass Sie immer noch einen zweiten Alias ​​in Ihrer sshKonfiguration wünschen , der den Pseudo-Proxy nicht aufruft.

Fuchs
quelle
Sehr kreativ. Vielen Dank für den Vorschlag. Ich benutze ProxyCommand bereits ausgiebig (einige Netzwerke / Hosts benötigen mehr als 5 Hops, um zu erreichen) und dies würde schnell zu einem Konfigurations-Albtraum führen. Bearbeiten: Wenn Sie sich alle Antworten ansehen, denke ich, dass Ihre am nächsten kommen.
Patrick
4

Ich würde gehen für:

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

Aber wenn Sie ein Administrator sind, können Sie etwas saubereres ausprobieren. Sie können beispielsweise die SendEnvOption ssh auf der Clientseite verwenden, um eine bestimmte Variable zu übertragen, sie AcceptEnvin der sshdKonfiguration (Serverseite) verwenden, um sie zu akzeptieren, und auf dieser Grundlage die Root- .bashrcDatei ändern , um das Verhalten entsprechend dem Wert der Variablen anzupassen.

Dies bedeutet, dass die sshdKonfiguration auf allen Hosts sowie auf deren Hosts geändert werden muss .bashrc. Nicht gerade eine "eigenständige" Methode ...

Uriel
quelle
3

Für eine einfache clientseitige Lösung:

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

Dies wird , wenn die Shell Initialisierungsskripts explizit Anwendungen Root fehlschlagen set -o emacsoder Sätze EDITORzu emacs, oder wenn root - .initrcDatei aufruft emacsTastenbelegungen.

Der Rest dieser Antwort betrifft serverseitige Lösungen.


Dies funktioniert, wenn Sie sshin die Maschine einsteigen und dann Folgendes verwenden sudo -i:

Für Ihre /root/.bashrc:

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

Auf diese Weise können Sie eine persönliche bashrcDatei mit dem Namen erstellen, /root/.bashrc-patrickin der Sie tun können, was Sie möchten set -o vi.

Kombinieren Sie dies mit einem etwas naiven Ansatz, um diese RC-Datei aufzunehmen, abhängig von $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

Dies funktioniert natürlich nur, wenn Sie ständig eine Verbindung von derselben IP-Adresse herstellen ...

Ein anderer Ansatz, der das Kommentarfeld des jeweiligen verwendeten SSH-Schlüssels verwendet und funktioniert, wenn Sie den SSH-Agenten an den Server weiterleiten:

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

Dadurch wird das Kommentarfeld für den Schlüssel ausgewählt, mit dem Sie eine Verbindung zum Server hergestellt haben. Das head -n 1ist da, falls Sie zufällig mehrere Ihrer Schlüssel in der authorized_keysDatei haben.

Sie können dann $ssh_commenteine RC-Datei für die Quelle auswählen, entweder direkt wie beim $SUDO_USERobigen Ansatz (bei dem der Kommentar in $ssh_commentmöglicherweise bereinigt werden muss, wenn es sich um einen Pfadnamen handelt) oder über eine caseAnweisung wie beim $SSH_CLIENTAnsatz.

Kusalananda
quelle
Matching SSH_CLIENTund SUDO_USERist das, was ich derzeit verwende, aber es erfordert serverseitige Änderungen und es ist nicht besonders zuverlässig. Ich hatte auf eine reine clientseitige Lösung gehofft. Vielen Dank für den Vorschlag.
Patrick
3

Wenn Sie es wirklich ohne Änderungen auf der Serverseite tun möchten, entweder:

1) Führen Sie so etwas wie

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

Ich denke nicht, dass die Dokumentation dazu zu klar ist, wird aber -o optionerwähnt und scheint zu funktionieren.

2) Verwenden Sie erwarten:

Das expectSkript:

$ 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

Machen Sie es ausführbar und führen Sie Folgendes aus:

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

Dies setzt voraus, dass Sie sich anmelden können, ohne Kennwörter einzugeben (für den Remote-Host oder für Ihre Schlüssel). Andernfalls müsste das Expect-Skript dies berücksichtigen. Aber mit vielen Maschinen haben Sie das wahrscheinlich schon. Außerdem habe ich für ein Dollarzeichen und ein Leerzeichen erwartet , bearbeiten Sie das entsprechend Ihrer Eingabeaufforderung: "# "vielleicht.

Wenn etwas, das vor der Eingabeaufforderung gedruckt wurde, dieselben Zeichen enthält, müssen Sie der erwarteten Zeichenfolge etwas Spezifischeres hinzufügen.

Außerdem unterstützt dieses Skript keine zusätzlichen Argumente ssh. Wenn Sie einen expliziten Befehl zum Ausführen geben möchten, benötigen Sie wahrscheinlich keinen vi-Modus. Wenn Sie jedoch beispielsweise Port-Tunneling benötigen, ist dies möglicherweise ein Problem.


Aber auf jeden Fall denke ich wirklich, dass dies auf den Zielsystemen mit separaten Konten ( sudooder einfach nur alter UID 0) gelöst werden sollte . Eine personalisierte Konfiguration wäre auch in vielen anderen Fällen nützlich, und im Allgemeinen verfügen Sie über eine Reihe von Konfigurationsdateien und Umgebungsvariablen, die Sie festlegen möchten. (Bedenken Sie, dass sich die Administratoren möglicherweise nicht über den Wert $EDITORoder den Inhalt von vircoder was auch immer einig sind .)

Auch das Entfernen von Benutzern wäre mit separaten Konten einfacher.

Jede Möglichkeit, Dateien auf allen Hosts zu synchronisieren, würde dies ebenfalls trivial lösen, indem Sie sich mit etwas wie ssh -t user@host 'patricks_shell.sh'oder anmelden können ssh -t user@host 'bash --rcfile patrick.rc'.

ilkkachu
quelle
Ich habe überlegt, Expect zu verwenden, aber wie Sie bereits in Ihrer Frage betont haben, stimmt das Problem mit etwas Einzigartigem überein, damit die Eingabeaufforderung gestartet wird interact. Es existiert nicht, Eingabeaufforderungen können sehr unterschiedlich sein, ebenso wie die Motds / Ausgaben von Profilen. Vielen Dank für den Vorschlag.
Patrick