Skip to content

Commit

Permalink
Add kube connect subcommand (#816)
Browse files Browse the repository at this point in the history
  • Loading branch information
jefferai authored Dec 14, 2020
1 parent d9a6bd1 commit b809d3f
Show file tree
Hide file tree
Showing 4 changed files with 124 additions and 5 deletions.
14 changes: 10 additions & 4 deletions internal/cmd/commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -257,10 +257,16 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
Func: "http",
}, nil
},
"connect ssh": func() (cli.Command, error) {
"connect kube": func() (cli.Command, error) {
return &connect.Command{
Command: base.NewCommand(ui),
Func: "ssh",
Func: "kube",
}, nil
},
"connect postgres": func() (cli.Command, error) {
return &connect.Command{
Command: base.NewCommand(ui),
Func: "postgres",
}, nil
},
"connect rdp": func() (cli.Command, error) {
Expand All @@ -269,10 +275,10 @@ func initCommands(ui, serverCmdUi cli.Ui, runOpts *RunOptions) {
Func: "rdp",
}, nil
},
"connect postgres": func() (cli.Command, error) {
"connect ssh": func() (cli.Command, error) {
return &connect.Command{
Command: base.NewCommand(ui),
Func: "postgres",
Func: "ssh",
}, nil
},

Expand Down
19 changes: 19 additions & 0 deletions internal/cmd/commands/connect/connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,9 @@ type Command struct {
// HTTP
httpFlags

// Kube
kubeFlags

// Postgres
postgresFlags

Expand Down Expand Up @@ -110,6 +113,8 @@ func (c *Command) Synopsis() string {
return rdpSynopsis
case "ssh":
return sshSynopsis
case "kube":
return kubeSynopsis
default:
return ""
}
Expand Down Expand Up @@ -237,6 +242,9 @@ func (c *Command) Flags() *base.FlagSets {

case "ssh":
sshOptions(c, set)

case "kube":
kubeOptions(c, set)
}

return set
Expand Down Expand Up @@ -300,6 +308,8 @@ func (c *Command) Run(args []string) (retCode int) {
c.flagExec = c.postgresFlags.defaultExec()
case "rdp":
c.flagExec = c.rdpFlags.defaultExec()
case "kube":
c.flagExec = c.kubeFlags.defaultExec()
}
}

Expand Down Expand Up @@ -750,6 +760,15 @@ func (c *Command) handleExec(passthroughArgs []string) {

case "ssh":
args = append(args, c.sshFlags.buildArgs(c, port, ip, addr)...)

case "kube":
kubeArgs, err := c.kubeFlags.buildArgs(c, port, ip, addr)
if err != nil {
c.Error(fmt.Sprintf("Error parsing session args: %s", err))
c.execCmdReturnValue.Store(int32(3))
return
}
args = append(args, kubeArgs...)
}

args = append(passthroughArgs, args...)
Expand Down
76 changes: 76 additions & 0 deletions internal/cmd/commands/connect/kube.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package connect

import (
"fmt"
"net/url"
"strings"

"github.com/hashicorp/boundary/internal/cmd/base"
"github.com/posener/complete"
)

const (
kubeSynopsis = "Authorize a session against a target and invoke a Kubernetes client to connect"
)

func kubeOptions(c *Command, set *base.FlagSets) {
f := set.NewFlagSet("Kubernetes Options")

f.StringVar(&base.StringVar{
Name: "style",
Target: &c.flagKubeStyle,
EnvVar: fmt.Sprintf("BOUNDARY_CONNECT_%s_STYLE", strings.ToUpper(c.Func)),
Completion: complete.PredictSet("kubectl"),
Default: "kubectl",
Usage: `Specifies how the CLI will attempt to invoke a Kubernetes client. This will also set a suitable default for -exec if a value was not specified. Currently-understood values are "kubectl".`,
})

f.StringVar(&base.StringVar{
Name: "host",
Target: &c.flagKubeHost,
EnvVar: fmt.Sprintf("BOUNDARY_CONNECT_%s_HOST", strings.ToUpper(c.Func)),
Completion: complete.PredictNothing,
Usage: `Specifies the host value to use, overriding the endpoint address from the session information. The specified hostname will be passed through to the client (if supported) for use in the TLS SNI value.`,
})

f.StringVar(&base.StringVar{
Name: "scheme",
Target: &c.flagKubeScheme,
Default: "https",
EnvVar: fmt.Sprintf("BOUNDARY_CONNECT_%s_SCHEME", strings.ToUpper(c.Func)),
Completion: complete.PredictNothing,
Usage: `Specifies the scheme to use.`,
})
}

type kubeFlags struct {
flagKubeStyle string
flagKubeHost string
flagKubeScheme string
}

func (f *kubeFlags) defaultExec() string {
return strings.ToLower(f.flagKubeStyle)
}

func (f *kubeFlags) buildArgs(c *Command, port, ip, addr string) ([]string, error) {
var args []string
host := f.flagKubeHost
if host == "" && c.sessionAuthzData.GetEndpoint() != "" {
hostUrl := c.sessionAuthzData.GetEndpoint()
u, err := url.Parse(hostUrl)
if err != nil {
return nil, fmt.Errorf("error parsing endpoint URL: %w", err)
}
host = u.Hostname()
}
switch f.flagKubeStyle {
case "kubectl":
if host != "" && f.flagKubeScheme == "https" {
host = strings.TrimSuffix(host, "/")
args = append(args, "--tls-server-name", host)
}
args = append(args, "--server", fmt.Sprintf("%s://%s", f.flagKubeScheme, addr))
}
return args, nil
}
20 changes: 19 additions & 1 deletion website/content/docs/getting-started/connect-to-target.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ sets for this target contain the default host, which has the address
`127.0.0.1`. When we run `boundary connect` against this target, the single
available host will be selected and we'll open a local authenticated proxy to
the target host on the target's default port (`127.0.0.1:22`). Because this
target is proxying to our local SSH server, we can use our built-in `connect ssh` command to wrap the proxied TCP connection and SSH via Boundary:
target is proxying to our local SSH server, we can use our built-in `connect
ssh` command to wrap the proxied TCP connection and SSH via Boundary:

```
$ boundary connect ssh -target-id ttcp_1234567890
Expand All @@ -37,6 +38,21 @@ different style expected by different SSH clients. At the moment, besides `ssh`
(the default), the `boundary connect ssh` command supports `-style putty` to
support passing connection information to PuTTY.

## Selecting Targets

When using `boundary connect` you must identify the target used for connecting.
Convention in this documentation is to use the target ID as that's a single
value and most explicit; however, other flags are supported:

- `target-name`: The name of the target
- `target-scope-id`: The ID of the scope in which the target lives
- `target-scope-name`: The name of the scope in which the target lives

Note however that these are not uniquely identifying as names can be re-used
across scopes. As a result, when not using the target ID, you must use the
target's name in conjunction with the scope name or scope ID so that Boundary
can correctly identify the desired target.

## Built-In vs. Exec

Boundary comes with built-in wrappers for popular layer 7 connection protocols,
Expand All @@ -45,6 +61,8 @@ such as:
- `ssh`: defaults to the local SSH client (`ssh`)
- `postgres`: defaults to the official Postgres CLI client (`psql`)
- `rdp`: defaults to the built-in Windows RDP client (`mstsc`)
- `http`: defaults to `curl`
- `kube`: defaults to `kubectl`

However, `boundary connect` can accommodate executing clients even when there is
no built-in support for a specific client using `-exec`. The `-exec` flag is a
Expand Down

0 comments on commit b809d3f

Please sign in to comment.