Add kube connect subcommand (#816)
This commit is contained in:
1 parent
d9a6bd1a60
commit
b809d3f12a
4 files changed
+124
-5
No files matched your search
@@ -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
|
||||
|
||||
Reference in new issue
Block a user