VPN

VPN [Virtual Private Network] は、異なる場所にあるパソコン同士をインターネット経由でありながらも セキュアなLAN 接続を可能にする技術です。フリーのSoftether VPN を使うと、簡単にレイヤー2ブリッジが実現できます。
更新日 2016-05-19

概要

VPN はインターネットを超えて異なるLAN を接続する技術です。ネットワーク構成はコチラ

Server1 とServer2 は異なる場所に存在します。どちらもフレッツ 光ネクスト隼によってインターネットに繋がっており それぞれに固定IP を用意してあります。別にYahooBB(IPv6高速ハイブリッド IPv6 IPoE + IPv4) にも入っています。
またCTU のポートフォワード機能(静的アドレス変換設定(ポート指定))ならびにファイアウォールを設定して、 SSH (Port22)および VPN(OpenVPN 時 port1194) がそれぞれの配下にあるSever1 およびServer2 に通るようにしてあります。

ssh を使う

ssh はよく知られたセキュアなリモート接続技術です。付属のポートフォワード機能を使えば、特定のポートに 対するパケットをSSH を通じて受け取る事も可能です。そしてOpenSSH 4.3 以降であれば、tun/tap デバイスに 仮のローカルIP アドレスをマッピングする事すらできます。既にSSH を使っているのであれば、もっとも簡単に VPN が構築できます。ssh の回線上に作られるので1対1の接続になります。

TCP over TCP の問題

SSH はTCP であり、その上にVPN のTCPパケットをカプセル化して通す事に起因する問題がTCP over TCP と呼ばれるものです。TCP は本来パケットエラー時の再送機能を持ち合わせており、通常は適切に補正されます。ところが 載せられてるTCP にも再送が生じる事でさらなる輻輳を生み出しかねません。品質の低いネットワークを経由すると致命的な 問題になります。

OpenVPN を使う

オープンソースのVPN アーキテクチャです。TCP over TCP 問題を回避する為にUDP(port 1194) を使って構築されます。一対一の他、サーバー対多クライアントの 関係も可能です。Layer2 のブリッジ方式もサポートされています。
IPv6 over IPv4 には対応してますが、逆はムリです。残念。

VPN 付きルーターを使う

フレッツ光でレンタルされるルーター(PR-400NE) にはVPN サーバー機能(L2TP/IPSec)が付きました。iPhone からは非常に簡単に接続できます。 ただしLinux クライアントから繋ぐのはちょっと大変。
BUFFALO のBHR-4GRV にはPPTPサーバー・クライアント機能が搭載されています。双方に設置すればVPN が構築できるようです。

IPv6 で直接アクセス

フレッツ光を使うとNIC 全てにIPv6 が自動付与されますが、サブネットプレフィックス(マスク)の範囲でしか通信が出来ません。 YahooBB の「IPv6高速ハイブリッド IPv6 IPoE + IPv4 」を契約しv6 オプションが有効になると、外にも出れるグローバルIP アドレスが 付与されます。通信相手先のグローバルIP がフレッツ光網(NGN) の中に存在する場合、パケットは超高速のNGN 網内だけで折返し通信されるので 非常に高速になります(IPv4 では常にプロバイダを経由する)。
またIPv6 はNAT を必要としないので、目的のNIC に直接アクセスできます。
// IPv6 (traceroute6)
PC1 - ルーター - *(NTT) - ルーター - PC2
同じ市内に存在する拠点間でファイル転送(scp)を行った場合、OpenVPN(IPv4) では200KB/s 程度しか出ませんが、 IPv6 では40MB/s 以上出ます。どこも経由しないから速い!

ソフトイーサ VPN を使う(大本命)

VPN 大手のソフトイーサ株式会社のPacketiX VPN アーキテクチャが、オープンソース版のSoftehter VPN として無償公開 されました。IPv6 完全対応なのでNTT の超高速 NGN 拠点間通信をフル活用したVPN が張れます。
Windows 用VPN クライアントアプリの他、Linux サーバー間での運用もリモート管理できる Windows アプリが用意されており、実運用でも重宝します。完全和製という事で日本語が標準となってます。

