使用 IAM 数据库身份验证登录

本页面介绍了用户和服务账号如何使用 Cloud SQL IAM 数据库身份验证登录 Cloud SQL 数据库。如需了解详情,请参阅 IAM 身份验证

准备工作

使用自动 IAM 数据库身份验证登录

您可以将 Cloud SQL 连接器配置为代表用户或应用自动处理向 Cloud SQL 实例进行身份验证的操作。连接器包括 Cloud SQL Auth 代理、Go 连接器、Java 连接器和 Python 连接器,所有这些连接器均支持自动 IAM 数据库身份验证。将 Cloud SQL 连接器与自动 IAM 数据库身份验证搭配使用时,用于启动连接器的 IAM 账号必须是对数据库进行身份验证的同一账号。

如需使用自动 IAM 数据库身份验证登录,请执行以下操作:

Cloud SQL Auth 代理

  1. 向 Google Cloud 进行身份验证。

    User

    使用应用默认凭据 (ADC) 向 Google Cloud 进行身份验证。

    使用 gcloud auth application-default login 命令。如需了解详情,请参阅设置应用默认凭据

    服务账号

    如需使用服务账号通过 ADC 向 Google Cloud 进行身份验证,您可以使用服务账号模拟或使用服务账号密钥。如需使用服务账号模拟,请替换 SERVICE_ACCOUNT_EMAIL_ADDRESS 并运行以下命令:

    gcloud auth application-default login --impersonate-service-account SERVICE_ACCOUNT_EMAIL_ADDRESS
    

    如需了解详情,请参阅设置应用默认凭据

  2. 使用 --auto-iam-authn 标志启动 Cloud SQL Auth 代理。

    请替换以下内容:

    ./cloud-sql-proxy --auto-iam-authn INSTANCE_CONNECTION_NAME
    

    如需详细了解如何启动代理,请参阅启动 Cloud SQL Auth 代理

  3. 当您准备好使用 Cloud SQL Auth 代理连接到实例时,请使用 psql 客户端登录。

    替换以下内容:

    • HOSTNAME:Cloud SQL Auth 代理使用的 IP 地址。默认情况下,Cloud SQL Auth 代理使用 127.0.0.1 的 localhost 地址,但您可以在启动 Cloud SQL Auth 代理时分配其他 IP 地址。
    • USERNAME:对于 IAM,用户名是用户的完整电子邮件地址。对于服务账号,这是服务账号的电子邮件地址(不带 .gserviceaccount.com 域名后缀)。
    • PORT_NUMBER:可选。如果您在实例连接字符串中指定了其他端口,请指定该端口号。
    • DATABASE_NAME:要连接的数据库名称。

    运行以下命令:

    psql -h HOSTNAME \
     -U USERNAME \
     --port PORT_NUMBER \
     --dbname=DATABASE_NAME
     

    如需详细了解如何连接到 Cloud SQL Auth 代理,请参阅使用 psql 客户端连接

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 connectWithConnectorIAMAuthN() (*sql.DB, error) {
	mustGetenv := func(k string) string {
		v := os.Getenv(k)
		if v == "" {
			log.Fatalf("Warning: %s environment variable not set.", 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 secrets safe.
	var (
		dbUser                 = mustGetenv("DB_IAM_USER")              // e.g. 'service-account-name@project-id.iam'
		dbName                 = mustGetenv("DB_NAME")                  // e.g. 'my-database'
		instanceConnectionName = mustGetenv("INSTANCE_CONNECTION_NAME") // e.g. 'project:region:instance'
		usePrivate             = os.Getenv("PRIVATE_IP")
	)

	d, err := cloudsqlconn.NewDialer(context.Background(), cloudsqlconn.WithIAMAuthN())
	if err != nil {
		return nil, fmt.Errorf("cloudsqlconn.NewDialer: %w", err)
	}
	var opts []cloudsqlconn.DialOption
	if usePrivate != "" {
		opts = append(opts, cloudsqlconn.WithPrivateIP())
	}

	dsn := fmt.Sprintf("user=%s database=%s", dbUser, dbName)
	config, err := pgx.ParseConfig(dsn)
	if err != nil {
		return nil, err
	}

	config.DialFunc = func(ctx context.Context, network, instance string) (net.Conn, error) {
		return d.Dial(ctx, instanceConnectionName, opts...)
	}
	dbURI := stdlib.RegisterConnConfig(config)
	dbPool, err := sql.Open("pgx", dbURI)
	if err != nil {
		return nil, fmt.Errorf("sql.Open: %w", err)
	}
	return dbPool, nil
}

Java JDBC

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;
import javax.sql.DataSource;

public class ConnectorIamAuthnConnectionPoolFactory extends ConnectionPoolFactory {

  // 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 secrets safe.
  private static final String INSTANCE_CONNECTION_NAME =
      System.getenv("INSTANCE_CONNECTION_NAME");
  private static final String DB_IAM_USER = System.getenv("DB_IAM_USER");
  private static final String DB_NAME = System.getenv("DB_NAME");

  public static DataSource createConnectionPool() {
    // The configuration object specifies behaviors for the connection pool.
    HikariConfig config = new HikariConfig();

    // The following URL is equivalent to setting the config options below:
    // jdbc:postgresql:///<DB_NAME>?cloudSqlInstance=<INSTANCE_CONNECTION_NAME>&
    // socketFactory=com.google.cloud.sql.postgres.SocketFactory&user=<DB_IAM_USER>&
    // password=password
    // See the link below for more info on building a JDBC URL for the Cloud SQL JDBC Socket Factory
    // https://github.com/GoogleCloudPlatform/cloud-sql-jdbc-socket-factory#creating-the-jdbc-url

    // Configure which instance and what database to connect with.
    config.setJdbcUrl(String.format("jdbc:postgresql:///%s", DB_NAME));

    config.addDataSourceProperty("socketFactory", "com.google.cloud.sql.postgres.SocketFactory");
    config.addDataSourceProperty("cloudSqlInstance", INSTANCE_CONNECTION_NAME);

    // If connecting using automatic database authentication, follow the instructions for
    // connecting using the connector, but set the DB_IAM_USER value to an IAM user or
    // service account that has been given access to the database.
    // See https://cloud.google.com/sql/docs/postgres/iam-logins for more details.
    config.addDataSourceProperty("enableIamAuth", "true");
    config.addDataSourceProperty("user", DB_IAM_USER);
    // Password must be set to a nonempty value to bypass driver validation errors.
    config.addDataSourceProperty("password", "password");
    // Explicitly set sslmode to disable to prevent driver from hanging.
    // The Java Connector will handle SSL so it is unneccesary to enable it at the driver level.
    config.addDataSourceProperty("sslmode", "disable");

    // ... Specify additional connection properties here.
    // ...

    // Initialize the connection pool using the configuration object.
    return new HikariDataSource(config);
  }
}

Java R2DBC

private static final String CONNECTION_NAME = System.getenv("POSTGRES_IAM_CONNECTION_NAME");
private static final String DB_NAME = System.getenv("POSTGRES_DB");
private static final String DB_USER = System.getenv("POSTGRES_IAM_USER");
  // Set up ConnectionFactoryOptions
  ConnectionFactoryOptions options =
      ConnectionFactoryOptions.builder()
          .option(DRIVER, "gcp")
          .option(PROTOCOL, "postgresql")
          // Password must be set to a nonempty value to bypass driver validation errors
          .option(PASSWORD, "password")
          .option(USER, DB_USER)
          .option(DATABASE, DB_NAME)
          .option(HOST, CONNECTION_NAME)
          .option(ENABLE_IAM_AUTH, true)
          .build();

  // Initialize connection pool
  ConnectionFactory connectionFactory = ConnectionFactories.get(options);
  ConnectionPoolConfiguration configuration =
      ConnectionPoolConfiguration.builder(connectionFactory).build();

  this.connectionPool = new ConnectionPool(configuration);

Python

import os

from google.cloud.sql.connector import Connector, IPTypes
import pg8000

import sqlalchemy

def connect_with_connector_auto_iam_authn() -> sqlalchemy.engine.base.Engine:
    """
    Initializes a connection pool for a Cloud SQL instance of Postgres.

    Uses the Cloud SQL Python Connector with Automatic IAM Database Authentication.
    """
    # 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 secrets safe.
    instance_connection_name = os.environ[
        "INSTANCE_CONNECTION_NAME"
    ]  # e.g. 'project:region:instance'
    db_iam_user = os.environ["DB_IAM_USER"]  # e.g. 'sa-name@project-id.iam'
    db_name = os.environ["DB_NAME"]  # e.g. 'my-database'

    ip_type = IPTypes.PRIVATE if os.environ.get("PRIVATE_IP") else IPTypes.PUBLIC

    # initialize Cloud SQL Python Connector object
    connector = Connector()

    def getconn() -> pg8000.dbapi.Connection:
        conn: pg8000.dbapi.Connection = connector.connect(
            instance_connection_name,
            "pg8000",
            user=db_iam_user,
            db=db_name,
            enable_iam_auth=True,
            ip_type=ip_type,
        )
        return conn

    # The Cloud SQL Python Connector can be used with SQLAlchemy
    # using the 'creator' argument to 'create_engine'
    pool = sqlalchemy.create_engine(
        "postgresql+pg8000://",
        creator=getconn,
        # ...
    )
    return pool

使用手动 IAM 数据库身份验证登录

用户或应用可以这样向数据库进行身份验证:使用 IAM,向 Google Cloud 手动请求访问令牌并将其提供给数据库。使用 gcloud CLI,您可以明确请求一个具有 Cloud SQL Admin API 范围的 OAuth 2.0 令牌,用于登录数据库。当您使用手动 IAM 数据库身份验证以数据库用户身份登录时,将您的电子邮件地址用作用户名,并将访问令牌用作密码。您可以将此方法与数据库的直接连接或 Cloud SQL 连接器搭配使用。

在此过程中,您将向 Google Cloud 进行身份验证,请求访问令牌,然后通过将该令牌作为 IAM 数据库用户的密码传入来连接到数据库。请按照以下步骤在不使用 Cloud SQL Auth 代理的情况下进行连接。

对于这些步骤,您必须执行以下操作:

  • 如果您要连接到具有公共 IP 的实例,请授予对实例的外部访问权限。如需了解详情,请参阅授权您机器的 IP 地址用于公共 IP 地址
  • 如果要连接到具有专用 IP 的实例,请在 Virtual Private Cloud (VPC) 网络中运行该命令。
  • 使用 gcloud sql generate-login-token 命令生成身份验证令牌。
  • 如需使用手动 IAM 数据库身份验证登录,请执行以下操作:

    gcloud

    1. 向 Google Cloud 进行身份验证。

      User

      使用 gcloud auth login 向 IAM 进行身份验证。如需了解详情,请参阅使用用户账号进行授权

      服务账号

      使用 gcloud auth activate-service-account 向 IAM 进行身份验证。如需了解详情,请参阅使用服务账号进行授权

    2. 请求访问令牌并使用客户端登录。

      替换以下内容:

      • HOSTNAME:实例的 IP 地址,即公共 IP 地址或专用 IP 地址。
      • USERNAME:对于 IAM,用户名是用户的完整电子邮件地址。对于服务账号,这是服务账号的电子邮件地址(不带 .gserviceaccount.com 域名后缀)。
      • DATABASE_NAME:要连接的数据库名称。

       PGPASSWORD=`gcloud sql generate-login-token` \
       psql "sslmode=require \
       hostaddr=HOSTNAME \
       user=USERNAME \
       dbname=DATABASE_NAME" \
       --no-password
       

      如果 Cloud SQL 实例上的 ssl_mode 配置为 TRUSTED_CLIENT_CERTIFICATE_REQUIRED,我们建议您使用自动 IAM 数据库身份验证登录,以强制执行客户端身份验证。

    后续步骤