Mirrativ Tech Blog

株式会社ミラティブの開発者(バックエンド,iOS,Android,Unity,機械学習,インフラ, etc.)によるブログです

クラウドネイティブなVPNを構築して運用している話

インフラストリーミングチームの近藤(@udzura)です。

今日は、ミラティブ社内向けツールの話をします。ミラティブではVPNの仕組みをクラウドをフル活用して自前で構築し、1年ほど運用しています。運用中にいろいろ課題はありつつ、現在かなり安定して動作してます。

今回の記事は、そのVPNの仕組みを紹介します。

既存VPNの課題

そもそも、ミラティブでは以前よりアプライアンスのVPN機器を導入しており、東京・目黒にあるオフィス内部に物理的に設置していました。ミラティブで使うVPNはいわゆる拠点間VPNではなく、リモートワークの環境から接続してスタティックルーティング+スプリットトンネリングを組み合わせ、安全な接続を提供するためのものです。その前提でお読みください。

このVPN機器を利用していて、以下のような点を課題に感じていました。

災害時に稼働できないリスクを避けたい

最も課題に感じていたのは、物理的にオフィスにあるVPNが業務のSPoFになり、いざという時に利用できなくなることでした。あくまでも仮の話ですが、たとえば東京圏での大きな地震など、電気やインフラの提供が不安定になる状況は起こりうるでしょう。

また、自然災害でなくとも、たとえばオフィスビルが火災に遭い機材が利用できなくなるパターンも考えられます。

どこに誰がアクセスできるか楽に管理したい

他にも、社員それぞれのアカウントについて、誰がどの場所にアクセスできるか管理が大変であるという課題もありました。

VPNでアクセスを制限したいサービスの中には、たとえば個人情報を扱うものなど、ごく一部の社員のみ許可したいものがあります。機密性の観点から必要以上の情報にアクセスできる状態は避けたいところです。

この点について、既存のアプライアンスでもVPNのポリシーを定義することができ、それに合ったチームを作成することでユーザー管理をしているところでした。とはいえ、これは登録や削除が手作業でしかできないものでした。そこでユーザー情報をコード化して自動化で反映できるようにし、手間を省きたいと考えました。また、可能であればすでに導入しているSSOの仕組みと一元化できればベターです。

新しいVPNをハッカソンで開発した話

と言うことで、この既存VPNの問題をなんとかすべく、新しいVPNをハッカソンで作ろうという呼びかけがマネージャーのハタさんからありました。

そして昨年の9月の話になりますが、実際に目黒オフィスにインフラメンバーが集まり、ハッカソンを実施しました。そのハッカソンで設計や要素技術の検証、プロトタイプ作成を行い、それ以降の繋ぎこみは業務の合間に少しずつ行いました。こうして今年2023年の冒頭には全社に展開できるところまで完成度が上がり、その後既存のVPNを置き換えることができたと言う経緯です。

以下はハッカソンの際のホワイトボードの様子です(このホワイトボードには会社固有の情報はないので公開しています)。

新VPNの設計思想

このハッカソンで決まった新VPNの設計の上での狙いや設計思想を紹介します。

災害時でも稼働できる

基本的にVPNの仕組みはクラウド上で構築し、リージョンを分散させることで災害時の冗長構成をする方針にしました。

具体的には、Google Cloudの東京リージョン、大阪リージョンのどちらでもVPNを利用可能な設計にしています。ミラティブはリモートワーク可能な会社で、社員によっては東京ではなく関西圏に住んでいるため、そういう場合は近いロケーションのVPNを使うこともできます。さらに、場合によってはリージョンを増やすこともできます。

どこに誰がアクセスできるか管理できる

アクセスの管理という観点では、まず、クラウド上でユーザごとにVPNの設定の入ったインスタンスを立てて、ユーザごとの経路を独立させます。その上で、そのインスタンスにネットワークタグを振ることでルーティングを変更し、一人ひとりがアクセスできるセグメントを制御できると考えました。

また、クラウド上に構築するため、立ち上げなどは全て自動化できます。一緒にグループの管理もプログラム管理し、自動化できるはずです。

攻撃時の影響を限定する

「1人につき1台のインスタンスを立てる」ことは、VPNシステムに攻撃を受けた場合についても考慮しています。

以前のVPN機器は1台しかないため、その機器に攻撃があった場合全社に影響が出てしまう恐れがありました。今回のように1人に1台インスタンスがある場合、万一VPNを通じて攻撃を受けてもその1人にだけ影響が出るにとどまりますし、そのインスタンスを迅速に破棄することで収束します。さらに言うと、NATインスタンスを手前に置く構成なので、NATから奥のVPNインスタンスへの通信を制限すればNATの段階で攻撃を止めることも期待できます。

