Pages - Menu

2016年2月14日日曜日

VPSでSoftEther VPNを使って自宅LANに外部から接続する

2016.02.18 スクリプトvpnserver.shに不備があったので修正。末尾に追記あり。

自宅(大阪の方)のインターネット環境をSoftbank Airに変えた。
ところが、Softbank Airの機器(Airターミナル)はポート開放ができないので、自宅のVPNサーバー(SoftEther VPN Server)に外から接続できなくなってしまった。
どうしたもんか、と色々調べていたら、似たような境遇の方がいて(Softbank Air導入期。VPSでVPNを使って自宅LANへアクセスしてみた。 | しま★りん.blog @ayurina)、この記事が大変参考になったので、同じような構成を組んでみることにした。
具体的には
  • VPSを借りて、そこにSoftEther VPN Serverを立てる。
  • 自宅サーバーにはSoftEther VPN Bridgeを入れて、VPS上のSoftEther VPN Serverにカスケード接続する。
という構成で、自宅LANをVPSまで伸ばそうというわけである。
外部からVPN接続する時は、VPS上のVPN Serverに対して接続しに行く。ここを経由することによって、外部からも自宅LANに入れるようになる。
構成を図に示すと、下のようになる。赤い太い線がVPNトンネル、緑の細い線がL2の論理接続を示す。
Fig.1 VPNネットワーク構成
図内の各機器の接続は、以下のようになる。
  • おうちサーバー上の仮想HUB"BRIDGE"とVPS上の仮想HUB"DEFAULT"の間は、SoftEther VPNによりL2のトンネルが形成される。
  • おうちサーバー(及び自宅内の他のデバイス。図では省略)は、仮想HUB"BRIDGE"経由でVPS上の仮想HUB"DEFAULT"とL2接続する。
  • VPSのOSは、TAPインターフェイス"tap_vpn"を介して仮想HUB"DEFAULT"とL2接続する(Linuxの場合、物理NIC"eth0"は仮想HUBと接続できない。SoftEther VPNマニュアル 3.6.9 tap デバイスの使用参照)。
  • 外部から接続する場合は、そのデバイスのVPNクライアント機能(Windowsの場合はMS-SSTP、iOSやAndroidの場合はL2TP over IPsec)で仮想HUB"DEFAULT"との間にトンネルを作って通信を行う。
細かいところは、以下の本文中で説明する。
なお、VPSにはDigitalOceanを使うことにした。
DigitalOceanとは
DigitalOceanは、1ヶ月$5から借りられる激安VPS。
サインアップしてクレジットカードを登録し、"Create Droplet"をクリックしてOSのディストリビューションとサイズ、データセンターを選ぶだけで、あっという間に仮想サーバーが手に入る(ちなみに、DigitalOceanでは仮想サーバーのことを"Droplet"と呼ぶ)。
Fig.2 DigitalOceanにてVPSを作成
今回は、OSはCentOS7.1、サイズは一番安い$5のやつ、リージョンは一番近いシンガポールを選択。

また、SSH接続するために公開鍵の登録を行う必要がある。この辺りの手順は、本稿のメインテーマではないので、詳細は割愛する。
VPSにSoftEther VPN Serverを導入する
公式サイトからLinux 64bit用のインストーラをダウンロードして、makeする。基本的にはマニュアル通り。
なお、2016年2月14日現在の最新バージョンは4.19。
$ wget http://jp.softether-download.com/files/softether/v4.19-9599-beta-2015.10.19-tree/Linux/SoftEther_VPN_Server/64bit_-_Intel_x64_or_AMD64/softether-vpnserver-v4.19-9599-beta-2015.10.19-linux-x64-64bit.tar.gz
--2016-02-xx hh:mi:ss--  http://jp.softether-download.com/files/softether/v4.19-9599-beta-2015.10.19-tree/Linux/SoftEther_VPN_Server/64bit_-_Intel_x64_or_AMD64/softether-vpnserver-v4.19-9599-beta-2015.10.19-linux-x64-64bit.tar.gz
Resolving jp.softether-download.com (jp.softether-download.com)... 130.158.75.49
Connecting to jp.softether-download.com (jp.softether-download.com)|130.158.75.49|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: 6277746 (6.0M) [application/x-gzip]
Saving to: ?softether-vpnserver-v4.19-9599-beta-2015.10.19-linux-x64-64bit.tar.gz?

