🔓

GitHub Actionsから踏み台経由でプライベートCloud SQLに接続 (OS Login + WIF + SSHトンネル編)

に公開

GitHub Actionsから踏み台サーバー経由でプライベートCloud SQLに接続する実践ガイド (OS Login + WIF + SSHトンネル編)

CI/CDパイプライン、特にGitHub Actionsから、VPCのプライベートネットワーク内に配置されたCloud SQLデータベースへ安全かつ自動的に接続したい、というニーズは多いのではないでしょうか?この記事では、Workload Identity Federation (WIF), OS Login そして gcloud compute ssh (beta) を組み合わせた、管理しやすい接続方法を解説します。

1. はじめに:なぜこの記事を書いたのか

CI/CDパイプラインからプライベートネットワーク内のCloud SQLインスタンスに安全かつ自動で接続するには、いくつかの方法があります。

  • Cloud SQL Auth Proxy: 定番ですが、サービスアカウントキーの扱いが少し煩雑になることがあります。※本当はこれでやりたかったけど、すでに踏み台GCEが存在したので不採用です
  • 踏み台サーバー + IP承認: 踏み台サーバーに固定IPを割り当て、Cloud SQLの承認済みネットワークに登録する方法は、IPアドレスの管理が必要です。
  • 踏み台サーバー + SSHキー配置: サーバーにSSHキーを直接置くのはセキュリティリスクが伴います。

この記事では、これらの課題を解決するため、Workload Identity Federation (WIF)OS LoginSSH Agent、そして gcloud compute ssh を組み合わせた方法を紹介します。

この記事で紹介する方法のメリット

  • IPアドレス不要: Cloud SQLの承認済みネットワークにCI/CDランナーのIPを登録する必要がありません。
  • サービスアカウントキー不要: WIFにより、有効期間の短い一時的な認証情報を利用するため、サービスアカウントキーファイルを管理・漏洩するリスクがありません。
  • SSHキーの安全な管理: SSH秘密鍵はGitHub Secretsで管理し、SSH Agent経由で利用するため、サーバー上に鍵を置く必要がありません。OS Loginプロファイルに追加する公開鍵もTTL(有効期間)を設定できます。
  • IAMによる一元管理: VMへのSSHアクセス権限も、他のGCPリソースと同じIAMで管理できます。

ターゲット読者

  • GitHub Actionsを利用している開発者
  • Google Cloud (Compute Engine, Cloud SQL, IAM) を利用している開発者
  • プライベートなDBへCI/CDから安全に接続したい方
  • WIF, OS Login, SSHトンネリングに興味がある、または利用する必要がある方

2. 前提条件と準備

  • 知識: GitHub Actionsの基本、GCPの基本 (IAM, Compute Engine, Cloud SQL, VPC), SSHの基本。Terraformの基本知識があるとスムーズです。
  • ツール: gcloud CLI, terraform CLI (本記事ではTerraformを使用), ssh-keygen
  • GCPリソース (準備済み):
    • VPCネットワーク
    • プライベートIPを持つCloud SQLインスタンス
    • 踏み台サーバー (Compute Engine VM):
      • OS Loginが有効化されていること (enable-oslogin=TRUE メタデータ)
      • 外部IPアドレスは不要
  • GitHub: 対象のリポジトリ。

3. TerraformによるGCP設定:認証と権限の基盤を作る

まず、GitHub ActionsがGCPと安全に連携し、必要な操作を行えるように、Terraformを使って認証連携 (WIF) とIAM権限を設定します。

variable.tf

variable "project_id" {
  description = "GCPプロジェクトID"
  type        = string
}

variable "github_repository" {
  description = "GitHubリポジトリ名 (例: 'owner/repo')"
  type        = string
}

variable "workload_identity_pool_id" {
  description = "Workload Identity PoolのID"
  type        = string
  default     = "github-actions-pool" # 環境ごとに分けることを推奨 (例: pool-dev)
}

