나는 사용이 매우 제한된 서버를 가지고 있으며 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.py
Python 스크립트( )에 대한 호출을 트리거한 다음 입력에서 일부 비디오를 녹화합니다. 두 번째 명령은 추가 인수를 사용하여 호출해야 하며, 여기서 문제가 시작됩니다.
다른 컴퓨터에서 원격으로 명령을 실행하려고 하면 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
호출될 때 읽힌다는 것입니다. 따라서 피하거나 적어도 호출하거나 사용자가 옵션을 작성하거나 사용하지 않도록 할 수 있습니다.~/.bashrc
ssh
bash
sh
~/.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
.