↓メインコンテンツへスキップ
  1. Blogs/

TerraformでGKEのコントロールプレーンをプライベート+パブリックの両方から接続してみた

2 分
Terraform GoogleCloud GKE
0222-nnn
著者
0222-nnn
猫が好き
目次
Terraform-GoogleCloud - この記事は連載の一部です
パート 27: この記事

概要
#

GKEプライベートクラスタと踏み台の構成は、コントロールプレーンへのパブリックエンドポイントを完全に無効化していました。踏み台がないとkubectlが一切できません。開発初期の検証など、許可した自分のIPからは踏み台を経由せず直接繋ぎたい場面もあります。

GKEプライベートクラスタは、プライベートエンドポイントとパブリックエンドポイントを併用できます。パブリック側は許可IPだけに絞れるので、「VPC内は無条件、VPC外は限定IPのみ」という2段構えのアクセス制御になります。

Terraformの基本操作と、前々回のGKEプライベートクラスタ+踏み台構成は前提としています。

Cloud VPN・Interconnect経由のオンプレミス接続は扱いません。IAM/RBACの認可設計も扱わず、接続経路の確認に絞ります。

検証すること
#

  • ローカルPCから、踏み台を経由せずパブリックエンドポイント経由で直接kubectlできること
  • 許可していないIPからは接続できないこと(apply成功とは別の確認)
  • 踏み台からは、プライベートエンドポイント経由で引き続き接続できること

前提環境
#

  • Google Cloud CLI、Terraform、Docker
  • Billingが有効な検証用Google Cloud Project
  • GKE、Compute Engineを操作できるGoogleアカウント
  • 検証時のバージョン:Terraform 1.14.3、google 7.46.0

使用するTerraformコード
#

一次情報での裏取り:enable_private_endpointの挙動
#

今回のコードは、別環境で検証したコードを土台にしています。ただし、そのコードはprivate_cluster_config.enable_private_endpoint = trueのままパブリックエンドポイントも使える、という前提で書かれていました。

terraform providers schema -jsonでプロバイダのフィールド説明を確認すると、次のように書かれています。

enable_private_endpoint -> When true, the cluster's private endpoint is
used as the cluster endpoint and access through the public endpoint is
disabled. When false, either endpoint can be used.

trueは「パブリックエンドポイント経由のアクセスを無効化する」フラグです。master_authorized_networksにどれだけパブリックIPを追加しても、enable_private_endpoint = trueのままではパブリックエンドポイント自体が無効なので到達できません。本記事のコードはfalseに修正してあります。

今回の構成
#

master_authorized_networks_configに、踏み台Subnetの範囲と管理者の公開IPの両方を許可リストとして渡します。

flowchart TB
    subgraph GCP["Google Cloud"]
        subgraph Project["Project"]
            subgraph Region["asia-northeast1"]
                subgraph VPC["VPC"]
                    Bastion["踏み台VM
(外部IPなし)"] end end CP["GKEコントロールプレーン
Private Endpoint + Public Endpoint"] end end Admin["管理者のローカルPC
(admin_public_cidr)"] Bastion -->|"プライベートIP経由
(VPC内、無条件)"| CP Admin -->|"パブリックIP経由
(許可IPのみ)"| CP

GKEクラスタの設定
#

resource "google_container_cluster" "primary" {
  # ...

  private_cluster_config {
    enable_private_nodes    = true
    enable_private_endpoint = false  # 11はtrue(パブリック無効)
    master_ipv4_cidr_block  = var.master_ipv4_cidr_block
  }

  master_authorized_networks_config {
    cidr_blocks {
      cidr_block   = var.bastion_subnet_cidr
      display_name = "bastion-subnet"
    }
    cidr_blocks {
      cidr_block   = var.admin_public_cidr
      display_name = "admin-public"
    }
  }
}

admin_public_cidrにはデフォルト値を設定していません。誤って緩いCIDRのままapplyしてしまう事故を避けるためです。

設定と実行
#

cd Advanced-Examples/15-gke-dual-endpoint
cp terraform.tfvars.example terraform.tfvars
curl -4 -s https://ifconfig.me   # 表示されたIPを admin_public_cidr に /32 で設定
project_id        = "YOUR_PROJECT_ID"
iap_member        = "user:YOUR_ACCOUNT"
admin_public_cidr = "YOUR_PUBLIC_IP/32"

ifconfig.meは、IPv6が使える環境ではIPv6アドレスを返すことがあります。GKEの許可ネットワークはIPv4を想定しているため、-4を付けてIPv4を明示的に取得します(この違いに気づかず、実際にIPv6アドレスが返ってきて確認し直しました)。

terraform init
terraform fmt -check
terraform validate
terraform plan
terraform apply
Apply complete! Resources: 25 added, 0 changed, 0 destroyed.

Outputs:

private_endpoint = "172.16.4.2"
public_endpoint = "35.187.205.172"

ローカルPCから直接接続できることを確認する
#

クラスタが作成できたことと、実際にパブリックエンドポイント経由で接続できることは別です。踏み台を経由せず、ローカルから直接確認します。

gcloud container clusters get-credentials "$(terraform output -raw cluster_name)" \
  --zone="$(terraform output -raw zone)" --project="$(terraform output -raw project_id)"
kubectl get nodes
NAME                                                  STATUS   ROLES    AGE   VERSION
gke-tf-adv-gke-duale-tf-adv-gke-duale-f1510c85-0f18   Ready    <none>   23s   v1.35.7-gke.1027000

踏み台なしで、ローカルから直接ノードの一覧が取得できました。

許可していないIPからは接続できないことを確認する
#

admin_public_cidrが正しく機能しているかは、正しいIPで繋がることだけでは確認できません。ドキュメント用の予約アドレス(203.0.113.1/32、RFC 5737で予約された、実在の割り当て先が存在しないアドレス)を一時的に設定し、接続が拒否されることを確認します。

terraform apply -var="admin_public_cidr=203.0.113.1/32"
kubectl get nodes --request-timeout=15s
Unable to connect to the server: dial tcp 35.187.205.172:443: i/o timeout

タイムアウトしました。許可リストに存在しないIPからは、コネクション自体が確立できません。正しいIPへ戻すと、接続が復帰します。

terraform apply -var="admin_public_cidr=$(curl -4 -s https://ifconfig.me)/32"
kubectl get nodes
NAME                                                  STATUS   ROLES    AGE     VERSION
gke-tf-adv-gke-duale-tf-adv-gke-duale-f1510c85-0f18   Ready    <none>   2m2s    v1.35.7-gke.1027000

master_authorized_networks_configの更新はクラスタやノードの再作成を伴わないため、数秒で反映されます。

踏み台からの接続には--internal-ipが必要
#

前々回の完全プライベート構成では、gcloud container clusters get-credentialsをそのまま実行すればプライベートエンドポイントに繋がりました。エンドポイントが1つしか存在しないため、選びようがなかったからです。

Dual Endpointにすると事情が変わります。enable_private_endpoint = falseのとき、get-credentialsはVPC内(踏み台)から実行してもパブリックエンドポイントのIPを書き込みます。

公式の選択順が外部IPを先に見るためです。

# 踏み台の中で実行(--internal-ipなし)
gcloud container clusters get-credentials "$(terraform output -raw cluster_name)" \
  --zone="$(terraform output -raw zone)" --project="$(terraform output -raw project_id)"
kubectl get nodes
E0901 03:52:14.094629 memcache.go:265] "Unhandled Error" err="couldn't get current server API group list: Get \"https://35.187.205.172/api?timeout=32s\": dial tcp 35.187.205.172:443: i/o timeout"

