Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ The Cloud SQL Auth proxy has support for:
- [HTTP Healthchecks][health-check-example]
- Service account impersonation
- Separate Dialer functionality released as the [Cloud SQL Go Connector][go connector]
- Configuration with environment variables
- Fully POSIX-compliant flags

If you're using Go, Java, or Python, consider using the corresponding Cloud SQL
Expand Down
138 changes: 109 additions & 29 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ import (
"github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/internal/log"
"github.com/GoogleCloudPlatform/cloud-sql-proxy/v2/internal/proxy"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
"github.com/spf13/viper"
"go.opencensus.io/trace"
)

Expand Down Expand Up @@ -207,10 +209,66 @@ Service Account Impersonation
SERVICE_ACCOUNT_3 which impersonates SERVICE_ACCOUNT_2 which then
impersonates the target SERVICE_ACCOUNT_1.

Configuration using environment variables

Instead of using CLI flags, the proxy may be configured using environment
variables. Each environment variable uses "CSQL_PROXY" as a prefix and is
the uppercase version of the flag using underscores as word delimiters. For
example, the --auto-iam-authn flag may be set with the environment variable
CSQL_PROXY_AUTO_IAM_AUTHN. An invocation of the proxy using environment
variables would look like the following:

CSQL_PROXY_AUTO_IAM_AUTHN=true \
./cloud-sql-proxy my-project:us-central1:my-db-server

In addition to CLI flags, instance connection names may also be specified
with environment variables. If invoking the proxy with only one instance
connection name, use CSQL_PROXY_INSTANCE_CONNECTION_NAME. For example:

CSQL_PROXY_INSTANCE_CONNECTION_NAME=my-project:us-central1:my-db-server \
./cloud-sql-proxy

If multiple instance connection names are used, add the index of the
instance connection name as a suffix. For example:

CSQL_PROXY_INSTANCE_CONNECTION_NAME_0=my-project:us-central1:my-db-server \
CSQL_PROXY_INSTANCE_CONNECTION_NAME_1=my-other-project:us-central1:my-other-server \
./cloud-sql-proxy

(*) indicates a flag that may be used as a query parameter

