ssh는 (가변 수의) 중간 홉을 통해 일부 명령을 실행합니다.

ssh는 (가변 수의) 중간 홉을 통해 일부 명령을 실행합니다.

여러 홉을 통해 (모든) 명령을 원격으로 실행하는 방법을 만들려면 아이디어가 필요합니다. 가장 큰 문제는 중간 홉의 수가 가변적이라는 것입니다! 따라서 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 등의 최종 목적지에 "직접"(그러나 이것이 모든 홉에서 작동하는지 확실하지 않음) 도달할 수 있습니다.

답변1

셸 함수 래퍼를 작성/사용하여 중첩을 사용하여 최종 호스트에 도달하는 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마지막 "가장 안쪽" 호스트에서 실행됩니다. 모든 중개자에서 명령을 실행해야 하는 경우 기능을 약간 조정해야 합니다.

답변2

나는 상당히 이식 가능한 솔루션을 찾았습니다(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)

이 예는 로컬 변수와 원격 변수를 혼합할 수 있고 원격 변수와 원격 큰따옴표 등을 "이스케이프"해야 함을 보여줍니다. 의도한 대로 수행되도록 주의를 기울여야 합니다.

하지만 3개 이상의 SSH 중첩을 함께 연결하더라도 항상 동일한(단 1!) 수준의 참조가 있기 때문에 매우 편리합니다... $(cmd)이전 `(안티 따옴표)와 동일합니다. ): 단순화합니다. 중첩 수준을 고려할 필요 없이 쓰기.

경고하다:이를 스크립트에 대한 인수로 사용하는 것(질문에서 요청한 것처럼 on_all_servers "for dir in /something* ; do cd \"\$dir\" && something using $thisLOCALvariable ; done":) 호출 쉘이 "\"와 따옴표를 먼저 해석하기 때문에 파악하기가 약간 어렵습니다... 그래서 이제 명령줄 버전을 사용합니다( 또는 스크립트와 유사한 구성을 사용하지만 인수에서 명령줄을 가져오는 스크립트 대신)...

나는 누군가가 더 나은 솔루션(또는 이에 대한 개선, 즉 "호출 스크립트"에 대한 인수로 유사한 명령을 전달할 수 있도록 허용)을 생각해 낼 수 있기를 바라면서 이 질문을 열어 둡니다.

편집하다: 원격 타르 추출에도 이 방법을 활용하는 방법도 알아냈습니다! 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

관련 정보