스크립트의 인수를 올바르게 구문 분석하고 SSH를 통해 호출된 셸처럼 작동합니다.

스크립트의 인수를 올바르게 구문 분석하고 SSH를 통해 호출된 셸처럼 작동합니다.

나는 사용이 매우 제한된 서버를 가지고 있으며 SSH를 통해 매우 구체적인(및 사용자 정의) 명령 두 개를 실행할 수 있기를 원합니다. 이를 위해 제한된 사용자의 셸을 이 두 가지만 허용하는 사용자 정의 BASH 스크립트로 설정했습니다."주문하다".

/etc/passwd사용자의 정보 는 다음과 같습니다 .

limited:x:1000:1000:,,,:/home/limited:/usr/sbin/limited_shell.bash

limited_shell.bash스크립트는 다음과 같습니다 (지금까지 가지고 있는 스크립트는 작동하지 않습니다).

#!/bin/bash

readonly RECORDER_SCRIPT="/usr/sbin/record.py"
echo "Verifying params: \$1: $1, \$2: $2"
shift

case $1 in
  [0-9]*)
    nc -z localhost $1 < /dev/null >/dev/null 2>&1
    result=$?
    if [[ $result -eq 0 ]]
    then
      echo "O"
    else
      echo "C"
    fi
    exit $result
    ;;
  record)
    $RECORDER_SCRIPT ${*:3}
    exit $?
    ;;
  *)
    exit 0
    ;;
  esac
exit 0

아마도 스크립트에서 추론할 수 있듯이 limited_shell.bash제가 받아들이고 싶은 두 가지 명령은 숫자와 "기록" 문자열입니다. limited_shell.bash번호로 호출 하면 열려있는 로컬 포트에 해당하는지 아니면 닫혀있는 로컬 포트에 해당하는지를 반환합니다. 허용되는 또 다른 명령은 /usr/sbin/record.pyPython 스크립트( )에 대한 호출을 트리거한 다음 입력에서 일부 비디오를 녹화합니다. 두 번째 명령은 추가 인수를 사용하여 호출해야 하며, 여기서 문제가 시작됩니다.

다른 컴퓨터에서 원격으로 명령을 실행하려고 하면 record...

ssh [email protected] record -option1 foo -option2 bar

...실제로 도착하는 것은 limited_shell.bash다음과 같습니다. ( -c 'record -option1 foo -option2 bar'기술적으로 두 개의 인수가 있는데, 그 중 하나는 -c전체 문자열이고 두 번째는명령+매개변수처형하고 싶다)

-c첫 번째 인수( )를 이동하고 두 번째 인수를 공백으로 분할하는 방법(인수와 실제 명령)을 고려했지만 이는 지저분하고 인수 중 하나를 스크립트에 전달하려는 경우 record많은 문제를 일으킬 수 있습니다. record.py공백이 포함된 문자열입니다.

명령을 구문 분석하는 올바른 방법은 무엇입니까? 이동하고 분할하는 것보다 더 좋은 방법이 있을 것이라고 확신합니다.

답변1

당신의스크립트구현하도록 설계껍데기. 이것이 바로 명령줄 해석기입니다.

실행할 때:

ssh host echo '$foo;' rm -rf '/*'

ssh(클라이언트), 매개변수를 연결하고(ASCII SPC 문자 사용) 이를 다음과 같이 사용자의 로그인 쉘에 보냅니다 sshd.sshd

exec("the-shell", "-c", "the command-line")

여기 있습니다:

exec("the-shell", "-c", "echo $foo; rm -rf /*")

다음을 실행하면 결과는 정확히 동일합니다.

ssh host echo '$foo;' 'rm -rf' '/*'
ssh host 'echo $foo;' "rm -rf /*"
ssh host 'echo $foo; rm -rf /*'

(후자가 수행 중인 작업을 더 명확하게 보여주기 때문에 더 나은 옵션입니다).

