Google Meet というビデオ会議システムの検証をしてみました。(ブラウザは最新のChromeを使ってます。)
しかし、接続後1分くらいで「ネットワークエラーのため、ビデオハングアウトを開始できませんでした。」というエラーが表示され切断されてしまいます。
まず、ネットワーク構成についてです。今回ハマったのは以下の構成のせいでした。
・FW:内→外はプロキシ経由以外は禁止。(端末直ではネットに出れない。FWはFortiGate使用)
・プロキシ:認証付きプロキシ。かつ、ホワイトリストにして許可URL以外通信禁止。(iFilterを使用)
G Suiteヘルプ:2. Meet 用にネットワークを最適化するをみると、プロキシは推奨されていないなのですが、セキュリティポリシー上仕方ありません。
G Suiteヘルプ:1. ネットワーク接続の要件では、以下のURLのホワイトリストが掲載されているので、これをiFilterの許可するURLリストに追加しました。
https://*.google.com/* https://*.googleapis.com/* https://*.gstatic.com/* https://*.googleusercontent.com/*
これで試しましたが、やはり切断されます。少し詳しく調査が必要なようです。
GoogleMeetが使っているプロトコル - WebRTC
GoogleMeetはWebRTCというプロトコルを使っているようです。まずはこれについて少し調査。。。
WebRTCはリアルタイムコミュニケーションをプラグインレスで可能にするプロトコルです。
P2Pでの通信が可能であり、原則UDPを使います。P2Pのため、NAT超えの仕組みもあります。
WebRTCでは、まずSTUNサーバーによって自身の通信環境を確認し(NATのグローバルIPやマッピングされたポート等)相手に知らせます。(相手に知らせるときはシグナリングサーバーを使います。) これでP2P相手とのNAT情報がわかるので、後はクライアント同士がP2Pで接続しNAT超えすることができます。
FW等で直接P2Pが難しい場合は、P2PではなくTURNというサーバーがストリームデータの受け渡しに介入します。
つまり、Client - FW - TURNsv - FW - CLient となるわけですね。
しかしTURNもUDPを使いますし、TURNサーバおよびシグナリングサーバーとの通信ポートをFWに開けないといけません。
これを克服するために、さらにTURN over TCPというものがあるようです。TURNのUDPデータをTCPでカプセル化したものぽいです。
WebRTCの詳しい話は以下サイトが参考になります。
壁を越えろ!WebRTCでNAT/Firewallを越えて通信しよう | HTML5Experts.jp
エンタープライズ向けWebRTCの外堀が埋まってきている
WebRTCの裏側 · GitHub
WebRTC コトハジメ · GitHub
通信の流れを追ってみた
3流PGの環境ではFWでクライアントは直接ネットに出れず、ホワイトリスト付きプロキシを使っています。
どのようなパケット流れて、どうブロックされてしまっているのかWireSharkで追っかけて見ました。
1.DNSでGoogleの複数のSTUNサーバの名前解決依頼。
2.クライアントはSTUNプロトコルで、Googleの複数のSTUNサーバに Binding Request(UDP) を投げる
3.FWでブロック
4.2.がタイムアウトしSTUNサーバ(IP)宛にプロキシ経由で通信試みる。(HTTPS)
5.iFilterにてブロック。
6.通信不可能。。。
WebRTCが利用できるかどうかのチェックサイトhttps://test.webrtc.org/があります。
現状のままでは、Network部分は Udp, Tcp 共に X でした。
対応方法1 FWに穴を開ける
ChromeのWebRTCが使うポートはデフォルトで、19302~19309です。
FWで、宛先:any , UDP:19302~19309 を許可してやるとうまくいきました。
この状態で、test.webrtc.org/ を実行すると、Network部分は Udp, Tcp 共に ○ でした。
この状態だと正常にMeetが使えます。
しかし、接続先はanyなのであまりよろしくないですよね。
接続先も指定できないか検討してみました。
WireSharkで調べると、Meetの時に使用しているのは、上述のMeetヘルプで掲載されているアドレス以外に、以下のホストにアクセスしているようでした。
stun.l.google.com stun1.l.google.com stun2.l.google.com stun3.l.google.com stun4.l.google.com
これらをFWのアドレスオブジェクトに追加し、ポートと合わせてフィルタするようにしました。
が、そうすると今度は正常に通信できなくなりました。
FQDN指定はだめなようです。おそらく理由は後述のiFilter使用時と同じで、Chrome側がFQDNアクセスではなくIPベースでアクセスしているからだと思います。FortiOSにDNS引きによるフィルタリング機能があればうまくいきそうですが、見つけることはできませんでした。
また、FWには穴を開けたくないので、少々パフォーマンス下がってもプロキシ経由で使えないものか試みました。
対応方法2 iFiter側で許可する(ホスト名DNS引き)
ChromeのWebRTCは直接接続がだめなら自動的にプロキシ経由でしてくれるので、単純にプロキシ(iFilter)側でGoogleMeetの通信を許可すればいいだけなのですが、これが意外と厄介でした。
なぜかというと、URL単位でのホワイトリスト形式を取っているからです。
Chromeがプロキシに送るリクエストは、URLではなくGoogleのSTUN,TRUNサーバのIPを送っています。
現時点でのサーバのホスト名とIPは以下の通りでした。(この中からランダムで接続先を選んでいるようです)
stun.l.google.com 74.125.23.127 stun1.l.google.com 108.177.98.127 stun2.l.google.com 173.194.199.127 stun3.l.google.com 173.194.196.127 stun4.l.google.com 64.233.177.127
Chromeからはstun.l.google.comというURLでアクセスするのではなく、直にIP=74.125.23.127でアクセスに行くわけですね。なので、stun.l.google.comをホワイトリストに登録してもだめなわけです。
まぁそのIPをホワイトリストに付け加えればいいんでしょうが、IPは変わる可能性があるのでなるべく避けておきたいところです。
iFilterの機能をいろいろ探していると、DNS引きした結果についてフィルタリングもしてくれるようです。 つまり、stun.l.google.com を登録すると、DNS引きの結果のIP 74.125.23.127に対してフィルタリングを行う仕組みです。
その設定は、[ルールセット / ルールパーツ / 識別名リスト] にて、タイプ:DNS引きホスト(部分)にて登録できます。 ここに上記のgoogleのサーバホスト名を登録します。
これでMeetが切断されず正常稼働するようになりました。
ただ、stun...のサーバが増えると、その分も登録してやる必要があります。。
余談(対応方法2 iFiter側で許可する(ポート)
余談ですが、iFilterにはポートをフィルタリングに加えることもできます。
その設定は、[ルールセット / ルールパーツ / アクセス情報セット]です。ただし、範囲指定ができないので1ポートずつ設定する必要があります。
ルール設定で、ポート19302ルールパーツ || ..(略).. || ポート19309ルールパーツ と組み合わせれば、切断されずに正常稼動しました。
ポートともにアクセス先をフィルタリングできれば、よりセキュアじゃん!と思ったんですが、iFilterのルール設定側では、複数のルールパーツを組み合わせる際、AND と OR でしか設定できません。
つまり、stun.l.google.comルールパーツ && (ポート19302ルールパーツ || ..(略).. || ポート19309ルールパーツ ) という組み合わせができません。
なんので、DNS引き識別名リスト か、ポートか、どちらかの条件しか設定できないようです。