概要 #
管理用の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で構築します。