Private Google Access 的 IPv6 Range
Table of Contents
問題 ¶
在 Google Cloud 的專案中,沒有外部 IP 的 VM 使用設定 IP Address Restriction 的 API Key 呼叫 Google Maps API。出現了錯誤訊息:
XML 版本:
<?xml version="1.0" encoding="UTF-8"?>
<GeocodeResponse>
<status>REQUEST_DENIED</status>
<error_message>This IP, site or mobile application is not authorized to use this API key. Request received from IP address fda3:e722:ac3:10:c0:58ab:a8c:6, with empty referer</error_message>
</GeocodeResponse>
JSON 版本:
{
"error_message" : "This IP, site or mobile application is not authorized to use this API key. Request received from IP address fda3:e722:ac3:10:c0:58ab:a8c:6, with empty referer",
"results" : [],
"status" : "REQUEST_DENIED"
}
透過 curl 查看連接時的 Destination IP 是 IPv4。
![curl result](/images/2024-03-26/202403260007.jpg)
在 2020 年就有人發現相關的問題 Restrict Google Maps API key for us in Private Google Access environment,但沒有很好的解釋。
環境分析 ¶
- 這是 Google Cloud 新專案,可以當做都是預設設定。
- VPC Network 與 Subnet 都是預設建立的
default
。在預設情況下並不會啟用 [dual-stack][IPv4/IPv6 dual-stack networking],所以在此 VPC Network 中的 VM 並不會被分配到 IPv6 的 IP。 - Subnet 啟用了 Private Google Access,
- 在此 Subnet 中的 VM 沒有給予 External IP。
- 建立了一個 API Key 用於呼叫 Google Maps API,並且設定了 IP Address Restriction,限制只有該 Subnet 可以存取 Google Maps API。
- 並沒有建立 Cloud NAT。所以 VM 一定會透過 Private Google Access 的功能去存取 Google Maps API。
![Initial API Key Setting](/images/2024-03-26/202403260003.jpg)
問題中的端點可以很簡單地分為:
- VM
- Private Google Access
- Google Maps API
在 VM 上都沒有與 IPv6 相關的設定;Google Maps API 也沒有可以修改的地方,那麼看起來問題就是在 Private Google Access 上。
![Initial API Key Setting](/images/2024-03-26/202403260008.jpg)
Private Google Access ¶
由於 Google Maps API 為公開端點,當透過公開 DNS 解析 Google Maps API 的網域時,會得到外部 IP,這表示如果 Client 端沒有連上 Internet 能力的話,便無法存取 Google Maps API。
既然 Google Cloud VM 跟 Google Maps API 都在 Google 的網路中,有沒有辦法不繞過 Internet,讓只有 Google Cloud VM 可以使用內部 IP 存取 Google Maps API?
Private Google Access 便是為了解決該問題的功能,我們可以在 VPC Network 中的 Subnet 去啟用。
![Enable Private Google Access](/images/2024-03-26/202403260002.jpg)
啟用後 Google Cloud VM 可以就可以使用內部 IP 去存取 Google Maps API,而不用再透過 Internet。
![Private Google Access Architecture](/images/2024-03-26/202403260001.jpg)
那麼 Private Google Access 是如何與只有外部 IP 的 Google Maps API 溝通的呢?
理論上 IPv4 與 IPv6 是不互通的,在 VM 呼叫 Google Maps API 的過程中應該需要做轉換。而且 IP 是有限的,Google Cloud 所有客戶的專案若直接用內部 IP 去存取 Google Maps API 勢必會遇到網段衝突。所以我們可以假設 Private Google Access 會替我們的 Subnet 做 SNAT,類似於 Cloud NAT 的功能將封包中的來源 IP 替換掉。
確認使用 Private Google Access 的請求來源 IP ¶
我們可以在 VM 上透過以下指令取得 Google Maps API 看到的請求來源 IP:
curl https://toolbox.googleapps.com/apps/browserinfo/info/
輸出結果會像:
{
"reportUnixTime":1711447452.2128165,
"charset":"",
"userAgent": "curl/7.68.0,gzip(gfe)",
"keepAlive": "Keep-Alive",
"remoteAddr": "fda3:e722:ac3:10:c0:58ab:a8c:6",
"reportTime": "Tue, 26 Mar 2024 10:04:12 +0000",
"language": "",
"encoding": "gzip(gfe)",
"xForwardedFor": "",
"httpVia": "",
"dnt": ""
}
其中 remoteAddr
欄位便是 Google Maps API 所看到的請求來源 IP。
我們也可以發現這與錯誤訊息中提到 IP 是相同的。
curl https://maps.googleapis.com/maps/api/geocode/json?place_id=ChIJeRpOeF67j4AR9ydy_PIzPuM&key=<YOUR_API_KEY>
{
"error_message" : "This IP, site or mobile application is not authorized to use this API key. Request received from IP address fda3:e722:ac3:10:c0:58ab:a8c:6, with empty referer",
"results" : [],
"status" : "REQUEST_DENIED"
}
透過以上方式可以確認,使用 Private Google Access 會替換掉請求封包中的來源 IP 並且是 IPv6。所以當我們要設定 API Key 的 IP Address Restriction 時,需要設定的是 Private Google Access 的 IPv6 Range,而不是 Subnet 的 IPv4 Range。
取得 Private Google Access 的 IPv6 Range ¶
那麼 Private Google Access 的 IPv6 Range 是什麼呢?
從網路上查看其他人的錯誤訊息交叉比對下發現是 fda3:e722:ac3:10:0:0:0:0/64
,但這個範圍也太大了,明顯超出原本 Subnet 10.140.0.0/20
太多。
在詢問 Google Support 後得知,Private Google Access 的 IPv6 Range 是依照 NAT64 並有固定的轉換規則:
- 前 64 bits 是固定的 Prefix
- 接下來的 32 bits 是 VPC 的 GUID 轉換而來的
- 最後 32 bits 是 Subnet 的 IP 轉換而來的
![Private Google Access IPv6 format](/images/2024-03-26/202403260006.jpg)
我們可以先透過 https://toolbox.googleapps.com/apps/browserinfo/info/
取得前 96 bits 的部分:fda3:e722:ac3:10:c0:58ab
後面 32 bits 我們再從 IPv4 10 進位換算 IPv6 16 進位。Subnet 10.140.0.0
就變成 a8c:0
。
遮罩的部分則是 128 - (32 - 20)
= 116
。
所以最後 Subnet 10.140.0.0/20
會對應到 Private Google Access 的 IPv6 Range 為: fda3:e722:ac3:10:c0:58ab:a8c:0/116
。
我們修改 API Key 的 IP Address Restriction:
![Update API Key Setting](/images/2024-03-26/202403260004.jpg)
再做測試,並得到成功的回應:
![Success Response](/images/2024-03-26/202403260005.jpg)
總結 ¶
Private Google Access 會做 SNAT 的部分並未在文件上特別說明,花上了不少時間做猜想。最後基於排除所有可能選項才確定是 Private Google Access 會做 SNAT。最後也還好有向 Google Support 確認到這是有一定的轉換規則。希望這部分能夠寫在公開說明文件上面。
Subnet 停用 Private Google Access,並使用 Cloud NAT 固定來源 IP 也是一種解決方法;但有些服務例如 GKE Private Cluster 會強制啟用 Private Google Access 就不適用了。