variable "workload_identity_provider_id" {
  description = "Workload Identity ProviderのID"
  type        = string
  default     = "github-actions-provider" # 環境ごとに分けることを推奨 (例: provider-dev)
}

variable "service_account_id" {
  description = "GitHub Actionsが使用するサービスアカウントのID"
  type        = string
  default     = "github-actions-sql-access" # 環境ごとに分けることを推奨
}

main.tf

# GitHub ActionsのWorkload Identity Federation設定

# Workload Identity Poolとプロバイダーの作成
resource "google_iam_workload_identity_pool" "github_actions" {
  project                     = var.project_id # project属性を追加
  workload_identity_pool_id = var.workload_identity_pool_id
  display_name                = "GitHub Actions Pool"
  description                 = "Identity pool for GitHub Actions"
}

resource "google_iam_workload_identity_pool_provider" "github_actions" {
  project                            = var.project_id # project属性を追加
  workload_identity_pool_id          = google_iam_workload_identity_pool.github_actions.workload_identity_pool_id
  workload_identity_pool_provider_id = var.workload_identity_provider_id
  display_name                       = "GitHub Actions Provider"

  attribute_mapping = {
    "google.subject"       = "assertion.sub"
    "attribute.actor"      = "assertion.actor"
    "attribute.repository" = "assertion.repository"
    "attribute.aud"        = "assertion.aud"
  }

  attribute_condition = "assertion.repository == '${var.github_repository}'"

  oidc {
    issuer_uri = "https://token.actions.githubusercontent.com"
  }
}

# Cloud SQLアクセス用のサービスアカウント
resource "google_service_account" "github_actions_cloud_sql" {
  project      = var.project_id # project属性を追加
  account_id   = "github-actions-cloud-sql"
  display_name = "GitHub Actions Cloud SQL"
  description  = "Service account for GitHub Actions to access Cloud SQL"
}

# IAM設定 - GitHubリポジトリとサービスアカウントの関連付け
resource "google_service_account_iam_binding" "github_actions_cloud_sql_binding" {
  service_account_id = google_service_account.github_actions_cloud_sql.name
  role               = "roles/iam.workloadIdentityUser"

  members = [
    "principalSet://iam.googleapis.com/${google_iam_workload_identity_pool.github_actions.name}/attribute.repository/${var.github_repository}"
  ]
}

# Cloud SQL関連のIAM権限
resource "google_project_iam_member" "github_actions_cloud_sql_client" {
  project = var.project_id
  role    = "roles/cloudsql.client"
  member  = "serviceAccount:${google_service_account.github_actions_cloud_sql.email}"
}

resource "google_project_iam_member" "github_actions_cloud_sql_instance_user" {
  project = var.project_id
  role    = "roles/cloudsql.instanceUser"
  member  = "serviceAccount:${google_service_account.github_actions_cloud_sql.email}"
}

# 機密情報アクセス権限
resource "google_project_iam_member" "github_actions_secretmanager_accessor" {
  project = var.project_id
  role    = "roles/secretmanager.secretAccessor"
  member  = "serviceAccount:${google_service_account.github_actions_cloud_sql.email}"
}

# Compute Engine関連のIAM権限
resource "google_project_iam_member" "github_actions_compute_viewer" {
  project = var.project_id
  role    = "roles/compute.viewer"
  member  = "serviceAccount:${google_service_account.github_actions_cloud_sql.email}"
}

resource "google_project_iam_member" "github_actions_iap_tunnel_user" {
  project = var.project_id
  role    = "roles/iap.tunnelResourceAccessor"
  member  = "serviceAccount:${google_service_account.github_actions_cloud_sql.email}"
}

resource "google_project_iam_member" "github_actions_compute_os_login" {
  project = var.project_id
  role    = "roles/compute.osLogin"
  member  = "serviceAccount:${google_service_account.github_actions_cloud_sql.email}"
}

