🛠️

作業端末から本番環境の Cloud SQL, AlloyDB のプライベート IP インスタンスへのアクセス

2024/12/11に公開

クラウドエースの北野です。

SSH ポートフォワーディングによるローカル端末から Cloud SQL と AlloyDB のプライベート IP インスタンスにアクセスする方法を紹介します。

概要

ローカル端末から インスタンスにアクセスするシステム構成は以下の通りです。

構成図

  • COS インスタンス上の Auth Proxy プロセスによる Cloud SQL, AlloyDB インスタンスへの接続
  • 作業端末から COS インスタンスへの SSH ポートフォーワーディグによる Cloud SQL, AlloyDB インスタンスへの接続

Compute Engine に SSH ポートフォワーディングするコマンド

gcloud compute ssh --tunnel-through-iap <GCENAME> -- -N -L 5432:localhost:5432 &

はじめに

本番環境のデータベースはセキュリティの観点からパブリック IP を付与しない構成になっているかと思います。
Google Cloud の RDB サービスである Cloud SQL, AlloyDB もプライベート IP のみを持つインスタンスを作成することができます。

しかし、プライベート IP しか持たない Cloud SQL, AlloyDB インスタンスは、インターネットからアクセスができないため、
インスタンスに接続できる VPC ネットワークからアクセスする必要があります。
そのため、ローカル端末からアクセスするとき、VPC ネットワークに踏み台インスタンスを作成しリモートアクセスして、
そこから Cloud SQL インスタンスに接続し操作されていることも多いかと思います。

しかし、この方法で開発をすると、踏み台端末で使い慣れたツールを使えないため、作業ミスをしたり作業効率が悪かったりします。
本記事では、ローカル端末で Cloud SQL, AlloyDB のプライベート IP インスタンスを操作する方法を紹介します。

システムの設計

この問題を Cloud Identity-Aware Proxy for TCP の SSH 接続である SSH ポートフォワーディングによって解決します。
SSH ポートフォワーディングでローカルの作業端末から Compute Engine 上の Auth Proxy プロセスにトンネルを作成し、
このトンネルを使って作業端末から Cloud SQL, AlloyDB インスタンスに接続します。

Cloud Identity-Aware Proxy for TCP の SSH 接続で Cloud SQL インスタンスに接続するシステムの構成は以下のようになります。

構成図

Auth Proxy により Cloud SQL に接続する Compute Engine の種類に指定はありませんが、
Auth Proxy をインストールし、接続するプロセスを起動する必要があります。
本記事では接続のみを実施し、簡単に接続する環境を紹介するため、Compute Engine に Container Optimized OS で実現する方法を紹介します。

また、本記事では Cloud SQL, AlloyDB と VPC ネットワークの接続は、
Private Service Access により接続し、プライベート IP での接続を実現します。
コンテナイメージの取得のための外部アクセスについても、Cloud NAT を使いインターネットへの外部アクセスを許可して取得ようにします。

以下では、Cloud SQL インスタンスへのローカル端末からの接続方法を具体的に紹介します。

VPC ネットワークの構築

ここでは、以下のネットワークリソースを作成します。

  • VPC Network
  • Firewall Policy
  • Cloud NAT
  • Private Service Access

Identity-Aware Proxy を使うため、IAP サーバーからのアクセスを許可するファイアーウォールを作成する必要があります。

上記のそれぞれのリソースを作成する Terraform コードは、以下の通りです。

  • VPCネットワークの作成
resource "google_compute_network" "main" {
  name = "private-instance"

  project                 = var.project
  auto_create_subnetworks = false
}

resource "google_compute_subnetwork" "main" {
  name = "private-instance-tkyo"

  project       = var.project
  region        = "asia-northeast1"
  network       = google_compute_network.main.id
  ip_cidr_range = "192.168.0.0/24"
}
  • Firewall Policy の作成
data "google_netblock_ip_ranges" "iap" {
  range_type = "iap-forwarders"
}

resource "google_compute_network_firewall_policy" "main" {
  name = "secure"

  project = var.project
}

resource "google_compute_network_firewall_policy_association" "main" {
  name = "secure"

  project           = var.project
  attachment_target = google_compute_network.main.id
  firewall_policy   = google_compute_network_firewall_policy.main.name
}

resource "google_compute_network_firewall_policy_rule" "iap" {
  firewall_policy = google_compute_network_firewall_policy.main.name

  rule_name = "1000-in-ok-iap-ssh"
  project   = var.project
  action    = "allow"
  direction = "INGRESS"
  priority  = 1000
  target_service_accounts = [
    google_service_account.main.email
  ]

  match {
    src_ip_ranges = data.google_netblock_ip_ranges.iap.cidr_blocks_ipv4

    layer4_configs {
      ip_protocol = "tcp"
      ports       = ["22"]
    }
  }
}
  • Private Service Access の作成
resource "google_compute_global_address" "main" {
  name = "psa-cloudsql"

  project       = var.project
  purpose       = "VPC_PEERING"
  address_type  = "INTERNAL"
  prefix_length = "20"
  address       = "172.16.0.0"
  network       = google_compute_network.main.id
}

