From 95404b2a5671252ff304cd7546b7e8c3c1e3ca6b Mon Sep 17 00:00:00 2001 From: Jeff Mitchell Date: Sat, 10 Oct 2020 16:12:04 -0400 Subject: [PATCH] Separate out protocol-specific functions (#610) Placing them into separate files slims down the main connect file and makes it more obvious where things need to be handled for future integrations. --- internal/cmd/commands.go | 64 ++-- .../{proxy/proxy.go => connect/connect.go} | 305 +++++------------- .../cmd/commands/{proxy => connect}/funcs.go | 2 +- internal/cmd/commands/connect/http.go | 95 ++++++ internal/cmd/commands/connect/postgres.go | 53 +++ internal/cmd/commands/connect/rdp.go | 61 ++++ internal/cmd/commands/connect/ssh.go | 58 ++++ 7 files changed, 381 insertions(+), 257 deletions(-) rename internal/cmd/commands/{proxy/proxy.go => connect/connect.go} (68%) rename internal/cmd/commands/{proxy => connect}/funcs.go (99%) create mode 100644 internal/cmd/commands/connect/http.go create mode 100644 internal/cmd/commands/connect/postgres.go create mode 100644 internal/cmd/commands/connect/rdp.go create mode 100644 internal/cmd/commands/connect/ssh.go diff --git a/internal/cmd/commands.go b/internal/cmd/commands.go index 7883f0ca2..7f2318892 100644 --- a/internal/cmd/commands.go +++ b/internal/cmd/commands.go @@ -11,13 +11,13 @@ import ( "github.com/hashicorp/boundary/internal/cmd/commands/authmethods" "github.com/hashicorp/boundary/internal/cmd/commands/authtokens" "github.com/hashicorp/boundary/internal/cmd/commands/config" + "github.com/hashicorp/boundary/internal/cmd/commands/connect" "github.com/hashicorp/boundary/internal/cmd/commands/database" "github.com/hashicorp/boundary/internal/cmd/commands/dev" "github.com/hashicorp/boundary/internal/cmd/commands/groups" "github.com/hashicorp/boundary/internal/cmd/commands/hostcatalogs" "github.com/hashicorp/boundary/internal/cmd/commands/hosts" "github.com/hashicorp/boundary/internal/cmd/commands/hostsets" - "github.com/hashicorp/boundary/internal/cmd/commands/proxy" "github.com/hashicorp/boundary/internal/cmd/commands/roles" "github.com/hashicorp/boundary/internal/cmd/commands/scopes" "github.com/hashicorp/boundary/internal/cmd/commands/server" @@ -60,37 +60,6 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { }, nil }, - "connect": func() (cli.Command, error) { - return &proxy.Command{ - Command: base.NewCommand(ui), - Func: "connect", - }, nil - }, - "connect http": func() (cli.Command, error) { - return &proxy.Command{ - Command: base.NewCommand(ui), - Func: "http", - }, nil - }, - "connect ssh": func() (cli.Command, error) { - return &proxy.Command{ - Command: base.NewCommand(ui), - Func: "ssh", - }, nil - }, - "connect rdp": func() (cli.Command, error) { - return &proxy.Command{ - Command: base.NewCommand(ui), - Func: "rdp", - }, nil - }, - "connect postgres": func() (cli.Command, error) { - return &proxy.Command{ - Command: base.NewCommand(ui), - Func: "postgres", - }, nil - }, - "authenticate": func() (cli.Command, error) { return &authenticate.Command{ Command: base.NewCommand(ui), @@ -276,6 +245,37 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) { }, nil }, + "connect": func() (cli.Command, error) { + return &connect.Command{ + Command: base.NewCommand(ui), + Func: "connect", + }, nil + }, + "connect http": func() (cli.Command, error) { + return &connect.Command{ + Command: base.NewCommand(ui), + Func: "http", + }, nil + }, + "connect ssh": func() (cli.Command, error) { + return &connect.Command{ + Command: base.NewCommand(ui), + Func: "ssh", + }, nil + }, + "connect rdp": func() (cli.Command, error) { + return &connect.Command{ + Command: base.NewCommand(ui), + Func: "rdp", + }, nil + }, + "connect postgres": func() (cli.Command, error) { + return &connect.Command{ + Command: base.NewCommand(ui), + Func: "postgres", + }, nil + }, + "database": func() (cli.Command, error) { return &database.Command{ Command: base.NewCommand(ui), diff --git a/internal/cmd/commands/proxy/proxy.go b/internal/cmd/commands/connect/connect.go similarity index 68% rename from internal/cmd/commands/proxy/proxy.go rename to internal/cmd/commands/connect/connect.go index de765ebbb..bfb01d046 100644 --- a/internal/cmd/commands/proxy/proxy.go +++ b/internal/cmd/commands/connect/connect.go @@ -1,4 +1,4 @@ -package proxy +package connect import ( "context" @@ -14,7 +14,6 @@ import ( "net/http" "os" "os/exec" - "runtime" "strconv" "strings" "sync" @@ -70,20 +69,16 @@ type Command struct { flagUsername string // HTTP - flagHttpStyle string - flagHttpHost string - flagHttpPath string - flagHttpMethod string - flagHttpScheme string - - // SSH - flagSshStyle string + httpFlags // Postgres - flagPostgresStyle string + postgresFlags // RDP - flagRdpStyle string + rdpFlags + + // SSH + sshFlags Func string @@ -106,15 +101,16 @@ func (c *Command) Synopsis() string { case "connect": return "Authorize a session against a target (or consume an existing authorization token) and launch a proxied connection" case "http": - return "Authorize a session against a target and invoke an HTTP client to connect" - case "ssh": - return "Authorize a session against a target and invoke an SSH client to connect" + return httpSynopsis case "postgres": - return "Authorize a session against a target and invoke a Postgres client to connect" + return postgresSynopsis case "rdp": - return "Authorize a session against a target and invoke an RDP client to connect" + return rdpSynopsis + case "ssh": + return sshSynopsis + default: + return "" } - return "" } func (c *Command) Help() string { @@ -128,42 +124,59 @@ func (c *Command) Help() string { " Example:", "", ` $ boundary connect -target-id ttcp_1234567890"`, + "", + "", + }) + c.Flags().Help() + + default: + return base.WrapForHelpText([]string{ + fmt.Sprintf("Usage: boundary connect %s [options] [args]", c.Func), + "", + fmt.Sprintf(` This command performs a target authorization (or consumes an existing authorization token) and launches a proxied %s connection.`, c.Func), + "", + " Example:", + "", + fmt.Sprintf(` $ boundary connect %s -target-id ttcp_1234567890"`, c.Func), + "", + "", }) + c.Flags().Help() } - return "" } func (c *Command) Flags() *base.FlagSets { - bits := base.FlagSetOutputFormat - if c.Func == "connect" { - bits = base.FlagSetHTTP | base.FlagSetClient | bits - } - set := c.FlagSet(bits) + set := c.FlagSet(base.FlagSetHTTP | base.FlagSetClient | base.FlagSetOutputFormat) + f := set.NewFlagSet("Connect Options") + + f.StringVar(&base.StringVar{ + Name: "authz-token", + Target: &c.flagAuthzToken, + EnvVar: "BOUNDARY_CONNECT_AUTHZ_TOKEN", + Completion: complete.PredictNothing, + Usage: `Only needed if -target-id is not set. The authorization string returned from the Boundary controller via an "authorize-session" action against a target. If set to "-", the command will attempt to read in the authorization string from standard input.`, + }) + + f.StringVar(&base.StringVar{ + Name: "target-id", + Target: &c.flagTargetId, + Usage: "The ID of the target to authorize against. Cannot be used with -authz-token.", + }) + + f.StringVar(&base.StringVar{ + Name: "host-id", + Target: &c.flagHostId, + Usage: "The ID of a specific host to connect to out of the hosts from the target's host sets. If not specified, one is chosen at random.", + }) + + f.StringVar(&base.StringVar{ + Name: "exec", + Target: &c.flagExec, + EnvVar: "BOUNDARY_CONNECT_EXEC", + Completion: complete.PredictAnything, + Usage: `If set, after connecting to the worker, the given binary will be executed. This should be a binary on your path, or an absolute path. If all command flags are followed by " -- " (space, two hyphens, space), then any arguments after that will be sent directly to the binary.`, + }) switch c.Func { - case "connect", "http", "ssh", "rdp", "postgres": - f := set.NewFlagSet("Connect Options") - - f.StringVar(&base.StringVar{ - Name: "authz-token", - Target: &c.flagAuthzToken, - EnvVar: "BOUNDARY_CONNECT_AUTHZ_TOKEN", - Completion: complete.PredictNothing, - Usage: `Only needed if -target-id is not set. The authorization string returned from the Boundary controller via an "authorize-session" action against a target. If set to "-", the command will attempt to read in the authorization string from standard input.`, - }) - - f.StringVar(&base.StringVar{ - Name: "target-id", - Target: &c.flagTargetId, - Usage: "The ID of the target to authorize against. Cannot be used with -authz-token.", - }) - - f.StringVar(&base.StringVar{ - Name: "host-id", - Target: &c.flagHostId, - Usage: "The ID of a specific host to connect to out of the hosts from the target's host sets. If not specified, one is chosen at random.", - }) - + case "connect": f.StringVar(&base.StringVar{ Name: "listen-addr", Target: &c.flagListenAddr, @@ -180,111 +193,17 @@ func (c *Command) Flags() *base.FlagSets { Usage: `If set, the CLI will attempt to bind its listening port to the given value. If it cannot, the command will error.`, }) - f.StringVar(&base.StringVar{ - Name: "exec", - Target: &c.flagExec, - EnvVar: "BOUNDARY_CONNECT_EXEC", - Completion: complete.PredictAnything, - Usage: `If set, after connecting to the worker, the given binary will be executed. This should be a binary on your path, or an absolute path. If all command flags are followed by " -- " (space, two hyphens, space), then any arguments after that will be sent directly to the binary.`, - }) - } - - switch c.Func { case "http": - f := set.NewFlagSet("HTTP Options") - - f.StringVar(&base.StringVar{ - Name: "style", - Target: &c.flagHttpStyle, - EnvVar: "BOUNDARY_CONNECT_HTTP_STYLE", - Completion: complete.PredictSet("curl"), - Default: "curl", - Usage: `Specifies how the CLI will attempt to invoke an HTTP client. This will also set a suitable default for -exec if a value was not specified. Currently-understood values are "curl".`, - }) - - f.StringVar(&base.StringVar{ - Name: "host", - Target: &c.flagHttpHost, - EnvVar: "BOUNDARY_CONNECT_HTTP_HOST", - Completion: complete.PredictNothing, - Usage: `Specifies the host value to use. The specified hostname will be passed through to the client (if supported) for use in the Host header and TLS SNI value.`, - }) - - f.StringVar(&base.StringVar{ - Name: "path", - Target: &c.flagHttpPath, - EnvVar: "BOUNDARY_CONNECT_HTTP_PATH", - Completion: complete.PredictNothing, - Usage: `Specifies a path that will be appended to the generated URL.`, - }) - - f.StringVar(&base.StringVar{ - Name: "method", - Target: &c.flagHttpMethod, - EnvVar: "BOUNDARY_CONNECT_HTTP_METHOD", - Completion: complete.PredictNothing, - Usage: `Specifies the method to use. If not set, will use the client's default.`, - }) - - f.StringVar(&base.StringVar{ - Name: "scheme", - Target: &c.flagHttpScheme, - Default: "https", - EnvVar: "BOUNDARY_CONNECT_HTTP_SCHEME", - Completion: complete.PredictNothing, - Usage: `Specifies the scheme to use.`, - }) - - case "ssh": - f := set.NewFlagSet("SSH Options") - - f.StringVar(&base.StringVar{ - Name: "style", - Target: &c.flagSshStyle, - EnvVar: "BOUNDARY_CONNECT_SSH_STYLE", - Completion: complete.PredictSet("ssh", "putty"), - Default: "ssh", - Usage: `Specifies how the CLI will attempt to invoke an SSH client. This will also set a suitable default for -exec if a value was not specified. Currently-understood values are "ssh" and "putty".`, - }) - - 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`, - }) + httpOptions(c, set) case "postgres": - 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`, - }) + postgresOptions(c, set) case "rdp": - f := set.NewFlagSet("RDP Options") + rdpOptions(c, set) - f.StringVar(&base.StringVar{ - Name: "style", - Target: &c.flagRdpStyle, - EnvVar: "BOUNDARY_CONNECT_RDP_STYLE", - Completion: complete.PredictSet("mstsc", "open"), - Usage: `Specifies how the CLI will attempt to invoke an RDP client. This will also set a suitable default for -exec if a value was not specified. Currently-understood values are "mstsc", which is the default on Windows and launches the Windows client, and "open", which is the default on Mac and launches via an rdp:// URL.`, - }) + case "ssh": + sshOptions(c, set) } return set @@ -323,38 +242,16 @@ func (c *Command) Run(args []string) (retCode int) { return 1 } - switch c.Func { - case "http": - if c.flagExec == "" { - c.flagExec = strings.ToLower(c.flagHttpStyle) - } - case "ssh": - if c.flagExec == "" { - c.flagExec = strings.ToLower(c.flagSshStyle) - } - case "postgres": - if c.flagExec == "" { - c.flagExec = strings.ToLower(c.flagPostgresStyle) - } - case "rdp": - if c.flagExec == "" { - c.flagRdpStyle = strings.ToLower(c.flagRdpStyle) - switch c.flagRdpStyle { - case "": - switch runtime.GOOS { - case "windows": - c.flagRdpStyle = "mstsc" - case "darwin": - c.flagRdpStyle = "open" - default: - // We may want to support rdesktop and/or xfreerdp at some point soon - c.flagRdpStyle = "mstsc" - } - } - if c.flagRdpStyle == "mstsc" { - c.flagRdpStyle = "mstsc.exe" - } - c.flagExec = c.flagRdpStyle + if c.flagExec == "" { + switch c.Func { + case "http": + c.flagExec = c.httpFlags.defaultExec() + case "ssh": + c.flagExec = c.sshFlags.defaultExec() + case "postgres": + c.flagExec = c.postgresFlags.defaultExec() + case "rdp": + c.flagExec = c.rdpFlags.defaultExec() } } @@ -775,60 +672,20 @@ func (c *Command) handleExec(passthroughArgs []string) { switch c.Func { case "http": - switch c.flagHttpStyle { - case "curl": - if c.flagHttpMethod != "" { - args = append(args, "-X", c.flagHttpMethod) - } - var uri string - if c.flagHttpHost != "" { - c.flagHttpHost = strings.TrimSuffix(c.flagHttpHost, "/") - args = append(args, "-H", fmt.Sprintf("Host: %s", c.flagHttpHost)) - args = append(args, "--resolve", fmt.Sprintf("%s:%s:%s", c.flagHttpHost, port, ip)) - uri = fmt.Sprintf("%s://%s:%s", c.flagHttpScheme, c.flagHttpHost, port) - } else { - uri = fmt.Sprintf("%s://%s", c.flagHttpScheme, addr) - } - if c.flagHttpPath != "" { - uri = fmt.Sprintf("%s/%s", uri, strings.TrimPrefix(c.flagHttpPath, "/")) - } - args = append(args, uri) - } - - case "ssh": - switch c.flagSshStyle { - case "ssh": - args = append(args, "-p", port, ip) - args = append(args, "-o", fmt.Sprintf("HostKeyAlias=%s", c.sessionAuthzData.HostId)) - case "putty": - args = append(args, "-P", port, ip) - } - if c.flagUsername != "" { - args = append(args, "-l", c.flagUsername) - } + args = append(args, c.httpFlags.buildArgs(c, port, ip, addr)...) case "postgres": - switch c.flagPostgresStyle { - case "psql": - args = append(args, "-p", port, "-h", ip) - if c.flagUsername != "" { - args = append(args, "-U", c.flagUsername) - } - } + args = append(args, c.postgresFlags.buildArgs(c, port, ip, addr)...) case "rdp": - switch c.flagRdpStyle { - case "mstsc.exe": - args = append(args, "/v", addr) - case "open": - args = append(args, "-n", "-W", fmt.Sprintf("rdp://full%saddress=s:%s", "%20", addr)) - } + args = append(args, c.rdpFlags.buildArgs(c, port, ip, addr)...) + + case "ssh": + args = append(args, c.sshFlags.buildArgs(c, port, ip, addr)...) } args = append(passthroughArgs, args...) - // Might want -t for ssh or -tt but seems fine without it for now... - stringReplacer := func(in, typ, replacer string) string { for _, style := range []string{ fmt.Sprintf("{{boundary.%s}}", typ), diff --git a/internal/cmd/commands/proxy/funcs.go b/internal/cmd/commands/connect/funcs.go similarity index 99% rename from internal/cmd/commands/proxy/funcs.go rename to internal/cmd/commands/connect/funcs.go index 37489c666..a93da93ff 100644 --- a/internal/cmd/commands/proxy/funcs.go +++ b/internal/cmd/commands/connect/funcs.go @@ -1,4 +1,4 @@ -package proxy +package connect import ( "time" diff --git a/internal/cmd/commands/connect/http.go b/internal/cmd/commands/connect/http.go new file mode 100644 index 000000000..4ae34cf37 --- /dev/null +++ b/internal/cmd/commands/connect/http.go @@ -0,0 +1,95 @@ +package connect + +import ( + "fmt" + "strings" + + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/posener/complete" +) + +const ( + httpSynopsis = "Authorize a session against a target and invoke an HTTP client to connect" +) + +func httpOptions(c *Command, set *base.FlagSets) { + f := set.NewFlagSet("HTTP Options") + + f.StringVar(&base.StringVar{ + Name: "style", + Target: &c.flagHttpStyle, + EnvVar: "BOUNDARY_CONNECT_HTTP_STYLE", + Completion: complete.PredictSet("curl"), + Default: "curl", + Usage: `Specifies how the CLI will attempt to invoke an HTTP client. This will also set a suitable default for -exec if a value was not specified. Currently-understood values are "curl".`, + }) + + f.StringVar(&base.StringVar{ + Name: "host", + Target: &c.flagHttpHost, + EnvVar: "BOUNDARY_CONNECT_HTTP_HOST", + Completion: complete.PredictNothing, + Usage: `Specifies the host value to use. The specified hostname will be passed through to the client (if supported) for use in the Host header and TLS SNI value.`, + }) + + f.StringVar(&base.StringVar{ + Name: "path", + Target: &c.flagHttpPath, + EnvVar: "BOUNDARY_CONNECT_HTTP_PATH", + Completion: complete.PredictNothing, + Usage: `Specifies a path that will be appended to the generated URL.`, + }) + + f.StringVar(&base.StringVar{ + Name: "method", + Target: &c.flagHttpMethod, + EnvVar: "BOUNDARY_CONNECT_HTTP_METHOD", + Completion: complete.PredictNothing, + Usage: `Specifies the method to use. If not set, will use the client's default.`, + }) + + f.StringVar(&base.StringVar{ + Name: "scheme", + Target: &c.flagHttpScheme, + Default: "https", + EnvVar: "BOUNDARY_CONNECT_HTTP_SCHEME", + Completion: complete.PredictNothing, + Usage: `Specifies the scheme to use.`, + }) +} + +type httpFlags struct { + flagHttpStyle string + flagHttpHost string + flagHttpPath string + flagHttpMethod string + flagHttpScheme string +} + +func (h *httpFlags) defaultExec() string { + return strings.ToLower(h.flagHttpStyle) +} + +func (h *httpFlags) buildArgs(c *Command, port, ip, addr string) []string { + var args []string + switch h.flagHttpStyle { + case "curl": + if h.flagHttpMethod != "" { + args = append(args, "-X", h.flagHttpMethod) + } + var uri string + if h.flagHttpHost != "" { + h.flagHttpHost = strings.TrimSuffix(h.flagHttpHost, "/") + args = append(args, "-H", fmt.Sprintf("Host: %s", h.flagHttpHost)) + args = append(args, "--resolve", fmt.Sprintf("%s:%s:%s", h.flagHttpHost, port, ip)) + uri = fmt.Sprintf("%s://%s:%s", h.flagHttpScheme, h.flagHttpHost, port) + } else { + uri = fmt.Sprintf("%s://%s", h.flagHttpScheme, addr) + } + if h.flagHttpPath != "" { + uri = fmt.Sprintf("%s/%s", uri, strings.TrimPrefix(h.flagHttpPath, "/")) + } + args = append(args, uri) + } + return args +} diff --git a/internal/cmd/commands/connect/postgres.go b/internal/cmd/commands/connect/postgres.go new file mode 100644 index 000000000..b7cfe3bdd --- /dev/null +++ b/internal/cmd/commands/connect/postgres.go @@ -0,0 +1,53 @@ +package connect + +import ( + "strings" + + "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`, + }) +} + +type postgresFlags struct { + flagPostgresStyle string +} + +func (p *postgresFlags) defaultExec() string { + return strings.ToLower(p.flagPostgresStyle) +} + +func (p *postgresFlags) buildArgs(c *Command, port, ip, addr string) []string { + var args []string + switch p.flagPostgresStyle { + case "psql": + args = append(args, "-p", port, "-h", ip) + if c.flagUsername != "" { + args = append(args, "-U", c.flagUsername) + } + } + return args +} diff --git a/internal/cmd/commands/connect/rdp.go b/internal/cmd/commands/connect/rdp.go new file mode 100644 index 000000000..af63699ef --- /dev/null +++ b/internal/cmd/commands/connect/rdp.go @@ -0,0 +1,61 @@ +package connect + +import ( + "fmt" + "runtime" + "strings" + + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/posener/complete" +) + +const ( + rdpSynopsis = "Authorize a session against a target and invoke an RDP client to connect" +) + +func rdpOptions(c *Command, set *base.FlagSets) { + f := set.NewFlagSet("RDP Options") + + f.StringVar(&base.StringVar{ + Name: "style", + Target: &c.flagRdpStyle, + EnvVar: "BOUNDARY_CONNECT_RDP_STYLE", + Completion: complete.PredictSet("mstsc", "open"), + Usage: `Specifies how the CLI will attempt to invoke an RDP client. This will also set a suitable default for -exec if a value was not specified. Currently-understood values are "mstsc", which is the default on Windows and launches the Windows client, and "open", which is the default on Mac and launches via an rdp:// URL.`, + }) +} + +type rdpFlags struct { + flagRdpStyle string +} + +func (r *rdpFlags) defaultExec() string { + r.flagRdpStyle = strings.ToLower(r.flagRdpStyle) + switch r.flagRdpStyle { + case "": + switch runtime.GOOS { + case "windows": + r.flagRdpStyle = "mstsc" + case "darwin": + r.flagRdpStyle = "open" + default: + // We may want to support rdesktop and/or xfreerdp at some point soon + r.flagRdpStyle = "mstsc" + } + } + if r.flagRdpStyle == "mstsc" { + r.flagRdpStyle = "mstsc.exe" + } + return r.flagRdpStyle +} + +func (r *rdpFlags) buildArgs(c *Command, port, ip, addr string) []string { + var args []string + switch r.flagRdpStyle { + case "mstsc.exe": + args = append(args, "/v", addr) + case "open": + args = append(args, "-n", "-W", fmt.Sprintf("rdp://full%saddress=s:%s", "%20", addr)) + } + return args +} diff --git a/internal/cmd/commands/connect/ssh.go b/internal/cmd/commands/connect/ssh.go new file mode 100644 index 000000000..df446e88c --- /dev/null +++ b/internal/cmd/commands/connect/ssh.go @@ -0,0 +1,58 @@ +package connect + +import ( + "fmt" + "strings" + + "github.com/hashicorp/boundary/internal/cmd/base" + "github.com/posener/complete" +) + +const ( + sshSynopsis = "Authorize a session against a target and invoke an SSH client to connect" +) + +func sshOptions(c *Command, set *base.FlagSets) { + f := set.NewFlagSet("SSH Options") + + f.StringVar(&base.StringVar{ + Name: "style", + Target: &c.flagSshStyle, + EnvVar: "BOUNDARY_CONNECT_SSH_STYLE", + Completion: complete.PredictSet("ssh", "putty"), + Default: "ssh", + Usage: `Specifies how the CLI will attempt to invoke an SSH client. This will also set a suitable default for -exec if a value was not specified. Currently-understood values are "ssh" and "putty".`, + }) + + 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`, + }) +} + +type sshFlags struct { + flagSshStyle string +} + +func (s *sshFlags) defaultExec() string { + return strings.ToLower(s.flagSshStyle) +} + +func (s *sshFlags) buildArgs(c *Command, port, ip, addr string) []string { + // Might want -t for ssh or -tt but seems fine without it for now... + var args []string + switch s.flagSshStyle { + case "ssh": + args = append(args, "-p", port, ip) + args = append(args, "-o", fmt.Sprintf("HostKeyAlias=%s", c.sessionAuthzData.HostId)) + case "putty": + args = append(args, "-P", port, ip) + } + if c.flagUsername != "" { + args = append(args, "-l", c.flagUsername) + } + return args +}