Bagaimana cara mengatur bash readline ke mode vi secara otomatis saat masuk ke sistem?

9

Tim saya bertanggung jawab atas ribuan mesin Linux / Unix, jadi tentu saja akun root "dibagikan" di antara admin. Saya lebih suka mode vi, yang lain lebih suka mode emacs.

Bagaimana saya bisa mengatur readline bash ke mode vi saat SSH masuk ke mesin apa pun, tanpa memaksa semua orang untuk menggunakan mode vi juga?

Intinya ingin memiliki efek set -o visetelah masuk tanpa benar-benar harus mengetik setiap kali, dan tanpa memaksakan pada orang lain (sebanyak mode emacs mengganggu saya, mode vi mengganggu mereka).

Saya tahu ini tidak akan menjadi masalah jika semua orang akan menggunakan akun mereka sendiri dengan sudo untuk mengeksekusi perintah istimewa, tetapi karena keadaan di luar kendali saya, sayangnya ini bukan pilihan.

Patrick
sumber
1
Itu tidak mudah. Salah satu caranya adalah dengan mem-parsing file log sshd dan untuk melihat kunci mana yang digunakan untuk login. Saya berharap untuk solusi sisi klien, misalnya cara untuk melewatkan konfigurasi readline lokal saya ke sisi jarak jauh atau sesuatu seperti itu, atau beberapa sihir OpenSSH gelap yang diam-diam dieksekusi set -o visebelum memberi saya kendali atas shell.
Patrick
1
Mungkin Anda bisa menggunakan skrip Harapkan pada klien yang ssh masuk ke server, kirim set -o viperintah, lalu beralih ke mode interaktif.
Barmar
1
Setidaknya OpenSSH sshdmembuat beberapa variabel lingkungan yang dapat membantu Anda menentukan siapa yang ada di ujung sana. Misalnya, SSH_CLIENTberisi alamat IP yang menghubungkan (dan port keluar / masuk klien juga). Mengotak-atik ini ~/.bashrcmemungkinkan Anda melakukan hal-hal hanya untuk Anda .
Sami Laine
1
Bounty mengatakan Anda sedang mencari "solusi sisi klien" - apa itu? ssh pada host Unix? Dempul? Burung kolibri? SSH Jawa? Cygwin?
Jeff Schaller
1
Anda menyebutkan puluhan ribu mesin. Apakah Anda ingin memulai dari satu mesin dan sampai ke mesin-mesin ini, atau apakah Anda ingin dapat melompat dari satu mesin ke mesin yang lain menggunakan mode vi?
icarus

Jawaban:

3

Berikut cara konyol untuk melakukannya, yang hanya berfungsi dengan baik dengan otentikasi kunci publik:

Pertama, pastikan bahwa mesin lokal Anda memilikinya nc.

Kedua, masih di komputer lokal Anda, buat skrip (saya akan menyebutnya connect-to-server) dan letakkan di tempat yang Anda ${PATH}ketahui tentang *:

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

Selanjutnya, modifikasi .bashrcpada sistem remote untuk memasukkan suatu tempat:

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

Akhirnya, kembali ke mesin lokal Anda, edit ~/.ssh/configuntuk menambahkan:

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

Kelemahan dari pendekatan ini (dan mengapa saya menyebutnya konyol):

  • Jika ada perintah proxy yang sebenarnya dibutuhkan, itu menjadi lebih rumit.
  • Jika orang lain masuk pada saat yang sama dengan Anda, ada kemungkinan .yournamefile tersebut belum dihapus, dalam hal ini mereka mendapatkan set -o vijuga.
  • Yang paling penting, jika Anda melakukannya ssh serverNickname command, maka commandakan berjalan, tetapi (karena .bashrctidak pernah bersumber) .yournamefile tetap, jadi akan sopan untuk memiliki alias kedua di konfigurasi ssh Anda yang tidak menggunakan pseudo-proxy.

Bahkan, satu - satunya keuntungan dari pendekatan ini adalah bahwa sshperintah Anda tidak perlu diberikan argumen tambahan.


* Jika Anda tidak ingin mengubah apa pun pada sistem jarak jauh, berikut ini adalah pseudo-proxy alternatif yang membuat sementara .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

Ini memiliki semua kelemahan yang sama dari metode lain, jadi Anda masih menginginkan alias kedua dalam sshkonfigurasi Anda yang tidak memanggil pseudo-proxy.

Rubah
sumber
Sangat kreatif. Terima kasih atas sarannya. Saya sudah menggunakan ProxyCommand secara ekstensif (beberapa jaringan / host memerlukan 5+ hop untuk mencapai) dan ini akan dengan cepat menghasilkan mimpi buruk konfigurasi. Sunting: Melihat semua jawaban yang menurut saya paling dekat dengan Anda.
Patrick
4

Saya akan pergi untuk:

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