resource "google_service_networking_connection" "main" {
  network                 = google_compute_network.main.id
  service                 = "servicenetworking.googleapis.com"
  reserved_peering_ranges = [google_compute_global_address.main.name]
  deletion_policy         = "ABANDON"
}
  • NAT の作成
resource "google_compute_router" "main" {
  name = "private-sqlinstance"

  project = var.project
  region  = "asia-northeast1"
  network = google_compute_network.main.id
}

resource "google_compute_router_nat" "main" {
  name = "private-sqlinstance"

  project                            = var.project
  router                             = google_compute_router.main.name
  region                             = "asia-northeast1"
  nat_ip_allocate_option             = "MANUAL_ONLY"
  source_subnetwork_ip_ranges_to_nat = "ALL_SUBNETWORKS_ALL_IP_RANGES"
  nat_ips                            = [google_compute_address.main.self_link]
}

resource "google_compute_address" "main" {
  name = "nat-private-sqlinstance"

  project = var.project
  region  = "asia-northeast1"
}

Cloud SQL, AlloyDB インスタンスの作成

Cloud SQL のプライベート IP インスタンスの作成は以下の通りです。

resource "google_sql_database_instance" "main" {
  name = "private-ip-instance"

  project             = var.project
  database_version    = "POSTGRES_15"
  region              = "asia-northeast1"
  deletion_protection = false
  settings {
    tier = "db-f1-micro"
    ip_configuration {
      ipv4_enabled                                  = false
      private_network                               = google_compute_network.main.self_link
      enable_private_path_for_google_cloud_services = true
    }
  }

  depends_on = [
    google_service_networking_connection.main
  ]
}

Compute Engine の作成

Auth Proxy を起動する Compute Engine は、Container Optimized OS (COS) 上に Auth Proxy のコンテナを作成して Cloud SQL インスタンスに接続させます。
本記事では、COS のメタデータ user-datacloud-init を定義し Auth Proxy のコンテナを起動します。

Auth Proxy のコンテナを起動する cloud-init の定義は以下の通りです。

#cloud-config

write_files:
  - path: /etc/systemd/system/accept-tcp${sql_instance.port}-firewall.service
    permissions: 0644
    owner: root
    content: |
      [Unit]
      Description=Accept tcp ${sql_instance.port}

      [Service]
      Type=oneshot
      RemainAfterExit=true
      ExecStart=/sbin/iptables -A INPUT -p tcp --dport ${sql_instance.port} -j ACCEPT

  - path: /etc/systemd/system/authproxy.service
    permissions: 0644
    owner: root
    content: |
      [Unit]
      Description=Start AuthProxy container
      After=docker.service accept-tcp${sql_instance.port}-firewall.service
      Wants=docker.service accept-tcp${sql_instance.port}-firewall.service

      [Service]
      ExecStop=/usr/bin/docker stop authproxy.service
      ExecStart=/usr/bin/docker run --rm --network host -p ${sql_instance.port}:${sql_instance.port} --name authproxy gcr.io/cloud-sql-connectors/cloud-sql-proxy --private-ip  ${sql_instance.uri}

runcmd:
  - systemctl daemon-reload
  - systemctl start authproxy.service

上記のファイルを cloud-init.yaml として保存し、Terraform から呼び出します。

resource "google_compute_instance" "main" {
  name = <GCENAME>

  project      = var.project
  zone         = "asia-northeast1-b"
  machine_type = "e2-micro"

  boot_disk {
    auto_delete = true
    initialize_params {
      image = "cos-cloud/cos-stable"
    }
  }

  network_interface {
    subnetwork = google_compute_subnetwork.main.id
  }

  service_account {
    email  = google_service_account.main.email
    scopes = ["cloud-platform"]
  }

  metadata = {
    enable-oslogin = true
    user-data = templatefile("cloud-init.yaml", {
      sql_instance = {
        port = 5432
        uri  = google_sql_database_instance.main.connection_name
      }
    })
  }
}

resource "google_service_account" "main" {
  account_id = "gce-proxy"

  project = var.project
}

resource "google_project_iam_member" "main" {
  project = var.project

  role   = "roles/cloudsql.client
  member = google_service_account.main.member
}

作業端末からの Cloud SQL インスタンスへの接続

最後に Cloud Identity-Aware Proxy for TCP の SSH 接続による Cloud SQL インスタンスに接続してみます。
Cloud Identity-Aware Proxy for TCP の SSH 接続による SSH のポートフォワーディングは、以下のコマンドでトンネルを作成します。

gcloud compute ssh --tunnel-through-iap <GCENAME> -- -N -L 5432:localhost:5432 &

最後に作業端末から psql コマンドで接続してみます。

❯ psql -h localhost -p 5432 -U postgres
Password for user postgres:
psql (14.13 (Homebrew), server 15.8)
WARNING: psql major version 14, server major version 15.
         Some psql features might not work.
Type "help" for help.

postgres=> 

さいごに

Cloud SQL のプライベート IP インスタンスに作業端末からアクセスする方法について、紹介しました。
AlloyDB も同じように COS 上に gcr.io/alloydb-connectors/alloydb-auth-proxy のイメージを使い Auth Proxy コンテナを作成し SSH ポートフォワーディングを通じてアクセスさせることができます。
また、Datastream で連携させるのも同じように COS インスタンスをプロキシさせて同期させることができます。

Discussion