ユーザー名(myuser)でしかログインできないシステムがありますが、他のユーザー(scriptuser)としてコマンドを実行する必要があります。これまでのところ、必要なコマンドを実行するために次のことを考え出しました。
_ssh -tq myuser@hostname "Sudo -u scriptuser bash -c \"ls -al\""
_
ただし、_[[ -d "/tmp/Some directory" ]] && rm -rf "/tmp/Some directory"
_などのより複雑なコマンドを実行しようとすると、すぐに引用符で問題が発生します。 _bash -c
_がすでに渡しているコマンドの境界を区切っているときに、この例の複雑なコマンドを_\"
_にどのように渡すことができるかわかりません(したがって、/ tmpを引用する方法がわかりません) /スペースを含む/ Someディレクトリ。
引用がどれほど複雑でクレイジーであっても、コマンドを渡すことができる一般的な解決策はありますか、またはこれは私が到達したある種の制限ですか?他に可能な、そしておそらくもっと読みやすい解決策はありますか?
私が時々使用するトリックは、base64を使用してコマンドをエンコードし、それを他のサイトのbashにパイプすることです。
_MYCOMMAND=$(base64 -w0 script.sh)
ssh user@remotehost "echo $MYCOMMAND | base64 -d | Sudo bash"
_
これにより、安全な文字列内のコンマ、バックスラッシュ、引用符、変数を使用してスクリプトがエンコードされ、他のサーバーに送信されます。 (_-w0
_は、デフォルトで列76で行われる行の折り返しを無効にするために必要です)。一方、$(base64 -d)
はスクリプトをデコードし、実行するためにbashにフィードします。
スクリプトがどれほど複雑であっても、問題はありませんでした。何もエスケープする必要がないため、エスケープの問題を解決します。リモートホスト上にファイルを作成せず、非常に複雑なスクリプトを簡単に実行できます。
_-tt
_オプションを参照してください。 ssh(1)
マニュアルをお読みください。
_ssh -tt root@Host << EOF
Sudo some # Sudo shouldn't ask for a password, otherwise, this fails.
lines
of
code
but be careful with \$variables
and \$(other) \`stuff\`
exit # <- Important.
EOF
_
私がよく行うことの1つは、vimを使用して_:!cat % | ssh -tt somemachine
_トリックを使用することです。
最も簡単な解決策は、@ thanasiskのコメントを変更することだと思います。
スクリプトを作成し、マシンにscp
してから実行します。
最初にスクリプトrm
自体を用意します。シェルがファイルを開いたため、ファイルが読み込まれ、問題なく削除できます。
この順序で(最初にrm
、次に他のものを)実行することにより、ある時点で失敗した場合でも削除されます。
更新:例ではSudo
を明示的に使用するようになりました。
Sudoを使用してSSHで任意の複雑なコマンドを実行するために、複合割り当てでBash構文を使用する方法を次に示します。
CMD=$( cat <<'EOT'
echo "Variables like '${HOSTNAME}' and commands like $( whoami )"
echo "will be interpolated on the server, thanks to the single quotes"
echo "around 'EOT' above."
EOT
) \
SSHCMD=$(
printf 'ssh -tq myuser@hostname Sudo -u scriptuser bash -c %q' "${CMD}" ) \
bash -c '${SSHCMD}'
CMD=$( cat <<EOT
echo "If you want '${HOSTNAME}' and $( whoami ) to be interpolated"
echo "on the client instead, omit the the single quotes around EOT."
EOT
) \
SSHCMD=$(
printf 'ssh -tq myuser@hostname Sudo -u scriptuser bash -c %q' "${CMD}" ) \
bash -c '${SSHCMD}'
他の場所(ネストされたシェル、リモートSSHシェルなど)で動的に実行されるようにコマンドを適切にフォーマットすることは非常に難しい場合があります- を参照してください 理由の適切な説明。上記の構文のように複雑なbash構造は、これらのコマンドが適切に機能するのに役立ちます。
cat
と<<
結合してインラインのドキュメントを作成します、 https://stackoverflow.com/a/21761956/を参照してください111948
%q
形式指定子とprintf
を使用して、変数のエスケープを処理します。
cmd="ls -al"
printf -v cmd_str '%q' "$cmd"
ssh user@Host "bash -c $cmd_str"
printf -v
は、出力を変数に書き込みます(この場合、$cmd_str
)。これが最も簡単な方法だと思います。ファイルを転送したり、コマンド文字列をエンコードしたりする必要はありません(私がトリックを好きなだけ)。
角かっこやアンパサンドなどでも機能することを示す、より複雑な例を次に示します。
$ ssh user@Host "ls -l test"
-rw-r--r-- 1 tom users 0 Sep 4 21:18 test
$ cmd="[[ -f test ]] && echo 'this really works'"
$ printf -v cmd_str '%q' "$cmd"
$ ssh user@Host "bash -c $cmd_str"
this really works
私はSudo
でテストしていませんが、次のように簡単なはずです。
ssh user@Host "Sudo -u scriptuser bash -c $cmd_str"
必要に応じて、ステップをスキップして、中間変数の作成を回避できます。
$ ssh user@Host "bash -c $(printf '%q' "$cmd")"
this really works
または、変数全体を作成しないようにすることもできます。
ssh user@Host "bash -c $(printf '%q' "[[ -f test ]] && echo 'this works as well'")"
Sudo
を使用して、選択したユーザーとしてコマンドを実行できるシェルを提供できることをご存知ですか?
-i、-login
ターゲットユーザーのパスワードデータベースエントリで指定されたシェルをログインシェルとして実行します。これは、.profileや.loginなどのログイン固有のリソースファイルがシェルによって読み取られることを意味します。コマンドが指定されている場合、シェルの-cオプションを介して実行するためにコマンドがシェルに渡されます。コマンドを指定しない場合、対話型シェルが実行されます。 Sudoは、シェルを実行する前に、そのユーザーのホームディレクトリへの変更を試みます。コマンドは、ユーザーがログイン時に受け取る環境と同様の環境で実行されます。sudoers(5)マニュアルのコマンド環境セクションでは、-iオプションがコマンドの実行環境にどのように影響するかを説明しています。 sudoersポリシーが使用されています。
ローカルマシンでスクリプトを定義してから、cat
をリモートマシンにパイプすることができます。
user@Host:~/temp> echo "echo 'Test'" > fileForSsh.txt
user@Host:~/temp> cat fileForSsh.txt | ssh localhost
Pseudo-terminal will not be allocated because stdin is not a terminal.
stty: standard input: Invalid argument
Test
シンプルで簡単。
ssh user @ servidor "bash -s" <script.sh
十分に新しいBashシェルを使用している場合は、コマンドを関数に入れ、export -p -f function_name
を使用してその関数を文字列として出力できます。その文字列の結果には、必ずしもルートとして実行する必要がない任意のコマンドを含めることができます。
nix.SEでの私の回答 からのパイプを使用した例:
#!/bin/bash remote_main() { local dest="$HOME/destination" tar xzv -C "$dest" chgrp -R www-data "$dest" # Ensure that newly written files have the 'www-data' group too find "$dest" -type d -exec chmod g+s {} \; } tar cz files/ | ssh user@Host "$(declare -pf remote_main); remote_main"
を取得する例では、ログインユーザーのファイルを保存し、プログラムをルートにインストールします。
remote_main() {
wget https://example.com/screenrc -O ~/.screenrc
Sudo apt-get update && Sudo apt-get install screen
}
ssh user@Host "$(declare -pf remote_main); remote_main"
Sudo
を使用してコマンド全体を実行する場合は、次のように使用できます。
remote_main() {
wget https://example.com/screenrc -O ~user/.screenrc
apt-get update && apt-get install screen
}
ssh user@Host "$(declare -pf remote_main);
Sudo sh -c \"\$(declare -pf remote_main); remote_cmd\""
# Alternatively, if you don't need stdin and do not want to log the command:
ssh user@Host "$(declare -pf remote_main);
(declare -pf remote_main; echo remote_cmd) | Sudo sh"
まず、ローカルスクリプトを作成し、次のコマンドを使用してリモートで実行します。
cat <Local Script.sh> | ssh user@server "cat - | Sudo -u <user> /bin/bash"
スクリプトにパラメーターを渡す必要があるこれらのユーザーにとっては、次のようになります。
ssh user@remotehost "echo `base64 -w0 script.sh` | base64 -d | Sudo bash -s <param1> <param2> <paramN>"
これが私の(実際にはテストされていない)くだらない解決策です:
#!/usr/bin/env Ruby
# Shell-escape: Escape each argument.
ARGV.each do|a|
print " '#{a.gsub("'","\'\\\\'\'")}' "
end
あなたはできません:
ssh -tq myuser@hostname "$(Shell-escape Sudo -u scriptuser bash -c "$(Shell-escape ls -al)")"
次のステップは、すでにこれを行っているbetterssh
スクリプトを作成することです:
betterssh -tq myuser@hostname Sudo -u scriptuser bash -c "$(Shell-escape ls -al)"