resource "google_project_iam_member" "github_actions_service_account_user" {
  project = var.project_id
  role    = "roles/iam.serviceAccountUser"
  member  = "serviceAccount:${google_service_account.github_actions_cloud_sql.email}"
}


output "workload_identity_provider" {
  description = "Workload Identity Provider ID for GitHub Actions"
  value       = google_iam_workload_identity_pool_provider.github_actions.name
}

output "service_account_email" {
  description = "Service account email for GitHub Actions to access Cloud SQL"
  value       = google_service_account.github_actions_cloud_sql.email
}


Terraformコード解説:

上記のTerraformコードでは、主に以下のリソースを作成・設定しています。

  • Workload Identity Federation (WIF)設定: GitHub ActionsのOIDCトークンをGCPが信頼し、特定のGitHubリポジトリからの要求のみを受け付けるように設定します。これにより、サービスアカウントキーを使わずにGitHub ActionsからGoogle Cloudへの認証が可能になります。
  • サービスアカウント作成: GitHub ActionsがGoogle Cloud内で活動するための「身分」となる専用のサービスアカウントを作成します。
  • IAM権限設定:
    • roles/iam.workloadIdentityUser: WIF経由で上記サービスアカウントの権限を借用(impersonate)することを許可します。
    • roles/compute.osLogin: 踏み台サーバーVMにOS Loginを利用してSSH接続するために必要な権限です。
    • roles/iam.serviceAccountUser: サービスアカウント自身が、自身のOS Loginプロファイル(SSH公開鍵など)を管理するために必要となります。
    • roles/iap.tunnelResourceAccessor: 外部IPアドレスを持たない踏み台サーバーVMに gcloud compute ssh を使って接続する場合、内部的にIdentity-Aware Proxy (IAP) を経由したトンネリングが利用されるため、この権限が必要になります。
    • その他、必要に応じてCloud SQL ClientロールやSecret Managerアクセスロールなどをサービスアカウントに付与します。

Terraformの実行後、outputs としてWorkload Identity Providerのリソース名とサービスアカウントのメールアドレスが出力されるようにしておくと、後のGitHub Actionsワークフロー設定で参照しやすくなります。

4. GitHub Actions ワークフロー:CI/CDパイプラインの構築

Terraformで設定した認証情報と権限を使って、実際にGitHub ActionsからSSHトンネルを作成し、Cloud SQLに接続するワークフローを構築します。

# .github/workflows/check-prisma-schema-via-ssh.yml
# このワークフローは、Pull Request時にジャンプサーバー経由で
# develop/main 環境のデータベースに接続し、Prismaスキーマの差分をチェックします。
# SSH接続に gcloud compute ssh コマンドを使用し、OS Login と SSH Agent を活用します。

name: Check Prisma Schema Drift via SSH (gcloud ssh)

on:
  pull_request:
    branches: # PRのターゲットブランチ
      - develop
      - main

