mirror of
https://github.com/optim-enterprises-bv/vault.git
synced 2025-11-02 03:27:54 +00:00
Add support for IAM Auth for Google CloudSQL DBs (#22445)
This commit is contained in:
@@ -14,11 +14,19 @@ import (
|
||||
|
||||
"github.com/hashicorp/errwrap"
|
||||
"github.com/hashicorp/go-secure-stdlib/parseutil"
|
||||
"github.com/hashicorp/go-uuid"
|
||||
"github.com/hashicorp/vault/sdk/database/dbplugin"
|
||||
"github.com/hashicorp/vault/sdk/database/helper/dbutil"
|
||||
"github.com/mitchellh/mapstructure"
|
||||
)
|
||||
|
||||
const (
|
||||
AuthTypeGCPIAM = "gcp_iam"
|
||||
|
||||
dbTypePostgres = "pgx"
|
||||
cloudSQLPostgres = "cloudsql-postgres"
|
||||
)
|
||||
|
||||
var _ ConnectionProducer = &SQLConnectionProducer{}
|
||||
|
||||
// SQLConnectionProducer implements ConnectionProducer and provides a generic producer for most sql databases
|
||||
@@ -29,8 +37,15 @@ type SQLConnectionProducer struct {
|
||||
MaxConnectionLifetimeRaw interface{} `json:"max_connection_lifetime" mapstructure:"max_connection_lifetime" structs:"max_connection_lifetime"`
|
||||
Username string `json:"username" mapstructure:"username" structs:"username"`
|
||||
Password string `json:"password" mapstructure:"password" structs:"password"`
|
||||
AuthType string `json:"auth_type" mapstructure:"auth_type" structs:"auth_type"`
|
||||
ServiceAccountJSON string `json:"service_account_json" mapstructure:"service_account_json" structs:"service_account_json"`
|
||||
DisableEscaping bool `json:"disable_escaping" mapstructure:"disable_escaping" structs:"disable_escaping"`
|
||||
|
||||
// cloud options here - cloudDriverName is globally unique, but only needs to be retained for the lifetime
|
||||
// of driver registration, not across plugin restarts.
|
||||
cloudDriverName string
|
||||
cloudDialerCleanup func() error
|
||||
|
||||
Type string
|
||||
RawConfig map[string]interface{}
|
||||
maxConnectionLifetime time.Duration
|
||||
@@ -107,6 +122,32 @@ func (c *SQLConnectionProducer) Init(ctx context.Context, conf map[string]interf
|
||||
return nil, errwrap.Wrapf("invalid max_connection_lifetime: {{err}}", err)
|
||||
}
|
||||
|
||||
// validate auth_type if provided
|
||||
authType := c.AuthType
|
||||
if authType != "" {
|
||||
if ok := ValidateAuthType(authType); !ok {
|
||||
return nil, fmt.Errorf("invalid auth_type %s provided", authType)
|
||||
}
|
||||
}
|
||||
|
||||
if authType == AuthTypeGCPIAM {
|
||||
c.cloudDriverName, err = uuid.GenerateUUID()
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to generate UUID for IAM configuration: %w", err)
|
||||
}
|
||||
|
||||
// for _most_ sql databases, the driver itself contains no state. In the case of google's cloudsql drivers,
|
||||
// however, the driver might store a credentials file, in which case the state stored by the driver is in
|
||||
// fact critical to the proper function of the connection. So it needs to be registered here inside the
|
||||
// ConnectionProducer init.
|
||||
dialerCleanup, err := c.registerDrivers(c.cloudDriverName, c.ServiceAccountJSON)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.cloudDialerCleanup = dialerCleanup
|
||||
}
|
||||
|
||||
// Set initialized to true at this point since all fields are set,
|
||||
// and the connection can be established at a later time.
|
||||
c.Initialized = true
|
||||
@@ -137,12 +178,24 @@ func (c *SQLConnectionProducer) Connection(ctx context.Context) (interface{}, er
|
||||
// If the ping was unsuccessful, close it and ignore errors as we'll be
|
||||
// reestablishing anyways
|
||||
c.db.Close()
|
||||
|
||||
// if IAM authentication is enabled
|
||||
// ensure open dialer is also closed
|
||||
if c.AuthType == AuthTypeGCPIAM {
|
||||
if c.cloudDialerCleanup != nil {
|
||||
c.cloudDialerCleanup()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// For mssql backend, switch to sqlserver instead
|
||||
dbType := c.Type
|
||||
if c.Type == "mssql" {
|
||||
dbType = "sqlserver"
|
||||
// default non-IAM behavior
|
||||
driverName := c.Type
|
||||
|
||||
if c.AuthType == AuthTypeGCPIAM {
|
||||
driverName = c.cloudDriverName
|
||||
} else if c.Type == "mssql" {
|
||||
// For mssql backend, switch to sqlserver instead
|
||||
driverName = "sqlserver"
|
||||
}
|
||||
|
||||
// Otherwise, attempt to make connection
|
||||
@@ -164,7 +217,7 @@ func (c *SQLConnectionProducer) Connection(ctx context.Context) (interface{}, er
|
||||
}
|
||||
|
||||
var err error
|
||||
c.db, err = sql.Open(dbType, conn)
|
||||
c.db, err = sql.Open(driverName, conn)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -192,6 +245,13 @@ func (c *SQLConnectionProducer) Close() error {
|
||||
|
||||
if c.db != nil {
|
||||
c.db.Close()
|
||||
|
||||
// cleanup IAM dialer if it exists
|
||||
if c.AuthType == AuthTypeGCPIAM {
|
||||
if c.cloudDialerCleanup != nil {
|
||||
c.cloudDialerCleanup()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.db = nil
|
||||
|
||||
Reference in New Issue
Block a user