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

Terraformで外部IPなしのGCEへIAP経由でSSH接続してみた

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

概要
#

管理用の22番Portをインターネット全体へ公開すると、常時スキャンや不正アクセスの対象になります。かといって毎回踏み台サーバーを維持するのも手間です。

Identity-Aware Proxy(IAP)のTCP forwardingを使えば、外部IPを持たないVMへSSHできます。SourceをGoogleのIAP用Address Rangeだけに絞り、接続者はIAMで制御する構成です。ここではTerraformで専用VPCとSpot VMを作成し、実際にIAP経由で接続します。

Terraformの基本操作は前提としています。IAPやOS Loginを初めて扱う方向けです。

Cloud NAT、踏み台VM経由でのHTTP/HTTPS転送、複数Userの権限設計は扱いません。

検証すること
#

  • 外部IPなしのSpot VMを専用VPCへ作成できること
  • SSH IngressをIAP TCP forwardingの範囲へ限定できること
  • OS LoginとIAMで接続者を制御できること
  • IAP Tunnel経由でVMへSSH接続できること

前提環境
#

  • Google Cloud CLIとApplication Default Credentials(ADC)、Terraform
  • Billingが有効な検証用Google Cloud Project
  • Compute Engine、IAP、IAMを操作できるGoogleアカウント
  • 検証時のバージョン:Terraform 1.14.3、google 7.43.0

今回の構成
#

VMは専用VPCの中にあり、外部IPを持ちません。IAPはVPCの外にあるGoogle管理のサービスです。

flowchart TB
    Op["Operator の端末"]

    subgraph GCP["Google Cloud"]
        IAP["Identity-Aware Proxy
(VPC外・Google管理)"] subgraph Project["Project"] IAM["IAM
roles/iap.tunnelResourceAccessor"] subgraph Region["asia-northeast1"] subgraph VPC["VPC"] FW["Firewall: allow-iap-ssh
src 35.235.240.0/20 / TCP:22"] subgraph Subnet["Subnet"] VM["Spot VM(外部IPなし)"] end end end end end Op -->|"HTTPS"| IAP IAP -->|"TCP:22"| FW FW --> VM IAM -.->|"接続可否を判定"| IAP

インターネットからVMへ直接届く経路はありません。到達できるのはIAPを通る経路だけです。

接続の流れは次のとおりです。

sequenceDiagram
    actor Op as Operator
    participant IAP as Identity-Aware Proxy
    participant IAM as IAM
    participant FW as Firewall
    participant VM as Spot VM

    Op->>IAP: gcloud compute ssh --tunnel-through-iap
    IAP->>IAM: 権限を確認
    IAM-->>IAP: 許可
    IAP->>FW: TCP:22(送信元 35.235.240.0/20)
    FW->>VM: 転送
    VM-->>Op: SSHセッション確立

使用するTerraformコード
#

外部IPを付けないVM
#

resource "google_compute_instance" "vm" {
  name         = var.instance_name
  machine_type = var.machine_type
  zone         = var.zone

  network_interface {
    subnetwork = google_compute_subnetwork.primary.id
  }

  scheduling {
    provisioning_model = "SPOT"
    preemptible         = true
    automatic_restart   = false
  }

  metadata = { enable-oslogin = "TRUE" }
}

network_interfaceにaccess_configを置かないため、VMに外部IPv4 Addressは割り当てられません。OS Loginを有効にし、SSH KeyをInstance Metadataへ直接配布しない形にしています。

IAPだけを許可するFirewall
#

resource "google_compute_firewall" "allow_iap_ssh" {
  name    = "allow-iap-ssh"
  network = google_compute_network.vpc.name

  direction     = "INGRESS"
  source_ranges = ["35.235.240.0/20"]
  target_tags   = ["iap-ssh"]

  allow {
    protocol = "tcp"
    ports    = ["22"]
  }
}

Sourceを0.0.0.0/0ではなくIAPの範囲に限定し、対象もNetwork Tagで絞ります。加えて接続者にはroles/iap.tunnelResourceAccessorとroles/compute.osLoginが必要です。サンプルではProject IAMに加え、Instance単位のIAP IAM Memberも使って権限境界を確認できます。

実行と接続確認
#

cd Advanced-Examples/01-gce-iap-vpc
cp terraform.tfvars.example terraform.tfvars
terraform init
terraform fmt -check
terraform validate
terraform plan
terraform apply

VPC、Subnet、Firewall、VM、IAM Memberなど、合わせて12リソースが作成されます。

Apply complete! Resources: 12 added, 0 changed, 0 destroyed.

Outputs:

instance_name = "tf-adv-gce-iap-01"
internal_ip = "10.30.0.2"
network_name = "tf-adv-gce-iap-vpc"
ssh_via_iap_example = "gcloud compute ssh tf-adv-gce-iap-01 --zone=asia-northeast1-a --tunnel-through-iap --project=YOUR_PROJECT_ID"
zone = "asia-northeast1-a"

出力された名前とZoneを使って接続します。

gcloud compute ssh "$(terraform output -raw instance_name)" \
  --zone="$(terraform output -raw zone)" \
  --tunnel-through-iap \
  --command='hostname && whoami'
tf-adv-gce-iap-01
you_example_com

whoamiがOS Loginによりユーザー名_ドメイン形式に変換されて表示されます。SSH Key自体をInstance Metadataへ配布していないことの裏付けです。

このコマンドはgcloud config get-value projectで選択中のProjectを使います。別Projectを操作する場合は、terraform.tfvarsのproject_idと同じ値を--projectへ明示してください。

外部IPがないことは次でも確認できます。

gcloud compute instances describe "$(terraform output -raw instance_name)" \
  --zone="$(terraform output -raw zone)" \
  --format='get(networkInterfaces[0].accessConfigs[0].natIP)'

何も出力されません。accessConfigs(外部IP)自体が存在しないためです。

後片付けと注意点
#

terraform destroy
Destroy complete! Resources: 12 destroyed.

Spot VMは停止される可能性があるため、継続稼働が必要なWorkloadには通常VMを選びます。また、VMからInternetへ出る必要がある場合、外部IPがないためCloud NATなどを別途設計してください。IAMは検証用Userを変数で明示し、不要になった権限を残さないようにします。

まとめ
#

  • 外部IPなしのVMへIAP Tunnel経由でSSH接続できる
  • SSHのIngress SourceをIAP用Rangeへ限定できる
  • OS LoginとIAMで接続者を管理できる
  • Network、Firewall、Instance、IAMを一つのTerraform構成で再現できる

参考資料
#

次回
#

次回はArtifact RegistryのImageを使ってCloud Run ServiceをTerraformで構築します。

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

関連記事

Terraform StateをCloud Storage Remote Backendへ移行してみた
2 分
Terraform GoogleCloud CloudStorage
TerraformからGoogle Cloudへ接続してProject情報を取得してみた
3 分
Terraform GoogleCloud
TerraformでArtifact RegistryのDocker Repositoryを作成してみた
2 分
Terraform GoogleCloud Docker
TerraformでArtifact RegistryのコンテナをCloud Runへデプロイしてみた
2 分
Terraform GoogleCloud CloudRun ArtifactRegistry
TerraformでBigQuery DatasetとTableを作成してみた
2 分
Terraform GoogleCloud
TerraformでCloud KMSのKeyRingとCryptoKeyを作成してみた
2 分
Terraform GoogleCloud