jobs:
  # Develop 環境用ジョブ
  check_dev_schema:
    runs-on: ubuntu-latest
    if: github.base_ref == 'develop' # PRのターゲットが develop なら実行
    environment: develop # GitHub Environment 名
    permissions:
      contents: "read" # actions/checkout に必要
      id-token: "write" # GCP 認証 (WIF) に必要
    timeout-minutes: 15

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Install pnpm
        uses: pnpm/action-setup@v4
        with:
          version: 8.15.5 # プロジェクトに合わせてください
          run_install: false

      - name: Set up Node.js using package.json
        uses: actions/setup-node@v4
        with:
          node-version-file: package.json # プロジェクトに合わせてください
          cache: "pnpm"
          cache-dependency-path: pnpm-lock.yaml

      - name: Install dependencies
        run: pnpm install # Prisma CLI も含めてインストール

      # GCP 認証 (WIF を使用) - Service Account として認証
      # 以降の gcloud コマンドはこの Service Account として実行されます。
      # プロジェクトIDは自動で環境変数 GOOGLE_CLOUD_PROJECT に設定されます。
      - id: "auth"
        name: "Authenticate to Google Cloud"
        uses: "google-github-actions/auth@v2"
        with:
          workload_identity_provider: ${{ vars.WORKLOAD_IDENTITY_PROVIDER }} # Variablesから読み込み
          service_account: ${{ vars.SERVICE_ACCOUNT }} # Variablesから読み込み

      - name: Set up Cloud SDK
        uses: google-github-actions/setup-gcloud@v2

      # --- SSH 接続設定 ---

      # SSH 秘密鍵をエージェントにロード
      # gcloud compute ssh がこの鍵を認証に利用できるようにします。
      # Secrets.SSH_PRIVATE_KEY にはパスフレーズなしの鍵の内容が必要です。
      - name: Setup SSH private key
        uses: webfactory/ssh-agent@v0.9.0
        with:
          ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}

      - name: Check Authentication and Environment
        run: |
          echo "--- Checking Environment Variables ---"
          env | grep CLOUDSDK_ # 関連する環境変数を確認
          env | grep GOOGLE_  # 関連する環境変数を確認

          echo "--- Checking gcloud config ---"
          gcloud config list # gcloud が認識している設定を表示

      - name: Add SSH public key for Service Account OS Login
        env:
          # SA_EMAIL: ${{ steps.auth.outputs.service_account_email }} # このコマンド自体では直接使わない
          PROJECT_ID: ${{ env.GOOGLE_CLOUD_PROJECT }} # 環境変数から取得
        run: |
          # SSH Agent にロードされた鍵から公開鍵の内容を取得
          PUBLIC_KEY=$(ssh-add -L | head -n 1)
          if [ -z "$PUBLIC_KEY" ]; then
            echo "Error: Could not get public key from SSH agent. Is the SSH_PRIVATE_KEY secret correct and passphrase-less?"
            exit 1
          fi

          # 現在認証されているアカウント(authステップで認証したSAのはず)に対してキーを追加
          echo "Adding public key for the authenticated account..."
          echo "Public Key: $PUBLIC_KEY" # デバッグ用

          # gcloud compute os-login ssh-keys add コマンドを修正
          # --service-account と --zone を削除
          gcloud compute os-login ssh-keys add \
              --project="$PROJECT_ID" \
              --key="$PUBLIC_KEY" \
              --ttl=1h # 鍵の有効期限を設定

          echo "Public key added (or updated)."

      # gcloud beta コマンドに必要なコンポーネントをインストール
      - name: Install gcloud beta component
        run: gcloud components install beta --quiet

      # ジャンプサーバー経由でデータベースへのSSHトンネルを作成
      # gcloud compute ssh コマンドを使用します。
      - name: Create SSH Tunnel to Database (using gcloud ssh)
        env:
          JUMP_SERVER_NAME: ${{ vars.JUMP_SERVER_NAME }}
          JUMP_SERVER_ZONE: ${{ vars.JUMP_SERVER_ZONE }}
          PROJECT_ID: ${{ env.GOOGLE_CLOUD_PROJECT }}
          DB_INTERNAL_IP: ${{ vars.DB_INTERNAL_IP }}
          DB_PORT: ${{ vars.DB_PORT }}
          SSH_LOCAL_PORT: ${{ vars.SSH_LOCAL_PORT }}
        run: |
          echo "Creating tunnel from 127.0.0.1:$SSH_LOCAL_PORT to $DB_INTERNAL_IP:$DB_PORT via ${JUMP_SERVER_NAME}..."
          # gcloud compute ssh は自動的に認証済みの Service Account と OS Login を使用します。
          # SSH Agent にロードされた鍵が認証に利用されます。
          # --ssh-flag="-L ..." でポートフォワーディングを設定
          # -N : リモートコマンドを実行しない (トンネルのみ)
          # -f : バックグラウンドで実行 (CI環境ではログ管理に注意)
          # --verbosity=debug : OS Login や SSH 接続の詳細ログを出力 (デバッグに有用) 一旦削除
          gcloud beta compute ssh "$JUMP_SERVER_NAME" \
              --project="$PROJECT_ID" \
              --zone="$JUMP_SERVER_ZONE" \
              --ssh-flag="-L 127.0.0.1:$SSH_LOCAL_PORT:$DB_INTERNAL_IP:$DB_PORT" \
              --ssh-flag="-N" \
              --ssh-flag="-f" \
              --quiet 

          # gcloud ssh がバックグラウンドで起動するまで少し待つ
          # CI環境ではバックグラウンドプロセスの信頼性が課題になることもあります。
          sleep 5

      # トンネルが確立されるまで待機
      # ローカルポートが開くかを nc コマンドで確認します。
      - name: Wait for SSH Tunnel
        env:
          SSH_LOCAL_PORT: ${{ vars.SSH_LOCAL_PORT }}
        run: |
          echo "Waiting for SSH tunnel on 127.0.0.1:$SSH_LOCAL_PORT (30 seconds)..."
          # timeout コマンドで最大待機時間を設定し、nc でポートが開くかチェック
          timeout 30 bash -c 'while ! nc -z 127.0.0.1 $SSH_LOCAL_PORT > /dev/null 2>&1; do sleep 1; done'

          if [ $? -ne 0 ]; then
            echo "Error: SSH tunnel on 127.0.0.1:$SSH_LOCAL_PORT failed to open within 30 seconds."
            # デバッグのため、バックグラウンドプロセスの状態確認などをここに追加しても良い
            # pgrep -fla "gcloud compute ssh" || true
            exit 1
          fi
          echo "SSH tunnel is established."

      # --- SSH 接続設定ここまで ---

      # === 接続テストステップ ===
      # (SSHトンネル経由でローカルポートに接続)
      - name: Install PostgreSQL client
        run: |
          sudo apt-get update -y
          sudo apt-get install -y postgresql-client
          echo "psql installed."

      # ローカルの転送ポートへのTCP接続をテスト (オプション)
      - name: Test TCP connection using nc
        env:
          SSH_LOCAL_PORT: ${{ vars.SSH_LOCAL_PORT }}
        run: |
          echo "Attempting basic TCP connection test using netcat..."
          nc -zv 127.0.0.1 ${{ env.SSH_LOCAL_PORT }} || true # 失敗しても致命的ではないため続行可能にする場合

      # psql でデータベース接続をテスト
      - name: Test DB connection using psql
        env:
          # DATABASE_URL を SSH トンネルのローカルポートを指すように変更
          DATABASE_URL: "postgresql://${{ vars.DB_USER }}:${{ secrets.DB_PASSWORD }}@127.0.0.1:${{ vars.SSH_LOCAL_PORT }}/${{ vars.DB_NAME }}?sslmode=disable"
          PGCONNECT_TIMEOUT: 30 # psql の接続タイムアウト
        run: |
          echo "Attempting to connect using psql via SSH tunnel..."
          # 接続し、すぐに終了するコマンド。失敗するとここでワークフローが停止。
          psql "$DATABASE_URL" -c "\q"
          echo "psql connection test finished successfully."

      # === 接続テストステップここまで ===

      # Prisma でデータベーススキーマを取得
      - name: Run prisma db pull
        env:
          # 上記 psql ステップと同じ DATABASE_URL を使用
          DATABASE_URL: "postgresql://${{ vars.DB_USER }}:${{ secrets.DB_PASSWORD }}@127.0.0.1:${{ vars.SSH_LOCAL_PORT }}/${{ vars.DB_NAME }}"
        run: |
          echo "Running prisma db pull via SSH tunnel..."
          # package.json に 'prisma:pull': 'prisma db pull' が定義されている想定
          pnpm run prisma:pull

      # schema.prisma の変更をチェック
      - name: Check for schema changes
        run: |
          echo "Checking for differences in prisma/schema.prisma..."
          # git diff --exit-code は差分があれば非ゼロ終了コードを返す
          git diff --exit-code prisma/schema.prisma
          if [ $? -ne 0 ]; then
            echo "Error: Schema drift detected! prisma/schema.prisma differs from the database schema."
            echo "Please run 'pnpm run prisma:pull' locally to update your schema.prisma and commit the changes."
            exit 1 # 差分があればワークフローを失敗させる
          fi
          echo "No schema drift detected."