the-shell명령줄을 사용하는 방법을 결정하는 것은 사용자에게 달려 있습니다 . 예를 들어, Bourne과 유사한 쉘은 $foo변수의 내용으로 확장되며 foo, 이는 명령 구분 기호로 처리되어 ;로 확장됩니다./*/

이제 당신이 원하는 것은 무엇이든 할 수 있습니다. 그러나 이것이 의미하는 바는제한된사용자 여러분은 아마도 변수 확장 금지, 명령 대체, 전역 변수, 여러 명령 허용 금지, 리디렉션 금지 등 가능한 한 적은 작업을 수행하고 싶을 것입니다.

명심해야 할 또 다른 점은 비대화형 상황(예: 스크립트를 해석할 때)에서도 bash호출될 때 읽힌다는 것입니다. 따라서 피하거나 적어도 호출하거나 사용자가 옵션을 작성하거나 사용하지 않도록 할 수 있습니다.~/.bashrcsshbashsh~/.bashrc--norc

이제 명령줄을 해석하는 방법은 사용자에게 달려 있으므로 간단히 공백, 줄 바꿈 또는 탭으로 분할할 수 있습니다.

#!/bin/sh -
[ "$1" = "-c" ] || exit
set -f
set -- $2
case $1 in
    (record) 
        shift
        exec /usr/sbin/record.py "$@"
        ;;
    ("" | *[!0-9]*)
        echo >&2 "command not supported"
        exit 1
        ;;
    (*)
        nc ...
        ;;
esac

그러나 이는 record공백, 탭, 줄 바꿈 또는 비어 있는 매개변수가 허용되지 않음을 의미합니다.

이를 수행할 수 있도록 하려면 일종의 인용 구문을 제공해야 합니다.

zsh다음과 같은 도움이 될 수 있는 견적 분석 도구가 있습니다.

#! /bin/zsh -f
[ "$1" = "-c" ] || exit
set -- "${(Q@)${(Z:cn:)2}}"
case $1 in
    (record) 
        shift
        exec /usr/sbin/record.py "$@"
        ;;
    ("" | *[!0-9]*)
        echo >&2 "command not supported"
        exit 1
        ;;
    (*)
        nc ...
        ;;
esac

작은따옴표, 큰따옴표 및 백슬래시를 지원합니다. 그러나 이와 같은 항목을 단일 매개변수로 처리합니다 $(foo bar)(확장하지 않더라도).

답변2

#!/bin/bash
shopt -s extglob

# domain-specific language: provide a "record" command
record () {
    /usr/sbin/record.py "$@"
}

command=$2
set -- $command   # re-use the positional parameters
case $1 in
    record) 
        # let the shell parse the given command
        eval "$command"
        ;;
    +([0-9]))    
        nc ... # as above
        ;;
    *) exit 0 ;;
esac

+([0-9])하나 이상의 숫자가 포함된 단어를 일치시키기 위해 확장된 glob 패턴을 사용하고 있습니다 . 패턴이 [0-9]*단어와 일치합니다.시작숫자와 일치하므로 1foo일치할 것입니다. 나는 그것이 당신이 원하는 것이라고 생각하지 않습니다.

경고하다사용하는 eval것은 매우 위험합니다 . 사용자가 제공하는 명령에 추가 명령 확인을 추가하는 것이 좋습니다. 다음과 같은 경우에는 어떻게 되나요?

ssh [email protected] record -option1 foo -option2 \`rm -rf /\`

답변3

더 많은 설정이 필요하지만 복잡하고 오류가 발생하기 쉬운 스크립트 작성 시간을 절약하는 더 간단한 접근 방식을 권장합니다.

SSH 공개 키가 다음과 같다고 선언할 수 있습니다.특정 명령만 실행되도록 허용. 키 쌍을 생성하고, 공개 키를 서버에 복사하고, command=…여기에 지침을 추가하세요. 허용하려는 몇 가지 명령 각각에 대해 이 작업을 수행합니다. 즉, ~/.ssh/authorized_keys서버의 파일에 다음과 같은 줄을 추가할 수 있습니다.

command="/path/to/command1" ssh-rsa AAAA…== key1
command="/path/to/command2" no-pty ssh-rsa AAAA…== key2

원격 명령을 실행할 때 적절한 개인 키를 선택합니다.

ssh -i ~/.ssh/key1.id_rsa server.example.com 'this is ignored'

command지시문 내에서 모든 셸 코드를 작성할 수 있습니다 . 클라이언트 제공 명령은 변수에서 사용할 수 있습니다 SSH_ORIGINAL_COMMAND.

관련 정보