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

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

准备工作

  • 将实例配置为使用 IAM 数据库身份验证。如需了解详情,请参阅配置新实例以使用 IAM 数据库身份验证
  • 将 IAM 用户、服务账号或群组添加到数据库。如需了解详情,请参阅将 IAM 用户或服务账号添加到数据库将群组添加到数据库
  • roles/cloudsql.instanceUser IAM 角色添加到您的 IAM 用户、服务账号或群组。这是一个预定义角色,其中包含必要的 Cloud SQL IAM cloudsql.instances.login 权限。您需要此权限才能使用 IAM 数据库身份验证登录数据库实例。如需了解详情,请参阅角色和权限
  • 默认情况下,将 IAM 用户添加到数据库时,新数据库用户无权访问任何数据库。您需要使用 GRANT 命令向 IAM 数据库用户授予任何必要的权限。如需了解详情,请参阅向 IAM 用户授予数据库权限向群组授予数据库权限

  • 如果您使用的是 IAM 群组身份验证,则 IAM 用户或服务账号必须是已获授予 IAM 角色或权限以登录 Cloud SQL 实例的群组的成员。Cloud SQL 会在用户或服务账号首次登录实例后创建一个账号。

使用自动 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 代理。将 INSTANCE_CONNECTION_NAME 替换为用于标识 Cloud SQL 实例的连接字符串。如果您使用的是默认 MySQL 端口以外的端口,请指定端口号。如需详细了解如何找到并构建此字符串,请参阅用于对 Cloud SQL Auth 代理进行身份验证的选项

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

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

  3. 当您准备好使用 Cloud SQL Auth 代理连接到实例时,请使用 mysql 客户端登录。替换以下内容:

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

    运行以下命令:

    mysql --host=HOSTNAME \
    --user=USERNAME \
    --port=PORT_NUMBER
    

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

Go

import (
	"context"
	"database/sql"
	"fmt"
	"log"
	"net"
	"os"

	"cloud.google.com/go/cloudsqlconn"
	"github.com/go-sql-driver/mysql"
)

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'
		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())
	}
	mysql.RegisterDialContext("cloudsqlconn",
		func(ctx context.Context, addr string) (net.Conn, error) {
			return d.Dial(ctx, instanceConnectionName, opts...)
		})

	dbURI := fmt.Sprintf("%s:empty@cloudsqlconn(localhost:3306)/%s?parseTime=true",
		dbUser, dbName)

	dbPool, err := sql.Open("mysql", 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 INSTANCE_UNIX_SOCKET = System.getenv("INSTANCE_UNIX_SOCKET");
  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:mysql:///<DB_NAME>?cloudSqlInstance=<INSTANCE_CONNECTION_NAME>&
    // socketFactory=com.google.cloud.sql.mysql.SocketFactory&user=<DB_USER>&password=<DB_PASS>
    // 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 user to connect with.
    config.setJdbcUrl(String.format("jdbc:mysql:///%s", DB_NAME));

    config.addDataSourceProperty("socketFactory", "com.google.cloud.sql.mysql.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);
    // 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

// Set up ConnectionFactoryOptions
ConnectionFactoryOptions options =
    ConnectionFactoryOptions.builder()
        .option(DRIVER, "gcp")
        .option(PROTOCOL, "mysql")
        .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 pymysql

import sqlalchemy


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

    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. 'service-account-name'
    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() -> pymysql.connections.Connection:
        conn: pymysql.connections.Connection = connector.connect(
            instance_connection_name,
            "pymysql",
            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(
        "mysql+pymysql://",
        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 代理的情况下进行连接。

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

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

gcloud

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

    User

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

    服务账号

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

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

    替换以下内容:

    • HOSTNAME:实例的 IP 地址,即公共 IP 地址或专用 IP 地址。
    • USERNAME:对于 IAM 用户账号,这是用户的电子邮件地址,不带 @ 符号或域名。例如,对于 test-user@example.com,输入 test-user。对于服务账号,这是服务账号的电子邮件地址(不带 @project-id.iam.gserviceaccount.com 后缀)。

     MYSQL_PWD=`gcloud sql generate-login-token` \
     mysql --enable-cleartext-plugin \
     --ssl-mode=REQUIRED \
     --host=HOSTNAME \
     --user=USERNAME
     

    如果 Cloud SQL 实例上的 ssl_mode 配置为 TRUSTED_CLIENT_CERTIFICATE_REQUIRED,请在您登录时添加客户端证书和客户端密钥。此外,如需让客户端验证服务器的身份以实现双向身份验证,请指定服务器证书 server-ca.pem。例如:

     MYSQL_PWD=`gcloud sql generate-login-token` \
     mysql --enable-cleartext-plugin \
     --ssl-mode=VERIFY_CA \
     --ssl-ca=server-ca.pem \
     --ssl-cert=client-cert.pem \
     --ssl-key=client-key.pem \
     --host=HOSTNAME \
     --user=USERNAME
     

    如需了解如何创建客户端证书和密钥,请参阅客户端证书

后续步骤