2015年5月27日水曜日

SSH over HTTPS(HTTP)でプロキシ、ファイアウォール超え


【目的】
社内や学内などのイントラネットからインターネット上にある外部のsshサーバに接続する.
ただし、イントラネットから外部へのアクセスはプロキシーサーバを通すようになっており、HTTP(80/tcp)、HTTPS(443/tcp)向けの通信しか許可されていない.



【構成概要】

 intranet  企業内や学内のイントラネット
    ↓
+--------+ proxy.example.com:8080(proxyサーバ)
| proxy  |  外部へのHTTP(80/tcp)、HTTPS(443/tcp)を許可
+--------+
    ↓
+--------+ remote.example.com(sshdサーバ)
| remote |
+--------+



【実現手段】
HTTPSはSSL/TLSで暗号化されているので、プロキシは内容を把握できない.
クライアントのWEBブラウザと接続先のWEBサーバーとの間に双方向の通信路を設定するだけである.
よってHTTPS以外のプロトコル、ここではsshのプロトコルも流すことが可能となる.

ただし、許可されているポートは443/tcpなので、
SSHサーバ側ポートは22/tcpから変更しておく必要がある.



【設定】
● リモートサーバ側での設定
HTTPS用のポートをsshサーバ側で開いておく.
ポートは2つ記載することが可能である.
22/tcpを閉じなくとも追記できる.
※selinuxが有効になっていると443ポートは開かない.

# vim /etc/ssh/sshd_config
Port 22
Port 443

# systemctl restart sshd.service

+--------+ remote.example.com:22 and 443
| remote |   sshサーバ(sshdが稼働)
+--------+ 


● イントラネット上のクライアント
connectリクエストを発行できるSSHクライアントが必要である.
HTTP通信は暗号化されているため、プロキシサーバに対し、HTTPSのセッションを透過させるよう依頼するconnectメソッドを発行するためである.

※脱線
これだとプロキシ側でセキュリティチェック、ウィルスチェックなどの機能を持たせられない.
最近では中継機能を有したプロキシも存在している.
プロキシサーバが代理のサーバ証明書を提示することで実現している.


(putty on windowsの場合)
puttyにはHTTPプロキシ経由のトンネリング用ビルトインが組み込まれているため、
追加モジュールは不要であり楽である.
自分は利用したことがないが、teratermも人気のようである.同様のことができるだろう.

プロキシサーバを指定して、
HTTPS通信を装うため、SSHサーバのポートを443番に変更して接続するだけである.

~putty内設定~
session
  host: remote.example.com
  port: 443/tcp

connection
  Proxy type: HTTP
  Proxyhostname: proxy.example.com
  Port: 8080

これで接続できる.
※クライアント用の鍵の設定などは必要に応じて指定すること.


(openssl on unix/linuxの場合)
opensshを利用する場合は、corkscrewを使うと便利であろう.

インストール後、コマンドとして直接使う場合はこうである.
$ corkscrew <proxyhost> <proxyport> <desthost> <destport> [authfile]

サーバーに接続するときに利用するコマンドとして、
接続元ローカルユーザのSSH用設定に以下を追加しておけばいいよい.
$ vi ~/.ssh/config
HOST remote.example.com
ProxyCommand /usr/local/corkscrew/bin/corkscrew proxy.example.com 8080 %h %p

%hは接続先ホスト、%pは接続先ポートに置き換えられる.
HOST *
と正規表現形式で記載して接続先によらないらないしておくこともできる。

プロキシが認証を必要とする場合はその情報を記載したファイルをオプションとして指定できる.
ProxyCommand /usr/local/corkscrew/bin/corkscrew proxy.example.com 8080 %h %p ~/.ssh/proxy.login.ini

$ vi ~/.ssh/proxy.login.ini
<proxy_username>:<proxy_password>

これで接続できるはずである.
$ ssh user@remote.example.com -i ./.ssh/remote_rsa -p 443



【動作ロジックの説明】
puttyにはディフォルトでconnect用のビルトインがあり、
opensslの場合はcorkscrewをインストールすれば同じことができる、
と簡単に書いたが何をやっているか、その動作原理・仕組みは気になるところだろう.

同じことを手動で試してみる.

$ telnet proxy.example.com 8080
Trying proxy.example.com...
Connected to proxy.example.com.
Escape character is '^]'.
CONNECT remote.example.com:443 HTTP/1.0
HTTP/1.0 200 Connection established
SSH-X.X-XpenSSH_X.X

SSHのバージョン応答が返ってくればサーバと接続できている.
あとはSSHのプロトコルに従っていけばよい.

これでイメージはついただろう.

上と同じ動作を行った後に標準入力から受け取ったデータをそのまま
remote.example.com:443へ送信し、remote.example.com:443から受け取ったデータをそのまま標準出力へ送ればよい.

Cで書かれたcorkscrewのコードを見れば一目である.
LL系の言語であればより簡単に書けるだろう.perlで書いたものがあったので紹介する.
http://www.gcd.org/sengoku/docs/NikkeiLinux00-12/config.ja.html



【その他】
仮にCONNECTメソッドも利用できない、HTTP(80/tcp)しか開放されていない、という状況ならどうすればいいだろうか.
アイデアとして思いつくのは、プロトコルはHTTPをそのままに、ボディ部分にSSHの通信を記載し、
クライアントとサーバ側で取り出せばいいだろう.
この考え方を使った、httptunnelというツールがあるようだ.



(参考)
sshで多段接続をしてweb閲覧、scpを実現
HTTPS通信とプロキシサーバ
SSH Over Proxy

(追記) これはよい!!
sslh でport443 を有効活用して、sshもhttpsも同時に待ち受けする。