`

const envPrefix = "CSQL_PROXY"

func instanceFromEnv(args []string) []string {
// This supports naming the first instance first with:
// INSTANCE_CONNECTION_NAME
// or if that's not defined, with:
// INSTANCE_CONNECTION_NAME_0
inst := os.Getenv(fmt.Sprintf("%s_INSTANCE_CONNECTION_NAME", envPrefix))
if inst == "" {
inst = os.Getenv(fmt.Sprintf("%s_INSTANCE_CONNECTION_NAME_0", envPrefix))
if inst == "" {
return nil
}
}
args = append(args, inst)

i := 1
for {
instN := os.Getenv(fmt.Sprintf("%s_INSTANCE_CONNECTION_NAME_%d", envPrefix, i))
// if the next instance connection name is not defined, stop checking
// environment variables.
if instN == "" {
break
}
args = append(args, instN)
i++
}
return args
}

// NewCommand returns a Command object representing an invocation of the proxy.
func NewCommand(opts ...Option) *Command {
cmd := &cobra.Command{
Expand All @@ -234,6 +292,10 @@ func NewCommand(opts ...Option) *Command {
}

cmd.Args = func(cmd *cobra.Command, args []string) error {
// If args is not already populated, try to read from the environment.
if len(args) == 0 {
args = instanceFromEnv(args)
}
// Handle logger separately from config
if c.conf.StructuredLogs {
c.logger, c.cleanup = log.NewStructuredLogger()
Expand All @@ -254,74 +316,92 @@ func NewCommand(opts ...Option) *Command {

cmd.RunE = func(*cobra.Command, []string) error { return runSignalWrapper(c) }

pflags := cmd.PersistentFlags()

// Override Cobra's default messages.
cmd.PersistentFlags().BoolP("help", "h", false, "Display help information for cloud-sql-proxy")
cmd.PersistentFlags().BoolP("version", "v", false, "Print the cloud-sql-proxy version")
pflags.BoolP("help", "h", false, "Display help information for cloud-sql-proxy")
pflags.BoolP("version", "v", false, "Print the cloud-sql-proxy version")

// Global-only flags
cmd.PersistentFlags().StringVarP(&c.conf.Token, "token", "t", "",
pflags.StringVarP(&c.conf.Token, "token", "t", "",
"Use bearer token as a source of IAM credentials.")
cmd.PersistentFlags().StringVarP(&c.conf.CredentialsFile, "credentials-file", "c", "",
pflags.StringVarP(&c.conf.CredentialsFile, "credentials-file", "c", "",
"Use service account key file as a source of IAM credentials.")
cmd.PersistentFlags().StringVarP(&c.conf.CredentialsJSON, "json-credentials", "j", "",
pflags.StringVarP(&c.conf.CredentialsJSON, "json-credentials", "j", "",
"Use service account key JSON as a source of IAM credentials.")
cmd.PersistentFlags().BoolVarP(&c.conf.GcloudAuth, "gcloud-auth", "g", false,
pflags.BoolVarP(&c.conf.GcloudAuth, "gcloud-auth", "g", false,
"Use gcloud's user credentials as a source of IAM credentials.")
cmd.PersistentFlags().BoolVarP(&c.conf.StructuredLogs, "structured-logs", "l", false,
pflags.BoolVarP(&c.conf.StructuredLogs, "structured-logs", "l", false,
"Enable structured logging with LogEntry format")
cmd.PersistentFlags().Uint64Var(&c.conf.MaxConnections, "max-connections", 0,
pflags.Uint64Var(&c.conf.MaxConnections, "max-connections", 0,
"Limit the number of connections. Default is no limit.")
cmd.PersistentFlags().DurationVar(&c.conf.WaitOnClose, "max-sigterm-delay", 0,
pflags.DurationVar(&c.conf.WaitOnClose, "max-sigterm-delay", 0,
"Maximum number of seconds to wait for connections to close after receiving a TERM signal.")
cmd.PersistentFlags().StringVar(&c.telemetryProject, "telemetry-project", "",
pflags.StringVar(&c.telemetryProject, "telemetry-project", "",
"Enable Cloud Monitoring and Cloud Trace with the provided project ID.")
cmd.PersistentFlags().BoolVar(&c.disableTraces, "disable-traces", false,
pflags.BoolVar(&c.disableTraces, "disable-traces", false,
"Disable Cloud Trace integration (used with --telemetry-project)")
cmd.PersistentFlags().IntVar(&c.telemetryTracingSampleRate, "telemetry-sample-rate", 10_000,
pflags.IntVar(&c.telemetryTracingSampleRate, "telemetry-sample-rate", 10_000,
"Set the Cloud Trace sample rate. A smaller number means more traces.")
cmd.PersistentFlags().BoolVar(&c.disableMetrics, "disable-metrics", false,
pflags.BoolVar(&c.disableMetrics, "disable-metrics", false,
"Disable Cloud Monitoring integration (used with --telemetry-project)")
cmd.PersistentFlags().StringVar(&c.telemetryPrefix, "telemetry-prefix", "",
pflags.StringVar(&c.telemetryPrefix, "telemetry-prefix", "",
"Prefix for Cloud Monitoring metrics.")
cmd.PersistentFlags().BoolVar(&c.prometheus, "prometheus", false,
pflags.BoolVar(&c.prometheus, "prometheus", false,
"Enable Prometheus HTTP endpoint /metrics on localhost")
cmd.PersistentFlags().StringVar(&c.prometheusNamespace, "prometheus-namespace", "",
pflags.StringVar(&c.prometheusNamespace, "prometheus-namespace", "",
"Use the provided Prometheus namespace for metrics")
cmd.PersistentFlags().StringVar(&c.httpAddress, "http-address", "localhost",
pflags.StringVar(&c.httpAddress, "http-address", "localhost",
"Address for Prometheus and health check server")
cmd.PersistentFlags().StringVar(&c.httpPort, "http-port", "9090",
pflags.StringVar(&c.httpPort, "http-port", "9090",
"Port for Prometheus and health check server")
cmd.PersistentFlags().BoolVar(&c.healthCheck, "health-check", false,
pflags.BoolVar(&c.healthCheck, "health-check", false,
"Enables health check endpoints /startup, /liveness, and /readiness on localhost.")
cmd.PersistentFlags().StringVar(&c.conf.APIEndpointURL, "sqladmin-api-endpoint", "",
pflags.StringVar(&c.conf.APIEndpointURL, "sqladmin-api-endpoint", "",
"API endpoint for all Cloud SQL Admin API requests. (default: https://sqladmin.googleapis.com)")
cmd.PersistentFlags().StringVar(&c.conf.QuotaProject, "quota-project", "",
pflags.StringVar(&c.conf.QuotaProject, "quota-project", "",
`Specifies the project to use for Cloud SQL Admin API quota tracking.
The IAM principal must have the "serviceusage.services.use" permission
for the given project. See https://cloud.google.com/service-usage/docs/overview and
https://cloud.google.com/storage/docs/requester-pays`)
cmd.PersistentFlags().StringVar(&c.conf.FUSEDir, "fuse", "",
pflags.StringVar(&c.conf.FUSEDir, "fuse", "",
"Mount a directory at the path using FUSE to access Cloud SQL instances.")
cmd.PersistentFlags().StringVar(&c.conf.FUSETempDir, "fuse-tmp-dir",
pflags.StringVar(&c.conf.FUSETempDir, "fuse-tmp-dir",
filepath.Join(os.TempDir(), "csql-tmp"),
"Temp dir for Unix sockets created with FUSE")
cmd.PersistentFlags().StringVar(&c.impersonationChain, "impersonate-service-account", "",
pflags.StringVar(&c.impersonationChain, "impersonate-service-account", "",
`Comma separated list of service accounts to impersonate. Last value
is the target account.`)
cmd.PersistentFlags().BoolVar(&c.quiet, "quiet", false, "Log error messages only")

// Global and per instance flags
cmd.PersistentFlags().StringVarP(&c.conf.Addr, "address", "a", "127.0.0.1",
pflags.StringVarP(&c.conf.Addr, "address", "a", "127.0.0.1",
"(*) Address to bind Cloud SQL instance listeners.")
cmd.PersistentFlags().IntVarP(&c.conf.Port, "port", "p", 0,
pflags.IntVarP(&c.conf.Port, "port", "p", 0,
"(*) Initial port for listeners. Subsequent listeners increment from this value.")
cmd.PersistentFlags().StringVarP(&c.conf.UnixSocket, "unix-socket", "u", "",
pflags.StringVarP(&c.conf.UnixSocket, "unix-socket", "u", "",
`(*) Enables Unix sockets for all listeners with the provided directory.`)
cmd.PersistentFlags().BoolVarP(&c.conf.IAMAuthN, "auto-iam-authn", "i", false,
pflags.BoolVarP(&c.conf.IAMAuthN, "auto-iam-authn", "i", false,
"(*) Enables Automatic IAM Authentication for all instances")
cmd.PersistentFlags().BoolVar(&c.conf.PrivateIP, "private-ip", false,
pflags.BoolVar(&c.conf.PrivateIP, "private-ip", false,
"(*) Connect to the private ip address for all instances")

v := viper.NewWithOptions(viper.EnvKeyReplacer(strings.NewReplacer("-", "_")))
v.SetEnvPrefix(envPrefix)
v.AutomaticEnv()
// Ignoring the error here since its only occurence is if one of the pflags
// is nil which is never the case here.
_ = v.BindPFlags(pflags)

pflags.VisitAll(func(f *pflag.Flag) {
// Override any unset flags with Viper values to use the pflags
// object as a single source of truth.
if !f.Changed && v.IsSet(f.Name) {
val := v.Get(f.Name)
pflags.Set(f.Name, fmt.Sprintf("%v", val))
}
})

return c
}

Expand Down
Loading