Add `kube` connect subcommand (#816)

jeff-websocket-netconn-framewrapping
Jeff Mitchell 5 years ago committed by GitHub
parent d9a6bd1a60
commit b809d3f12a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -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) {
@ -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
},

@ -72,6 +72,9 @@ type Command struct {
// HTTP
httpFlags
// Kube
kubeFlags
// Postgres
postgresFlags
@ -110,6 +113,8 @@ func (c *Command) Synopsis() string {
return rdpSynopsis
case "ssh":
return sshSynopsis
case "kube":
return kubeSynopsis
default:
return ""
}
@ -237,6 +242,9 @@ func (c *Command) Flags() *base.FlagSets {
case "ssh":
sshOptions(c, set)
case "kube":
kubeOptions(c, set)
}
return set
@ -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()
}
}
@ -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...)

@ -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
}

@ -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
@ -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,
@ -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

Loading…
Cancel
Save