12時間でインスタンスを停止する

先ほど述べた通りユーザごとにVPNインスタンスを1つ作成しますが、そのインスタンスは12時間で自動削除するようにしています。そのため毎日使う人は再作成をする必要があり、勤務時のルーチン作業にVPN作成を加えていただく必要があります。

これにはメリットデメリットはあると思います。ただ、今回は以下のようなメリットを重視した上で、この運用をしています。

  • 公開鍵ペアを確実に12時間で失効させる
  • 夜中など、不要な時間帯はコンピュートリソースを使わない
  • インスタンスの状態を強制的に排除する
  • 万一マルウェアの攻撃を受けても、12時間で強制停止するので、攻撃の試行を強制遮断できる

クラウドネイティブなVPNである

ミラティブインフラでは、このVPNを「クラウドネイティブなVPN」と表現することがあります。実際のところどうなのでしょうか。 Cloud Native Computing Foundation におけるクラウドネイティブの定義(日本語版)を参照してみることにしました。

github.com

我々のVPNは以下の点を満たしていると言えます。

  • パブリッククラウドを使いつつ、他のクラウドでも構築できるような設計にしている
  • イミュータブルかつマイクロサービス的な設計を採用し、回復性と管理性を意識している
  • コア技術がオープンソース(WireGuard)である

以上より、「クラウドネイティブ」を名乗っても差し支えないと考えています。

アーキテクチャと技術の説明

まずこの設計思想を踏まえて実際に作成したアーキテクチャを図に示します。

構成全体図

具体的に利用した技術について説明します。

WireGuard

今回は、クラウド上にインスタンスを立ち上げた上で、各ユーザーの手元のクライアントとそのインスタンスでVPNのトンネルを構築し、それを経由して通信する形をとりました。そのVPN部分にWireGuardを利用しています*1

www.wireguard.com

WireGuardはオープンソースの VPN プロトコルで、それを実装したソフトウェアを指すこともあります。オープンソースのVPN実装は数多くありますが、WireGuardはその中でもセットアップが比較的簡単で、かつWindows/Mac/Linuxでクライアントが揃って公開されているため、扱いやすいと言えるでしょう。

WireGuardは基本的にUDPを使ったトンネリング技術で、いわゆるNAT越えでも問題が起こりません。また認証は公開鍵認証、暗号化はChaCha20-Poly1305という十分安全かつ広く使われた技術を採用しています。

このような扱いやすさと、安全性を含み要件的に十分ということで今回コア部分に採用しました。

Google Cloud VPCの各機能

さて、VPNインスタンス自体はGoogle Cloud Compute Engineに専用のプロジェクトを構築してそこに立てています。

後述するSlack + Cloud Functionsを起動することで、VPNインスタンスを立ち上げ、起動スクリプトでWireGuardをはじめとした必要なミドルウェアをセットアップしています。この際、基本的に毎日削除して起動する前提で、ゼロからスクリプトを流した後は状態を持たせていません。

また、インスタンスごとにアクセスの制御をするにあたって、Google Cloud VPCの機能をいくつか利用しています。その詳細も説明します。

まず、起動時にVPNインスタンスにはネットワークタグを付与しています。このネットワークは作成者の所属するグループで決まります。

この時、VPCの機能で、ネットワークタグごとにルーティングを変更することができます(Cloud Routes)。例えば、以下のようなルールを指定できます。

  • Egressの通信の時
  • かつ、宛先が 8.8.8.8/32 (例)の時
  • 通信を NATインスタンスである nat-instance-1 経由にすること

これにより、特定IPへの外向きの通信をNATインスタンス経由にさせることできます。例えばアクセス元のIPを固定する必要があるサイトへのアクセスの際に、VPN経由のアクセスをすれば出口のIPをNATインスタンスに付与したEIPに固定できるため、要件を実現できます。

この機能についての簡単な図を以下に掲示します。ポイントは、手元の設定を変更してもVPC Routesに影響しないため、NATインスタンス経由にはできないというところです。

このようなルーティングのルールを、グループごとに指定することで、特定サイトへのアクセスを限定したユーザに制限しています。

Cloud Functions + Pub/Sub + Slack App API

VPNインスタンスやNAT用インスタンスは12時間起動しますが、それ以外の箇所は必要な時以外は起動させないようにし、コストを抑えるようにしています。

