実践で学ぶ、一歩進んだサーバ構築・運用術

第 10 回 ssh (中編)


8 ビット透過でない場合

Web プロキシの場合と異なり, telnet プロキシの種類によっては特定のキャラクタ(例えば,0x00) を通さなかったり, 改行コードの変換が行われたり, 特定のタイミングで特定のキャラクタが挿入されたりする場合があります。 ssh では通信路が 8bit キャラクタをすべて通すことを前提にしていますので, 通さないキャラクタがあったり, 変換や挿入が行われると, ssh による接続ができなくなってしまいます。

そこで, プロキシの種類によらず確実に通過することが期待できる 英数字と一部の記号だけで通信を行う方法を考えます。 アルファベット大文字と小文字で 52 文字,数字で 10 文字, これに記号文字を 2 つ付け加えれば 64 文字になり, 6 bit データが表現できます。 7 bit を表現するには 128 文字必要ですが, 0x80 以降のキャラクタはプロキシを通過できるとは限らないので, 128 文字は確保できません。

1 文字につき 6 bit のデータを送ることにすると, 4 文字で 24bit 分, すなわち 3 バイトのデータを送ることができます。 このような変換方法は MIME* で実際に使われていて, base64 エンコーディングと呼ばれています。

base64 エンコーディングでは, 「A」〜「Z」,「a」〜「z」,「0」〜「9」,「+」,「-」の 64 文字が使われ, この順に 0 〜 63 のデータを表現します。 送信するデータの長さが 3 の倍数でない場合は, 残りを「=」で埋めてエンコーディング後の文字列長が 4 の倍数になるようにします。 例えば, 「sengoku」というデータ列を base64 エンコーディングすると, 「c2VuZ29rdQ==」というデータ列になります(図 7)。 以下, base64 エンコーディングした結果のデータ列を「base64 文字列」と呼びます。

base64 エンコーディング例
図 7 base64 エンコーディング例

base64 でエンコーディングされた状態で telnet プロキシをデータを通過させるためには, 図 5 において, ssh クライアントから送信されたデータを, base64 エンコーディングで変換した上で telnet プロキシを通過させ, ssh サーバーが受信する前に逆変換を行って元のデータへ戻す必要があります。

さらに逆方向のデータ, つまり ssh サーバーから送られたデータは, telnet プロキシが受信する前に base64 エンコーディングで変換し, ssh クライアントが受信する前に逆変換を行って 元のデータに戻さなければなりません。 つまり図 8 のような構成になります。

8 ビット透過でない telnet プロキシ経由の ssh 接続
図 8 8 ビット透過でない telnet プロキシ経由の ssh 接続

図 8 から分かる通り, base64 の変換および逆変換を ssh クライアント側と, ssh サーバー側の 2 カ所で行う必要があります。 ただし, ssh クライアント側においては, telnet プロキシの認証を行う間は base64 変換を行ってはいけません。 プロキシに送信すべきユーザー ID やパスワードまで base64 変換してしまうと 認証ができなくなってしまいます。

認証が終わって, ssh サーバー側との接続が確立した時点(図 3 の(9)の段階) で変換を始める必要があります。 そのためには, proxy-telnet コマンドに base64 変換機能を組み込んでしまうのが 一番手っ取り早いでしょう。 幸い, perl には base64 変換のための関数が用意されていますから, 組み込みは至って簡単です。 ここでは base64 変換機能付の proxy-telnet コマンドを proxy-base コマンドと呼ぶことにします。

クライアント側の base64 変換

proxy-base コマンドは, 次に示すように図 6 の proxy-telnet コマンドに 4 カ所修正を加えるだけで作ることができます。

まず,1 行目と2 行目の間に次の行を挿入します。


use MIME::Base64;

この行は base64 変換関数を使うために必要です。

ssh サーバーとの接続を完了すると, connect サブルーチン(52 行目〜 70 行目)に処理が移りますから, base64 変換と逆変換は, このサブルーチン内のみで行えば OK です。

まず, login サブルーチンの最後で ssh サーバー側から受信した base64 文字列が 変数 Raw に設定されていますから, これを元の 8 bit データ列に戻して標準出力へ出力します。 そのためには, 57 行目を次の 3 行で置き換えます。


$_ = decode_base64($Raw);
syswrite(STDOUT, $_, length);
$buf = "";

これ以降, ssh サーバー側から受信したデータ列は, 62 行目で標準出力に出力されますから, この 62 行目を図 9 の 7 行で置き換えることによって, 出力する前に decode_base64 関数によって逆変換を行い, 元の 8bit データ列に戻します。


            print "received: $_\n" if $Verbose > 1;
            $buf .= $_;
            while ($buf =~ /=+/) {
                $buf = $';
                $_ = decode_base64("$`$&");
                syswrite(STDOUT,$_,length);
            }

図 9図 6 の 62 行目をこの 7 行で置き換える

一方, 標準入力から入力した 8bit データ列は, 97 行目でサーバー側へ送信されますから, この 97 行目を次の 2 行で置き換えることによって, 送信する前に encode_base64 関数によって base64 文字列へ変換します。


$_ = encode_base64($_);
syswrite(S, $_, length);

サーバー側の base64 変換

図 8 のサーバー側の base64 変換は, クライアント側の base64 変換と異なり, 常に変換と逆変換を行うだけで良いので簡単です。 適当な変換プログラムを書くだけで良いのですが, stone*5 に base64 変換の機能があるので, stone を使うことにします。 ssh サーバーと同じホスト上で図 10 のように実行すれば, proxy-base コマンドから telnet プロキシおよびインターネットを経由して ポート 10022 番に届いたパケットを base64 逆変換して ポート 22 番の ssh サーバーに転送し, 逆に ssh サーバーから受信したパケットを base64 変換して proxy-base コマンドへ返します。


stone -l localhost:22 10022/base &
図 10 stone を使ってサーバー側の base64 変換を行う

この場合, proxy-base の接続先ポートを 10022 番に設定する必要があるので, クライアント側の ~/.ssh/config で例えば図 11 のように設定します。


Host   asao.gcd.org
    ProxyCommand   /home/sengoku/bin/proxy-base %h 10022

図 11 クライアント側の ~/.ssh/config で proxy-base の接続先ポートを 10022 番に設定する
*5
http://www.gcd.org/sengoku/stone/Welcome.ja.html を参照。 本連載の 第 5 回第 6 回で stone の解説を行っています。

(ファイアウオールの外から中へ)


banner 本稿は日経Linux 2001 年 1 月号に掲載された、 実践で学ぶ、一歩進んだサーバ構築・運用術, 第 10 回「ssh (中編)」を日経BP 社の許可を得て転載したものです。

Copyright(C)2001 by 仙石浩明 <sengoku@gcd.org>
無断転載を禁じます

| home | up |

sengoku@gcd.org