Правильно проанализируйте аргументы в сценарии, ведущем себя как оболочка, названная через 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 назван с числом, оно возвратится, соответствует ли оно открытому или закрыло локальный порт. Другая позволенная команда инициировала вызов к сценарию Python (/usr/sbin/record.py) который затем записывает некоторое видео от входа. Эту вторую команду нужно назвать с дополнительными аргументами, и это - то, где проблемы запускаются.

Когда, в другой машине я пытаюсь удаленно выполниться record команда...

ssh limited@192.168.1.5 record -option1 foo -option2 bar

... что на самом деле прибывает в limited_shell.bash : -c 'record -option1 foo -option2 bar' (технически два аргумента, один из них быть -c и причем второй является целой строкой command+args, что я хочу выполниться),

Я думал о смещении первого аргумента ( -c), и разделение второй аргумент (фактическое record команда с аргументами) пространством, но это грязно и даст мне много проблем, если один из параметров я хочу передать record.py сценарий является строкой, содержащей пространство.

Каков правильный способ проанализировать команды? Я уверен, что должен быть лучший путь, чем смещение и разделение.

3
19.11.2013, 15:15
3 ответа

Ваш сценарий предназначен для реализации оболочки. Таким образом, интерпретатор командной строки.

Когда Вы работаете:

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 решить, что сделать с той командной строкой. Например, подобная Границе оболочка расширилась бы $foo к содержанию foo переменная, это рассмотрело бы ; как разделитель команды, и расширился бы /* в отсортированный список нескрытых файлов в /.

Теперь, в Вашем случае с тех пор, можно сделать то, что Вы хотите. Но так как это предназначено, чтобы быть ограниченным пользователем, можно хотеть сделать как можно меньше, например, не развернуть переменную, замену команды, шарики, не позволить несколько команд, не сделать перенаправления...

Другая вещь принять во внимание в этом bash чтения ~/.bashrc при вызывании ssh даже когда неинтерактивная (как в при интерпретации сценариев). Таким образом, Вы, вероятно, хотите избежать bash (или, по крайней мере, назовите его как sh) или удостоверьтесь ~/.bashrc не writabe пользователем, или используйте --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
27.01.2020, 21:17
#!/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]) распознать слово, которое является одной или несколькими цифрами. Шаблон [0-9]* распознает слово, начинающееся с цифры, таким образом, 1foo соответствовал бы - я предполагаю, что это не то, что Вы хотите.

Предостерегите использование eval очень опасно. Рассмотрите добавляющую дополнительную проверку команды к команде, которую пользователь дает Вам. Что происходит когда:

ssh limited@192.168.1.5 record -option1 foo -option2 \`rm -rf /\`
2
27.01.2020, 21:17

Я предложу более простой подход, который требует немного большего количества установки, но сохраняет запись сложного, подверженного ошибкам сценария.

Можно объявить, что открытому ключу SSH только позволяют выполнить определенную команду. Генерируйте пару ключей, скопируйте открытый ключ в сервер и добавьте a 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.

1
27.01.2020, 21:17

Теги

Похожие вопросы