踏み台のCloud NAT出口IPはadmin_public_cidrに含まれていないため、パブリックエンドポイント経由の接続はタイムアウトします。プライベートエンドポイントを明示的に使うには、--internal-ipが必要です。

gcloud container clusters get-credentials "$(terraform output -raw cluster_name)" \
  --internal-ip --zone="$(terraform output -raw zone)" --project="$(terraform output -raw project_id)"
kubectl get nodes
NAME                                                  STATUS   ROLES    AGE     VERSION
gke-tf-adv-gke-duale-tf-adv-gke-duale-f1510c85-0f18   Ready    <none>   4m27s   v1.35.7-gke.1027000

--internal-ipを付けて実行し直すと、プライベートエンドポイント経由で接続できました。同じ「踏み台からget-credentials」でも、パブリックエンドポイントが有効になったことで、明示的にプライベート側を選ぶ操作が必要になったことになります。

後片付け
#

terraform destroy
Destroy complete! Resources: 25 destroyed.

GKE Clusterと踏み台VMは利用中に料金が発生します。検証後は必ずdestroyします。

まとめ
#

  • enable_private_endpoint = trueは「パブリックエンドポイントを無効化する」フラグ。Dual Endpointにするにはfalseにする
  • master_authorized_networks_configに踏み台Subnetと管理者の公開IPの両方を許可すると、VPC内外の両方から接続できる
  • 許可していないIPからの接続はタイムアウトする。正しいIPに戻すとクラスタの再作成なしに復帰する
  • Dual Endpointにすると、VPC内から実行するget-credentialsもデフォルトでパブリックエンドポイントを選ぶ。プライベートエンドポイントを使うには--internal-ipが必要

参考資料
#

次回
#

GKE Dataplane V2を有効化し、NetworkPolicyでPod間通信を遮断します。

Terraform-GoogleCloud - この記事は連載の一部です
パート 27: この記事

関連記事

TerraformでGKEのPodをInternal HTTP LB(NEG)でポート別に振り分けてみた
3 分
Terraform GoogleCloud GKE LoadBalancing
TerraformでGKEプライベートクラスタからMemorystore for Redis Clusterへ接続してみた
3 分
Terraform GoogleCloud GKE Redis
TerraformでGKEプライベートクラスタからMemorystore for Redisへ接続してみた
2 分
Terraform GoogleCloud GKE Redis
TerraformでGKEプライベートクラスタと踏み台VMを作成してみた
3 分
Terraform GoogleCloud GKE IAP
TerraformでGKE Workload IdentityからCloud Storageへアクセスしてみた
2 分
Terraform GoogleCloud GKE WorkloadIdentity
Terraform StateをCloud Storage Remote Backendへ移行してみた
2 分
Terraform GoogleCloud CloudStorage