ソフトイーサを使う(推奨!)

拠点A(CentOS 6.4 x86_64 KVM ホスト) と拠点B(CentOS 6.4 x86_64 KVM ホスト) をつなぐVPN を構築します。また手元にあるWindows 7 にサーバー(及びBridge)管理用ソフトとSoftEther VPN Client ソフトも入れます。

SoftEther vpnserver をインストールする

SoftEther VPN には最低1つのSoftEther server が必要です。今回は拠点A にインストールします。オフィシャルサイトからダウンロードします。また公式マニュアルから7.3 Linux へのインストールと初期設定 を参考にします。大変わかりやすく、簡単にセットアップできます。
vpnserver 起動後は仮想Hub を作成し、そこにtap デバイス(仮想NIC) をローカルブリッジ接続します。これらの作業はWindows7 にインストールした SoftEther VPN サーバー管理マネージャからリモートで設定できます。tap デバイスはKVM ホストのブリッジ(br0) に含めますが、これはLinux にて設定。

アップグレード場合

設定情報は vpnserver.config に書かれています。これをコピーすれば移行も楽です。

SoftEther vpnbridge をインストールする

拠点B にインストールします。vpnserver に接続する点でvpnclient と同じですが、ブリッジとしても機能します。オフィシャルサイトからダウンロードします。また公式マニュアルから9.3 Linux へのインストールと初期設定 を参考にします。

SoftEther vpnclient をインストールする(Windows7)

試しに拠点B 側に存在するWindows7 から拠点A にVPN 接続してみます。vpnclient インストールは後は、同じくインストールした SoftEther VPN クライアント接続から接続設定を行います。
相手先と仮想Hub 名、ユーザー認証(SoftEther VPN サーバー管理マネージャにてvpnserver 側に設定)を選んで完了です。あっけなく つながりDHCP にて拠点A 側のネットワークセグメントに属するIP アドレスが付与されます。

Layer2 接続の注意点

仮想Hub 同士を繋ぐと、そこにぶら下がる機器も相互にアクセスできるようになります。NTT ルーターのようなブロードキャストを 放つ機器からのパケットも届きます。これが要らぬトラブルを巻き起こし、またVPN 自体も不安定になります。
従って仮想Hub の中にはそういうトラブルを起こすような機器を含めないようにレイアウトしないといけません。 また ebtables を使って、無用なパケットが入ってこないようにフィルタリングする必要もあります。
例えばNTT ルーター経由のインターネットを使いながら仮想HUB にも属したいなら、NIC を2つ用意するか、 仮想ハブでSecureNAT(SoftEther VPN付属) を使う必要があります。ただし後者はIPv6 が使えません。

ssh を使ったVPN 構築

ssh の新機能によって接続先と接続元のtun(もしくはtap) デバイスを連結します。それぞれにローカルIP アドレスを 振り当てれば完了です。基本的にピア・ツー・ピアの接続です。常時運用向けではありません。

ssh による接続

Server1 からServer2 への通常のssh 接続にオプションを加えます。実にシンプルです。
ssh -v -oTunnel=point-to-point -w0:0 root@server2
-oTunnel=point-to-point は接続において -w0:0 の数字はServer1 及びServer2 において使用したいtun デバイス(point-to-point の場合)の通し番号です。
通常のssh 同様にServer2にログインします。ifconfig tun0 でデバイスが表示できればひとまず成功です。 またこのssh のログオフ後も接続は継続される為に、プロセス自体も生存し続けます。故にその後の接続元コンソールでは 制御は移らず、ctrl + c をもってプロセス及び連結の終了がなされます。
次にアドレスを割り振ります。ifconfig でtun0 デバイスにローカルIP アドレスを割り当てますが、その際に pointopoint (pointtopoint ではない!)というオプションを添えて相手側のtun デバイスに割り当てたいローカルIP アドレスも 付け加えます。Server1 側でも同様にします。(Server1 には別の端末もしくは別プロセスのssh によってログインします)
// Server1
ifconfig tun0 192.168.24.33 pointopoint 192.168.23.33
route add -net 192.168.23.0 netmask 255.255.255.0 dev tun0


