Windows10のPCでvagrantで作ったVM(OSはubuntu)に外部からアクセスするにはこちらにあるようにpublic networkを追加しますが、インターネットからVM内のWEBサーバにアクセスするとタイムアウトしてしまう現象が発生しました。ただしLAN内のPCからはアクセスできます。いろいろ調べたところ、原因はVM内のルートテーブルにありました。
ネットワークの構成としては、下の図のようになっています。ブロードバンドの設定としてポート80へのアクセスが来た場合にはVMのIPアドレスに向けてポートフォワードするように設定しています。
このような構成で、インターネットからVM上に立ち上げたnginxサーバにアクセス(指定するIPアドレスはブロードバンドルータのグローバルIPアドレス)しようとしても、タイムアウトしてしまうのです。いろいろ調査した結果、インターネットから送ったTCPのSYNパケット(参考:3WAY HANDSHAKE)への応答が返ってきていないことがわかりました。
そこでまず、VM上でwiresharkを使ってSYNパケットが来ているかどうか、を調べてみたのですが、確かにeth1宛にSYNパケットが来ていました。しかしながら、eth1からその応答であるSYN/ACKパケットが送られていません。どうやらeth0から送られているようです。
VM内でip routeを実行すると、デフォルトゲートウェイが表示されますが、vagrantでは特に何も設定しない状態ですと、ホスト側のインターフェイスのIPアドレスになるようです(eth0を通る)。この場合、外部からきたデータはpublic networkのインターフェイス(eth1)が受信するのに対し、外部に送り返す場合はホスト側のインターフェイス(eth0)を経由することになります。
インターネットからブロードバンドルーターのIPのポート80にアクセスすると、ポートフォワードによりVMにSYNパケットが送られます。上に書いたようにこのパケットは問題なくVMのインターフェイス(eth1)に届いています。このインターフェイス(eth1)はVirtualBoxのタイプでいうとブリッジアダプターになります。一方、VMからインターネットへ出ていく通信はHOST PCのインターフェイスを経由します。このインターフェイス(eth0)はVirtualBoxのタイプでいうとNATになります。よって、HOST PCからブロードバンドルーターに届くパケットの送り元のIPアドレスはVMのeth1ではなく、HOST PCのものになります。そうするとどうなるかというと、VM宛のSYNパケットは、HOST PCのIPアドレスが送信元となったSYN/ACKパケットでブロードバンドルータに送られることになります。送り先はもちろん、インターネット上のグローバルIPアドレスです。
実際にキャプチャして確認したわけではないのでここからは推測になりますが、ブロードバンドルーターはインターネットから受信したSYNパケットをポートフォワード機能によって送り出したときの宛先IPアドレスと、SYN+ACKパケットを受信したときの送信元IPアドレスが異なる場合(ポートフォワード先ではない)は後者をドロップしているのではないか、と思われます。また、LAN内のPCからのアクセスでは問題がないのは、同一ネットワーク内で閉じているためレイヤ2の通信のみとなり、このようなドロップは起こらなかったものと思われます。
この問題を解決するには、Vagrantfileに以下を付け加えます。
config.vm.provision "shell",
run: "always",
inline: "sudo ip route del default dev eth0 && sudo ip route add default via 192.168.0.1"
このように設定により、元々設定されていたVMのデフォルトゲートウエイ(HOST PC)が削除され、その代わりにブロードバンドルーター(192.168.0.1)をデフォルトゲートウェイとして新たに設定します。こうすることにより、インターネットからVMに届いたSYNパケットに対する応答(SYN/ACK)は、直接ブロードバンドルータに送られるため、送り元IPアドレスの変換は行われません。
この設定によって、無事インターネットからVM上のWEBサーバにアクセスできることを確認できました。
