2012年12月25日

ICMP(PING)を使ってリモートサーバーにSSH(VPN)できるPing Tunnel

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

access-here


リモートアクセスを諦めかけた! ICMPしか疎通しないネットワークの概要


サーバー上からはインターネット接続できるが、ファイヤーウォールの関係で外部からは直接サーバーにアクセスできない、という状況はよくありますよね。

こういった時にはHamachi、OpenVPNなどを使ってソフトウェアVPNを構築したりするんですが、先日次のような困った状況に遭遇してしまいました。

ping1

ターゲット(接続先)・サーバーにはグローバル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を作ってみましょうか。

今回の全体像を整理するとこんな構成ですね。

ping2

ローカル側にもグローバル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にはそのアドレスを指定します。

ping3


-dpは接続先ポート。今回はSSH接続できれば良いのでポート番号には22番(SSH)を指定。

さあ、これで準備が出来ましたね。


僕のラップトップ(SSHクライアント)からPING Tunnel - proxyサーバーのポート8000番に接続してみます。

[hoge@ssh-client ]$ ssh -p 8000 hoge@tunnel-proxy
hoge@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の現場シリーズ)
秋山 慎一 
翔泳社 
売り上げランキング: 121,506


Posted by netbuffalo at 22:00│Comments(0)TrackBack(0)ネットワーク | Linux


この記事へのトラックバックURL

http://trackback.blogsys.jp/livedoor/netbuffalo/4318550

コメントする

名前
URL
 
  絵文字