2012年12月25日
ICMP(PING)を使ってリモートサーバーにSSH(VPN)できるPing Tunnel
VPNというのも少々大袈裟なんですが、今日はICMP(PING)パケットしか疎通しない・許可されていないネットワークを使って遠隔地にあるサーバにSSHする方法を考えてみます。


リモートアクセスを諦めかけた! ICMPしか疎通しないネットワークの概要
サーバー上からはインターネット接続できるが、ファイヤーウォールの関係で外部からは直接サーバーにアクセスできない、という状況はよくありますよね。
こういった時にはHamachi、OpenVPNなどを使ってソフトウェアVPNを構築したりするんですが、先日次のような困った状況に遭遇してしまいました。

ターゲット(接続先)・サーバーにはグローバルIPが設定されており、自身のサーバー上からはある程度自由に外部(インターネット)と通信できますが、ソフトウェアVPNで利用するような比較的特殊なポート、パケットはファイヤーウォールを通過できないんです・・・。
勿論、外部から直接接続することは殆ど不可能。 唯一許可されているのがICMP(PING)パケット。
こんな状況なので、これは専用回線(ただし遅い、コスト高)を用意・使うしかないかと思い始めていたんですが、ターゲットはグローバルIPを持ち、PINGは疎通するのでICMPでIPトンネルを作るという僅かな希望が残っています。
ICMPパケットのペイロードにデータを載せピアと通信するというのは比較的有名な話ですから、少し探してみるとPing Tunnel(http://www.cs.uit.no/~daniels/PingTunnel/)、ICMPTX(https://github.com/jakkarth/icmptx)という2つのオープンソース・プロダクトが見つかります。
ICMPTXは仮想インタフェース(tun)を使う分、設定が複雑になるので、今回はPing Tunnelを使ってIP Tunnel over ICMPを作ってみましょうか。
ICMPTXは仮想インタフェース(tun)を使う分、設定が複雑になるので、今回はPing Tunnelを使ってIP Tunnel over ICMPを作ってみましょうか。
今回の全体像を整理するとこんな構成ですね。

ローカル側にもグローバルIPを持ち、自由にアクセス、コントロール可能なICMPピア・サーバー(これをPING Tunnel - proxyと呼びます)を用意します。
(ICMPが疎通するのであれば必ずしもグローバルIPを持つ必要はありませんが、今回は利便性を考えてPING Tunnel - proxy用サーバーをLAN外部に用意します)
このサーバーと本来接続したいターゲット・サーバー(PING Tunnel - server)間では表面上ICMPパケットでやり取りを行い、実はそのペイロードはSSHデータという訳です。
Ping Tunnelのインストールと起動
Ping Tunnelはセッション(トンネル)を張る双方のサーバー間で必要になります。
こちらのプロジェクト・サイトから自身の環境にあったバイナリ・パッケージをダウンロードしましょう。
Ubuntuであれば $ sudo apt-get install ptunnel でもインストールできます。
起動方法は2つ。接続ターゲット(PING Tunnel - server)と接続プロキシ側(PING Tunnel - proxy)の2箇所で、それぞれ別のオプションを指定・起動します。
まずは、PING Tunnel - server側で ptunnel を起動します。この例では-syslogオプションでログ出力先をsyslog経由にしています。
[ptunnel-server ]# (sudo) ptunnel -syslog &※1 -syslogの代わりに-fで標準出力リダイレクト先ファイルを指定することも可能。※2 バージョン0.7.1以降では-daemon PIDファイル・パスで明示的にデーモン起動することも可能です。※3 root権限が必要な為、一般ユーザで起動する場合は sudo を付ける。
続いてプロキシ(PING Tunnel - proxy)側。
[ptunnel-proxy ]# (sudo) ptunnel -syslog -p [PING Tunnel - server]アドレス -lp 8000 -da 127.0.0.1 -dp 22 &
ここが分かり辛いところなんですが、-pでPING Tunnel - proxyサーバーから見てICMPピアになるサーバーのIPアドレス、つまり本来接続したいSSHターゲット(PING Tunnel - server)サーバーのIPアドレスを指定します。
(pなのでproxyサーバーのIPだと勘違いすると接続できませんよ)
-da(最終到達先アドレス)にはPING Tunnel - serverを経由して接続したいLAN側のIPアドレスを指定しますが、ここではPING Tunnel - server自身に接続したいので127.0.0.1(localhost)を指定します。
もし、次のような構成で、PING Tunnel - server経由でLAN側にある別のサーバーに接続したい場合、-daにはそのアドレスを指定します。

-dpは接続先ポート。今回はSSH接続できれば良いのでポート番号には22番(SSH)を指定。
さあ、これで準備が出来ましたね。
僕のラップトップ(SSHクライアント)からPING Tunnel - proxyサーバーのポート8000番に接続してみます。
[hoge@ssh-client ]$ ssh -p 8000 hoge@tunnel-proxyhoge@ping-proxy's password:Last login: Tue Dec 25 19:45:29 2012 from localhost.localdomain[hoge@tunnel-server ]$
お、不可能と思われていたICMPしか疎通しないリモート・サーバーに接続できました!
使用感はICMPでデータをやり取りしている分、データ量のある情報を出力すると、一定間隔(300~500ミリ秒程度)をおきながら、部分的に情報が出力されていく感じです(正しくpingの送受信でデータをやり取りしている感じ)。ただし、我慢できない程の遅延ではありません。
うーん、ネットワーク・エンジニア、サーバー管理者であれば、いざという時に為に頭の片隅で覚えておきたい”IPトンネル Tips”かもしれませんね。
簡単な起動・停止スクリプトを作るならこんな内容でしょうか。
#!/bin/sh PTUN_PROXY_PORT=8000 PTUN_PID_FILE="/var/run/ptunnel.pid" PTUN_SERVER_IP="127.0.0.1" PTUN_DST_IP="127.0.0.1" PTUN_DST_PORT=22 #PTUN_SERVER_OPTS="-syslog -daemon $PTUN_PID_FILE" ptunnel 0.7.1 or later PTUN_SERVER_OPTS="-syslog" PTUN_PROXY_OPTS="-p $PTUN_SERVER_IP -lp $PTUN_PROXY_PORT -da $PTUN_DST_IP -dp $PTUN_DST_PORT -syslog" case "$1" in 'start-server') echo -n $"Starting Ping Tunnel Server: " echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all ptunnel $PTUN_SERVER_OPTS > /dev/null 2>&1 & if [ $? -eq 0 ]; then echo $! > $PTUN_PID_FILE echo "OK" exit 0 else echo "Failed" exit 1 fi ;; 'stop-server') if [ -f $PTUN_PID_FILE ]; then echo -n $"Stopping Ping Tunnel Server: " kill -kill `cat $PTUN_PID_FILE` if [ $? -eq 0 ]; then rm -rf $PTUN_PID_FILE echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_all fi echo "OK" exit 0 else echo "Failed" exit 1 fi ;; 'start-proxy') echo -n $"Starting Ping Tunnel Proxy: " echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_all ptunnel $PTUN_PROXY_OPTS > /dev/null 2>&1 & if [ $? -eq 0 ]; then echo $! > $PTUN_PID_FILE echo "OK" exit 0 else echo "Failed" exit 1 fi ;; 'stop-proxy') if [ -f $PTUN_PID_FILE ]; then echo -n $"Stopping Ping Tunnel Proxy: " kill -kill `cat $PTUN_PID_FILE` if [ $? -eq 0 ]; then rm -rf $PTUN_PID_FILE echo 0 > /proc/sys/net/ipv4/icmp_echo_ignore_all fi echo "OK" exit 0 else echo "Failed" exit 1 fi ;; *) echo "Usage: $0 { start-server | stop-server | start-proxy | stop-proxy }" exit 1 ;; esac
それでは、より良いネットワーク・ライフを!
SEのためのネットワークの基本 (SEの現場シリーズ)
posted with amazlet at 12.12.25
秋山 慎一
翔泳社
売り上げランキング: 121,506
翔泳社
売り上げランキング: 121,506
プロのための〔図解〕ネットワーク機器入門 スイッチ、ルータからファイアウォールまで徹底解説
posted with amazlet at 12.12.25
三輪 賢一
技術評論社
売り上げランキング: 5,400
技術評論社
売り上げランキング: 5,400