私は使用が非常に限られたサーバーを持っており、SSHを介して非常に具体的な(およびカスタマイズされた)2つのコマンドを実行できるようにしたいと思います。これを実現するために、制限されたユーザーのシェルをこの2つだけ許可するカスタム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
私が受け入れたい2つのコマンドは数字と「記録」文字列です。limited_shell.bash
番号で呼び出すと、開いているローカルポートに対応するのか、閉じているローカルポートに対応するのかを返します。別の許可されたコマンドは/usr/sbin/record.py
Pythonスクリプト()への呼び出しをトリガーし、入力からいくつかのビデオを記録します。 2番目のコマンドは追加の引数を使用して呼び出す必要があり、ここで問題が発生します。
別のコンピュータでリモートでコマンドを実行しようとするとrecord
...
ssh [email protected] record -option1 foo -option2 bar
...実際に到着するのはlimited_shell.bash
次のとおりです。 (-c 'record -option1 foo -option2 bar'
技術的には2つの引数があり、そのうちの1つは-c
完全な文字列であり、2番目はコマンド+パラメータ処刑したい)
-c
最初の引数()を移動し、2番目の引数をスペースに分割する方法(引数と実際のコマンド)を考えましたが、これは汚れていて、引数の1つをスクリプトに渡そうとすると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
、これはコマンド区切り文字として扱われて拡張;
されます。/*
/
今あなたが望むものは何でもすることができます。しかし、これが意味するのは限られたユーザーは、おそらく、可変拡張禁止、コマンド置換、グローバル変数、複数のコマンド許可禁止、リダイレクト禁止など、できるだけ少ない操作をしたいと思います。
覚えておくべきもう1つの点は、非対話型の状況(スクリプトを解釈するときなど)でも呼び出された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])
1つ以上の数字を含む単語を一致させるために拡張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
。