限定公開の Google アクセスを使用したオンプレミスからの GCP プライベートアクセス

## はじめに
GCP のクラウドサービス (API) に、インターネットを介さず Google 内部ネットワーク経由でアクセスを可能にする、**限定公開の Google アクセス**がある
Public IP を持たず、**Private IP しか持たない VM インスタンスから**も Google API / GCP サービスへ**アクセスを可能**にする
VPC Service Controls でサービス境界内にした GCP サービスへのアクセスも、
**限定公開の Google アクセスをオンプレミスからプライベートで利用可能**にすることで、境界内の VPC ネットワークを介してオンプレミス側が境界内に含まれたようにアクセスを可能にする

限定公開の Google アクセスの調査した際のメモと、実際にオンプレミスから利用できるように試した内容を記載する

![GCP (4).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/0e9809aa-e867-1cdc-d57f-57078b4f1956.png)



## 限定公開の Google アクセス

Google API とサービスへの API アクセスを、インターネットを介さずに GCP 内のプライベート IP からアクセスを可能にする
下記ざっくりメモ。正確なもの・詳細は[GCP ドキュメント](https://cloud.google.com/vpc/docs/configure-private-google-access?hl=ja#config)を参照。

### ドメインと IPアドレス範囲
限定公開の Google アクセス用に、2 つのドメインがあり、VPC Service Controls での制限有無で選択することになる
VPC Service Controls で制限するなら、`restricted.googleapis.com`の方を選択する

| ドメイン | IPアドレス範囲 | VPC Service Controls (VPC SC) |
|:--|:--|:--|
| private.googleapis.com | 199.36.153.8/30 | VPC SC のサポート有無にかかわらず、ほとんどの Google の API アクセス可能 <br>ただし、Google Workspace (旧 G Suite) はサポートされてない<br>googleapis.com 以外のサポートドメイン[詳細](https://cloud.google.com/vpc/docs/configure-private-google-access?hl=ja#config) |
| restricted.googleapis.com | 199.36.153.4/30 | VPC SC でサポートされている Google API とサービスへの API のみアクセス可能 |

### DNS 構成
GCP 内では CloudDNS にて、選択したドメインの A レコードの登録(/30 だと 4IP 分)と、
`*.googleapis.com`を登録したドメインに CNAME で書き換えるようにする
オンプレミスからは CloudDNS のフォワーダ用 IP へフォワードして解決する

### ルーティング
選択したドメインの IP アドレス範囲を静的カスタムルートで VPC 内のプライベート IP からルーティングできるように設定する
オンプレミスには、CloudRouter からドメインの IP アドレス範囲を BGP でルーティング広報する

### サブネット単位での有効化
プライベート IP での限定公開 Google アクセスを可能にするには、
サブネット単位で限定公開 Google アクセスを有効化が必要

## オンプレミスからプライベート IP で Google API / サービスへ接続する環境構築
実際に、オンプレミスから限定公開の Google アクセスを可能にして、サービス境界内のサービスにアクセスを可能にしたので、作業手順を記載する
だいたいは [GCP のドキュメント](https://cloud.google.com/vpc-service-controls/docs/set-up-private-connectivity?hl=ja)の通り実施している

### 実施環境
下記のオンプレミス(自宅ラボ)と接続しサービス境界を作成済みの GCP 環境

 - 共有VPCの構築
    - [GCP で組織を作成して共有 VPC 構築](https://qiita.com/suzuyui/items/947867f52897417ee31b)
 - CloudRouter と HA VPN での自宅ラボとの VPN + BGP ルーティングの構築
    - [GCP に Cloud Router を構築して自宅ラボと HA VPN で接続して BGP での経路交換](https://qiita.com/suzuyui/items/562a0a05ee6d424478c1)
 - VPC Service Controls を試し、Cloud Storage をサービス境界内にする perimeter 構築
    - [GCP VPC Service Controls を試す](https://qiita.com/suzuyui/items/c38a7c694ce0e7a6133c)


### 実施概要図

実施概要図は下記の通り (吹き出しのところを実施し、青色線の経路アクセスを可能にする)

![GCP (2).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/326183/117fbb8c-5514-f97b-9522-646220e33d9f.png)

| 項目 | 内容 |
|:--|:--|
| vpc | `vpc01`と言う名前で構築済み |
| zone | 無料枠が使える `us-west1` で構築済み |
| CloudRouter | `cloudrouter01` と言う名前で構築済み |
| オンプレミス接続 | VPN + BGP でプライベート接続・ルーティング交換実施済み |
| Service Perimeter | CloudStorage を境界内に含むように実施済み |
| オンプレミス側 | ルータは `EdgeRouterX` という機器で構築済み |
| PROJECT_ID | GCP プロジェクト名.下記作業では自身のプロジェクト名に書き換えて実施している |


### 手順

1. [オンプレミス] 作業前状態確認
2. [GCP] 199.36.153.4/30 カスタム静的ルートの追加
3. [GCP] 199.36.153.4/30 のルート広報
4. [GCP] CloudDNS での A/CNAME レコード追加
5. [オンプレミス] DNS フォワーダの *.googleapis.com の CloudDNS へフォワード設定の追加
6. [オンプレミス/GCP] 作業後状態確認
7. [GCP] GCP 内のプライベート IP のみのインスタンスからの利用を有効化(サブネットでの有効化)

### 1. [オンプレミス] 作業前状態確認

作業前に、名前解決が限定公開用のセグメント(`199.36.153.4/30`)になっていないことを確認する。

```sh:オンプレミス作業前dig
% dig A +noall +answer storage.googleapis.com
storage.googleapis.com.	277	IN	A	172.217.175.240
storage.googleapis.com.	277	IN	A	172.217.175.16
storage.googleapis.com.	277	IN	A	172.217.175.112
storage.googleapis.com.	277	IN	A	172.217.26.16
storage.googleapis.com.	277	IN	A	216.58.197.208
storage.googleapis.com.	277	IN	A	172.217.161.80
storage.googleapis.com.	277	IN	A	172.217.31.144
storage.googleapis.com.	277	IN	A	172.217.25.80
storage.googleapis.com.	277	IN	A	216.58.197.144
storage.googleapis.com.	277	IN	A	172.217.26.48
storage.googleapis.com.	277	IN	A	216.58.220.144
storage.googleapis.com.	277	IN	A	172.217.175.80
storage.googleapis.com.	277	IN	A	216.58.220.112
storage.googleapis.com.	277	IN	A	172.217.25.208
storage.googleapis.com.	277	IN	A	172.217.175.48
storage.googleapis.com.	277	IN	A	172.217.25.112
```

VPC Service Controls のサービス境界内にある CloudStorage にアクセスして、アクセスが拒否されることを確認する

```sh:オンプレミスCloudStorageアクセス
% gsutil ls -l gs://my-serviceproject-bucket/
AccessDeniedException: 403 Request is prohibited by organization's policy. vpcServiceControlsUniqueIdentifier:
```

### 2. [GCP] 199.36.153.4/30 カスタム静的ルートの追加

VPC内に`199.36.153.4/30`ルート追加を実施する
ここでは`restricted-private-googleapis`という名前で作成している

```sh:カスタム静的ルートの追加
gcloud compute routes create restricted-private-googleapis \
  --network=vpc01 \
  --destination-range=199.36.153.4/30 \
  --next-hop-gateway=default-internet-gateway
```

```sh:出力例
Created [https://www.googleapis.com/compute/v1/projects/PROJECT_ID/global/routes/restricted-private-googleapis].
NAME                           NETWORK  DEST_RANGE       NEXT_HOP                  PRIORITY
restricted-private-googleapis  vpc01    199.36.153.4/30  default-internet-gateway  1000
```

### 3. [GCP] 199.36.153.4/30 のルート広報

`199.36.153.4/30` のルーティングを CloudRouter で広報する

```sh:ルート広報に追加
gcloud compute routers update cloudrouter01 \
    --add-advertisement-ranges 199.36.153.4/30
```

```sh:出力例
Updating router [cloudrouter01]...done. 
```

### 4. [GCP] CloudDNS での A/CNAME レコード追加

VPCに非公開マネージドゾーンを作成する
ここでは`restrictedgoogleapis`という名前で作成している

```sh:clouddns_非公開マネージドゾーン作成
gcloud beta dns managed-zones create restrictedgoogleapis \
 --visibility=private \
 --networks=https://www.googleapis.com/compute/v1/projects/PROJECT_ID/global/networks/vpc01 \
 --description=restrictedgoogleapis \
 --dns-name=googleapis.com
```

```sh:出力例
Created [https://dns.googleapis.com/dns/v1beta2/projects/PROJECT_ID/managedZones/restrictedgoogleapis]
```

トランザクションを開始

```sh:clouddns_トランザクションスタート
gcloud dns record-sets transaction start --zone=restrictedgoogleapis
```

```sh:出力例
Transaction started [transaction.yaml].
```

DNS レコードを追加

```sh:DNSレコード追加(CNAME)
gcloud dns record-sets transaction add --name=\*.googleapis.com. \
    --type=CNAME restricted.googleapis.com. \
    --zone=restrictedgoogleapis \
    --ttl=300
```

```sh:出力例
Record addition appended to transaction at [transaction.yaml].
```

```sh:DNSレコード追加(A)
gcloud dns record-sets transaction add --name=restricted.googleapis.com. \
    --type=A 199.36.153.4 199.36.153.5 199.36.153.6 199.36.153.7 \
    --zone=restrictedgoogleapis \
    --ttl=300
```

```sh:出力例
Record addition appended to transaction at [transaction.yaml].
```

トランザクションを実行

```sh:トランザクション実行
gcloud dns record-sets transaction execute --zone=restrictedgoogleapis
```

```sh:出力例
Executed transaction [transaction.yaml] for managed-zone [restrictedgoogleapis].
Created [https://dns.googleapis.com/dns/v1/projects/PROJECT_ID/managedZones/restrictedgoogleapis/changes/1].
ID  START_TIME                STATUS
1   2020-10-17T03:52:58.057Z  pending
```

DNS ポリシーを作成し、受信 DNS 転送を有効にして、
VPC ネットワークの名前解決サービスをオンプレミスネットワーク内のシステムから使用できるようにする

```sh:Add_DNS_Policy
gcloud beta dns policies create apipolicy \
 --networks=https://www.googleapis.com/compute/v1/projects/PROJECT_ID/global/networks/vpc01 \
 --enable-inbound-forwarding \
 --description=apipolicy
```

```sh:出力例
Created Policy [https://dns.googleapis.com/dns/v1beta2/projects/PROJECT_ID/policies/apipolicy].
{
  "description": "apipolicy",
  "enableInboundForwarding": true,
  "enableLogging": false,
  "id": "8234532086092541535",
  "kind": "dns#policy",
  "name": "apipolicy",
  "networks": [
    {
      "kind": "dns#policyNetwork",
      "networkUrl": "https://www.googleapis.com/compute/v1/projects/PROJECT_ID/global/networks/vpc01"
    }
  ]
}
```

Cloud DNS のフォワーダ IP アドレスを表示する

```sh:CloudDNSフォワーダIPアドレス表示
gcloud compute addresses list --filter='name ~ ^dns-forwarding.*' \
 --format='csv[no-heading](address, subnetwork)'
```

```sh:出力例
10.10.0.12,subnet01
```

### 5. [オンプレミス] DNS フォワーダの *.googleapis.com の CloudDNS へフォワード設定の追加

前の作業で出力されたフォワーダのIPアドレス(例：`10.10.0.12`)へ`googleapis.com`をフォワードする
本環境ではオンプレミス側(自宅ラボ)のDNSフォワーダが VPN をしているルータと同じなため、
GCP 接続がリンクローカルアドレスを使用しているため、 GCP とルーティング可能なアドレスをソース IP (例:`192.168.129.254`)を指定してフォワードする

```config:EdgeRouter_Set_DNS_Forwarding
set service dns forwarding options server=/googleapis.com/10.10.0.12@192.168.129.254
```

※edgerouter の dns forwarding options は dnsmasq の設定と同じ
※@をつけることでソースIPを指定できる

### 6. [オンプレミス/GCP] 作業後状態確認

GCP でもプライベートアクセスに切り替わったので、
dig で `*.googleapis.com` 向けが `199.36.153.4/30` で解決されていることを確認する

```sh:GCP_VMインスタンスでのdig
$ dig A +noall +answer storage.googleapis.com
storage.googleapis.com.	300	IN	CNAME	restricted.googleapis.com.
restricted.googleapis.com. 300	IN	A	199.36.153.6
restricted.googleapis.com. 300	IN	A	199.36.153.5
restricted.googleapis.com. 300	IN	A	199.36.153.4
restricted.googleapis.com. 300	IN	A	199.36.153.7
```

オンプレミス側で dig で `*.googleapis.com` 向けが `199.36.153.4/30` で解決されていることを確認する

```sh:オンプレミス側でのdig
% dig A +noall +answer storage.googleapis.com
storage.googleapis.com.	300	IN	CNAME	restricted.googleapis.com.
restricted.googleapis.com. 300	IN	A	199.36.153.7
restricted.googleapis.com. 300	IN	A	199.36.153.4
restricted.googleapis.com. 300	IN	A	199.36.153.5
restricted.googleapis.com. 300	IN	A	199.36.153.6
```

VPC Service Controls のサービス境界範囲内の CloudStorage へアクセスできるようになったことを確認する(事前では拒否されていた)

```sh:オンプレミスからCloudStorageアクセス
% gsutil ls -l gs://my-serviceproject-bucket/ 
      6046  2020-10-11T10:50:52Z  gs://my-serviceproject-bucket/neko.png
TOTAL: 1 objects, 6046 bytes (5.9 KiB)
```

以上で、オンプレミスから GCP サービスへのプライベートアクセスが可能になった

### 7. [GCP] GCP 内のプライベート IP のみのインスタンスからの利用を有効化(サブネットでの有効化)
プライベート IP のみの VM インスタンスからの疎通について、6.のテストでは Public IP を持ったインスタンスでやっていたが、
プライベート IP のみの VM インスタンスからアクセス確認したところできなかったので、VPC のPrivate Subenet からのアクセス許可には下記対応を実施する

GCP VPC 内のプライベート IP のみの VM インスタンスから、
限定公開の Google アクセスを利用にするにはサブネットでの有効化が必要 (詳細：[ドキュメント](https://cloud.google.com/vpc/docs/configure-private-google-access?hl=ja#config-pga))

#### 有効化前

有効化前は API へアクセスできない

```sh:GoogleStorageにはアクセスできないが名前解決はrestricted~になっている
$ gsutil ls -l gs://my-serviceproject-bucket
[何も返ってこない]
$ ping storage.googleapis.com
PING restricted.googleapis.com (199.36.153.5) 56(84) bytes of data
```

#### 有効化コマンド

対象サブネット (例：subnet01) の限定公開 Google アクセスを有効化する

```sh:privategoogleアクセス有効化コマンド_subnet01はサブネット名
gcloud compute networks subnets update subnet01 \                                                         
--region=us-west1 \
--enable-private-ip-google-access
```

#### 有効化後

有効化後は API へアクセスが可能になり、下記 GoogleStorage へのアクセスもできるようになったことが確認できた

```sh:GoogleStorageへアクセス可能
$ gsutil ls -l gs://my-serviceproject-bucket
      6046  2020-10-11T10:50:52Z  gs://my-serviceproject-bucket/neko.png
TOTAL: 1 objects, 6046 bytes (5.9 KiB)
```

#### 補足.無効化コマンド

有効化したものを無効化するには、下記コマンドで無効化できる。
disableではなく、no-enableなので注意。

```sh:privategoogleアクセス無効化コマンド_subnet01はサブネット名
gcloud compute networks subnets update subnet01 \                                                         
--region=us-west1 \
--no-enable-private-ip-google-access
```

## おわりに
オンプレミス側から VPN を通して、GCP の API へプライベートでのアクセスを可能にできた
オンプレミスのクローズドなリソース環境として GCP サービスの活用をしていきたい

### 注意. googleapis は様々な Google サービスで使われている
オンプレミス(自宅ラボ)側に Chrome リモートデスクトップを入れてたが、
`https://remotedesktop-pa.googleapis.com/`を使用するらしく、
`*.googleapis.com` を今回で VPN に引っ張りつつ VPC SC 非対応なので使えなくなった。

よくよく考えたら、**googleapis.com は様々な Google サービスで利用されている**ので、環境によっては注意が必要です。

## 参考
限定公開の Google アクセスの構成
https://cloud.google.com/vpc/docs/configure-private-google-access?hl=ja
VPC Service Controls を使用した限定公開の Google アクセス
https://cloud.google.com/vpc-service-controls/docs/private-connectivity?hl=ja
Google API およびサービスへのプライベート接続を設定する
https://cloud.google.com/vpc-service-controls/docs/set-up-private-connectivity?hl=ja