ワークフロー解説:

上記YAMLコードは、以下の主要なステップで構成されています。

  1. GCP認証 (WIF):
    • ワークフローの permissionsid-token: write を設定し、OIDCトークンの発行を許可します。
    • google-github-actions/auth アクションを使用し、Terraformで作成したWorkload Identity Providerとサービスアカウントの情報を指定してGCPに認証します。これらの情報はGitHubのVariablesやSecretsに格納しておくのが一般的です。
  2. SSH秘密鍵のエージェントへのロード:
    • ssh-keygen コマンドで事前にパスフレーズなしのSSHキーペアを作成し、その秘密鍵をGitHubのSecrets (SSH_PRIVATE_KEY など) に登録しておきます。
    • webfactory/ssh-agent アクションを使い、この秘密鍵をSSHエージェントにロードします。これにより、後続のSSHコマンドがこの鍵を自動的に利用できるようになります。
  3. OS Login 公開鍵の動的追加:
    • SSHエージェントにロードされた鍵に対応する公開鍵を ssh-add -L コマンドで取得します。
    • gcloud compute os-login ssh-keys add コマンドを使い、認証中のサービスアカウントのOS Loginプロファイルに、取得した公開鍵を一時的に追加します。--ttl=1h のように有効期限を設定することで、一定時間後に自動的に鍵が無効になるため、セキュリティが向上します。このステップが、踏み台サーバーにOS Login経由でSSH接続するための鍵登録処理となります。
  4. gcloud beta コンポーネントのインストール:
    • Workload Identity Federation経由で gcloud compute ssh を利用する場合、現状ではベータ版のコマンド (gcloud beta compute ssh) を使う必要があるため、事前に gcloud components install beta --quiet コマンドで beta コンポーネントをインストールしておきます。
  5. SSHトンネルの作成 (バックグラウンド実行):
    • gcloud beta compute ssh コマンドを使用し、踏み台サーバーへのSSH接続を確立します。
    • --ssh-flag="-L <ローカルポート>:<Cloud SQLプライベートIP>:<Cloud SQLポート>" オプションで、ローカルマシンの指定ポートへのアクセスを、踏み台サーバー経由でCloud SQLのIPとポートに転送するように設定します。
    • --ssh-flag="-N" オプションで、リモートコマンドを実行せずにポートフォワーディングのみを行うようにします。
    • --ssh-flag="-f" オプションで、SSH接続プロセスをバックグラウンドで実行します。これにより、ワークフローの次のステップに進むことができます。
    • --quiet オプションで、SSH接続時のホストキー確認などのインタラクティブなプロンプトを抑制します。
  6. トンネル確立待機:
    • バックグラウンドで起動したSSHトンネルが実際に通信可能になるまで、nc -z localhost <ローカルポート> のようなコマンドとループ処理、timeout コマンドを組み合わせて一定時間待ち合わせます。
  7. DB操作実行:
    • SSHトンネルが確立されたら、GitHub Actions Runner上の localhost と指定したローカルポートに対して、psql やアプリケーション(例: Prisma ORM)からCloud SQLデータベースに接続します。データベース接続文字列のホスト名を 127.0.0.1 または localhost に、ポートをフォワーディングで指定したローカルポートに変更するのがポイントです。

