Files
threshold/internal/cmd/commands/connect/postgres.go
T
Jeff MitchellandJohan Brandhorst-Satzkorn fff5f9506f Migrate proxy logic to the API package (#4216)
This allows arbitrary programs to have an easy mechanism to make
connections through Boundary using Go. Internal cluster tests and test
helpers have been migrated to use this package, as well as the `connect`
command.

---------

Co-authored-by: Johan Brandhorst-Satzkorn <johan.brandhorst@gmail.com>
2024-01-21 22:34:33 -05:00

115 lines
3.5 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package connect
import (
"fmt"
"os"
"strings"
"github.com/hashicorp/boundary/api/proxy"
"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/posener/complete"
)
const (
postgresSynopsis = "Authorize a session against a target and invoke a Postgres client to connect"
)
func postgresOptions(c *Command, set *base.FlagSets) {
f := set.NewFlagSet("Postgres Options")
f.StringVar(&base.StringVar{
Name: "style",
Target: &c.flagPostgresStyle,
EnvVar: "BOUNDARY_CONNECT_POSTGRES_STYLE",
Completion: complete.PredictSet("psql"),
Default: "psql",
Usage: `Specifies how the CLI will attempt to invoke a Postgres client. This will also set a suitable default for -exec if a value was not specified. Currently-understood values are "psql".`,
})
f.StringVar(&base.StringVar{
Name: "username",
Target: &c.flagUsername,
EnvVar: "BOUNDARY_CONNECT_USERNAME",
Completion: complete.PredictNothing,
Usage: `Specifies the username to pass through to the client. May be overridden by credentials sourced from a credential store.`,
})
f.StringVar(&base.StringVar{
Name: "dbname",
Target: &c.flagDbname,
EnvVar: "BOUNDARY_CONNECT_DBNAME",
Completion: complete.PredictNothing,
Usage: `Specifies the database name to pass through to the client.`,
})
}
type postgresFlags struct {
flagPostgresStyle string
}
func (p *postgresFlags) defaultExec() string {
return strings.ToLower(p.flagPostgresStyle)
}
func (p *postgresFlags) buildArgs(c *Command, port, ip, _ string, creds proxy.Credentials) (args, envs []string, retCreds proxy.Credentials, retErr error) {
var username, password string
retCreds = creds
if len(retCreds.UsernamePassword) > 0 {
// Mark credential as consumed so it is not printed to user
retCreds.UsernamePassword[0].Consumed = true
// For now just grab the first username password credential brokered
username = retCreds.UsernamePassword[0].Username
password = retCreds.UsernamePassword[0].Password
}
switch p.flagPostgresStyle {
case "psql":
if port != "" {
args = append(args, "-p", port)
}
args = append(args, "-h", ip)
if c.flagDbname != "" {
args = append(args, "-d", c.flagDbname)
}
switch {
case username != "":
args = append(args, "-U", username)
case c.flagUsername != "":
args = append(args, "-U", c.flagUsername)
}
if password != "" {
passfile, err := os.CreateTemp("", "*")
if err != nil {
return nil, nil, proxy.Credentials{}, fmt.Errorf("Error saving postgres password to tmp file: %w", err)
}
c.cleanupFuncs = append(c.cleanupFuncs, func() error {
if err := os.Remove(passfile.Name()); err != nil {
return fmt.Errorf("Error removing temporary password file; consider removing %s manually: %w", passfile.Name(), err)
}
return nil
})
_, err = passfile.WriteString(fmt.Sprintf("*:*:*:*:%s", password))
if err != nil {
return nil, nil, proxy.Credentials{}, fmt.Errorf("Error writing password file to %s: %w", passfile.Name(), err)
}
if err := passfile.Close(); err != nil {
return nil, nil, proxy.Credentials{}, fmt.Errorf("Error closing password file after writing to %s: %w", passfile.Name(), err)
}
envs = append(envs, fmt.Sprintf("PGPASSFILE=%s", passfile.Name()))
if c.flagDbname == "" {
c.UI.Warn("Credentials are being brokered but no -dbname parameter provided. psql may misinterpret another parameter as the database name.")
}
}
}
return
}