要約
SSH_MSG_CHANNEL_WINDOW_ADJUSTが与えるのは、SSHチャネルでさらに送ってよいバイト数であり、リモートコマンドがそれを処理したという確認ではない。exec要求への応答、任意の終了状態、EOF、双方向のcloseは別々の段階を表す。アプリケーションの永続的な結果を代用できない。- 副作用を持つ自動化では、SSHセッションを越えて残る操作IDと結果照会を用意し、切断後の無条件な再実行を避ける必要がある。
一つの「成功」に畳まれた四つの状態
リモート実行の利用者が知りたいのは、たいてい「終わったか」である。ところがRFC 4254が答えるのは、より限定された複数の問いだ。チャネルは開いたか。サーバーは exec 要求を受け入れたか。送信側はさらにデータを流せるか。リモートプロセスは終了状態を返したか。両端はチャネルを閉じたか。
これらを分けるのは形式主義ではない。開始要求が受理されても、プログラムは後で失敗し得る。入力が流れている間、処理はまだ始まっていないかもしれない。プロセスが別のキューへ仕事を渡して終了することもある。最後の応答がネットワーク上で失われることもある。
運用モデルは少なくとも、要求の受理、チャネルの流量枠、プロセスの終了、アプリケーションの永続的な結果を別々に持つべきだ。最初の三つはSSHから観測できる。最後の一つを定義できるのは、実際に操作されるプログラムである。
ウィンドウが許しているもの
RFC 4254の5.2節は、チャネルウィンドウをバイトで定義する。相手は枠を使い切るまでデータを送れ、尽きたら調整を待つ。SSH_MSG_CHANNEL_WINDOW_ADJUST には受信側のチャネル番号と追加バイト数が入り、到着後はその分だけ送信可能量が増える。通常データと標準エラーなどの拡張データは同じ枠を消費する。
ここにあるのは流量制御の契約だ。フィールドが表すのはチャネルと数量であり、コマンドID、アプリケーション状態、永続化の印、結果の要約ではない。規格はまた、実装がどの内部イベントを契機に枠を追加すべきかを固定していない。
したがって、相手側が追加のチャネル通信を受け入れる用意をした、と読むことはできる。だが、あるバイトがSSHライブラリ、OS、起動されたプロセス、その先のサービスまで通過したとは読めない。ウィンドウは合意した容量を守る仕組みであり、処理済みの台帳ではない。
開始要求の受理と完了は違う
チャネル固有の要求には別の応答機構がある。want reply が真なら、受信側はチャネル成功、失敗、または要求固有の継続メッセージを返す。exec は指定したコマンドの実行開始をサーバーに求める要求で、RFCは応答を要求し確認することを推奨している。
この応答は重要である。開始要求を受理したサーバーと、拒否したサーバーを区別できる。しかしコマンドはようやく生涯を始めただけだ。成功応答には、後の終了コードも、アプリケーションが作る永続オブジェクトも入っていない。
ライブラリがこの段階を「ready」や「success」という一語で公開すると、呼び出し側は層を見失いやすい。ログには、そのイベントが何を証明し、どのセッション、チャネル、要求に結びつくのかを残さなければならない。
終了状態は強いが、万能ではない
RFC 4254は、相手側のコマンドが終了した後に送れる exit-status を定義している。返すことは推奨であって必須ではなく、そのメッセージへの確認応答もない。クライアントは無視してよい。値ゼロは「通常」正常終了を意味する、と慎重に書かれている。
終了状態は、バイト枠より強い証拠だ。同期的なプログラムが「要求を完了してからゼロで終了する」と約束しているなら、十分な終点になり得る。
一方、ラッパーがジョブをキューへ登録した時点でゼロを返すことも、再起動要求の受理と健全性回復が離れることもある。デプロイの制御面が更新されても、実体は収束中かもしれない。SSHはプロセスの結果を正確に運べるが、呼び出されたプログラムの業務契約までは作れない。
closeはチャネルを終えるが、未知の結果は消さない
EOFは一方がもうデータを送らないという通知で、明示的な応答はなく、逆方向は閉じない。どちらの側もEOFなしでcloseを送れる。自分がcloseを送り、相手からも受け取って初めて、その側のチャネルは閉じたと見なされる。先行データの実宛先への配送も「可能なら」推奨される。
これは通信資源を秩序立てて終わらせる仕組みである。先行する全バイトがアプリケーション効果を生んだという保証ではない。最後のメッセージが見えなかったことも、効果がなかった証拠にはならない。切断の瞬間次第では、リモート側が確定し、クライアントだけが結果を知らない。
その不明状態を自動的に失敗へ変換し、再実行するのが最も危険な短絡である。
認証された通信と結果の証明
RFC 4251はSSHをトランスポート、ユーザー認証、接続の三要素に分ける。RFC 4253は暗号化、サーバー認証、完全性を担い、RFC 4252はクライアント側ユーザーを認証する。その上で接続プロトコルが論理チャネルを多重化する。
これらは、相手ホストは誰か、どのユーザーが認証されたか、転送中のデータが守られたかという重要な問いに答える。しかし、移行処理がコミットしたか、パッケージが目標状態になったか、委譲先APIが仕事を終えたかは答えない。強い認証は証拠の帰属を強くするが、証拠の意味を広げない。
セッションより長く生きる操作ID
やり直せない操作には、SSH接続が消えても残るIDが必要だ。リモート側は認証主体、対象、要求の指紋、受理時刻、現在状態、最終結果、作成した永続オブジェクトをそのIDに結びつける。呼び出し側は再接続後に照会できる。
SSHの拡張を新設する必要はない。コマンドの契約が冪等キーを受け取り、操作IDを返し、その状態を問い合わせられればよい。読み取りや確実に冪等な処理なら、確認済み終了コードで十分なことも多い。資格情報を二度更新する、資源を二重作成する、機器群を再度止める危険があるときに、より強い仕組みを使う。
呼び名を正しく保つのが出発点だ。ウィンドウ調整はバイト枠、要求成功は開始の受理、終了状態はプロセスの証拠、closeは接続層の終点である。業務完了は業務操作自身が示さなければならない。
情報源
会員向け解説
プロフィールの詳細
適切な会員レベルでログインすると、解説全文と出典メモをご覧いただけます。
Strategic Circle 限定
Strategic Circle
すべての読者に公開されています。参加してログインすると プロフィール解説 を閲覧できます。
Strategic Circle に参加Leadership Alliance 会員限定
Leadership Alliance
対象となる IP 資産の所有者・管理者向けです。ログインすると Leadership Alliance の解説を閲覧できます。
Leadership Alliance に参加