5. トラブルシューティング:ハマりどころと解決策

この構成は複数の要素が絡むため、問題が発生することもあります。主な原因と対処法をまとめます。

  • 認証エラー (google-github-actions/auth で失敗):
    • ワークフローの permissions ブロックに id-token: write が正しく設定されているか確認してください。
    • google-github-actions/auth アクションに渡している workload_identity_providerservice_account の値が、Terraformで作成したものと完全に一致しているか確認してください(GitHub Variables/Secretsの設定ミスに注意)。
    • Workload Identity Federation Providerの attribute_condition (リポジトリ名など) が、ワークフローを実行しているリポジトリと一致しているか確認してください。
  • gcloud コマンド失敗 (認証後だが gcloud auth list でアカウントが表示されないなど):
    • google-github-actions/auth の後に google-github-actions/setup-gcloud を明示的に使用していない場合、gcloud の設定が期待通りに行われないことがあります。setup-gcloud を追加するか、auth アクションのログで環境変数が正しくエクスポートされているか確認してください。
  • OS Loginキー追加失敗:
    • 認証に使用しているサービスアカウントに roles/compute.osLogin および roles/iam.serviceAccountUser のIAMロールが正しく付与されているか確認してください。
    • 踏み台サーバーとなるCompute EngineインスタンスでOS Loginが有効化されているか(メタデータ enable-osloginTRUE になっているか)確認してください。
  • SSHトンネル作成失敗 (gcloud beta compute ssh でエラー):
    • gcloud components install beta --quiet ステップが正常に完了し、beta コンポーネントがインストールされているか確認してください。
    • 外部IPアドレスを持たない踏み台サーバーに接続する場合、サービスアカウントに roles/iap.tunnelResourceAccessor のIAMロールが付与されているか確認してください。
    • サービスアカウントに roles/compute.osLogin が付与されているか、OS Loginキーが正しく追加されているか再確認してください。
    • 踏み台サーバーの名前、ゾーンが正しいか確認してください。
  • トンネル確立後の接続失敗:
    • Wait for SSH Tunnel ステップが成功しているか、sleep の時間が短すぎないか確認してください。
    • データベース接続文字列のホスト名が 127.0.0.1 または localhost に、ポートがSSHトンネル作成時に指定したローカルポート (SSH_LOCAL_PORT) に正しく設定されているか確認してください。
    • 踏み台サーバーからCloud SQLインスタンスのプライベートIPとポートへのVPC内ファイアウォールルールが適切に設定されているか確認してください(通常、同一VPC内またはVPCピアリングが正しければ接続可能です)。
    • データベースのユーザー名、パスワード、データベース名が正しいか確認してください。

