IPアドレス制限で保護したWebサイトにアクセスしたい with GCP

## はじめに

例えばですが、IPアドレス制限を行っているWebサイト（ex. SaaSのコンソール）に対して、VPNやプロキシを利用し、IPアドレスを固定した上でアクセスしたいケースはままあると思います。

AWSであれば[Client VPN](https://docs.aws.amazon.com/ja_jp/vpn/latest/clientvpn-admin/what-is.html)があるので、開発ほぼ無しでユースケースを満たすことができますが、Google Cloud（GCP） の公式VPNサービスとしては[拠点間VPN](https://cloud.google.com/network-connectivity/docs/vpn/concepts/overview?hl=ja)しかありません。
（[BeyondCorp](https://cloud.google.com/beyondcorp?hl=ja)な世界を目指している以上クライアントVPNはアンチシナジーなのでしょうか。要出典）

GCPで「安全に」「開発少なく（なるべくマネージドで）」「IPアドレス制限で守ったリソースにアクセスする」仕組みを作る場合どうすれば良いでしょうか🤔

## ソリューション

主な方法として、マーケットプレイスのクライアントVPNを導入する方法とIdentity-Aware Proxyを利用する方法が考えられます。

### 1. マーケットプレイスのOpenVPN Access Serverを利用する

ひとつめは、マーケットプレイスを利用してクライアントVPNを導入する方法です。
マーケットプレイスは[Googleから認証を受けたパートナー企業](https://cloud.google.com/marketplace/docs/partners/get-started?hl=ja)がソフトウェアパッケージを提供するサービスです。

>Google Cloud Marketplace は Google Cloud(GCP) の管理コンソールで提供されているカタログサービスです。
>
>Google Cloud Marketplace を使うことで Google Cloud(GCP) 上で利用できる様々なソリューションを検索し、必要に応じてデプロイ（初期設定をして利用可能な状態にすること）まで実行することができます。難しい操作は必要なく、わずか数クリックの作業で必要な処理が完了します。

[Google Cloud Marketplace とは？料金体系、メリット、できることまで徹底解説！](https://www.topgate.co.jp/google-cloud-marketplace)

前述の通り、GCP公式サービスのクライアントVPNはありませんが、[OpenVPN Inc.](https://openvpn.net/about/)が提供するOpenVPN Access Server（OpenVPNにGUIベースの管理画面が追加されたソフトウェア）がマーケットプレイス上にあります。

https://openvpn.net/google-cloud-vpn/

マーケットプレイスをポチると[Deployment Manager](https://cloud.google.com/deployment-manager/docs?hl=ja)のテンプレートが走り、OpenVPN Access Serverが載ったGCEインスタンスがデプロイされます。

https://zenn.dev/tand826/articles/494292aa79d7ac

人数がスケールすると有料（2アカウントまで無料）ですが、GCPでクライアントVPNを手に入れたい場合の筆頭候補になると思います。

### 2. [iap-connector](https://github.com/GoogleCloudPlatform/iap-connector)を利用する

ふたつめはiap-connectorを利用し、保護したいリソースの前に[Identity-Aware Proxy（IAP）](https://cloud.google.com/iap/docs/concepts-overview?hl=ja)を設置する方法です。

#### Identity-Aware Proxy（IAP）

まずはIAPのざっくりした概要です。
IAPという用語はGCP固有のものではなく、例えば次に示すのは[すべてわかるゼロトラスト大全](https://www.amazon.co.jp/dp/B08PCRX7G9)に記載のIAPの説明です。

>アイデンティティー認識型プロキシー（IAP）は、ユーザーとアプリケーションの間に入って通信を仲介するプロキシーである。「アイデンティティー認識型」と呼ばれるのは、ユーザーがアプリケーションにアクセスするたびに、アイデンティティ&アクセス管理（IAM）と連携して認可をやり直すためだ。

IAPはざっくり言うと、ゼロトラストな世界における**アクセス制御を行うプロキシの総称**です。
IAPで保護されたリソースにアクセスを行うと、リクエストの正当性に関する検証が走りアクセスの可否が判断されるようになります。
[Pomerium](https://github.com/pomerium/pomerium)というIAPのOSSがあったりもします。

GCPにおけるIAPは「ゼロトラスト用語としてのIAP」をGCPサービス化したものです。
GAEやLoad Balancing、GCE、Cloud Run（β版）とマネージドに統合されており、デプロイしたWebアプリの前にコマンドひとつでIAPを設置できます。
例えばIAPを設置したGAE上のWebアプリにアクセスすると、「Google Sign-in」即ちGoogleの認証画面が挟まるようになります。
この認証は、事前に許可したアカウント（プリンシパル）だけが通過できます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287774/604b67d4-1d37-34ae-2395-bbc648ff4c9c.png)

[Identity-Aware Proxy の概要](https://cloud.google.com/iap/docs/concepts-overview?hl=ja)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287774/8e7b6c8c-6705-9c38-590e-bcd4ffec972c.png)
[GCP Identity-Aware Proxy (IAP), Access Context Managerを使ってみる](https://qiita.com/mksamba/items/21ce9c4f97d9ae04a82a)

[Access Context Managerで設定したアクセスレベル（ex. 特定のIPアドレスのみ許可、特定のデバイスのみ許可）をアタッチできたり](https://cloud.google.com/access-context-manager/docs/overview?hl=ja)、Googleアカウントに設定済みのMFAも動作する等、セキュアなアクセス制限を実現できます。

#### iap-connector

公式ドキュメントの[オンプレミスアプリのIAPの概要](https://cloud.google.com/iap/docs/cloud-iap-for-on-prem-apps-overview?hl=ja)にも記載がありますが、iap-connectorというGitHubリポジトリをGoogleが公開しています。

https://github.com/GoogleCloudPlatform/iap-connector

リポジトリ上のIaCテンプレートを使うとiap-connector（IAPで保護されたロードバランサ及びGKE）がデプロイされます。
iap-connector上で動いている[Ambassador](https://www.getambassador.io/)のOSS（[Emissary-ingress](https://github.com/emissary-ingress/emissary)[^1]）はドメインのルーティングができるため、「iap-connectorのロードバランサに付与したドメイン」と「保護したいWebサイトのドメイン」をマッピングすれば、iap-connectorへのアクセスがWebサイトに抜けるようになります。
iap-connectorで保護するリソースは任意のWebサーバ（Webアプリ）を指定できます。

>バックエンドは任意のWebAPサーバーを指定できるので、（中略）site-to-site VPNやCloud Interconnectで他社クラウドやオンプレミスと閉域ネットワークで接続する構成にすると、既存WebアプリケーションのリバースプロキシとしてCloud IAPを置く構成がイメージできると思います。もちろんInternet FacingなWebアプリケーションをバックエンドとすることもでき、バックエンド側でCloud IAPからのリクエストか否かなどリクエスト真正性を評価したい場合はCloud IAPが付与するJWTトークンを手がかりにすると良いでしょう。

[BeyondCorp Remote Accessを支える技術 #1 GCP Cloud IAP connectorを試してみた](https://dev.classmethod.jp/articles/beyondcorp-remote-access-getting-started1/)

IPアドレス制限をかけたWebサイトとiap-connectorを繋げる場合は、Cloud NATでiap-connectorから抜けるIPアドレスを固定すると良いでしょう[^2]。

### 3. IAP + GCEをSOCKSプロキシ化する

みっつめ、最後は、GCEとssh接続を行いSOCKSプロキシを構成する方法です。

### gcloudコマンドを使ったポートフォワード

`gcloud compute ssh`コマンドを実行するとGCEインスタンスにssh接続できますが、通常のsshコマンドと同様にダイナミックポートフォワードできます。
ダイナミックポートフォワードとは何ぞやですが、下記の図が非常に分かりやすいと思います。
図のように、sshのクライアントが接続を張っているポート番号をPCやブラウザのSOCKSプロキシとして設定すると、ssh接続先のサーバから通信が抜けるようになります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287774/7ac2118e-fbe8-e7e2-e24b-ce5225e7f573.png)

[【改訂版】SSHポートフォワード（SSHトンネル）【ローカル・リモート・ダイナミック総集編】](https://www2.filewo.net/wordpress/2019/03/31/%E3%80%90%E6%94%B9%E8%A8%82%E7%89%88%E3%80%91ssh%E3%83%9D%E3%83%BC%E3%83%88%E3%83%95%E3%82%A9%E3%83%AF%E3%83%BC%E3%83%89%EF%BC%88ssh%E3%83%88%E3%83%B3%E3%83%8D%E3%83%AB%EF%BC%89%E3%80%90%E3%83%AD/)

`gcloud compute ssh`では、次のコマンドでダイナミックポートフォワードができます（「--」以降がsshコマンドのオプションになっています）。

```sh
gcloud beta compute ssh --zone "ZONE_NAME" "INSTANCE_NAME" --project "PROJECT_NAME" -- -D 9080
```

コマンド実行後、9080番ポートでsshクライアントがGCEインスタンス（内のsshd）と接続を張ってくれます。
例えばmacOSにSOCKSプロキシを設定する場合なら、`システム環境設定 > ネットワーク > 詳細 > プロキシ > SOCKSプロキシ > localhost:9080`を設定すれば、ブラウザからのアクセス等がGCEのインスタンス経由で抜けるようになります。
もちろん、IPアドレスはGCEインスタンスのものに変わります。

### IAPを組み合わせる

ここで、より発展的なセキュリティを考えると、インスタンスに直接グローバルなIPアドレスを付与したくないおキモチが生じます。

結論から言うと`gcloud compute ssh`コマンドは、グローバルなIPアドレスを持たないインスタンスに対しても、IAPを介することで接続できます。
手順は公式ドキュメントに記載されている通りなので、詳細は割愛いたします。

https://cloud.google.com/iap/docs/using-tcp-forwarding

先ほどのコマンドに`--tunnel-through-iap`を加えるとIAP経由でダイナミックポートフォワードできます。

```sh
gcloud beta compute ssh --zone "ZONE_NAME" "INSTANCE_NAME" --tunnel-through-iap --project "PROJECT_NAME" -- -D 9080
```

iap-connectorと同様、インスタンス自体にはグローバルなIPアドレスがなくても、Cloud NATでIPアドレスを固定すれば本ユースケースを満たせます。
繰り返しにはなりますが、IAPにはAccess Context Managerで設定したアクセスレベルをアタッチできます。
Googleの仕組みに乗っかりつつ、ssh接続のセキュリティを底上げできるのでステキです。
このあたりの設定方法は、以下の記事が詳しかったです。

https://cloud.google.com/blog/products/identity-security/cloud-iap-enables-context-aware-access-to-vms-via-ssh-and-rdp-without-bastion-hosts

### 余談

「なぜIAP経由でssh接続なんてできるのか」については、gcloud SDKとIAP間は通常のhttpsな通信なので、httpsで包んだsshコマンド文を送り、IAP通過後に中身のsshコマンドを取り出し実行しているイメージっぽいですね。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/287774/3bc61980-872c-db66-80be-b87e07231f2a.png)

[TCP 転送の概要](https://cloud.google.com/iap/docs/tcp-forwarding-overview?hl=ja#how-tcp-forwarding-works)


[^1]: 元々Ambassador API Gatewayという名前だったが[CNCF加入に伴い改名](https://blog.getambassador.io/emissary-ingress-ambassadors-api-gateway-is-officially-an-incubation-project-at-the-cncf-5030a3754c2)
[^2]: デプロイされたiap-connectorはただのGKE等なので、いつもと変わらず設定ができます。できればIaCに組み合わせたいところ