// Server2
ifconfig tun0 192.168.23.33 pointopoint 192.168.24.33
route add -net 192.168.24.0 netmask 255.255.255.0 dev tun0

// 注)pointopoint  pointtopoint では無い!
ifconfig で確認してみましょう。Server1 側のtun0 に割り当てられたローカルIP アドレスに対して、Server2 側からping が通るでしょうか? 逆も成功するで万事成功でしょう。

OpenVPN を使う

ソースビルド

それぞれのパソコンにオフィシャルサイトからソースをダウンロードしてビルドします。
yum groupinstall "Development Tools" -y
yum groupinstall "Compatibility libraries" -y

yum install openssl-devel pam-devel -y


// lzo
cd /usr/src
wget "http://www.oberhumer.com/opensource/lzo/download/lzo-2.06.tar.gz"
tar zxvf lzo-2.06.tar.gz
cd lzo-2.06

./configure
make
make install


// openvpn
cd /usr/src
wget "http://swupdate.openvpn.org/community/releases/openvpn-2.3.2.tar.gz"
tar zxvf openvpn-2.3.2.tar.gz
cd openvpn-2.3.2

./configure
make
make install

// 起動スクリプトをコピー
cp distro/rpm/openvpn.init.d.rhel /etc/rc.d/init.d/openvpn
chkconfig openvpn on

認証の為の公開鍵基盤を構築

OpenVPN の接続認証を行う為の鍵を easy-rsa というツールで作ります。今回サーバー側で作業します。
// easy-rsa のダウンロード
cd /usr/src
wget "https://github.com/downloads/OpenVPN/\
easy-rsa/easy-rsa-2.2.0_master.tar.gz"

tar zxvf easy-rsa-2.2.0_master.tar.gz
cd easy-rsa-2.2.0_master/easy-rsa/2.0


// 鍵の作成で参考されるデータを事前に用意
vi vars

 // 以下を適宜入力
 export KEY_COUNTRY="JP"
 export KEY_PROVINCE="Aichi"
 export KEY_CITY="Nagoya"
 export KEY_ORG="PersonalUse"
 export KEY_EMAIL="mail@address.com"


// 初期化
source ./vars
./clean-all

// 認証局(CA)の証明書(ca.crt)と鍵(ca.key)を生成
./build-ca

// サーバ証明書(server.crt)と鍵(server.key)を生成する
./build-key-server server

// クライアント証明書(client1.crt)と鍵(client1.key)を生成する
./build-key client1

// DH (Diffie Hellman) パラメータ(dh1024.pem)を生成する
./build-dh
基本的にenter キーで、[Y/n] ではy を打つだけです。
// 作成されるキー郡(keys)
// ca.crt		サーバー・クライアントで共有
// server.crt	サーバー側でのみ所有
// server.key	サーバー側でのみ所有
// client1.crt	クライアント側でのみ所有
// client1.key	クライアント側でのみ所有

// サーバーにコピー
cp ca.crt /etc/openvpn/keys/
cp server.crt /etc/openvpn/keys/
cp server.key /etc/openvpn/keys/
cp dh1024.pem /etc/openvpn/keys

// クライアントにコピー
scp ca.crt root@Client:/etc/openvpn/keys/
scp client1.crt root@Client:/etc/openvpn/keys/
scp client1.key root@Client:/etc/openvpn/keys/

TLS 認証鍵を作る

以下のコマンドで作成します。クライントにもコピーを置いておきます。
openvpn --genkey --secret /etc/openvpn/keys/ta.key
scp /etc/openvpn/keys/ta.key root@Client:/etc/openvpn/keys/

