Cloud SQL に Cloud Run から SSL 接続する
Google Cloud の Security Command Center (SCC) で、Cloud SQL に関する脆弱性 SSL not enforced が検知されました。
当初はプライベートネットワーク構成(Private IPのみ)だったので許容していましたが、アラート対応として SSL 接続を強制する設定に変更しました。
対応には Cloud SQL Node.js Connector を使用しました。
今後の備忘録として手順を残します。

前提: インフラ構成
インフラは Terraform で管理しており、以下のようなプライベート構成をとっていました。
-
Cloud SQLの完全なプライベート化
- パブリックIPなし
- VPCピアリング経由のみアクセス可能
-
Cloud Runの保護
- 内部ロードバランサー経由のみアクセス可能
- Serverless VPC Access Connector 経由でプライベートネットワークに接続
-
Bastionへのアクセス制御
- IAP (Identity-Aware Proxy) 経由でのみSSH接続可能
- プライベートIPでCloud SQLに接続
データフロー図
┌────────────────────────────────────────────────────────────┐
│ VPC Network │
│ │
│ ┌───────────────────────────────────────────────────────┐ │
│ │ Subnetwork: │ │
│ │ │ │
│ │ ┌──────────────┐ ┌──────────────────────┐ │ │
│ │ │ Bastion │ │ Cloud SQL Proxy │ │ │
│ │ │ Tag: iap │─────────> Port: 5432 │ │ │
│ │ └──────────────┘ └──────────┬───────────┘ │ │
│ │ │ │ │
│ └──────────────────────────────────────┼────────────────┘ │
│ │ │
│ ┌────────────────────────────────────┐ │ │
│ │ VPC Connector: │ │ │
│ │ (for Cloud Run) │ │ │
│ │ ┌───────────────┐ │ │ │
│ │ │ Cloud Run │─────────────────│─│ │
│ │ └───────────────┘ │ │ │
│ └────────────────────────────────────┘ │ │
│ │ │
│ ┌──────────────────────────────────────▼────────────────┐ │
│ │ VPC Peering (Service Networking) │ │
│ │ Private IP Range: /24 │ │
│ │ │ │
│ │ ┌────────────────────────────────────────────────┐ │ │
│ │ │ Cloud SQL (POSTGRES_15) │ │ │
│ │ │ - プライベートIPのみ (ipv4_enabled: false) │ │ │
│ │ └────────────────────────────────────────────────┘ │ │
│ └───────────────────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────┘
対応方針
今回修正が必要な点は以下の2点です。
- Cloud SQL の設定で、SSL モードを「SSL 接続のみを許可する」に変更する。
- アプリケーション(Cloud Run)からの接続を SSL に対応させる。
接続方式の検討
SSL 接続に対応させるにあたり、コンテナのサイドカーとして Cloud SQL Auth Proxy を動かす方法もありますが、管理などが面倒になりそうなため避けたいです。
代わりに、Cloud SQL Node.js Connector を採用しました。
これを利用することで、サイドカー等の追加なしで容易に IAM 認証および SSL 接続が可能になります。
前提条件:
- Cloud Run のサービスアカウントに
roles/cloudsql.clientが付与されていること。- Cloud SQL Admin API が有効化されていること。
今回の環境では上記権限周りは設定済みでした。
1. Terraform の修正
Cloud SQL Connector を利用するには「接続名」が必要です。
これを環境変数として Cloud Run に渡すよう修正します。
DB接続名を output に追加
output "sql_connection" {
description = "The SQL connection"
value = google_sql_database_instance.instance.connection_name
}
Cloud Run の環境変数に追加
module "api" {
source = "../../modules/cloud_run"
# ...
env_vars = {
"DB_CONNECTION_NAME" = module.cloud_sql.sql_connection
}
# ...
}
SSLモードを「ENCRYPTED_ONLY」に変更
モジュール側で ssl_mode を受け取れるようにし、呼び出し元で指定します。
resource "google_sql_database_instance" "instance" {
# ...
ip_configuration {
ipv4_enabled = var.ipv4_enabled
private_network = var.private_network
ssl_mode = var.ssl_mode # <- 追加
}
# ...
}
module "cloud_sql" {
source = "../../modules/cloud_sql"
# ...
ssl_mode = "ENCRYPTED_ONLY" # ENCRYPTED_ONLYを指定
# ...
}
これでインフラ側の変更は完了です。
2. アプリケーションコードの修正 (NestJS)
API は NestJS + node-postgres で構築しており、ORM は使用していません。
まず、コネクタをインストールします。
npm install @google-cloud/cloud-sql-connector
DB接続モジュールを書き換えます。
変更前のコード:
import { Global, Module } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Pool } from 'pg';
@Global()
@Module({
exports: ['PG_POOL'],
providers: [
{
inject: [ConfigService],
provide: 'PG_POOL',
useFactory: async (configService: ConfigService) => {
const pool = new Pool({
database: configService.get('DB_NAME'),
host: configService.get('DB_HOST'),
password: configService.get('DB_SECRET'),
port: Number(configService.get('DB_PORT')),
user: configService.get('DB_USER'),
});
return pool;
},
},
],
})
export class DbModule {}
変更後のコード:
import { Connector, IpAddressTypes } from '@google-cloud/cloud-sql-connector';
import { Global, Module, OnApplicationShutdown } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { Pool } from 'pg';
let pool: Pool | null = null;
let connector: Connector | null = null;
@Global()
@Module({
exports: ['PG_POOL'],
providers: [
{
inject: [ConfigService],
provide: 'PG_POOL',
useFactory: async (configService: ConfigService) => {
connector = new Connector();
const clientOpts = await connector.getOptions({
instanceConnectionName: configService.get('DB_CONNECTION_NAME'),
ipType: IpAddressTypes.PRIVATE,
});
pool = new Pool({
...clientOpts,
user: configService.get('DB_USER'),
password: configService.get('DB_SECRET'),
database: configService.get('DB_NAME'),
});
return pool;
},
},
],
})
export class DbModule implements OnApplicationShutdown {
async onApplicationShutdown() {
await pool?.end();
connector?.close();
}
}
結果
Cloud Run に新しいコンテナをデプロイしたところ、無事に API アクセスが成功しました。
また、SCC で検知されていた SSL not enforced のアラートも解消されました。

Discussion