デバッグTips:

  • gcloud beta compute ssh コマンドに一時的に -v--verbosity=debug フラグを追加すると、SSH接続の試行に関する詳細なログが出力されます(ただし、非常に多くのログが出力されるため注意が必要です)。
  • ローカル環境での再現: 複雑な問題を切り分けるには、GitHub Actionsワークフローで使うサービスアカウントのキーファイルとSSH秘密鍵をローカル環境に用意し、各 gcloud コマンドを手動で実行してみるのが最も効果的です。(詳細は[ローカルでの動作確認手順 - (ここに別記事へのリンクを挿入)]を参照してください。)

6. まとめと考察

本記事では、GitHub ActionsからWorkload Identity Federation, OS Login, SSH Agent, そして gcloud beta compute ssh を活用し、踏み台サーバー経由でプライベートネットワーク内のCloud SQLに安全に接続する方法を解説しました。

この構成により、従来の方法で課題となりやすかったサービスアカウントキーの管理や固定IPアドレスの管理から解放され、IAMによる一元的な権限管理と、よりセキュアなCI/CDパイプラインを実現できます。

設定項目は多岐にわたりますが、一度この仕組みを構築すれば、セキュリティを維持しつつ、効率的にプライベートなデータベースリソースへCI/CDパイプラインからアクセスできるようになります。

あと、ローカルで実際に借用されたservice accountで認証して同じような実行を行う方法を別記事で備忘として残しておこうと思います。🤔

7. 参考資料


Discussion