Як встановити режим bash readline у ​​режим vi автоматично після входу в систему?


9

Моя команда відповідає за тисячі машин Linux / Unix, тому, природно, кореневий обліковий запис "ділиться" серед адміністраторів. Я віддаю перевагу режиму vi, інші віддають перевагу режиму emacs.

Як я можу встановити режим зчитування bash у режим vi під час входу в SSH на будь-якій машині, не примушуючи всіх інших також використовувати режим vi?

По суті, хотілося б мати ефект set -o viпісля входу, фактично не вводячи його кожного разу і не примушуючи його до всіх інших (стільки, як режим emacs мені дратує, режим vi їх дратує).

Я знаю, що це не буде проблемою, якби кожен використовував свої власні акаунти з sudo для виконання привілейованих команд, але через обставини, які поза моїм контролем, це, на жаль, не є можливим.


1
Це непросто. Один із способів - проаналізувати файл журналу sshd і побачити, який ключ використовувався для входу. Я сподівався на рішення на стороні клієнта, наприклад, спосіб передати свій локальний конфігурацію читання на віддалену сторону чи щось подібне, або якась темна магія OpenSSH, яка мовчки виконує, set -o viперш ніж дати мені керувати оболонкою.
Патрік

1
Можливо, ви могли б використовувати сценарій Expect на клієнті, який входить в сервер, відправити set -o viкоманду, а потім перейти в інтерактивний режим.
Бармар

1
Принаймні OpenSSH sshdвстановлює кілька змінних середовища, які можуть допомогти вам визначити, хто знаходиться на іншому кінці. Наприклад, SSH_CLIENTмістить з'єднувальну IP-адресу (і вихідний / вхідний порт клієнта). Погортання цього пункту ~/.bashrcможе допомогти вам робити те саме .
Самі Лайн

1
Баунті говорить, що ви шукаєте "рішення на стороні клієнта" - що це? ssh на хості Unix? Шпаклівка? Колібрі? Java SSH? Сігвін?
Jeff Schaller

1
Ви згадуєте десятки тисяч машин. Ви хочете почати з однієї машини і дістатися до цих машин, чи ви хочете мати можливість переходити з машини на машину до машини, що перевозить режим vi з собою?
ікар

Відповіді:


3

Ось дурний спосіб зробити це, який справді добре працює лише з аутентифікацією відкритого ключа:

Спочатку переконайтеся, що ваша локальна машина ncна ньому.

По-друге, все ще на локальній машині зробіть скрипт (я його зателефоную connect-to-server) і поставте його в місце, про яке ви ${PATH}знаєте *:

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

Далі змініть .bashrcна віддаленій системі, щоб десь включити:

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

Нарешті, поверніться на локальній машині та змініть, ~/.ssh/configщоб додати:

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

Недоліки такого підходу (і чому я називаю це дурним):

  • Якщо потрібна фактична команда проксі, вона ускладнюється.
  • Якщо хтось інший заходить у той самий час, коли ви це зробите, є ймовірність, що .yournameфайл ще не буде видалено, і в цьому випадку він також отримає set -o vi.
  • Найголовніше, якщо ви це зробите ssh serverNickname command, тоді commandбуде запущено, але (оскільки .bashrcвін ніколи не отримується) .yournameфайл залишається, тому було б ввічливо мати другий псевдонім у вашому ssh-конфігурації, який не використовує псевдопроксі.

Насправді єдиний перебіг такого підходу полягає в тому, що вашій sshкоманді не потрібно давати зайвих аргументів.


* Якщо вам не потрібно нічого змінювати у віддалених системах, ось альтернативний псевдопроксі, який створює тимчасовий .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

Це має однакові недоліки іншого методу, тому ви все одно хочете отримати другий псевдонім у своїй sshконфігурації, який не викликає псевдопроксі.


Дуже креативний. Дякую за пропозицію. Я вже широко використовую ProxyCommand (для деяких мереж / хостів потрібен 5+ стрибків), і це швидко призведе до кошмару конфігурації. Редагувати: Дивлячись на всі відповіді, я думаю, що ваш найближчий.
Патрік

4

Я б пішов на:

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

але це ви адміністратор, ви можете спробувати щось чистіше. Наприклад, ви можете використовувати параметр ssh SendEnvна стороні клієнта для передачі певної змінної, використовуйте AcceptEnvв sshdконфігурації (стороні сервера), щоб прийняти її, і, виходячи з цього, змінити .bashrcфайл root, щоб регулювати поведінку відповідно до значення змінної.