tetapi jika Anda seorang admin, Anda dapat mencoba sesuatu yang lebih bersih. Misalnya, Anda dapat menggunakan SendEnvopsi ssh di sisi klien untuk mengirimkan variabel tertentu, gunakan AcceptEnvdalam sshdkonfigurasi (sisi server) untuk menerimanya, dan berdasarkan ini, modifikasi .bashrcfile root untuk menyesuaikan perilaku sesuai dengan nilai variabel.

Ini menyiratkan untuk mengubah sshdkonfigurasi pada semua host serta mereka .bashrc. Bukan cara yang "mandiri" untuk dilakukan, namun ...

Uriel
sumber
3

Untuk solusi sisi klien yang mudah:

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

Ini akan gagal jika skrip inisialisasi shell root secara eksplisit menggunakan set -o emacsatau menyetel EDITORke emacs, atau jika .initrcfile root memanggil emacsbinding kunci.

Sisa jawaban ini menyangkut solusi sisi server.


Ini berfungsi saat Anda sshmasuk ke mesin dan kemudian menggunakan sudo -i:

Untuk Anda /root/.bashrc:

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

Ini memungkinkan Anda memiliki bashrcfile pribadi yang disebut di /root/.bashrc-patrickmana Anda dapat melakukan apa pun yang Anda suka set -o vi.

Menggabungkan ini dengan pendekatan yang agak naif untuk mengambil file rc tergantung pada $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

Ini jelas hanya berfungsi jika Anda terhubung dari alamat IP yang sama sepanjang waktu ...

Pendekatan lain yang menggunakan bidang komentar kunci SSH tertentu yang Anda gunakan, yang berfungsi jika Anda meneruskan agen SSH ke server:

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

Ini memilih bidang komentar untuk kunci yang Anda gunakan untuk terhubung ke server. Apakah head -n 1ada kalau-kalau Anda memiliki beberapa kunci Anda dalam authorized_keysfile.

Anda kemudian dapat menggunakan $ssh_commentuntuk mengambil file rc ke sumber, baik secara langsung seperti dengan $SUDO_USERpendekatan di atas (di mana komentar di $ssh_commentmungkin perlu menjalani beberapa pembersihan jika itu nama path), atau melalui casepernyataan seperti dengan $SSH_CLIENTpendekatan.

Kusalananda
sumber
Cocok SSH_CLIENTdan SUDO_USERapa yang saya gunakan saat ini, tetapi membutuhkan modifikasi sisi server dan itu tidak terlalu dapat diandalkan. Saya berharap untuk solusi sisi klien murni. Terima kasih atas sarannya.
Patrick
3

Jika Anda benar-benar ingin melakukannya tanpa modifikasi di sisi server, baik:

1) Jalankan sesuatu seperti

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

Saya tidak berpikir dokumentasi terlalu jelas tentang itu, tetapi -o optiondisebutkan, dan tampaknya berhasil.

2) Gunakan harapkan:

The 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

Jadikan itu dapat dieksekusi dan jalankan:

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

Ini mengasumsikan Anda dapat masuk tanpa memasukkan kata sandi (untuk host jarak jauh atau untuk kunci Anda), jika tidak, skrip harapan harus mempertimbangkannya. Tetapi dengan banyak mesin Anda cenderung sudah memilikinya. Juga, saya mengharapkan tanda dolar dan spasi, sunting yang sesuai dengan prompt Anda: "# "mungkin.

Meskipun jika sesuatu yang dicetak sebelum prompt menyertakan karakter yang sama, Anda harus memasukkan sesuatu yang lebih spesifik dalam string yang diharapkan.

Juga, skrip itu tidak mendukung memberikan argumen tambahan ssh. Jika Anda akan memberikan perintah eksplisit untuk dijalankan, Anda mungkin tidak perlu mode-vi, tetapi jika Anda perlu mengatakan tunneling port, itu mungkin menjadi masalah.


Tetapi bagaimanapun juga, saya benar-benar berpikir ini harus diselesaikan pada sistem target dengan akun terpisah ( sudoatau hanya UID 0 lama). Konfigurasi yang dipersonalisasi juga akan berguna untuk banyak kasus lain, dan secara umum Anda akan memiliki banyak file konfigurasi dan variabel lingkungan yang ingin Anda atur. (Pertimbangkan bahwa admin mungkin tidak menyetujui nilai $EDITOR, atau konten vircatau apa pun.)

Menghapus pengguna juga akan lebih mudah dengan akun terpisah.

Segala cara untuk menyinkronkan file pada semua host juga akan menyelesaikan masalah ini dengan memungkinkan Anda untuk masuk dengan sesuatu seperti ssh -t user@host 'patricks_shell.sh'atau ssh -t user@host 'bash --rcfile patrick.rc'.

ilkkachu
sumber
Saya berpikir untuk menggunakan ekspektasi, tetapi seperti yang telah Anda tunjukkan dalam pertanyaan Anda, masalahnya cocok dengan sesuatu yang unik untuk dimulainya interact. Itu tidak ada, prompt mungkin jauh berbeda, seperti motd / output dari profil. Terima kasih untuk sarannya.
Patrick