sshdが実行するコマンドを確認する方法は?

sshdが実行するコマンドを確認する方法は?

SSHを介して環境変数をコマンドに渡すつもりです。

$ ssh HOST A=1 set | grep ^A
$ ssh HOST A=1 sh -c set | grep ^A
A='1'
$ ssh HOST A=1 echo '$A'
$ ssh HOST A=1 sh -c 'echo $A'

したがって、set環境変数は見えず、別々のプロセスに見えます。同じことが起こらないようですecho

sshd正確に何が送信されたのか、どのように実行されたのか(どのコマンドが実行されたのか)を確認する方法はありますか?

答え1

を実行すると、ssh user@host command拡張プログラムによって生成されたトークンはcommand中間スペースに関連付けられ、文字列sshとしてリモートホストに送信されます。その後、リモートホストはリモートユーザーのデフォルトシェル(で指定されたもの/etc/passwd)のインスタンスを実行し、クライアントから受け取った文字列をシェル-cオプションの引数として渡します(詳細は以下を参照)。

例えば

$ foo="bar    baz"
$ ssh user@host echo "$foo"
bar baz

ローカルは2つのパラメータをssh受け取ります。これを連結して文字列を形成し、それをサーバーに送信します。サーバーは次のタスクを実行し、コマンドの出力は標準出力にローカルに印刷されます。echobar bazecho bar baz/bin/sh -c 'echo bar baz'ssh

クライアントが実際にサーバーに送信した内容をどのように確認できますか?

〜のようにKamil Machorovskyは次のように指摘した。質問のコメントからssh -vサーバーに送信されたコマンド文字列は、デバッグメッセージとして出力されます。

$ ssh -v host A=1 sh -c set | grep ^A
...
debug1: Sending command: A=1 sh -c set
...

sshd実際に実行される内容をどのように確認できますか?

クライアントに自分のタスクの一種のトレースを表示するようにサーバーに要求する方法がわかりません。ただし、テスト目的でstraceデーモンインスタンスを使用できます。

$ sudo strace -e execve -f /usr/bin/sshd -p 2222

これは、リモート実行用のコマンドラインを作成するときに正しく引用できないことが簡単であるため、特に便利です。たとえば(あなたの質問に与えられた例のバリエーション):

$ ssh -p 2222 host bash -c 'A=1 set'
                                        # Prints nothing

strace私たちが見たコマンド文字列は実際にはリモートホストで分割されており、最初にユーザーのデフォルトシェルでコマンドライン全体を実行したことがわかります(ローカルシェルが引数リストを作成したときに一重引用符が削除されましたssh)。

... execve("/bin/bash", ["bash", "-c", "bash -c A=1 set"] ...

それから:

... execve("/usr/bin/bash", ["bash", "-c", "A=1", "set"] ...

コマンド文字列(のパラメータ-c)はですA=1。 0番目の引数としてset渡されます(コマンド名として使用bash可能)。$0

リモートシェルが実際に実行した内容を確認するには?

繰り返しますが、私は簡単な方法を知りません。単純なケースでは、シェルオプションを使用するのはxtrace簡単です。たとえば、ssh host A=1 set次のようになります。

$ command=$(printf '%s ' A=1 set); printf '%s\n' "${command% }" |
  ssh host sh -x

shリモートユーザーのプライマリシェルと交換する必要があります。

コマンドはssh標準入力にパイプされます。このフォームを使用すると、変更せずに入力できます(ssh host sh -xc A=1 set上記の理由で機能せず、引用符を追加すると、この練習の実際の目的は失われます)。

簡単ではない場合、特にssh調査中の呼び出しがすでに標準入力を使用している場合は、リモートホストにシェルを開いたxtrace状態で強制的に実行させることができます。実験目的とエラーが原因でリモートシステムにログインできないことを認識するため1つの(やや粗雑な)アプローチは、リモートユーザーのデフォルトシェルを交換することです/etc/passwd。と仮定して/bin/shに変更し/home/your_user/bin/sh、後者を実行可能スクリプトとして生成します。

#!/bin/sh
exec /bin/sh -x "$@"

これにより、シェルは実行中のコンテンツのトレースを表示します。

$ ssh host A=1 echo foo
foo
+ A=1
+ echo foo

最後に、サンプルコマンドに関して次のようにします。

  • ssh HOST A=1 set印刷されない理由は、A=1ユーザーのデフォルトシェルがBash(おそらく呼び出されるsh)であるためです。dash、、kshBash(少なくとも私が持っているバージョン)yashで発生するのとは対照的に、変数セットは出力されませんzshset5.0.17setコマンド自体の場合。一方、次のことも確認できます。

    $ ssh host 'A=1
    > set' | grep ^A
    A=1
    

    Ieが呼び出し前に宣言されると、出力Aに表示されます。setset

  • で環境に追加されたので、A=1 sh -c set当然印刷されます。Ashset

  • A=1 echo '$A'A拡張後に実行環境にのみ追加されるため、値は印刷されませんA(組み込みと仮定)。印刷されたものと比較してください。echo$Assh host 'A=1; echo $A'1

  • A=1 sh -c 'echo $A'上記のように、リモートホストはオプション-cと引数を使用してA=1 sh -c echo $Aリモートユーザーのデフォルトシェルを実行し、そのシェルが実行されるため何も印刷されませんsh -c echo $A。コマンド文字列はecho引数なしですぐになります。

    これを機能させるには、二重引用符がリモートホストに送信echo $Aされていることを確認する必要があります。そして $A早期拡張防止(ローカルシェルまたは「外部」リモートシェルを介して):例:ssh host A=1 sh -c '"echo \$A"'またはssh host A=1 sh -c "'echo \$A'"

    リモートシェルを明示的に呼び出すときは、標準入力からコマンドを読み取る方が簡単です。

    $ echo 'echo $A' | ssh host A=1 sh -s
    1
    

また、見ることができます

関連情報