Це означає зміну sshdконфігурації на всіх хостах, а також їхніх .bashrc. Однак не зовсім «самостійний» спосіб ...


3

Для простого рішення на стороні клієнта:

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

Це не спрацює , якщо при ініціалізації оболонки сценаріїв в корені явно , використовує set -o emacsабо набори EDITORдля emacs, або якщо кореневого .initrcфайлу викликає emacsприв'язки клавіш.

Решта цієї відповіді стосується серверних рішень.


Це працює, коли ви sshвходите в машину, а потім використовуєте sudo -i:

Для вашого /root/.bashrc:

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

Це дозволяє мати особистий bashrcфайл під назвою, в /root/.bashrc-patrickякому ви можете робити все, що завгодно, як завгодно set -o vi.

Поєднуючи це з дещо наївним підходом до вибору файлу rc залежно від $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

Це, очевидно, працює лише в тому випадку, якщо ви постійно підключаєтесь до однієї і тієї ж IP-адреси ...

Інший підхід, який використовує поле коментаря певного ключа SSH, який ви використовуєте, який працює, якщо ви пересилаєте агент SSH на сервер:

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

Тут вибирається поле коментаря для ключа, який ви використовували для підключення до сервера. Це head -n 1є у випадку, якщо у вас є кілька ваших ключів у authorized_keysфайлі.

Потім ви можете скористатися $ssh_commentдля вибору rc-файлу до джерела, або безпосередньо, як із $SUDO_USERпідходом вище (в якому коментар, $ssh_commentможливо, знадобиться пройти деяке очищення, якщо це ім'я шляху), або через caseзаяву, як у випадку $SSH_CLIENTпідходу.


Відповідність SSH_CLIENTі SUDO_USERщо я використовую в даний час, але це вимагає боку сервера модифікації , і це не дуже надійно. Я сподівався на чисте рішення на стороні клієнта. Дякую за пропозицію, хоча.
Патрік

3

Якщо ви дійсно хочете зробити це без змін на стороні сервера, виконайте такі дії:

1) Виконати щось на кшталт

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

Я не думаю, що документація щодо цього занадто чітка, але -o optionзгадується і, здається, працює.

2) Використовуйте очікувані:

expectскрипт:

$ 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

Зробіть його виконуваним і запустіть:

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

Це передбачає, що ви можете увійти без введення паролів (для віддаленого хоста або для ваших ключів), інакше сценарій очікування повинен врахувати це. Але з великою кількістю машин ви, ймовірно, це вже маєте. Крім того, я розраховував на знак долара та пробіл, відредагуйте це відповідно до вашого підказу: "# "можливо.

Хоча якщо щось надруковане перед підказкою включає ці самі символи, вам потрібно буде включити щось очікуване в очікуваний рядок.

Крім того, цей скрипт не підтримує надання додаткових аргументів ssh. Якщо ви збираєтесь дати явну команду для запуску, вам, ймовірно, не потрібен режим vi-mode, але якщо вам потрібно сказати тунелювання портів, це може бути проблемою.


Але в будь-якому випадку, я дійсно думаю, що це слід вирішити в цільових системах з окремими обліковими записами ( sudoабо просто простим старим UID 0). Персоналізована конфігурація була б корисною і для багатьох інших випадків, і загалом у вас є купа файлів конфігурації та змінних оточення, які ви хочете встановити. (Вважайте, що адміністратори можуть не погодитись із значенням $EDITOR, вмістом vircчи іншим.)

Також видалити користувачів буде простіше за допомогою окремих облікових записів.

Будь-який спосіб синхронізації файлів на всіх хостах також тривіально вирішить це, дозволяючи увійти в систему з чимось на зразок ssh -t user@host 'patricks_shell.sh'або ssh -t user@host 'bash --rcfile patrick.rc'.


Я думав використати очікування, але, як ви вже зазначали у своєму запитанні, проблема полягає у узгодженні чогось унікального для підказки interact. Його не існує, підказки можуть бути різними, як і мотиви / результати з профілів. Дякую за пропозицію, хоча.
Патрік

1

Ви можете мати спеціальний файл rc, дотримуючись цього посібника:

Файл користувача rc | Безпечна оболонка: остаточне керівництво

або введіть дію командного рядка у файл санкціонованих_кілей:

Конфігурація SSH автоматично виконує віддалену команду | Обмін стеками Unix та Linux

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.