I have a server with a very limited used whom I want to be able to run two very specific (and custom) instructions through SSH. In order to do that, I have set the shell for that limited user to be a custom BASH script that will only accept those two commands.This is the /etc/passwd for that user:limited:x:1000:1000:,,,:/home/limited:/usr/sbin/limited_shell.bashThis is the limited_shell.bash script (well, what I have so far, which is not working properly):#!/bin/bashreadonly RECORDER_SCRIPT=/usr/sbin/record.pyecho Verifying params: \$1: $1, \$2: $2shiftcase $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    ;;  esacexit 0As you might deduce from the script, the two commands I want limited_shell.bash to accept are: a number and a record string. When limited_shell.bash is called with a number, it will return whether it corresponds with an opened or closed local port. The other allowed command triggers a call to a python script (/usr/sbin/record.py) which then records some video from an input. This second command needs to be called with extra arguments, and that's where the problems start.When, in another machine I try to remotely execute the record command...ssh limited@192.168.1.5 record -option1 foo -option2 bar... what actually arrives to limited_shell.bash is: -c 'record -option1 foo -option2 bar' (technically two arguments, one of them being -c and the second one being the whole string command+args that I want to execute)I thought about shifting the first argument (the -c), and split the second argument (the actual record command with arguments) by space, but that is dirty and will give me a lot of troubles if one of the parameters I want to pass to the record.py script is an string containing an space.What is the right way of parsing the commands? I'm sure there has to be a better way than shifting and splitting.
Correctly parse arguments in script behaving like a shell called through SSH
bash;shell;ssh;command;arguments
Your script is meant to implement a shell. That is, a command line interpreter.When you run:ssh host echo '$foo;' rm -rf '/*'ssh (the client), concatenates the arguments (with ASCII SPC characters), and sends that to sshd. sshd calls the user's login shell as:exec(the-shell, -c, the command-line)That is here:exec(the-shell, -c, echo $foo; rm -rf /*)And that would have been exactly the same had you run:ssh host echo '$foo;' 'rm -rf' '/*'ssh host 'echo $foo;' rm -rf /*ssh host 'echo $foo; rm -rf /*'(the later one being the preferable one as it makes it clearer what is being done).It's up to the-shell to decide what to do with that command line. For instance, a Bourne-like shell would expand $foo to the content of the foo variable, it would consider ; as a command separator, and would expand /* into a sorted list of non-hidden files in /.Now, in your case since, you can do whatever you want. But since that's meant to be a restricted user, you may want to do as little as possible, for instance, not expand variable, command substitution, globs, not allow several commands, not do redirections...Another thing to bear in mind in that bash reads ~/.bashrc when called over ssh even when non-interactive (as in when interpreting scripts). So you probably want to avoid bash (or at least call it as sh) or make sure ~/.bashrc is not writabe by the user or use the --norc option.Now, since it's up to you do define how the command line is interpreted, you can either simply split one space, newline or tab:#!/bin/sh -[ $1 = -c ] || exitset -fset -- $2case $1 in    (record)         shift        exec /usr/sbin/record.py $@        ;;    ( | *[!0-9]*)        echo >&2 command not supported        exit 1        ;;    (*)        nc ...        ;;esacBut that means record won't be able to take arguments that contain spaces, tabs or newlines or that are empty.If you want them to be able to do that, you need to provide with some sort of quoting syntax.zsh has a quote parsing tool that can help you there:#! /bin/zsh -f[ $1 = -c ] || exitset -- ${(Q@)${(Z:cn:)2}}case $1 in    (record)         shift        exec /usr/sbin/record.py $@        ;;    ( | *[!0-9]*)        echo >&2 command not supported        exit 1        ;;    (*)        nc ...        ;;esacThat supports single, double quotes and backslashes. But it would also consider things like $(foo bar) as a single argument (even if not expanding it).