インターネット接続なしの完全にプライベートなGKEクラスター

>こちらの記事は、2019年5月に公開された『 [Completely Private GKE Clusters with No Internet Connectivity](https://medium.com/google-cloud/completely-private-gke-clusters-with-no-internet-connectivity-945fffae1ccd) 』の和訳になります。

# はじめに

![](https://miro.medium.com/max/450/1*HGfTKQyQ_oqyySfAiIc05w.jpeg)

Google Kubernetes Engine（GKE）クラスターをインターネットアクセスから分離する理由はいくつかありますが、主な理由はセキュリティです。多くの金融機関、政府機関、および同様の機関が、Google Cloud Platform（GCP）でワークロードを実行しています。

この投稿では、インターネットへのアクセスをブロックする、隔離されたネットワークでGKEクラスターを立ち上げるために必要な手順を説明します。

#TL;DR

1. プライベートGoogleアクセスを有効にしてGKEクラスターのVPCとサブネットを作成します。
2. 送信時に0.0.0.0/0(全指定)でブロックするファイアウォールのルールでVPCをロックダウンし、Googleヘルスチェックからの受信を許可し、Googleヘルスチェック、制限されたAPI、およびGKEプライベートマスター範囲への送信を許可します。
3. VPCで自動的に作成されたデフォルトルートを削除します（ネクストホップとしてデフォルトインターネットゲートウェイを指定しているルール）。デフォルトのインターネットゲートウェイを介してGoogle Restricted API（199.36.153.4/30）に到達するためのルートを作成します。
4. 次のCloud DNSの変更を行い、ゾーンをVPCにアタッチします。
 -  * .googleapis.comからrestrict.googleapis.comを引くためのCNAMEレコード、restricted.googleapis.comから199.36.153.4/30を引くためのAレコードをもつ、プライベートDNSゾーンgoogleapis.comを作成します。
 - * .gcr.ioからgcr.ioを引くためのCNAMEレコード、gcr.ioから199.36.153.4/30を引くためのAレコードをもつプライベートDNSゾーンgcr.ioを作成します。
5. プライベートノードとプライベートマスターでGKEクラスターを作成します。

#環境
![image.png](https://miro.medium.com/max/700/1*VKk5tGDFOhT3M0WUAh0a_A.png)

#段階的な展開
[以降で利用可能なTerraformコード](https://github.com/andreyk-code/no-inet-gke-cluster.git)

##VPCとサブネットを作成する
クラスターを作成する前に、クラスターに使用する環境にVPCとサブネットが必要です。それぞれのクラスターに3つのサブネットがあります。

- ノードレンジ：GKEワーカーノードはこのサブネットに存在します。
- クラスターレンジ：GKEはこの範囲を取得し、ノード間で分割します。デフォルトでは、各ノードはこの範囲から/ 24を取得しますが、 フレックスポッドCIDRを使用してカスタマイズできます。
- サービスレンジ：クラスター内で実行されるクラスターIPサービスに使用されます。

クラスタとサービスのレンジは、ノードのプライマリレンジに対する[セカンダリレンジ](https://cloud.google.com/kubernetes-engine/docs/how-to/alias-ips#secondary_ranges)として構成されます。セカンダリレンジは、クラスターでエイリアスIPを構成するために使用されます。これにより、より良いSDNインテグレーションを実現します。[これらの理由](https://cloud.google.com/kubernetes-engine/docs/how-to/alias-ips#benefits)により、他の方法を使用せずに、別名IPサブネットを使用することをお勧めします。

```
gcloud compute networks create gke-no-internet-network \
--subnet-mode custom \
--bgp-routing-mode global

gcloud compute networks subnets create priv-cluster-01 \
--network gke-no-internet-network \
--range 10.10.10.0/24 \
--region us-central1 --enable-flow-logs \
--enable-private-ip-google-access \
--secondary-range services=10.10.11.0/24,pods=10.1.0.0/16
```

![](https://miro.medium.com/max/1000/1*wq_kJpZ_qbLq7KG_n2Gj1g.png)

##ファイアウォールのルールを作成する
VPCが作成されたら、それをロックダウンして、インターネットへのあらゆるタイプのアクセスを防止しましょう。[デフォルトではすべての出力トラフィックが許可](https://cloud.google.com/vpc/docs/firewalls#default_firewall_rules)されているため、このトラフィックをブロックするルールを作成する必要があります。

```
gcloud compute firewall-rules create deny-egress \
--action DENY \
--rules all \
--destination-ranges 0.0.0.0/0 \
--direction EGRESS \
--priority 1100 \
--network gke-no-internet-network
```

次に、ヘルスチェックを実行するGoogleのIPアドレスへの入力/出力を許可しましょう。これらはGCPがノードが正常にスピンアップしたことを認識し、将来のロードバランサーまたはイングレスサービスのために許可される必要があります。

```
gcloud compute firewall-rules create allow-healthcheck-ingress \
--action ALLOW \
--rules tcp:80,tcp:443 \
--source-ranges 130.211.0.0/22,35.191.0.0/16 \
--direction INGRESS \
--network gke-no-internet-network

gcloud compute firewall-rules create allow-healthcheck-egress \
--action ALLOW \
--rules tcp:80,tcp:443 \
--destination-ranges 130.211.0.0/22,35.191.0.0/16 \
--direction EGRESS \
--network gke-no-internet-network
```

また、制限されたGoogle APIのVIPへトラフィックを許可する必要があります。これは、Googleサービスとの通信に使用され、GKE APIと通信するために必要です。

```
gcloud compute firewall-rules create allow-google-apis-egress \
--action ALLOW \
--rules all \
--destination-ranges 199.36.153.4/30 \
--direction EGRESS \
--network gke-no-internet-network
```

この次の出力ルールは、GKEワーカーノードから、ステップ＃5で作成されるマスターノードへのトラフィックを許可します。クラスターを作成する前にこのルールを作成する必要があります。そうしないと、ワーカーノードがマスターノードに到達できなくなります。ここでは出力ルールのみが必要です。入力ルールは、クラスターの作成中にGCPによって自動的に作成されます。

```
gcloud compute firewall-rules create allow-master-node-egress \
--action ALLOW \
--rules tcp:443,tcp:10250 \
--destination-ranges 172.16.0.0/28 \
--direction EGRESS \
--network gke-no-internet-network
```

注：このVPCは現在、0.0.0.0/0へのすべての出力をブロックしています。内部IP空間（10.0.0.0/8、172.16.0.0/20、192.168.0.0/16）に向かうトラフィックには、クラスターがそれらのIPに到達できるようにするための追加の出力ルールが必要になります。

![](https://miro.medium.com/max/1000/1*UKIy19IskRzqWD-TB4Fhpg.png)

## デフォルトルートを削除し、Google APIへのルートを作成する
VPCを作成すると、ネクストホップが`default-internet-gateway`としてインターネットへのデフォルトルート（0.0.0.0/0）が自動的に作成されます。インターネットへのパスを削除するために、このルートを削除する必要があります。GUIを使用して削除するのが最も簡単です。

![](https://miro.medium.com/max/1000/1*qeMTsqHAsr6jZBUHWWZJew.png)

次に、VPCでルートを作成して、制限されたGoogle APIサブネットにトラフィックを送信します。[プライベートGoogle API](https://cloud.google.com/vpc/docs/configure-private-google-access)にアクセスするための要件に従って、ネクストホップを`default-internet-gateway`として設定する必要があります 。

「インターネットゲートウェイ」と言っても、実際にはトラフィックをインターネットに送信しません。サブネットで有効にしたプライベートGoogleアクセス機能のおかげで、トラフィックはGoogle APIへのプライベート内部パスを辿るようにルーティングされます。

```
gcloud compute routes create google-apis \
--destination-range 199.36.153.4/30 \
--next-hop-gateway default-internet-gateway \
--network gke-no-internet-network
```

![image.png](https://baby-degu.qiita.com/files/880e5acc-967b-2d96-40be-b6105cfcdba3.png)

##Google APIおよびGCR.ioのDNSゾーンを作成します
次のステップとして、`googleapis.com`のVPCでDNSをカスタマイズします。`googleapis.com`はパブリックIPにて解決されるため、`restricted.googleapis.com`を使用する必要があります。

このURLは、GCPネットワーク内でアクセス可能な特定の範囲199.36.153.4/30に解決されます（前の手順でルートを作成した範囲と同じです）。GKEワーカーノードを正常に起動するためには、`googleapis.com`の代わりに`restricted.googleapis.com`を使用する必要があります。

これは、GCP内のプライベートクラウドDNS機能を使用して実施できます。まず、Cloud DNSで`googleapis.com`のプライベートゾーンを作成します。

ゾーンが作成されたら、`restricted.googleapis.com`を指す`* .googleapis.com`のCNAMEレコードを追加します。この作業を行うためにはもう1つのレコード、`restricted.googleapis.com`の制限付きVIP IPを指すAレコードが必要です。

```
gcloud dns managed-zones create google-apis \
--description "private zone for Google APIs" \
--dns-name googleapis.com \
--visibility private \
--networks gke-no-internet-network

gcloud dns record-sets transaction start --zone google-apis

gcloud dns record-sets transaction add restricted.googleapis.com. \
--name *.googleapis.com \
--ttl 300 \
--type CNAME \
--zone google-apis

gcloud dns record-sets transaction add "199.36.153.4" \
"199.36.153.5" "199.36.153.6" "199.36.153.7" \
--name restricted.googleapis.com \
--ttl 300 \
--type A \
--zone google-apis

gcloud dns record-sets transaction execute --zone google-apis
```

![](https://miro.medium.com/max/1000/1*RqJNFRWDMRZqQqJcztjj-g.png)

GCR.ioドメインに対して同様の設定を行い、コンテナレジストリに到達するためにトラフィックを制限されたGoogle APIサブネットに送信する必要があります。これらの変更がないと、GKEクラスターは、必要なすべてのコンテナをプルダウンできないため、正常に起動しません。

```
gcloud dns managed-zones create gcr-io \
--description "private zone for GCR.io" \
--dns-name gcr.io \
--visibility private \
--networks gke-no-internet-network

gcloud dns record-sets transaction start --zone gcr-io

gcloud dns record-sets transaction add gcr.io. \
--name *.gcr.io \
--ttl 300 \
--type CNAME \
--zone gcr-io

gcloud dns record-sets transaction add "199.36.153.4" "199.36.153.5" "199.36.153.6" "199.36.153.7" \
--name gcr.io \
--ttl 300 \
--type A \
--zone gcr-io

gcloud dns record-sets transaction execute --zone gcr-io
```

![](https://miro.medium.com/max/1000/1*NWoACJdnAWXFfyF9P18Euw.png)

## GKEクラスターを作成する
ネットワークといくつかのサブネットができたので、プライベートGKEクラスターを作成しましょう。クラスターを作成するときに調整する箇所は多数ありますが、プライベートIPのみを使用してワーカーノードとマスターノードを作成するために必要なものはわずかです。次の設定を確認します。

- VPCネイティブの有効化
- プライベートクラスタノードの有効化
- 外部IPアドレスを使用したマスターへのアクセスを無効化

```
gcloud beta container clusters create private-gke-cluster \
--zone "us-central1-a" \
--enable-private-nodes \
--enable-private-endpoint \
--master-ipv4-cidr "172.16.0.0/28" \
--enable-ip-alias \
--network "projects/<project_id>/global/networks/gke-no-internet-network" \
--subnetwork "projects/<project_id>/regions/us-central1/subnetworks/priv-cluster-01" \
--cluster-secondary-range-name "pods" \
--services-secondary-range-name "services" \
--enable-master-authorized-networks
```

#プライベートクラスタがデプロイされました！

すべてがプランどおりに進んだ場合、クラスターはすべてのワークロードとサービスが正常な状態で起動するはずです。この記事の設定で問題が発生した場合は、[Twitter](https://twitter.com/AndreyK_)でご連絡ください。お手伝いさせていただきます。

![](https://miro.medium.com/max/700/1*hVANDfkD3JAT8jrEGgtLcw.png)


#Terraform
本記事の手順をスキップしてすぐに起動したい場合は、次のGithubリポジトリで利用可能なterraform構成ファイルがあります。

https://github.com/andreyk-code/no-inet-gke-cluster.git


# 翻訳協力
Author: [Andrey Kumanov](https://medium.com/@andreyk_)
Thank you for letting us share your knowledge!

記事選定: @takitakis 
翻訳/技術監査: @aoharu / @azumana
Markdown化: @aoharu