【GCP】IAP TCP転送を使用して、ローカルマシンから直接PrivateなCloudSQLにアクセス

IAP TCP転送をローカルプロトコルに割り当てて、ローカルマシンから直接PrivateなCloudSQLにアクセスする方法を書いていきます。

タイトルのアクセス方法を見つけるまでは、VMにSSH接続してからCloudSQLにアクセスしていました。
VMには外部IPアドレスを持たせていないのでIdentity Aware Proxy（以降IAP）経由でVMにアクセスしています。
![無題のプレゼンテーション (1).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357470/82fe3e62-4cb1-53d6-ae56-90ff1f704432.jpeg)

この方法だとSHELLでしか操作ができないので、DBクライアントツール大好きマンな私はとても辛い日々を過ごしておりました。
そんな日々を過ごす中で、ふと思いつきました。
**『IAP経由でVMにアクセスしてMySQLにアクセスできるのなら、IAPをポートフォワーディングするなりなんなりできればローカルマシンからもMySQLにアクセスできるのではないか』** と。

そしてIAPのドキュメントから見つけ出したのが **『IAP TCP 転送』** です。
https://cloud.google.com/iap/docs/using-tcp-forwarding?hl=ja
> IAP TCP 転送では、SSH、RDP、その他のトラフィックを VM インスタンスに転送できる暗号化トンネルを確立できます。IAP TCP 転送では、トンネルの確立を許可するユーザーと、ユーザーの接続を許可する VM インスタンスをきめ細かく制御することもできます

まさに私が求めていたもの！！！
早速試します。

### 1. VPC作成
IP範囲`35.235.240.0/20`はIAP が TCP 転送に使用するすべての IP アドレスが含まれてます。
このIP範囲を許可するFWルールがないとIAP経由でのSSHができないので、必須ルールになります。
``` terraform
module "vpc_iap_tcp" {
  source  = "terraform-google-modules/network/google"
  version = "~> 6.0"

  project_id   = var.project_id
  network_name = "vpc-iap-tcp"
  routing_mode = "GLOBAL"

  subnets = [
    {
      subnet_name   = "vpc-iap-tcp-sub"
      subnet_ip     = "10.10.10.0/24"
      subnet_region = "asia-northeast1"
    }
  ]

  firewall_rules = [
    # IAP TCP 転送に使用するすべてのIPアドレスからの上り（内向き）トラフィックを許可
    {
      name      = "fw-allow-ssh-ingress-from-ssh"
      direction = "INGRESS"
      allow = [{
        protocol = "tcp"
        ports    = ["22"]
      }]
      ranges = [
        "35.235.240.0/20"
      ]
    },
  ]
}

```

### 2. 外部IPアドレスを持たないVM（Compute Engine）を作成
``` terraform
resource "google_compute_instance" "gce_vpn_test" {
  name         = "gce-iap-tcp"
  machine_type = "e2-standard-2"
  zone         = "asia-northeast1-a"

  boot_disk {
    initialize_params {
      image = "ubuntu-os-cloud/ubuntu-2204-lts"
    }
  }

  network_interface {
    subnetwork = module.vpc_iap_tcp.subnets["asia-northeast1/vpc-iap-tcp-sub"].self_link
  }
}
```

### 3. 外部IPアドレスを持たないCloudSQLインスタンスを作成
``` terraform
module "private_service_access" {
  source  = "GoogleCloudPlatform/sql-db/google//modules/private_service_access"
  version = "14.0.0"

  project_id = var.project_id
  vpc_network = module.vpc_iap_tcp.network_name
}

module "sql_iap_tcp" {
  source  = "GoogleCloudPlatform/sql-db/google//modules/mysql"
  version = "14.0.0"

  project_id           = var.project_id
  name                 = "sql-iap-tcp"
  random_instance_name = true

  database_version = "MYSQL_8_0"
  region           = var.region
  zone             = var.zone
  tier             = "db-f1-micro" # 検証用なので低スペックなマシンタイプを使用

  deletion_protection = false      # 検証用なので削除保護は無効

  ip_configuration = {
    ipv4_enabled        = false    # 外部IPアドレスは無効
    private_network     = module.vpc_iap_tcp.network_id
    require_ssl         = false
    allocated_ip_range  = module.private_service_access.google_compute_global_address_name
    authorized_networks = []
  }

  additional_databases = [
    {
      name      = "iap-tcp",
      charset   = "utf8mb4"
      collation = "utf8mb4_bin"
    }
  ]

  depends_on = [module.private_service_access.peering_completed]
}
```

### 4. VMにSSH接続する
SSH接続が成功すると`~/.ssh`ディレクトリにSSH認証鍵関連ファイルが生成されます。
``` shell
gcloud compute ssh IAMユーザ名@GCEインスタンス名
# `gcloud compute ssh GCEインスタンス名`でもSSH接続できますが、DBクライアントツールからCloudSQLにアクセスする時にエラーになります
```
``` shell
.ssh
├── google_compute_engine
├── google_compute_engine.pub
├── google_compute_known_hosts
├── ...
├── ..
├── .
```

VMのSSH接続解除
``` shell
exit
```

### 5. ローカルマシンとVMの間にIAPトンネルを作成する（IAP TCP 転送）
``` shell
gcloud compute start-iap-tunnel gce-iap-tcp 22 \
  --local-host-port=localhost:2022 \
  --zone=asia-northeast1-a
```

上記コマンドを実行するとローカルマシンはHTTPSでIAPにアクセスしVMとのトンネルを作成します。
トンネルが存在している間、ローカルポート（Port:22）に送信されるすべてのトラフィックはVMに転送されるようになります。
![無題のプレゼンテーション (2).jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357470/a68cfa6b-154b-056b-cb52-66c40838ac3b.jpeg)

`Listening on port [ローカルポート]`と表示されれば成功です。
```shell
...
Testing if tunnel connection works.
Listening on port [2022].
```


### DBクライアントツールでPrivateなCloudSQLにアクセスしてみる
今回はMySQLWorkbenchを使用します。
| 項目 | 値 |
| --- | --- |
| SSH Hostname | localhost:ローカルポート |
| SSH Username | 自身のIAMユーザ名 |
| SSH Password | 未設定 |
| SSH Key File | `gcloud compute ssh`で生成したSSH認証鍵 |
| MySQL Hostname | CloudSQLにプライベートIP |
| MySQL Server Port | MySQLのポート |
| Username | MySQLユーザ名 |
| Password | MySQLユーザパスワード |

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357470/8ac56904-09a3-167e-552b-1f1e7632c990.png)

「Test Connection」ボタンをクリックして`Successfully`と表示されたら成功です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357470/b292332b-a1ca-e5cd-dc5f-017f330f66a5.png)

クライアントツールから実際に接続して、データベースも確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/357470/c58e1fe3-e680-81c1-00a1-b57a4d2c1cc6.png)


これで、PrivateなCloudSQLもDBクライアントツールで快適に操作できるようになりました。
VPN不要なのに手軽 かつ セキュアにCloudSQLに接続できるCloud IAP最高。