設定ファイル(サーバー)

サンプル設定ファイルをコピーして修正しておきます。
cd /usr/src/openvpn-2.3.2/sample/sample-config-files
cp server.conf /etc/openvpn

vi /etc/openvpn/server.conf
 
 // tap デバイスを使用(具体的にtap0 とする事!)
 dev tap0
 ;dev tun

 // UDP パケットのサイズを変更 (フレッツ光ルーター PR-400NE のNAT 溢れ対策)
 fragment 1280
 mssfix

 // 公開鍵基盤のパスの修正
 ca   keys/ca.crt
 cert keys/server.crt
 key  keys/server.key

 // TLS 認証鍵を設定(サーバーなら0、クライアントなら1)
 tls-auth keys/ta.key 0

 // Diffie-Hellman鍵を設定
 dh keys/dh1024.pem

 // ブリッジでは使わないので無効に
 ;server 10.8.0.0 255.255.255.0

 // IP再割り当て用のキャッシュ。不要なので無効に
 ;ifconfig-pool-persist ipp.txt

 // クライアントに渡すゲートウェイ、ネットマスク、DHCP アドレスの開始と終わり
 server-bridge 192.168.24.3 255.255.255.0 192.168.24.50 192.168.24.60

 // クライアントに作られるtap0 デバイスにルーティングを張る
 push "route 192.168.24.0 255.255.255.0"

 // 圧縮する
 comp-lzo

 // user:group の指定
 user nobody
 group nobody

 // ping check
 keepalive 10 60
 ping-timer-rem

 // ログ指定
 log   /var/log/openvpn.log

 // デバッグ出力
 varb 1

 // varb 5 にするとkeepalive のRW がログに溢れます

設定ファイル(クライアント)

次にServer2 にてクライアント設定ファイルを準備します。こちらもサンプルから雛形を コピーし、環境に合わせて若干の修正を加えます。
cd /usr/src/openvpn-2.3.2/sample/sample-config-files
cp client.conf /etc/openvpn

 // tap デバイスを使用(具体的にtap0 とする事!)
 dev tap0
 ;dev tun

 // 接続先のアドレスとポートを記述
 remote 219.117.xxx.xx2 1194

 // UDP パケットのサイズを変更 (フレッツ光ルーター PR-400NE のNAT 溢れ対策)
 fragment 1280
 mssfix

 // 公開鍵基盤のパスの修正
 ca   keys/ca.crt
 cert keys/client1.crt
 key  keys/client1.key

 // TLS 認証鍵を設定(サーバーなら0、クライアントなら1)
 tls-auth keys/ta.key 1

 // 圧縮する
 comp-lzo

 // user:group の指定
 user nobody
 group nobody

 // ログ指定
 log   /var/log/openvpn.log

 // デバッグ出力
 varb 1

 // varb 5 にするとkeepalive のRW がログに溢れます

ブリッジ処理(サーバー側)

サーバー側起動スクリプトを手直しします。
vi /etc/init.d/openvpn

 // start) ブロックにて

 // if [ $successes = 1 ]; then
 //      touch $lock
 // 以下を追加
 sleep 10

 ifconfig tap0 0.0.0.0 promisc up
 ifconfig br0 multicast promisc up

 brctl addif br0 tap0

 // stop) ブロックにて
 // 以下を追加
 brctl delif eth0 tap0
ルーティングはネットワーク再起動でリセットされるので、設定ファイルにて用意
vi /etc/sysconfig/network-scripts/route-br0

 192.168.23.0/24 via 192.168.24.50 dev br0

ブリッジ処理(クライアント側)

クライアント側起動スクリプトを手直しします。
vi /etc/init.d/openvpn

 // start) ルーチンの最後に以下を追加
 if [ $successes = 1 ]; then
     sleep 10

     brctl addif eth0 tap0
	 ifconfig br0 multicast promisc up

     route del -net 192.168.24.0 netmask 255.255.255.0 dev tap0
 fi


 // stop) ルーチンの最初に以下を追加
 brctl delif br0 tap0
