SSH 원격 명령줄 매개변수를 구문 분석하는 방법

SSH 원격 명령줄 매개변수를 구문 분석하는 방법

원격 SSH 명령 매개변수를 이중 이스케이프해야 하는 필요성에 대한 질문과 답변을 보았습니다. 내 질문은: 두 번째 구문 분석이 정확히 언제 어디서 완료됩니까?

다음 명령을 실행하면:

$ ssh otherhost pstree -a -p

출력에 다음이 표시됩니다.

  |-sshd,3736
  |   `-sshd,1102
  |       `-sshd,1109
  |           `-pstree,1112 -a -p

원격 명령( ) pstree의 상위 프로세스에는 sshd원격 명령에 대한 명령줄 인수를 구문 분석하는 쉘이 없는 것 같으므로 큰따옴표나 이스케이프가 필요하지 않은 것 같습니다(그러나 실제로는 그렇습니다). 대신, 먼저 ssh를 통해 로그인 셸을 얻은 다음 실행하면 pstree -a -p출력에 다음이 표시됩니다.

  ├─sshd,3736
  │   └─sshd,3733
  │       └─sshd,3735
  │           └─bash,3737
  │               └─pstree,4130 -a -p

bash분명히 이런 상황에서 명령줄 구문 분석을 수행할 수 있는 쉘이 있습니다 . 그런데 원격 명령을 직접 사용하면 쉘이 없는 것 같은데 왜 큰따옴표가 필요한 걸까요?

답변1

항상 원격 쉘이 있습니다. SSH 프로토콜에서 클라이언트는 실행할 문자열을 서버에 보냅니다. SSH 명령줄 클라이언트는 명령줄 인수를 가져와 인수 사이에 공백을 사용하여 연결합니다. 서버는 문자열을 가져와서 사용자의 로그인 셸을 실행하고 여기에 문자열을 전달합니다. (좀 더 정확하게 말하자면, 서버는 사용자 데이터베이스에 사용자 쉘로 등록된 프로그램을 실행하여 두 개의 명령줄 인수 -c와 클라이언트가 보낸 문자열을 전달합니다. 이 쉘은 로그인 쉘로 호출되지 않습니다. 서버는 이를 수행하지 않습니다. 0번째 매개변수는 )로 시작하는 문자열로 설정됩니다 -.

원격 셸을 우회하는 것은 불가능합니다. 프로토콜은 서버에서 argv 배열로 구문 분석할 수 있는 문자열 배열과 같은 것을 보내지 않습니다. 그리고 SSH 서버는 원격 셸을 우회하지 않습니다. 이는 보안 제한일 수 있기 때문입니다. 제한된 프로그램을 사용자의 셸로 사용하는 것은 특정 명령(예: rsync 전용 계정 또는 Git 전용 계정 계정).

pstree케이스가 사라져서 보지 못할 수도 있습니다 . 많은 쉘에는 "이 외부 명령을 실행하고 완료될 때까지 기다린 후 명령 상태로 종료"가 곧 실행될 것임을 감지하면 쉘이 실행되는 최적화 기능이 있습니다.execveexternal command" 대신에. 이것은 첫 번째 예에서 발생하는 일입니다. 다음 세 가지 명령을 비교하십시오.

ssh otherhost pstree -a -p
ssh otherhost 'pstree -a -p'
ssh otherhost 'pstree -a -p; true'

처음 두 개는 동일합니다. 클라이언트는 정확히 동일한 데이터를 서버에 보냅니다. 세 번째는 쉘의 exec 최적화를 중단시키는 쉘 명령을 보냅니다.

답변2

나는 그것을 알아낸 것 같아요:

$ ssh otherhost pstree -a -p -s '$$'
init,1         
  `-sshd,3736
      `-sshd,11998
          `-sshd,12000
              `-pstree,12001 -a -p -s 12001

매개변수는 pstree명령줄 인수 표시, pid 표시 및 지정된 pid에 대한 상위 프로세스만 표시입니다. 이것은 '$$'bash가 명령줄 인수를 평가할 때 bash가 자체 pid로 대체하는 특수 쉘 변수입니다. 내 로컬 쉘에서 해석되는 것을 방지하기 위해 한 번 인용됩니다. 그러나 원격 셸에서 해석할 수 있도록 큰따옴표로 묶거나 이스케이프 처리하지 않습니다.

보시다시피 이는 12001다음으로 대체됩니다. 따라서 이것은 쉘의 pid입니다. 출력에서도 확인할 수 있습니다. pstree,12001PID가 12001인 프로세스는 pstree 자체입니다. pstree쉘도 마찬가지인가요?

내가 수집한 것은 그것이 bash호출되고 있고 명령줄 인수를 구문 분석하고 있지만 exec실행 중인 명령으로 자신을 대체하기 위해 호출된다는 것입니다.

단일 원격 명령의 경우에만 이 작업을 수행하는 것 같습니다.

$ ssh otherhost pstree -a -p -s '$$' \; echo hi
init,1         
  `-sshd,3736
      `-sshd,17687
          `-sshd,17690
              `-bash,17691 -c pstree -a -p -s $$ ; echo hi
                  `-pstree,17692 -a -p -s 17691
hi

이 경우 두 개의 명령 을 실행하도록 요청합니다: 그 pstree뒤에 echo.bashpstree

답변3

다른 답변을 뒷받침해 원격 명령을 호출하는 코드를 찾아보니,https://github.com/openssh/openssh-portable/blob/4f29309c4cb19bcb1774931db84cacc414f17d29/session.c#L1660...

1660    /*
1661     * Execute the command using the user's shell.  This uses the -c
1662     * option to execute the command.
1663     */
1664    argv[0] = (char *) shell0;
1665    argv[1] = "-c";
1666    argv[2] = (char *) command;
1667    argv[3] = NULL;
1668    execve(shell, argv, env);
1669    perror(shell);
1670    exit(1);

shell...보시다시피 첫 번째 -c와 두 번째 인수를 무조건 호출합니다 command. 이전에 이 변수는 shell다음에 /etc/passwd설명된 대로 사용자의 로그인 셸로 설정되었습니다. command궁극적으로 문자형 읽기로 설정되는 이 함수에 대한 인수입니다(참조:session_exec_req같은 파일에). 따라서 서버는 명령을 전혀 해석하지 않고 항상 원격으로 셸을 호출합니다.

하지만, 그SSH 프로토콜 사양의 관련 부분하다아니요이 동작은 필요한 것 같습니다.

 byte      SSH_MSG_CHANNEL_REQUEST
 uint32    recipient channel
 string    "exec"
 boolean   want reply
 string    command

이 메시지는 서버에 주어진 명령 실행을 시작하도록 요청합니다. "명령" 문자열에는 경로가 포함될 수 있습니다. 승인되지 않은 주문이 실행되는 것을 방지하려면 일반적인 예방 조치를 취해야 합니다.

이는 모든 운영 체제에 명령줄 셸 개념이 없기 때문일 수 있습니다. 예를 들어, 클래식 MacOS SSH 서버는 "exec" 명령 문자열을 제공합니다.사과 스크립트통역사를 대신합니다.

관련 정보