概要
GCPのVPNにOSSで繋ぎたい。
基本的にはこの記事とやってることは同じ。ただ重要なMSS Clampingの説明とか、オンプレ(ローカル)側に複数サブネットある時どうすんの?とかが足りてないので補足。
あとフレッツPPPoE配下の場合の最適なMSSの計算方法も書く。
環境
- PPPoEのフレッツ使用
- ISPからはグローバルIPがアサインされる
- ルーターはHGW等でNAPTはそこで実施している
- pfsenseはLAN配下
- 足はLANに1本のみ
- LANのプライベートアドレスをstaticに設定する
- pfsense 2.4.1
手順
GCP側での作業
VPN作成
WebコンソールからGUIポチポチでVPNを設定する。ポイントは2つ。
-
リモートピアIPアドレス つまりこちら側のアドレスは、PPPoEでアサインされたグローバルアドレスを入力する
- 確認くんを使うとかで調べておく
- CLIでなら
curl ifconfig.me
-
リモートネットワークIPの範囲 にはこちら側のLANのサブネットを入れておく
- 複数ある場合は、複数サブネットを1つのトンネル定義に入れてOK
FW設定
定義しないと何も通信出来ない。忘れずに許可ルールを適当に作成する。
オンプレ(自宅)側での作業
pfsenseインストール
VMでも何でもOK。ESXiに入れた奴と、MacのVirtualBoxに入れたやつ両方で動作確認済み。公式サイトからiso拾ってきてよしなにやる。特に詰まるところは無い。
pfsense設定
…を細かく全部書くと膨大になるので、要点を書く。
- System > General Setup
- Hostname, Domain, DNS Servers, Timezone, Timeservers、等など適宜設定
- System > Advanced > Firewall & NAT
- Disable Firewall に チェックを入れてはいけません。 MSS Clampingが効かなくなる!パケットフィルタを全く使わない場合でも、面倒でも許可ルールをちゃんと定義しましょう。これでかなりハマった。
- VPN > IPsec > Tunnels > Edit Phase 1
- Key Exchange versionはIKEv2を明示的に指定した方が良い。Autoでも問題なく繋がるが。
- My IdentifierはIP addressを選択し、PPPoEのグローバルアドレスを入力する。
- 各種パラメータはGCPのドキュメントに合わせて入力する。
- DH Groupはauto等がないので、適当に選ぶ必要がある。ドキュメントに記載されているものであれば問題ない。14を(2048bit)選択。
- VPN > IPsec > Tunnels > Edit Phase 2
- こちらも上記のGCPドキュメントを参考に値を入れる
- PFS key groupがデフォルトでnoになっているので要注意。適切な値に変えておかないと、最初は接続出来るが暫く経って接続が切れる現象(CHILD_SAの再作成時にパラメータミスマッチでエラー、の無限ループ)にハマる。5(1536bit)を選択。
- Local Network / Remote Network どちらもprefixが一つしかはいらない。複数設定する必要がある場合は、一つのPhase 1に対して複数のPhase 2 Entryを作れば良い。
- パラメータはLocal Network以外全て揃えておく
- こちらも上記のGCPドキュメントを参考に値を入れる
- VPN > IPsec > Advanced Settings
- Enable Maximum MSSを有効化する
- Maximum MSSには1334を入れる (詳細後述)
- Firewall > Rules > Floating
- 特にフィルタが必要なければ全許可のルールを作る
- InterfacesのところでNICとIPsecの2つ両方を選択するのを忘れずに
- 特にフィルタが必要なければ全許可のルールを作る
- Services
- GCP VPNのゲートウェイとしか使わないのであれば不要なので全部Disableする
- 必要な物があれば適宜設定する
クライアントのルーティング
GCP側のVPCのprefixのnexthopをpfsenseの足に向けてあげる。例としてLinuxの場合はこんな感じ。
# LAN prefix: 192.168.0.0/24
# pfsense ip: 192.168.0.10
# GCP VPC prefix: 10.7.0.0/26
ip route add 10.7.0.0/26 via 192.168.0.10
もしくは、HGWなりのLAN側ルーターに上記のStatic Routeを追加してあげばクライアント側に個別の設定は要らない。ちゃんとやるならIGP喋らせるんだけど、pfsenseはOSPFとかは出来ない。
接続
NAT配下なので オンプレ側から接続しないと繋がらない。 GCP側のVPCのアドレスに対してローカルからpingを打つなりすると、自動的にトンネルがセットアップされる、はず。
手動で操作する場合は Status > IPsec からConnectボタンをポチッとな。
考察
MTUとMSS Clamping
IPSecトンネルを通すとオーバーヘッドによりMTUが減少する。PMTUDが効かない場合、経路上のルーターがTCP SynのMSSを書き換えることで経路途中でのフラグメント処理を回避しスループット低下を防ぐ。
例えばPPPoEフレッツの場合、MTUは1454なのでMSSは1414となるが、HGWがPCからインターネットへ抜ける際にTCP SynのMSSを1460(NICの設定をいじっていない場合、EthernetのMTU1500からIP 20byte + TCP 20byteを引いた値)から1414に書き換えてくれている。この辺はブロードバンドが普及した頃にたくさん記事が公開されているので、ちょっとググれば色々な情報がすぐ見つかるだろう。
さてIPSecトンネルを通す場合も、そのオーバーヘッドでトンネルを通せるパケットのMTUは減少する。そしてGCP VPNはIPパケットのフラグメント処理を許可しないので、LANから入ってきてトンネルに抜ける際に、パケットサイズがIPSecのヘッダ等で増加してMTUを超えてしまった場合は通信が出来なくなる。逆も然り。これを防ぐため、pfsenseでMSS Clampingを有効化したのだ。
さて問題は、ここで何故 1334 という数字を設定したか。
結論から言うと、
1454 (フレッツPPPoE MTU)
- 14 (Ethernet Header)
- 20 (IP Header)
- 8 (UDP Header: IPSec NAT-T)
- 8 (ESP Header)
- 16 (IV: AESの場合)
- 2 (ESP Trailer: Padding lengthで1 + Next Headerで1)
- 12 (ESP 認証ヘッダ: SHA1の場合)
= 1374
- 20 (IP Header Inner)
- 20 (TCP Header Inner)
= 1334
という感じで、そうなっている。IPSecのオーバーヘッドは80byteということになる。
実はこの調査をしている時、フレッツのMTUから減算してMSSに設定する値は1334だ、という数字を導き出したわけではなく、VM間でpingを打って通る最大パケットサイズを調べ、そこから逆算して検算した。
ping -M do -s 1346 10.7.0.2
# → 通る
ping -M do -s 1347 10.7.0.2
# → 通らない
pingのオーバーヘッドは28byte(IP 20byte + ICMP 8byte)なので、通る最大サイズの1346に28を足した1374がIPSecのMTU、という算出方法。
ちなみにMSS Clampingの設定を外すと、sshが出来なくなる(最初のホストキー交換あたりでハングする)ので、うまく動いているということが分かりやすい。
tcp-segmentation-offload
MSSの調査をしている時、VM上でtcpdumpをしていると何故かframe sizeが2000以上のパケットがキャプチャされて、なんだこれ?と思って調べてみたらTCP segmentation offloadなるものがあるらしい。送信・受信側両方で有効なので、どちらでtcpdumpしてもMTUを遥かに越えたサイズのパケットが見えた。
pfsenseのホストでIPSecでカプセリングされた後のパケットを見たら、ちゃんと全部MTU内に収まっていたのが確認できた。