はじめに
クラウド環境でのデータベース管理において、セキュリティは日々進化する脅威への対応が求められる重要課題です。特に、以下の課題が顕在化しています:
- 従来の認証情報(パスワード)の漏洩リスク
- 認証情報のローテーション管理の煩雑さ
- アプリケーション展開時の認証情報受け渡しの安全性確保
これらの課題に対し、IAM認証を活用することで、より強固でかつ運用負荷の少ないセキュリティ体制を実現できます。IAM認証のメリットは以下の通りです:
- 一時的な認証トークンの利用による漏洩リスクの低減
- クラウドプロバイダーの統合認証基盤との連携による管理の一元化
- きめ細かなアクセス制御とアクセスログの監査対応
本ガイドでは、Cloud FunctionsからCloudSQL(PostgreSQL)へIAM認証で接続する実践的な手順を解説します。この設定により、セキュアかつスケーラブルなデータベースアクセス環境を構築できます。
Cloud Function から CloudSQL(PostgreSQL) へ IAM 認証で接続するために必要な手順
- Cloud Function 用 Service Account に必要な権限を付与
- PGUSER の作成(IAM認証用)
- データベースへの権限付与
- Cloud Function の実装
- Service Account での Cloud Function のデプロイ
具体的な設定手順
1. Cloud Function 用 Service Account に必要な権限を付与
以下の権限が必要:
- Cloud SQL インスタンス ユーザー
- Cloud SQL クライアント
今回はコンソールから設定したが、以下のコマンドで設定も可能:
gcloud projects add-iam-policy-binding example-project \
--member="serviceAccount:example-function@example-project.iam.gserviceaccount.com" \
--role="roles/cloudsql.instanceUser"
gcloud projects add-iam-policy-binding example-project \
--member="serviceAccount:example-function@example-project.iam.gserviceaccount.com" \
--role="roles/cloudsql.client"
2. PGUSER の作成
IAM認証用のユーザーを作成する。
gcloud sql users create example-function@example-project.iam \ --instance=example-project-fdc \ --type=cloud_iam_service_account
PostgreSQLの場合、以下の点に注意が必要:
- ユーザー名から
.gserviceaccount.com
を除いた形で作成 - タイプは
cloud_iam_service_account
を指定
- ユーザー名から
MySQL の場合は
@project-id.iam.gserviceaccount.com
を除いた形で作成する模様Postgres: For an IAM user account, this is the user’s email address. For a service account, it is the service account’s email without the .gserviceaccount.com domain suffix.
MySQL: For an IAM user account, this is the user’s email address, without the @ or domain name. For example, for test-user@gmail.com , set the user field to test-user. For a service account, this is the service account’s email address without the @project-id.iam.gserviceaccount.com suffix.
3. データベース権限の付与
作成したユーザーに必要な権限を付与:
grant ALL on ALL TABLES IN SCHEMA "public" to "example-function@example-project.iam";
別途
cloud-sql-proxy
を使用して、CloudSQLへポートフォワードしてCLIや、手持ちのクライアントソフト(DBeaverなど)から接続できる# ポートフォワードを実行 cloud-sql-proxy $(instance_name) # CLIから接続する場合 gcloud sql connect $(instance) --user=$(psql_user) --database=$(database)
4. Cloud Function の実装
Go の場合、だいたい以下のような実装になる
import ( "context" "database/sql" "fmt" "log" "net" "os" "cloud.google.com/go/cloudsqlconn" "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/stdlib" ) func connectWithConnector() (*sql.DB, error) { mustGetenv := func(k string) string { v := os.Getenv(k) if v == "" { log.Fatalf("Fatal Error in connect_connector.go: %s environment variable not set.\n", k) } return v } // Note: Saving credentials in environment variables is convenient, but not // secure - consider a more secure solution such as // Cloud Secret Manager (https://cloud.google.com/secret-manager) to help // keep passwords and other secrets safe. var ( dbUser = mustGetenv("DB_USER") // e.g. 'my-db-user' dbName = mustGetenv("DB_NAME") // e.g. 'my-database' dbSslMode = mustGetenv("DB_SSL_MODE") // e.g. 'disable' dbInstanceName = mustGetenv("DB_INSTANCE") // e.g. 'instance' project = mustGetenv("PROJECT") // e.g. 'project' region = mustGetenv("REGION") // e.g. 'region' usePrivate = os.Getenv("PRIVATE_IP") ) dsn := fmt.Sprintf("user=%s dbname=%s sslmode=%s", dbUser, dbName, dbSslMode) config, err := pgx.ParseConfig(dsn) if err != nil { return nil, err } var opts []cloudsqlconn.Option if usePrivate != "" { opts = append(opts, cloudsqlconn.WithDefaultDialOptions(cloudsqlconn.WithPrivateIP())) } d, err := cloudsqlconn.NewDialer(context.Background(), opts...) if err != nil { return nil, err } // Use the Cloud SQL connector to handle connecting to the instance. // This approach does *NOT* require the Cloud SQL proxy. config.DialFunc = func(ctx context.Context, network, instance string) (net.Conn, error) { // return d.Dial(ctx, instanceConnectionName) return d.Dial(ctx, fmt.Sprintf("%s:%s:%s", project, region, dbInstanceName)) } dbURI := stdlib.RegisterConnConfig(config) dbPool, err := sql.Open("pgx", dbURI) if err != nil { return nil, fmt.Errorf("sql.Open: %w", err) } if err := dbPool.Ping(); err != nil { return nil, fmt.Errorf("dbPool.Ping: %w", err) } return dbPool, nil }
5. Cloud Function のデプロイ
以下の点に注意してデプロイを行う:
- Gen2 ランタイムを使用
- Service Account を指定
--run-service-account
または--service-account
で指定--run-service-account
は Gen2 用- 確認した環境ではどちらでも動作した
- 最終的には
--run-service-account
で指定した
- 最終的には
- 必要なメモリやランタイムを設定
gcloud functions deploy example-function-name \ --gen2 \ --trigger-http \ --runtime=go122 \ --memory=256MB \ --allow-unauthenticated \ --source=/path/to/source \ --env-vars-file=./path/to/env.yaml \ --run-service-account=example-function@example-project.iam.gserviceaccount.com \ --entry-point=ExampleFunctionName \ --region=your-region
まとめ
PGUSERの作成時に cloud_iam_service_account
を指定する必要があるが、これをしていなかったので、原因の切り分けに時間がかかった。
IAM認証を使用した CloudSQL への接続設定は、セキュリティを強化しつつ、管理のしやすい方式を提供する。適切な権限設定と手順の遵守で、安全なデータベース接続環境を構築できる。