Мне нужны идеи, чтобы создать способ удаленного выполнения (любых) команд через несколько переходов. Основная проблема в том, что количество промежуточных переходов непостоянно! Итак, мне нужно 1) описать «топологию» серверов, а затем 2) иметь функцию? / Script, которая может выполнять некоторые команды на всех из них, используя промежуточные переходы по мере необходимости для достижения конечного пункта назначения.
пример топологии : (но это тоже может быть разным)
server A communicates with server B directly.
servers C and D are "behind" B.
server E is behing C
etc.... (many servers, many indirect ways to reach some of them, some have multiple ways)
Then from A, you can execute "some command" on E with:
ssh foo@B ssh bar@C ssh baz@E "some command"
or you can build intermediary tunnels connecting A to **C** and then:
ssh -P 1234 baz@E "some command"
У меня есть несколько «основных переходов», которые могут напрямую подключаться к множеству серверов
Я хочу иметь возможность (потенциально сложную) на всех 5 серверах через не слишком сложную команду, позволяющую мне (в скрипте) сделать что-то вроде
do_on_all_servers "for dir in /something* ; do cd \"\$dir\" && something using $thisLOCALvariable ; done"
# with an easy way to say "from X, ssh to specificuser@Y using this ssh command and this username" to describe each intermediary hops
# dir is a remote variable, thisLOCALvariable is at the initiating script level, and the amount of "\" needs to be adjusted if going through multiple hops...
Дополнительное ограничение : нет инструментов GNU, нет "nc", просто простой (очень) старый ssh & awk и аналогичные старые инструменты, чтобы иметь переносимый способ. Переносимость должна быть высокой (следует избегать использования изящных опций и инструментов "> 1990?"). И я бы предпочел не делать этого, копируя скрипт поверх конечного пункта назначения & ssh через несколько переходов этого скрипта, но это может быть нормально, если вы думаете, что это проще / лучше.
Прямо сейчас я использую переменные для объединения ssh .... но это не помогает решить проблему «сколько -" \ "- нужно ли мне.
Моя идея заключалась в следующем: объединить переменные, содержащие "shh user @ host" вместе. Или, может быть, с помощью этого очень аккуратного https: // stackoverflow.com / a / 15198031/1841533 способ туннелирования, который, если я смогу сделать это должным образом, позволит выполнять scp и т. д., используя правильный локальный порт для достижения конечного пункта назначения "напрямую" (но не уверен, что этот способ доступен для всех хмель).
Вы можете написать / использовать функцию оболочки ssh
, которая генерирует настраиваемый файл конфигурации, который использует вложенные ProxyCommands
, чтобы добраться до вашего последнего хоста.
Затем функция вызовет ssh
с -F
, указывающим на сгенерированный временный файл конфигурации.
Примерно так:
sssh() {
TMPF=$(mktemp);
echo -n "$1" |
awk '
BEGIN {RS=" "}
{ split($0,a,":"); u=a[1]; p=a[3]; h=a[2]
print "\nHost "h"\n\tUser "u"\n\tPort "p;
if (hop) print "\tProxyCommand ssh -q -W %h:%p "hop;
hop=h }
END {print "\nHost *\n\tControlMaster auto\n\tControlPath /tmp/%r@%h:%p"}
' > $TMPF; TMPA=($1); TMPAA=(${TMPA[-1]//:/ }); TMPH=${TMPAA[1]}; shift;
ssh -F $TMPF $TMPH $@;
rm $TMPF; }
При таком запуске:
sssh "user1:so.me.ho.st:1234 user2:router.internal:2345 user3:device.internal:3456" do_complex_command
сгенерирует tmp-файл вроде этого:
Host so.me.ho.st
User user1
Port 1234
Host router.internal
User user2
Port 2345
ProxyCommand ssh -q -W %h:%p so.me.ho.st
Host device.internal
User user3
Port 3456
ProxyCommand ssh -q -W %h:%p router.internal
Host *
ControlMaster auto
ControlPath /tmp/%r@%h:%p
и, наконец, запустит:
ssh -F /tmp/tmp.IUVSRrer45 device.internal do_complex_command
Это выполнит do_complex_command
на последнем "самый внутренний" хозяин.Если вам нужно, чтобы ваша команда выполнялась на всех посредниках, вам придется немного адаптировать функцию.
Я нашел довольно переносимое решение (не требует никаких изменений в файлах конфигурации ssh, и его легко содержать в вызывает сам скрипт, и его довольно легко использовать): просто соедините вместе ssh, и последний вызовет оболочку (ksh или bash хорошо работают для "немного старых" unix):
echo "for remotedir in /some/path/*/ ; do \
cd \"\$remotedir\" && \
something using $thisLOCALvariable \
; done" | ssh user1@host1 ssh user2@host2 ssh user3@host3 "bash"
# note: the 'obvious' approach:
# ssh u1@h1 ssh u2@h2 "cmd1 ; cmd2; ..."
# does NOT work : it would execute "cmd2; ..." on h1, not on h2 !!!
# only cmd1 would be executed on h2 ... BE CAREFUL ! (at least on a not-too-old cygwin)
Этот пример показывает, что мы можем смешивать локальные переменные и удаленные ... и необходимость "избегать" удаленных, а также удаленных двойных кавычек и т.д ... необходимо проявлять некоторую осторожность, чтобы гарантировать, что мы делаем то, что намеревались сделать.
Но это очень удобно, так как всегда есть один и тот же (только 1!) Уровень цитирования, даже если мы объединим 3 или более ssh-вложений, что очень удобно ...Это похоже на то, что приносит $ (cmd)
по сравнению со старым подходом `
(обратная кавычка): он упрощает запись без необходимости принимать во внимание уровень вложенности.
предостережение : указав это в качестве параметров сценария (как я задал в вопросе: on_all_servers "для каталога в / something *; выполните cd \" \ $ dir \ "&& something с помощью $ thisLOCALvariable; done "
) немного сложнее понять, поскольку вызывающая оболочка сначала интерпретирует" \ "и кавычки ... поэтому сейчас я использую версию для командной строки (или аналогичную конструкцию со сценарием, но не сценарий, который получает командную строку из своих аргументов) ...
Я по-прежнему оставляю вопрос открытым, надеясь, что кто-то получит еще лучшее решение (или его улучшение, например: разрешить передачу аналогичной команды как параметр "вызывающего сценария")
Edit : Я также узнал, как выполнять удаленное извлечение tar с помощью этого метода! Это нетривиально, поскольку stdin необходимо передать удаленной оболочке дважды, один раз, чтобы попасть в соответствующий каталог, затем локальный tar cf - необходимо перенаправить на этот удаленный tar в нужном месте: вот оно:
# the easy one, nothing special needed: from remote to local:
echo "cd /remote/path && tar cf - *" | ssh user1@host1 ssh user2@host2 ssh user3@host3 "bash" | ( cd /local/path && tar xvf - )
# and the hard one: the other way around: needs a trick:
( cd /local/path && tar cf - * ) | { { echo "cd /remote/path/ && tar xvf -" ; cat ;} | ssh user1@host1 ssh user2@host2 ssh user3@host3 "bash" ;}
#once again: this trick allows you to have 1, 2, or any level of ssh.
# PLEASE NOTE: I just run bash, and feed it the command, to avoid
# the command to be split between the remote hose and the first hop!
# ex of the way the 'obvious' way fails:
# ssh user1@host1 ssh user2@host2 "hostname ; hostname;"
# will return not twice the name "host2", but "host2" then "host1" !!
# which means the first part of the command was executed on the last hop,
# but then the rest of it was executed on the first hop!!! DANGEROUS!
# that is why I choose instead to use that way:
# echo "hostname; hostname" | ssh user1@host1 ssh user2@host2 "bash"
# this one properly executes the full (complex) command on host2