【GCP/Terraform】VMインスタンスをサブネット内に作成し、Firewallで許可したIPからのみSSH接続する
TL;DR
- こちらのリポジトリを参考にしてください
-> https://github.com/nekoshita/gcp-vm-instance-in-subnet-example
こんな人向け
- GCPのVPCとかsubnetとかFirewallとかよくわかんない
- GCPで踏み台サーバー立てたい
- AWSと全然違うからよくわかんない
- terraformでGCPさわりたい
解説
ネットワークとサブネット
まずは新しいVPCネットワークを定義します。
(VPCネットワークについて: https://cloud.google.com/vpc/docs/vpc?hl=ja )
GCPプロジェクトを作成すると、デフォルトのVPCネットワークが作成されますが、こちらは公開設定されているので使いません。
VPCネットワークはグローバルリソースなので、リージョンやゾーンは指定しません。
resource "google_compute_network" "my_network" {
name = "my-network"
# リージョンごとにサブネットを自動で作成してくれます。今回は使わないのでfalseにします。
auto_create_subnetworks = false
}
次にVPCネットワーク内にサブネット定義します。
サブネットはリージョンリソースで、サブネットごとにIPアドレスの範囲を指定します。
GCPでは、サブネットを作成すると、自動でデフォルトゲートウェイとルートが設定されます。
デフォルトゲートウェイについて -> https://cloud.google.com/vpc/docs/vpc?hl=ja#affiliated_resources
ルートについて -> https://cloud.google.com/vpc/docs/routes?hl=ja#types_of_routes
resource "google_compute_subnetwork" "my_subnetwork" {
name = "my-subnetwork"
region = "asia-northeast1"
# サブネットで使用したい内部IPアドレスの範囲を指定する
ip_cidr_range = "10.0.0.0/16"
network = google_compute_network.my_network.self_link
# CloudLoggingにFlowLogログを出力したい場合は設定する
log_config {
metadata = "INCLUDE_ALL_METADATA"
}
}
サブネット内にVMインスタンスを作成する
VMインスタンスに付与するservice accountを作成することをGoogleが推奨しているので、service accountも定義します。
resource "google_service_account" "my_service_account_for_bastion" {
account_id = "my-service-account-for-bastion"
display_name = "My Service Account For Bastion"
}
resource "google_compute_instance" "bastion" {
# 好きな名前、マシンタイプ、ゾーンに作成してください
name = "bastion"
machine_type = "f1-micro"
zone = "asia-northeast1-a"
# Firewallでタグごとにルールを設定したいので、VMインスタンスにタグを設定しておく
tags = ["bastion-tag"]
# すきなディスクを選んでください
boot_disk {
initialize_params {
image = "debian-cloud/debian-10"
}
}
network_interface {
# 自分で定義したsubnetwork内にインスタンスを起動する
network = google_compute_network.my_network.name
subnetwork = google_compute_subnetwork.my_subnetwork.name
# 外部IPアドレスを割り振る方法は次の2種類ある。静的外部IPアドレス、エフェメラル外部IPアドレス
# 今回はエフェメラル外部IPアドレスを使うので、access_configの中は何も指定しない
access_config {}
}
service_account {
email = google_service_account.my_service_account_for_bastion.email
scopes = ["cloud-platform"]
}
scheduling {
# 料金を抑えるためにプリエンプティブルにしておく
preemptible = true
# プリエンプティブルの場合は下のオプションが必須
automatic_restart = false
}
}
暗黙のFirewallルール
GCPの全てのVPCネットワークには2つの暗黙のFirewallルールがあります。
- ほとんどの下りトラフィックを許可
- すべての上りトラフィックを拒否
(詳細 -> https://cloud.google.com/vpc/docs/vpc?hl=ja#affiliated_resources )
この暗黙のルールはコンソールに表示されません。なので、編集することもできません。
GCPではサブネットを作成すると自動でデフォルトゲートウェイとルートが設定されており、先ほど作成したVMインスタンスは外部IPアドレスが割り振られているので、もうSSH接続できそうなですが、まだできません。
上にあるように暗黙のFirewallルールにより、VMインスタンスへの上りのトラフィックは拒否されてしまいます。
なので、新たにFirewallルールを設定して、VMインスタンスへSSH接続できるようにします。
ネットワークにアクセスするためにFirewallを設定する
GCPはVPCネットワーク単位でFirewallを設定します。
resource "google_compute_firewall" "my_network" {
name = "my-network-firewall"
network = google_compute_network.my_network.name
# 今回は内向きを選択する
direction = "INGRESS"
# 通信を許可するprotocolとportを指定する
allow {
protocol = "tcp"
ports = ["22"]
}
# 対象のVMインスタンスのタグを指定する
target_tags = ["bastion-tag"]
# VMインスタンスへのSSH接続元IPアドレス範囲を指定する
# 今使ってるグローバルIPアドレスだけを許可したい場合は
# $ curl httpbin.org/ip
# で取得したIPアドレスを指定する
# (例: 123.123.123.123/32)
source_ranges = ["0.0.0.0/0"]
# CloudLoggingにFlowLogログを出力したい場合は設定する
log_config {
metadata = "INCLUDE_ALL_METADATA"
}
}
SSH接続するユーザーにRoleを付与する
SSH接続するのに必要なRoleは以下になります
resource "google_project_iam_binding" "access_user" {
role = google_project_iam_custom_role.my_custom_role.id
members = [
"user:許可したいGoogleユーザーのメールアドレス",
]
}
resource "google_project_iam_custom_role" "my_custom_role" {
role_id = "MyCustomRole"
title = "My Custom Role"
permissions = [
"compute.projects.get",
"compute.instances.get",
"compute.instances.setMetadata",
"iam.serviceAccounts.actAs",
]
}
なので、今回作成したVPCネットワーク内の外部IPアドレスを持つVMインスタンスにSSH接続するには、以下の両方が必要ということになります。
- SSH接続するためのRoleを持つGoogleユーザーであること
- Firewallルールで許可したIPアドレス範囲からのアクセスであること
実際にリソースを作ってみる
準備
-
Terraform v0.14.5
のインストール - Google Cloud Platformのprojectを作成する
- GCP StorageのBucketを作成する
-
gcloud CLI
のインストール
作成してSSH接続してみる
#-------------------
# リポジトリをクローンする
#-------------------
$ git clone https://github.com/nekoshita/gcp-vm-instance-in-subnet-example
$ cd gcp-vm-instance-in-subnet-example
#-------------------
# グローバルIPアドレスの取得
#-------------------
$ curl httpbin.org/ip
# でグローバルIPアドレスを取得し(たとえば、123.123.123.123)、環境変数にセットする
$ export GLOBAL_IP_ADDRESS="123.123.123.123"
#-------------------
# terraform applyしてリソースを作成する
#-------------------
$ export GCP_PROJECT_ID="your-gcp-project-id"
$ export GCS_BUCKET_NAME="your-gcs-bucket-name"
$ export USER_MAIL="your-google-user-mail-to-allow-ssh-acccess@gmail.com"
$ bin/apply $GCP_PROJECT_ID $GCS_BUCKET_NAME $USER_MAIL $GLOBAL_IP_ADDRESS
#-------------------
# SSH接続する
#-------------------
$ export ZONE="asia-northeast1-a"
$ export VM_INSTANCE_NAME="bastion"
# GCPのprojectを作成したgoogleアカウントでログインする(すでにログインしてる場合は不要)
$ gcloud auth login
$ gcloud beta compute ssh --zone $ZONE $VM_INSTANCE_NAME --project $GCP_PROJECT_ID
# パスフレーズの入力を求められるので、適当なパスフレーズを入力してあげてください
#-------------------
# 最後にリソースの削除をお忘れなく!
#-------------------
$ export GCP_PROJECT_ID="your-gcp-project-id"
$ export GCS_BUCKET_NAME="your-gcs-bucket-name"
$ export USER_MAIL="your-google-user-mail-to-allow-ssh-acccess@gmail.com"
$ bin/destroy $GCP_PROJECT_ID $GCS_BUCKET_NAME $USER_MAIL $GLOBAL_IP_ADDRESS
ログをみてみる
サブネットのログをみる
GCPコンソールの VPC Network を開き、作成したサブネットを選択します。
「View flow logs」というリンクがあるので、それをクリックするとCloudLoggingのページが開きます。
VMインスタンに接続したあとならば、CloudLoggingの検索フィルターのところに "自分のグローバルIPアドレス" を追加すると、自分のアクセスしたログが見れるはずです。
Firewallルールのログをみる
GCPコンソールの Firewall を開き、作成したFirewallルールを選択します。
「view in Logs Explorer」というリンクがあるので、それをクリックするとCloudLoggingのページが開きます。
最後に
公式ドキュメントを元に理解したつもりですが、私の認識が間違っていたらご指摘いただけると嬉しいです!
Discussion