
質問
次のコマンドを検討してください。
<binary_input ssh user@server 'sudo tool' >binary_output 2>error.log
ここでtool
ランダムなのは、上記の操作を可能にするssh
ラッパーまたは一部のラッパーです。ssh-like-contraption
一般的な単語はssh
機能しません。
ここで試しましたが、sudo
ちょうどそれでした。はいtty コマンドが必要です。 sudo
.
研究:なぜ
通常、ssh
次の理由で機能しません。
sudo
パスワードを要求するにはttyが必要です(それとも単に仕事に行きます。)だから私は必要ですssh -t
;実際にこの場合は が必要ですssh -tt
。- 一方、パスワードを読むことが
ssh -tt
できます。私はローカルttyを介してパスワードを提供したいと思います。パスワードなしで動作するように設定した場合、またはパスワードを挿入した場合でも、リモートttyから出力を読み込み、次に書き込みます。sudo
binary_input
sudo
binary_input
ssh -tt
sudo
tool
そしてリモートttyにエラーとプロンプトを送信します。ローカルで出力とエラー/プロンプトを区別することはできませんが、すべてのストリームはリモートttyによって処理され、データが破損します(次の例に示すように)私の答え、「一部の実践」を参照)。
研究:有効な命令との比較
このローカルコマンドは参照点です。いくつかのバイナリデータが正常に処理されたとします。
<binary_input tool >binary_output
tool
サーバーで実行する必要がある場合は、これを行うことができます。ssh
パスワードを聞いても、次のように動作します。<binary_input ssh user@server tool >binary_output
この場合、
ssh
バイナリデータに透過的です。同様に、ローカル
sudo
も透明にすることができます。次のコマンドは、sudo
パスワードを要求してもデータを破壊しません。<binary_input sudo tool >binary_output
tool
しかし、サーバーで実行するのはsudo
面倒な作業です。<binary_input ssh user@server 'sudo tool' >binary_output
この構成
ssh
とsudo
一緒に通常、透明性は不可能です。これを透明にする方法を見つけることがこの質問の中心です。
研究:同様の質問
いくつかの同様の質問を見つけました。
-
この質問は標準出力にのみ関連しています。質問作成者の既存の回答は、
sudo -S
標準入力を使用することを示唆しています。私は私を変えたくありませんbinary_input
。私は具体的な解決策を望んでいませんsudo
。 -
Ctrl+はここで中央に渡され、c背景はGNUです
parallel
。リモートttyなしでCtrl+を操作するための解決策は不十分です。c SSH:stdin、stdout、stderrに加えて、追加の「パイプライン」fdが提供されます。
良い始まりです(特にこの回答、私の思考に)。しかし、ここではttyの必要性を強調したいと思います。私は仕事を自動化し、
sudo
ローカルだけでなくリモート(または他の)も使用できるようにするソリューションが欲しいです。
私の明確な質問
次のコマンドで:
<binary_input ssh user@server 'requires-tty' >binary_output 2>error.log
requires-tty
ttyが必要ですが、stdinからstdoutまでのバイナリデータを処理するコードのプレースホルダです。必要なようですssh -tt
。それ以外の場合はrequires-tty
動作しません。同時に使用できません。ssh -tt
そうしないと、バイナリデータが破損します。この問題をどのように便利に解決できますか?
requires-tty
可能sudo …
ですが、具体的に説明したくありませんsudo
。
理想的な(?)ソリューションは、ssh
上記の呼び出しに代わってうまく機能するスクリプト/ツールになりそうです。リモートのstdin、stdout、およびstderrをそれぞれローカル対応エントリに関連付ける必要があります(?)。そしてリモートttyをローカルttyに。
可能であれば、サーバー側コンパニオンプログラムを必要としないクライアント側ソリューションを好みます。
答え1
スクリプト
私はこの質問の作成者であり、これが問題を解決するスクリプトを作成しようとしています。このスクリプトはクライアント側で動作するように設計されており、ssh
問題のあるコマンドを置き換えます。それ実験的。私はそれを呼ぶsshe
。スクリプトは次のとおりです。
#!/bin/sh -
# the name of the script
me="${0##*/}"
# error handling functions
scream() { printf '%s\n' >&2 "$1"; }
die() { scream "$2"; exit "$1"; }
# initialization of variables
redir0=''
redir1=''
redir2=''
tty="/dev/$(ps -p "$$" -o tty=)"
# edge cases
[ "$tty" = '/dev/?' ] && { scream "$me: no tty detected, falling back to regular ssh"
exec ssh "$@"; }
[ "$#" -lt 2 ] && die 1 "usage: $me [options] [user@]hostname command"
# see what needs to be redirected
exec 7>&1
if [ "$(<&0 tty 2>/dev/null)" != "$tty" ]; then redir0=y; fi
if [ "$(<&7 tty 2>/dev/null)" != "$tty" ]; then redir1=y; fi
if [ "$(<&2 tty 2>/dev/null)" != "$tty" ]; then redir2=y; fi
exec 7>&-
# edge case
[ "$redir0$redir1$redir2" ] || { scream "$me: no redirection detected, falling back to ssh -t"
exec ssh -t "$@"; }
# command line parsing, extract two last arguments: ... host command
z="$#"
n="$z"
for arg do
if [ "$n" -eq "$z" ]; then
set --
fi
case "$n" in
1) command="$arg"
;;
2) host="$arg"
;;
*)
set -- "$@" "$arg"
esac
n="$(($n - 1))"
done
# prepare to clean on exit
trap 'status="$?"; rm -r "$tmpd" 2>/dev/null; trap - EXIT; exit "$status"' EXIT HUP INT QUIT PIPE TERM
# temporary directory and socket
tmpd="$(mktemp -d)"
[ "$?" -eq 0 ] || exit 1
sock="$tmpd/sock"
# main pipe: ssh master connection -> background cat
(
[ "$redir0" ] || exec 0</dev/null
# ssh master connection, it will report the remote PID of the remote shell via its stdout
ssh -M -S "$sock" "$@" -T "$host" '</dev/null echo "$$"; exec sleep 2147483647'
) | {
# read the remote PID
IFS= read -r rpid || exit 1
# background process to pass data
exec 6<&0
cat <&6 2>/dev/null &
# move original descriptors out of the way
exec </dev/tty >/dev/tty 6>&-
# prepare remote redirections
if [ "$redir0" ]; then redir0="<&6"; fi
if [ "$redir1" ]; then redir1=">&7"; fi
if [ "$redir2" ]; then redir2="2>&8"; fi
# ssh to run the command, with remote tty
ssh -S "$sock" -t "$host" "
trap 'status=\"\$?\"; kill $rpid 2>/dev/null; trap - EXIT; exit \"\$status\"' EXIT HUP INT QUIT PIPE TERM
exec 6</proc/$rpid/fd/0 7>/proc/$rpid/fd/1 8>/proc/$rpid/fd/2 9>/dev/tty $redir0 $redir1 $redir2 || exit 3;
$command"
}
一般免責事項
このスクリプトはさまざまな状況でうまく機能します。私いいえランダムに失敗するという意味です。ランダムに失敗しません。私いいえこれは、特定のデータを処理できないことを意味します。任意のデータを処理します。
「動作しない」場合は、ローカルttyとの相互作用によって発生します。 tty(任意のバイナリデータを含む)が含まれていないチャンネルを流れるデータは常に大丈夫です。問題を理解して回避する必要があることを確認するには、この回答の残りの部分、特に「障害と警告」のセクションを読んでください。
次のコマンド:
<binary_input sshe user@server 'sudo tool' >binary_output 2>error.log
この問題は回避されます。技術要件(以下の「要件」を参照)を満たしている場合は、正しく機能します。
スクリプトは実験的であり、
trap
終了状態を維持し、終了時に正気な(?)方法でクリーンアップするためにsを設定しようとしました。私が成功したかどうかはわかりません。スクリプトは決して完璧ではありません。コンセプト証明だと思います。
使用法
sshe
次のように使用してください。
sshe … [user@]hostname command
つまり、…
実行ファイルがある場合、ここにも(または)ssh
を配置する必要はありません。このスクリプトはリモート側でttyを使用すると仮定します(そうでない場合は単にttyを使用)。スクリプト-t
-tt
-T
ssh
予想するローカルのstdin、stdout、およびstderrの少なくとも1つは、ローカルのttyからリダイレクトする必要があります。ssh -t
すべてがローカルttyに接続されている場合、スクリプトは置き換えられます。
重要もの:
command
サーバーで実行するシェルコードです。それ〜しなければならないは単一のパラメータであり、最後のパラメータですsshe
。省略できません。hostname
または、user@hostname
最後の2番目のパラメータでなければなりません。省略できません。
内部的に、スクリプトはcommand
前にいくつかのコードを追加する方法を知る必要があります。[user@]hostname
2回使用されたので知っておく必要があります。スクリプトはそれぞれ最後の引数と最後の2番目の引数のみを選択するため、上記の制限が適用されます。
単にに置き換えるだけで、すべての有効な呼び出しを呼び出しssh
に変換できるわけではありません。sshe
しかし、私はコードを実行するすべての有効な呼び出し(対話型シェルを生成するのとは対照的)が有効なコマンドに再配置できると思います。例:ssh
sshe
ssh
sshe
ssh user@server -p 1234 echo foo
次のように並べ替える必要があります。
sshe -p 1234 user@server 'echo foo'
sshe
(本当に必要ない限りこれケースこれは正しい構文の例にすぎません。 scriptを使用した場合、sshe user@server -p 1234 echo foo
そのスクリプトは以前と同じ引数を解析しないため、echo
サーバーとコマンドとして機能します。foo
ssh
ここにいくつかの例があります。
要件、移植性の問題
ローカル要件(sshe
作業場所):
/dev/$(ps -p "$$" -o tty=)
制御端末の「実名」と推定される。比較するこの問題。mktemp -d
。ssh
このスクリプトはマスターとスレーブの接続をサポート-M
します。-S
リモート要件(サーバー上):
- SSHサーバーはマスター - スレーブ接続を処理できます。
/proc
擬似ファイルシステム/proc/nnnn/fd/N
同じユーザーに属する他のプロセスを使用できます。- POSIX互換エンクロージャ。
- 自動起動スクリプト(比較
echo
SCPがアイテムに機能しない.bashrc
、sshe
状況に似ています)。
テスト中に、Kubuntu(18.04.5 LTS)のさまざまなDebianまたはDebian派生サーバーに正常に接続しました。鉱山ssh
とsshd
OpenSSHから。
仕事
sshe
ssh
(またはで置き換えることを決定しない限りssh -t
)ssh
2回実行します。
ssh -M … -T …
これはデフォルトの接続であり、リモート側にttyを割り当てません。そこで実行されるシェルコードは、exec
stdoutとsを介して長期実行(約68年)にsleep
PIDを報告します。プロセスの標準ファイル記述子は、他のプロセスで使用されます。マスターから報告されたPIDは
ssh
によって取得されますread
。その後、マスターの標準出力はそれを(ローカル)標準出力に渡す目的でのみssh
バックグラウンドに移動します。cat
sshe
後で
ssh … -t …
リモート側にttyを割り当てるスレーブ接続が提供されます。リモートPIDがマスター接続で既に認識された後、リモート側で別々のstdin、stdout、stderr(マスター接続を介して)、およびtty(スレーブ接続を介して)を使用できるsshe
ように提供されるコードをリダイレクトします。スレーブは、生のstdinまたはstdoutを使用せずにローカルを使用します。command
ssh
ssh
ssh
sshe
/dev/tty
アイデアは似ています。この回答(すでに質問につながっています)はい。リンクされた回答のコードは、追加の記述子を提供するためにssh
(暗黙的に)2回実行されます。ssh -T
私のスクリプトが実行さssh -T
れ、ssh -t
標準記述子とttyを提供します。そしてマスタースレーブ機能を使用するので、ssh
認証(パスワード要求など)を実行します。
ローカル stdin、stdout、stderr がローカル tty でない場合、データフローの方向は次のようになります。
ローカルのstdinはmasterに移動し、
ssh
他のローカルプロセスはスクリプトのstdinから読み込まれません。 (リモートで)読み取ることで、/proc/nnnn/fd/0
リモートプロセスはローカル標準入力にアクセスできます。スレーブssh
接続はリモート側のシェルに事前にリダイレクトされ、標準入力command
として使用されます。/proc/nnnn/fd/0
同様に、リモート側のシェルが
/proc/nnnn/fd/1
標準出力として使用されます。何が起こっても、地域の所有者が出てくるでしょうssh
。これは、マスターがssh
実行されている(リモート)シェルコードから正しいPIDを取得した後です。 PIDが消費され、その後のすべてのデータがread
バックグラウンドで元の標準出力に送信されます。sshe
cat
同様に、リモート側のシェルは
/proc/nnnn/fd/2
stderrを使用します。このストリームはローカルマスターからssh
stderrに直接送信されますsshe
。スクリプトによって生成されたローカルプロセスによっては、スクリプトの標準エラーが標準エラーとして使用されるため、sshe … 2>error.log
ログにエラーメッセージも含まれます。特に期待されますShared connection to server closed.
。これはssh -T … 2>error.log
、ログがリモートコマンドとそれ自体からメッセージを収集する方法と似ていますssh
。sshe
他のstdoutに関連付けられたチャネルを介してリモートコマンドからstderrを渡すバリアントを作成することが可能だと思います。ssh
この場合、リモートstderrとローカルツールによって生成された診断メッセージを区別できます。しかし、スクリプトはそうしません。ローカルttyはマスター
ssh
(パスワードを要求する必要がある場合)に使用し、スレーブに使用できますssh
。 (正直スクリプトで使用するローカルツールはlocalにアクセスできますが、/dev/tty
使用しません。)スレーブデバイスは標準入力および標準出力ssh -t
として使用されます。/dev/tty
これにより、/dev/tty
他のリダイレクト(ssh -t
リダイレクトなしで端末で実行)にもかかわらず、ローカルおよびリモートで接続できます。リモートプロセスから読み取った内容は、ローカルスレーブがローカルで読み取った内容を/dev/tty
取得します。リモートプロセスで書き込み操作を実行すると、ローカルスレーブはローカルで書き込み操作を実行します。ssh
/dev/tty
/dev/tty
ssh
/dev/tty
ローカルstdin、stdout、またはstderrがローカルttyの場合、そのリモート側(スレーブcommand
でリモートで実行中)はリダイレクトされず、リモートttyへの接続は維持されます。どちらにしても地元ターミナルに到着します。要点は、リモートttyを迂回してはならないということです。その理由はすぐに明らかになるでしょう。ssh
/proc/nnnn/fd/N
sshe
必ずしも機能する必要がないローカルとリモートのリダイレクトはほとんどありません。これは私の他の実験的なものによるものです。私はsshe
私が覚えているよりも単独でより重要な場合に備えて追加のリダイレクトを維持することにしました。
障害物と考慮事項
全体の概念は思ったより簡単ではありません。 ttyはユーザーが入力した内容を処理できます(例:翻訳^M
する^J
)と印刷される内容(たとえば、cat
端末から* nix行の末尾にファイルを送信すると、各改行はキャリッジリターン+改行のように機能します)。移動するstty -a
多くの設定を参照してください。
これがランダムなバイナリデータを処理するときにttyが必要ない理由です。そして対話するときに本当に必要です。
プロセスは必要に応じてttyを設定できます。バラより生のそして料理。
ssh
何とかサーバーにttyを割り当てると、そこのプロセスはそれを自分のttyとして扱います。 ttyを設定する必要がある場合は、サーバーに表示されるttyを設定します。ローカルttyを直接設定する方法はありませんssh
。すべての(?)「クッキング」はリモートttyで行われ、ssh
ローカルttyは邪魔にならないように設定されています。
sshe
これが最終的にローカルttyに接続する必要があるリモート記述子をリダイレクトするときにリモートttyをバイパスしないでください。リモートttyがバイパスされると、ストリームを「クッキング」するオブジェクトはありません。 stdin、stdout、または stderr をsshe
ローカルの tty にリンクすると、そのエントリが「クッキング」されることを示します。これは、すべての標準ストリームがローカルttyによって「クッキング」される対話型シェルで実行されるのとsshe
似ています(これは、ローカル端末を「raw」モードに設定しないことに注意してください)。ssh … command
ssh
ssh -T
だからsshe
明らかに「料理」したいことを「料理」してください。問題は、次のような場合に発生します。
sshe … command | whatever
データは(予想どおり)「クッキング」されず、リモートからローカルcommand
に流れますが、出力はローカルで「クッキング」されません。ローカルttyは「料理」に再構成できますが、ローカルttyはそのttyで印刷されている場合は「料理」にしてはいけません(つまり、設定に応じて「料理」にすることも、そうでない場合もあるリモートttyに印刷する必要があります)。 。whatever
ssh … command | whatever
whatever
sshe
command
sshe
問題を解決しようとしないでください。デフォルトでは、出力が端末以外の場所(通常のファイルやブロックデバイスなど)で終わる状況をサポートするように設計されています。この問題では、次のコードが優れています。
sshe … command | whatever >some_file
stderrはwhatever
「料理」ではありません。予想される診断メッセージバラより奇妙な。ファイル(または「クッキング」する他のローカルtty)にリダイレクトできます。
入力側では状況がさらに悪化します。他のローカルプロセスがローカルttyからデータを読み取ろうとすると、元のデータを読み取るだけでなくsshe
入力のために競合します。これは、同じ端末からデータを読み取る両方のプロセスの一般的な問題です。
sshe
要約すると、「生」出力を許可できない限り、ローカルttyから印刷以外のツールがsshe
ローカルttyに印刷することを許可しないようにローカルコマンド(パイプ)を作成します。
私はsshe
バイナリデータを転送または処理する能力を開発しました。私の場合、ローカルttyからデータを読み書きする必要はほとんどありません。ローカルツールの診断メッセージが十分に「洗練されていない」まま生きることができます。そのsshe
代わりに、ローカルのようにリモコンを使用できます。sudo
はい
読み書きするには、リモートブロックデバイスへのアクセスが必要です
sudo
。読む:
sshe user@server 'sudo cat /dev/sdx1' >local_file # or sshe user@server 'sudo pv /dev/sdx1' >local_file
執筆:
<local_file sshe user@server 'sudo tee /dev/sdx1 >/dev/null'
私のテストでは、ローカルはローカル
pv
ttyが「生」であるという事実を気にしないようです。あるいは、むしろそれが課す構成が課す構成とsshe
矛盾することはなく、どのツールが最初にローカルttyを構成するかは重要ではありません。だからこれはうまくいくようです:pv local_file | sshe user@server 'sudo tee /dev/sdx1 >/dev/null'
設定が
pv
中断されると、sshe
リモコンにパスワードを入力できないことがありますsudo
。設定がsshe
妨げpv
られると、pv
端末に印刷された内容が破損しているように見えることがあります。これらの仮定の下でも、コンテンツはリモートlocal_file
でそのまま送信されます/dev/sdx1
。
ローカル
sudo
とリモートがsudo
付属しています。ローカル
sudo
でパスワードを求める場合は、ローカルと同時にローカルttyで読むので良い考えではありませんsudo … | sshe … 'sudo …'
。完全ローカルは、ロック機構が実装され、両方のローカルが同時に同じ端末と対話しないために機能します。ローカルとリモートが混在している場合は機能しません。sshe … 'sudo …' | sudo …
sudo
sshe
sudo … | sudo …
sudo
sudo
sudo
あなたの地域を願って
sudo
タイムアウトを許可。そのsudo -v
場合は、事前に電話して(必要な場合)、中断することなくローカルパスワードを入力してから、次のものをパイプしてください。リモートデバイスからローカルデバイスにコピー:
sudo -v # input local password if needed sshe user@server 'sudo cat /dev/sdx1' | sudo tee /dev/sdy1 >/dev/null
ローカルデバイスからリモートデバイスにコピー:
sudo -v # input local password if needed sudo cat /dev/sdy1 | sshe user@server 'sudo tee /dev/sdx1 >/dev/null'
質問が求めたのがまさにそれです。
そして
sudo
:<binary_input sshe user@server 'sudo tool' >binary_output 2>error.log
またはより一般的に:
<binary_input sshe user@server 'requires-tty' >binary_output 2>error.log
最終メモ
私はstdinからstdinへ、stdoutからstdoutへ、stderrからstderrへのトンネリングを考えました。そして /dev/tty
する/dev/tty
のは些細なことだ。私はなぜこれを行うためのssh
オプション(と同様)がないのかと思いました。-t
今、私はそれがそれほど単純ではないことを知っています。おそらくまだ何かが足りません。