ルーティングはネットワーク再起動でリセットされるので、設定ファイルにて用意
vi /etc/sysconfig/network-scripts/route-br0

 192.168.24.0/24 dev br0

ブロードキャストパケットのフィルタリング

異なるネットワークセグメントからのDHCP 等のブロードキャストもOpenVPN ブリッジでは届いてしまうので、 ebtables でフィルタリングを使います。
yum install ebtalbes

// 起動時に設定ファイルを読むようにする
vi /etc/sysconfig/ebtables-config

 EBTABLES_BINARY_FORMAT="no"

// 設定ファイルを編集
vi /etc/sysconfig/ebtables

 *filter
 :INPUT ACCEPT
 :FORWARD ACCEPT
 :OUTPUT ACCEPT

 # PR-400NE(1C:B1:XF:12:16:92)
 -A INPUT -i tap0 -s 1C:B1:XF:12:16:92 -j DROP
 -A INPUT -p IPv4 -i tap0 --ip-proto udp --ip-dport 67 -j DROP

 *broute
 :BROUTING ACCEPT

 # PM-700(0c:0a:X2:d5:4e:f5)
 -A BROUTING -s 0c:0a:X2:d5:4e:f5 -j DROP

 # VPN Destination PR-400NE(10:66:X8:36:70:AC)
 -A BROUTING -s 10:66:X8:36:70:AC -j DROP

// 再起動
service ebtables restart
フレッツ光隼のレンタルルーターPR-400NE のLAN 側MAC アドレス(1C:B1:XF:12:16:92)からのパケットは、VPN 接続先に送られると不具合になるのでINPUT にてDROP。
ひかりTV のチューナーPM-700 のMAC アドレス(0c:0a:X2:d5:4e:f5) からのパケットも一切不要なのでDROP(これしないと不具合がある)。
OpenVPN の接続相手側のルーターPR-400NE(10:66:X8:36:70:AC) からのパケットもBROUTING にてDROP。

DHCP の仕組み

1.[DHCP ディスカバー] クライアントPC はポート67 を指定してブロードキャストパケットを送ります。
2.[DHCP オファー] DHCP サーバーからは、このブロードキャストに書かれたMAC アドレス宛のユニキャストで、振り出しIP アドレスが届きます。
3.[DHCP リクエスト] クライアントPC は提示されたIP アドレスを申請する旨を再度ブロードキャストで送ります。
4.[DHCPアック] DHCP サーバーからは、振り出し完了のメッセージが再度ユニキャストによって届きます。
相手側のネットワークセグメントと繋がるtap0 インターフェイスに入ってくるDHCP ディスカバーをブロックすれば良い訳です。

IPv6 ステートレス設定がVPN を超えないようにする(ひかりTV 対策)

IPv6 が自動で設定されますが、VPN 先のルータを元に設定されると不具合が生じる場合があります(ひかりTVのチューナー PM-700など)。 ip6tables にてそれらのやりとりをブロックします。
vi /etc/sysconfig/ip6tables

 -A INPUT -i tap0 -p icmpv6 --icmpv6-type router-solicitation -j REJECT
 -A INPUT -i tap0 -p icmpv6 --icmpv6-type router-advertisement -j REJECT
 -A INPUT -i tap0 -p icmpv6 --icmpv6-type neighbor-solicitation -j REJECT
 -A INPUT -i tap0 -p icmpv6 --icmpv6-type neighbor-advertisement -j REJECT
上記の処理はブリッジでは通用していない?ebtables 優先でこれは不要かも…。

IP フォワードを有効に

vi /etc/sysctl.conf

 net.ipv4.ip_forward = 0
 // ↓ 有効に
 net.ipv4.ip_forward = 1


// ネットワークの再起動
service network restart