具体的には、構成図でいうSlackとGoogle Cloudの連携API部分は、Cloud Functionsを利用しています。Cloud FunctionsはGoogle Cloudの提供するFunstion as a Serviceです。これを利用することでAPIのために固定したサーバインスタンスを立ち上げる必要がなくなり、起動した分のみ課金されます。

このVPNの仕組みは、緊急対応時以外真夜中には起動されることがないため、HTTPSの最低限のやり取りのタイミングのみ課金されるFunction as a Serviceは相性がいいと考えました。

さらなる留意点として、Slackと連携するAPIはレスポンスタイムに制約があります。具体的には、以下のヘルプにあるようにユーザーの体験を損ねないよう「3秒以内」で返すことが仕様として指定されています。

api.slack.com

APIのレスポンスのたびにインスタンスの起動まで実行していたらこの条件を満たせないため、最初にSlackからのAPIリクエストを受け取ったあとはCloud Pub/Subのtopicを発行するだけにとどめ、後続のインスタンス起動や設定生成などの時間がかかる処理を非同期で実行しています。

この項目のまとめを図に示します。

Slackベースのユーザ管理

インスタンス起動、設定などが一通り完了したら、Slack APIで対象のユーザにDMを送り、設定ファイルと二次元バーコード(QRコード)を添付します。

PCから利用する際はその設定ファイルをダウンロードして使えばOKです。また、モバイル端末向けのWireGuard設定アプリも存在するため、モバイル端末からは二次元バーコードをスキャンするだけでVPN接続できます。検証用のアプリを使う際、そのアプリのための検証用APIはVPN経由でアクセスしたいため、モバイル端末から簡単に利用できることも要件に入れています。

この時、「VPN作成を依頼したユーザ」と「設定を受け取るユーザ」は必ず一致するようになっています。それにより、Slackアカウントによる認証と同等のものを実施したことになります。例えば成りすましでVPNを利用することをこれで防いでいます。

また、退職者についてはSlackアカウントを削除することがすでに情報システムのフローに組み込まれているので、VPN側では追加で何かをすることなく、退職者のVPNアクセスを禁止することができます。こうしてユーザー管理の工数を少し減らしています。

ただ、後述するようにユーザーごとのVPN上の権限までは紐付けを行なっていないため、その点は課題となります。

今後やりたいこと

このVPNシステム自体はほぼ丸1年運用し、細かい点を修正しつつ概ね問題なくなるところまで改善しています。とはいえまだまだ改善したい点は存在するため、振り返りも兼ねてここに書き起こします。

権限の管理についての改善

先述したようにSlackのユーザーを基本としてVPNのユーザーを管理しています。一方で、特定の管理ウェブサービスには特定の人だけアクセスさせたい、といったような接続先の権限管理については、YAMLファイルをベースにユーザグループを作成しそれで行なっています。

マスターをYAMLファイルではなく、例えばGoogle Groupのようなすでに社内で利用しているサービスにユーザグループ管理を一元化できないかを検討しています。

現状、反映自体は簡易的に自動化(といっても、YAMLファイルをパースし、Firestoreに保存しているだけ)していますが、例えばこのYAMLファイルをGoogle Groupの情報を元に生成する等は考えられそうです。

運用のさらなる自動化

関連して、一部の作業をいまもインフラチームで行っていると言う状況があり、情報システムチームにスムーズに引き継げるようさらなる自動化を進めたいと考えています。

現在、例えばルーティングの設定変更の反映や、Cloud Functionのデプロイは手動で行なっていますが、これらはCloud Build経由で行うようにできそうです。あるいは滅多には行わないと言うことで、手順Wikiに従って行なっているような設定追加作業もあるのですが、そういった作業も少しずつCloud Buildに移行したいです。

Cloud Buildであれば、そもそもGoogle CloudとIAMの連携もしやすく、またGitHub上で管理している設定のマージのタイミングの他、手動でのトリガー実行も可能なの上、ログも残るので便利なはずです。

Cloud Build自体は、以下の記事の通りミラティブのサーバアプリケーション自体のデプロイなどで利用が広がっているので、より一層自動化作業をCloud Buildに寄せたいところです。

tech.mirrativ.stream

We’re Hiring!!

ということで、ミラティブでは生産性と安全性を両立させるため、時には一歩踏み込んでツールを内製することも実施しています。

運用もコードも頑張って成長したい方はぜひカジュアル面談からご連絡ください!お待ちしています。

mirrativ.co.jp

mirrativ.notion.site

speakerdeck.com

*1:Google CloudにはCloud VPNもありますが、これは拠点間VPNを想定した機能なので検討から外しました