100%[======================================>] 6,277,746   4.67MB/s   in 1.3s   

2016-02-14 12:53:00 (4.67 MB/s) - ?softether-vpnserver-v4.19-9599-beta-2015.10.19-linux-x64-64bit.tar.gz? saved [6277746/6277746]

$ tar xvzf ./softether-vpnserver-v4.19-9599-beta-2015.10.19-linux-x64-64bit.tar.gz
vpnserver/
vpnserver/Makefile
(snip)
vpnserver/hamcore.se2
$ cd vpnserver
$ make
--------------------------------------------------------------------

SoftEther VPN Server (Ver 4.19, Build 9599, Intel x64 / AMD64) for Linux Install Utility
Copyright (c) SoftEther Project at University of Tsukuba, Japan. All Rights Reserved.

--------------------------------------------------------------------


Do you want to read the License Agreement for this software ?

 1. Yes
 2. No

Please choose one of above number: 

以後、画面の指示に従ってmakeを進める。

makeが終わったら、vpnserverディレクトリを/usr/localに移動し、root以外触れないようパーミッションを変更。
ここからは、rootで作業する。
(Syntax Highlighterの都合上、プロンプトは#ではなく$です。悪しからず)
$ cd ..
$ mv vpnserver /usr/local
$ cd /usr/local/vpnserver
$ chmod 600 *
$ chmod 700 vpncmd
$ chmod 700 vpnserver
ここまでで配置は完了。
次に、vpnserverをサービス化するが、公式マニュアルにはchkconfigを使う方法しか書いてない。せっかくCentOS7なので、systemdを使うようにする。
Systemd用SoftEther設定ファイル - blog.204504byse.infoを参考にさせていただいた。
[Unit]
Description=SoftEther VPN Server
After=network.target

[Service]
Type=forking
ExecStart=/usr/local/vpnserver/vpnserver start
ExecStop=/usr/local/vpnserver/vpnserver stop

[Install]
WantedBy=multi-user.target
サービスを起動し、自動起動の設定を行う。
$ systemctl start vpnserver
$ systemctl enable vpnserver
これで無事サーバーが立ち上がった。
VPN Serverの設定
"SoftEther VPN Server Manager for Windows"のインストーラをダウンロードし、おうちサーバーにインストールする(以後"SoftEther VPN サーバー管理マネージャ"と呼ぶ)。
インストールが完了したら、SoftEther VPN サーバー管理マネージャを起動し、VPS上のVPN Serverに接続する。
初回接続時にはパスワードの設定を求められるので、設定する。
メイン画面はこんな感じ。
Fig.3 SoftEther VPN サーバー管理マネージャのメイン画面
仮想HUBは、初期状態で作られる"DEFAULT"をそのまま使う。
まず"仮想 HUB の管理"をクリックして仮想HUB管理画面に移動する。
"ユーザーの管理"にて「外部から接続するユーザー」と「VPN Brigdeが使うユーザー」の2つを作る。
Fig.4 ユーザーを2つ追加
次に、外部から接続してきたVPNクライアントに対してDHCPでIPアドレスを割り当てるための設定を行う。これには"SecureNAT"という機能(仮想ルーターみたいなもん?)を使う。
"仮想 NAT および仮想 DHCP サーバー機能" にて、SecureNAT 機能を有効にする。これでSecureNAT 仮想ホストが起動する。
"SecureNAT の設定"にて、SecureNAT 仮想ホストのIPアドレスを設定。
仮想DHCPサーバー機能を有効化し、配布IPアドレス帯とサブネットマスクを設定する。
Fig.5 DHCPサーバーとして動作させるための設定
SoftEther VPN サーバー管理マネージャのメイン画面に戻り、iOS/AndroidのVPNクライアント用に"IPsec / L2TP 設定"を行う。
ここでやることは、"L2TP サーバー機能を有効にする (L2TP over IPsec)"にチェックを入れて、"IPsec 共通設定"に好きな事前共有鍵(PSK)を設定するだけ。

次に、WindowsのVPNクライアント用に"OpenVPN / MS-SSTP 設定"を行う。
ここでやることはもっと簡単で、"MS-SSTP VPN サーバー機能を有効にする"にチェックを入れるだけ。

次に、MS-SSTP用のサーバー証明書を作る。
"暗号化と通信関係の設定"に移動し、"サーバー証明書"の"新規作成"をクリックし、画面の指示に従う。
サーバー証明書が出来上がったら、"エクスポート"をクリックして、"X509 証明書ファイル(.CER)と秘密鍵ファイル(.KEY)の組み合わせとして保存する"を選び、CERファイルを保存する。
このCERファイルは、別途、Windowsクライアントの証明書ストア(信頼されたルート証明機関)にインポートしておく。

また、外部からDNS名で接続できるようにダイナミックDNS機能を使う必要があるが、これはデフォルトで有効になっている(ありがたい!)ので、特にいじる必要はない。メイン画面下部に表示されている"現在の DDNS ホスト名"をメモっておけばOK。ちなみに、先ほど作成したサーバー証明書のCNは、このDDNSホスト名になっている。

最後に、VPSのOSが仮想HUBと通信をするためのTAPインターフェイスを作る(これをやらないと、おうちサーバーからも外部からVPN接続したデバイスからも、VPSサーバーと通信することができない)。
"ローカルブリッジ設定"で、仮想 HUB"DEFAULT"を選び、"新しい tap デバイスとのブリッジ接続"を選択。新しい tap デバイス名を "vpn" として"ローカルブリッジを追加"をクリック。
Fig.6 tapデバイスの作成
これを行うと、OS上に"tap_vpn"(自動的に"tap_○○"という名前になる。○○は作成したtapデバイス名)というデバイスが認識されるので、そいつにIPアドレス(今回は192.168.1.252)を設定してやる。
…これだけでよい、はずなのだけど、私の環境では、VPSのOSを再起動した後、うまくtap_vpnにIPアドレスが設定されない現象に出くわした。
(どうも、vpnserverが起動してtapデバイスを作りに行く前に、Network ManagerがtapデバイスにIPアドレスを割り当てようとして空振りしているっぽい?
systemd用のファイルに"After=network.target"って書いてるのにな?
この辺り、ちゃんと原因切り分けできてません)
仕方がないので、とりあえずの回避策として、vpnserverの起動スクリプトを用意することにした。
スクリプトでやっていることは、『vpnserverのプロセスを立ち上げた後、tapデバイスが作られるのを待ってから、tapデバイスにIPアドレスを割り当てる』というもの。

作成したスクリプトは以下の通り。/opt の下に vpnserver.sh という名前で保存し、chmod +Rで実行権限を付けておく。
※昔ながらのrcスクリプト風の書き方になっていてダサい(お作法に沿っていない)気がするので、後日見直したい。
#!/bin/sh
# description: SoftEther VPN Server
DAEMON=/usr/local/vpnserver/vpnserver
LOCK=/var/lock/subsys/vpnserver
test -x $DAEMON || exit 0
case "$1" in
start)
$DAEMON start
touch $LOCK
i=0
while [ $i -lt 10 ]; do
  sleep 1
  if [ `nmcli c show | grep tap_vpn | wc -l` -gt 0 ]; then
    /usr/bin/nmcli connection modify tap_vpn ipv4.method manual ipv4.addresses 192.168.1.252/24 ipv4.gateway 192.168.1.1
    /usr/bin/nmcli connection down tap_vpn
    /usr/bin/nmcli connection up tap_vpn
    logger /opt/vpnserver.sh:tap_vpn ip address set successful
    break
  fi
  i=$((i+1))
