SSH를 통해 환경 변수를 명령에 전달할 생각입니다.
$ ssh HOST A=1 set | grep ^A
$ ssh HOST A=1 sh -c set | grep ^A
A='1'
$ ssh HOST A=1 echo '$A'
$ ssh HOST A=1 sh -c 'echo $A'
따라서 set
환경변수는 보이지 않고, 별도의 프로세스에서 보입니다. 같은 일이 일어나지 않는 것 같습니다 echo
.
sshd
정확히 무엇이 전송되었는지, 어떻게 실행되었는지( 어떤 명령이 실행되었는지) 확인할 수 있는 방법이 있나요 ?
답변1
를 실행하면 ssh user@host command
확장 프로그램에서 생성된 토큰이 command
중간 공백과 연결되어 ssh
문자열로 원격 호스트에 전송됩니다. 그런 다음 원격 호스트는 원격 사용자의 기본 셸( 에 지정된 것 /etc/passwd
)의 인스턴스를 실행하고 클라이언트로부터 받은 문자열을 셸 -c
옵션에 대한 인수로 전달합니다(자세한 내용은 아래 참조).
예를 들어
$ foo="bar baz"
$ ssh user@host echo "$foo"
bar baz
로컬은 두 개의 매개변수 와 를 ssh
받습니다 . 이를 연결하여 문자열을 형성 하고 이를 서버로 보냅니다. 서버는 다음과 같은 작업을 수행 하고 명령의 출력은 표준 출력에 로컬로 인쇄됩니다.echo
bar baz
echo bar baz
/bin/sh -c 'echo bar baz'
ssh
클라이언트가 실제로 서버에 보낸 내용을 어떻게 확인할 수 있나요?
~처럼Kamil Machorovsky는 다음과 같이 지적했습니다.질문의 주석에서 ssh -v
서버로 전송된 명령 문자열은 디버그 메시지로 출력됩니다.
$ ssh -v host A=1 sh -c set | grep ^A
...
debug1: Sending command: A=1 sh -c set
...
sshd
실제로 실행되는 내용을 어떻게 확인할 수 있나요 ?
클라이언트에 자체 작업에 대한 일종의 추적을 표시하도록 서버에 요청하는 방법을 모르겠습니다. 그러나 테스트 목적으로 strace
데몬 인스턴스를 사용할 수 있습니다.
$ sudo strace -e execve -f /usr/bin/sshd -p 2222
이는 원격 실행을 위한 명령줄을 작성할 때 올바르게 인용하지 못하는 경우가 쉽기 때문에 특히 유용합니다. 예를 들어(귀하의 질문에 제공된 예의 변형):
$ ssh -p 2222 host bash -c 'A=1 set'
# Prints nothing
strace
우리가 본 명령 문자열은 실제로 원격 호스트에서 분할되었으며, 먼저 사용자의 기본 셸에서 전체 명령줄을 실행했음을 알 수 있습니다(로컬 셸이 인수 목록을 작성할 때 작은따옴표가 제거되었습니다 ssh
).
... execve("/bin/bash", ["bash", "-c", "bash -c A=1 set"] ...
그런 다음:
... execve("/usr/bin/bash", ["bash", "-c", "A=1", "set"] ...
명령 문자열( 의 매개변수 -c
)은 입니다 A=1
. 0번째 인수로 set
전달됩니다 ( 명령 이름으로 사용 bash
가능 ).$0
원격 셸이 실제로 실행한 내용을 확인하는 방법은 무엇입니까?
다시 말하지만, 나는 쉬운 방법을 모릅니다. 간단한 경우에는 쉘 옵션을 사용하는 것이 xtrace
간단합니다. 예를 들어 ssh host A=1 set
다음과 같을 수 있습니다.
$ command=$(printf '%s ' A=1 set); printf '%s\n' "${command% }" |
ssh host sh -x
sh
원격 사용자의 기본 셸로 교체되어야 합니다.
명령은 ssh
표준 입력으로 파이프됩니다. 이 양식을 사용하면 변경 없이 입력할 수 있습니다( ssh host sh -xc A=1 set
위에 표시된 이유로 작동하지 않으며 따옴표를 추가하면 이 연습의 실제 목적이 무산됩니다).
덜 간단한 경우, 특히 ssh
조사 중인 호출이 이미 표준 입력을 사용하는 경우 원격 호스트가 xtrace
쉘을 연 상태에서 강제로 실행하도록 할 수 있습니다.실험 목적 및 오류로 인해 원격 시스템에 로그인하지 못할 수 있음을 인지하기 위해, 한 가지 (다소 조잡한) 접근 방식은 원격 사용자의 기본 셸을 교체하는 것일 수 있습니다 /etc/passwd
. 이라고 가정하고 /bin/sh
로 변경 /home/your_user/bin/sh
하고 후자를 실행 가능한 스크립트로 생성합니다.
#!/bin/sh
exec /bin/sh -x "$@"
그러면 쉘이 실행 중인 내용에 대한 추적을 표시합니다.
$ ssh host A=1 echo foo
foo
+ A=1
+ echo foo
마지막으로 예제 명령과 관련하여 다음을 수행하십시오.
ssh HOST A=1 set
인쇄되지 않는 이유는A=1
사용자의 기본 셸이 Bash(아마도 호출됨sh
)이기 때문일 수 있습니다.dash
,ksh
, Bash(적어도 내가 가지고 있는 버전 )yash
에서 발생하는 것과는 반대로 변수 세트는 출력되지 않습니다zsh
.set
5.0.17
set
명령 자체 의 경우. 반면에 다음을 확인할 수도 있습니다.$ ssh host 'A=1 > set' | grep ^A A=1
Ie가 호출 전에 선언되면 출력
A
에 표시됩니다 .set
set
에서 환경에 추가되었으므로
A=1 sh -c set
당연히 인쇄됩니다 .A
sh
set
A=1 echo '$A'
A
확장 후 실행 환경에만 추가되기 때문에 값이 인쇄되지 않습니다A
(내장되어 있다고 가정). 인쇄된 것과 비교해 보세요.echo
$A
ssh host 'A=1; echo $A'
1
A=1 sh -c 'echo $A'
위에 표시된 것처럼 원격 호스트는 옵션-c
과 인수를 사용하여A=1 sh -c echo $A
원격 사용자의 기본 셸을 실행한 다음 해당 셸이 실행되기 때문에 아무 것도 인쇄되지 않습니다sh -c echo $A
. 명령 문자열은echo
인수 없이 바로 가 됩니다.이것이 작동하려면 주변 따옴표가
echo $A
원격 호스트로 전송되는지 확인해야 합니다.그리고$A
조기 확장 방지(로컬 셸 또는 "외부" 원격 셸을 통해): 예:ssh host A=1 sh -c '"echo \$A"'
또는ssh host A=1 sh -c "'echo \$A'"
.원격 셸을 명시적으로 호출할 때 표준 입력에서 명령을 읽도록 하는 것이 더 쉬울 수 있습니다.
$ echo 'echo $A' | ssh host A=1 sh -s 1