done
;;
stop)
$DAEMON stop
rm $LOCK
;;
restart)
$DAEMON stop
sleep 3
$DAEMON start
i=0
while [ $i -lt 10 ]; do
  sleep 1
  if [ `nmcli c show | grep tap_vpn | wc -l` -gt 0 ]; then
    /usr/bin/nmcli connection modify tap_vpn ipv4.method manual ipv4.addresses 192.168.1.252/24 ipv4.gateway 192.168.1.1
    /usr/bin/nmcli connection down tap_vpn
    /usr/bin/nmcli connection up tap_vpn
    logger /opt/vpnserver.sh:tap_vpn ip address set successful
    break
  fi
  i=$((i+1))
done
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac
exit 0
systemd用ファイルを以下のように書き換える。
[Unit]
Description=SoftEther VPN Server
After=network.target

[Service]
Type=forking
ExecStart=/opt/vpnserver.sh start
ExecStop=/opt/vpnserver.sh stop
ExecReload=/opt/vpnserver.sh restart

[Install]
WantedBy=multi-user.target
今のところ、これでうまく動いている。
おうちサーバーにSoftEther VPN Bridgeを導入し、設定する
公式サイトからWindows用のインストーラをダウンロードして、Bridgeをインストールする。
インストールが終わったら設定を行う。
おうちサーバー上でSoftEther VPN サーバー管理マネージャを起動し、localhost に接続する。
メイン画面を見ると分かるが、インストール時点で自動的に"BRIDGE"という固定名称の仮想HUBが作成されている(VPN Bridgeは、この1つの仮想HUBしか持つことができない)。
まず、"ローカルブリッジ設定"で仮想HUB"BRIDGE"と物理NICとのひも付けを行う。
Fig.7 ブリッジの作成
次に、仮想HUBの管理画面にある"カスケード接続の管理"にてVPS上の仮想HUBとの接続設定を行う。ユーザーはVPN Bridge用に作ったものを指定する。
Fig.8 カスケード接続で自宅のブリッジとVPSの仮想HUBを接続する
これで全ての設定が完了。
後は、クライアントデバイス(PCやタブレット等)にVPN接続設定を行うだけ。そっちは本稿の主旨ではないので省略。
2016.02.18 自分用追記
サービス起動順を制御しないと、Apacheがtap_vpnのIPアドレス 192.168.1.252 のバインドに失敗して起動がコケるので、httpd.serviceをvpnserverより後に起動する必要がある。3行目の"After"を修正。
$ cp /usr/lib/systemd/system/httpd.service /etc/systemd/system/
$ vi /etc/systemd/system/httpd.service
[Unit]
Description=The Apache HTTP Server
After=network.target remote-fs.target nss-lookup.target vpnserver.service

[Service]
Type=notify
EnvironmentFile=/etc/sysconfig/httpd
ExecStart=/usr/sbin/httpd $OPTIONS -DFOREGROUND
ExecReload=/usr/sbin/httpd $OPTIONS -k graceful
ExecStop=/bin/kill -WINCH ${MAINPID}
# We want systemd to give httpd some time to finish gracefully, but still want
# it to kill httpd after TimeoutStopSec if something went wrong during the
# graceful stop. Normally, Systemd sends SIGTERM signal right after the
# ExecStop, which would kill httpd. We are sending useless SIGCONT here to give
# httpd time to finish.
KillSignal=SIGCONT
PrivateTmp=true

[Install]
WantedBy=multi-user.target

0 件のコメント:

コメントを投稿