Skip to content

Commit

Permalink
Add TLS support for crictl exec, portforward and attach
Browse files Browse the repository at this point in the history
Add flags to allow using the TLS based streaming configuration.

Signed-off-by: Sascha Grunert <sgrunert@redhat.com>
  • Loading branch information
saschagrunert committed Nov 5, 2024
1 parent c9a62f2 commit 9c8cb37
Show file tree
Hide file tree
Showing 4 changed files with 123 additions and 16 deletions.
33 changes: 29 additions & 4 deletions cmd/crictl/attach.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,24 @@ var runtimeAttachCommand = &cli.Command{
Value: transportSpdy,
Usage: fmt.Sprintf("Transport protocol to use, one of: %s|%s", transportSpdy, transportWebsocket),
},
&cli.StringFlag{
Name: flagTLSServerName,
Usage: "Server name used in the TLS client to check server certificates against",
Aliases: []string{"tls-server", "tls-sn"},
Value: "localhost",
},
&cli.StringFlag{
Name: flagTLSCA,
Usage: "Path to the streaming TLS CA certificate",
},
&cli.StringFlag{
Name: flagTLSCert,
Usage: "Path to the streaming TLS certificate",
},
&cli.StringFlag{
Name: flagTLSKey,
Usage: "Path to the streaming TLS key",
},
},
Action: func(c *cli.Context) error {
id := c.Args().First()
Expand All @@ -70,10 +88,17 @@ var runtimeAttachCommand = &cli.Command{
defer cancel()

opts := attachOptions{
id: id,
tty: c.Bool("tty"),
stdin: c.Bool("stdin"),
id: id,
tty: c.Bool("tty"),
stdin: c.Bool("stdin"),
transport: c.String("transport"),
}

opts.tlsConfig, err = tlsConfigFromFlags(c)
if err != nil {
return fmt.Errorf("get TLS config from flags: %w", err)
}

if err = Attach(ctx, runtimeClient, opts); err != nil {
return fmt.Errorf("attaching running container failed: %w", err)
}
Expand Down Expand Up @@ -116,5 +141,5 @@ func Attach(ctx context.Context, client internalapi.RuntimeService, opts attachO
}

logrus.Debugf("Attach URL: %v", URL)
return stream(ctx, opts.stdin, opts.tty, opts.transport, URL)
return stream(ctx, opts.stdin, opts.tty, opts.transport, URL, opts.tlsConfig)
}
68 changes: 59 additions & 9 deletions cmd/crictl/exec.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,24 @@ var runtimeExecCommand = &cli.Command{
Aliases: []string{"x"},
Usage: "Run the command in parallel if multiple containers are selected",
},
&cli.StringFlag{
Name: flagTLSServerName,
Usage: "Server name used in the TLS client to check server certificates against",
Aliases: []string{"tls-server", "tls-sn"},
Value: "localhost",
},
&cli.StringFlag{
Name: flagTLSCA,
Usage: "Path to the streaming TLS CA certificate",
},
&cli.StringFlag{
Name: flagTLSCert,
Usage: "Path to the streaming TLS certificate",
},
&cli.StringFlag{
Name: flagTLSKey,
Usage: "Path to the streaming TLS key",
},
},
Action: func(c *cli.Context) error {
if c.NArg() < 1 {
Expand Down Expand Up @@ -200,6 +218,11 @@ var runtimeExecCommand = &cli.Command{
transport: c.String(transportFlag),
}

opts.tlsConfig, err = tlsConfigFromFlags(c)
if err != nil {
return fmt.Errorf("get TLS config from flags: %w", err)
}

funcs := []func() error{}
for _, id := range ids {
funcs = append(funcs, func() error {
Expand All @@ -210,7 +233,7 @@ var runtimeExecCommand = &cli.Command{
fmt.Println(id + ":")
}
if c.Bool("sync") {
exitCode, err := ExecSync(runtimeClient, optsCopy)
exitCode, err := ExecSync(runtimeClient, &optsCopy)
if err != nil {
return fmt.Errorf("execing command in container %s synchronously: %w", id, err)
}
Expand All @@ -220,7 +243,7 @@ var runtimeExecCommand = &cli.Command{
} else {
ctx, cancel := context.WithCancel(c.Context)
defer cancel()
err = Exec(ctx, runtimeClient, optsCopy)
err = Exec(ctx, runtimeClient, &optsCopy)
if err != nil {
return fmt.Errorf("execing command in container %s: %w", id, err)
}
Expand All @@ -241,10 +264,37 @@ var runtimeExecCommand = &cli.Command{
},
}

const (
flagTLSServerName = "tls-server-name"
flagTLSCA = "tls-ca"
flagTLSCert = "tls-cert"
flagTLSKey = "tls-key"
)

func tlsConfigFromFlags(ctx *cli.Context) (*rest.TLSClientConfig, error) {
cfg := &rest.TLSClientConfig{
ServerName: ctx.String(flagTLSServerName),
CAFile: ctx.String(flagTLSCA),
CertFile: ctx.String(flagTLSCert),
KeyFile: ctx.String(flagTLSKey),
}
if cfg.CAFile == "" && cfg.CertFile == "" && cfg.KeyFile == "" {
return &rest.TLSClientConfig{Insecure: true}, nil
}
if cfg.CAFile == "" || cfg.CertFile == "" || cfg.KeyFile == "" {
return nil, fmt.Errorf(
"all three flags --%s, --%s and --%s are required for TLS streaming",
flagTLSCA, flagTLSCert, flagTLSKey,
)
}

return cfg, nil
}

// ExecSync sends an ExecSyncRequest to the server, and parses
// the returned ExecSyncResponse. The function returns the corresponding exit
// code beside an general error.
func ExecSync(client internalapi.RuntimeService, opts execOptions) (int, error) {
func ExecSync(client internalapi.RuntimeService, opts *execOptions) (int, error) {
request := &pb.ExecSyncRequest{
ContainerId: opts.id,
Cmd: opts.cmd,
Expand All @@ -271,7 +321,7 @@ func ExecSync(client internalapi.RuntimeService, opts execOptions) (int, error)
}

// Exec sends an ExecRequest to server, and parses the returned ExecResponse.
func Exec(ctx context.Context, client internalapi.RuntimeService, opts execOptions) error {
func Exec(ctx context.Context, client internalapi.RuntimeService, opts *execOptions) error {
request := &pb.ExecRequest{
ContainerId: opts.id,
Cmd: opts.cmd,
Expand Down Expand Up @@ -305,11 +355,11 @@ func Exec(ctx context.Context, client internalapi.RuntimeService, opts execOptio
}

logrus.Debugf("Exec URL: %v", URL)
return stream(ctx, opts.stdin, opts.tty, opts.transport, URL)
return stream(ctx, opts.stdin, opts.tty, opts.transport, URL, opts.tlsConfig)
}

func stream(ctx context.Context, in, tty bool, transport string, parsedURL *url.URL) error {
executor, err := getExecutor(transport, parsedURL)
func stream(ctx context.Context, in, tty bool, transport string, parsedURL *url.URL, tlsConfig *rest.TLSClientConfig) error {
executor, err := getExecutor(transport, parsedURL, tlsConfig)
if err != nil {
return fmt.Errorf("get executor: %w", err)
}
Expand Down Expand Up @@ -348,8 +398,8 @@ func stream(ctx context.Context, in, tty bool, transport string, parsedURL *url.
return t.Safe(func() error { return executor.StreamWithContext(ctx, streamOptions) })
}

func getExecutor(transport string, parsedURL *url.URL) (exec remoteclient.Executor, err error) {
config := &rest.Config{TLSClientConfig: rest.TLSClientConfig{Insecure: true}}
func getExecutor(transport string, parsedURL *url.URL, tlsConfig *rest.TLSClientConfig) (exec remoteclient.Executor, err error) {
config := &rest.Config{TLSClientConfig: *tlsConfig}

switch transport {
case transportSpdy:
Expand Down
30 changes: 27 additions & 3 deletions cmd/crictl/portforward.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,24 @@ var runtimePortForwardCommand = &cli.Command{
Value: transportSpdy,
Usage: fmt.Sprintf("Transport protocol to use, one of: %s|%s", transportSpdy, transportWebsocket),
},
&cli.StringFlag{
Name: flagTLSServerName,
Usage: "Server name used in the TLS client to check server certificates against",
Aliases: []string{"tls-server", "tls-sn"},
Value: "localhost",
},
&cli.StringFlag{
Name: flagTLSCA,
Usage: "Path to the streaming TLS CA certificate",
},
&cli.StringFlag{
Name: flagTLSCert,
Usage: "Path to the streaming TLS certificate",
},
&cli.StringFlag{
Name: flagTLSKey,
Usage: "Path to the streaming TLS key",
},
},
Action: func(c *cli.Context) error {
if c.NArg() < 2 {
Expand All @@ -61,6 +79,12 @@ var runtimePortForwardCommand = &cli.Command{
ports: c.Args().Tail(),
transport: c.String(transportFlag),
}

opts.tlsConfig, err = tlsConfigFromFlags(c)
if err != nil {
return fmt.Errorf("get TLS config from flags: %w", err)
}

if err = PortForward(runtimeClient, opts); err != nil {
return fmt.Errorf("port forward: %w", err)
}
Expand Down Expand Up @@ -99,7 +123,7 @@ func PortForward(client internalapi.RuntimeService, opts portforwardOptions) err
}

logrus.Debugf("PortForward URL: %v", parsedURL)
dialer, err := getDialer(opts.transport, parsedURL)
dialer, err := getDialer(opts.transport, parsedURL, opts.tlsConfig)
if err != nil {
return fmt.Errorf("get dialer: %w", err)
}
Expand All @@ -114,8 +138,8 @@ func PortForward(client internalapi.RuntimeService, opts portforwardOptions) err
return pf.ForwardPorts()
}

func getDialer(transport string, parsedURL *url.URL) (exec httpstream.Dialer, err error) {
config := &rest.Config{TLSClientConfig: rest.TLSClientConfig{Insecure: true}}
func getDialer(transport string, parsedURL *url.URL, tlsConfig *rest.TLSClientConfig) (exec httpstream.Dialer, err error) {
config := &rest.Config{TLSClientConfig: *tlsConfig}

switch transport {
case transportSpdy:
Expand Down
8 changes: 8 additions & 0 deletions cmd/crictl/util.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
"google.golang.org/protobuf/protoadapt"
"google.golang.org/protobuf/runtime/protoiface"
utilyaml "k8s.io/apimachinery/pkg/util/yaml"
"k8s.io/client-go/rest"
internalapi "k8s.io/cri-api/pkg/apis"
pb "k8s.io/cri-api/pkg/apis/runtime/v1"
"sigs.k8s.io/yaml"
Expand Down Expand Up @@ -157,7 +158,10 @@ type execOptions struct {
cmd []string
// transport to be used
transport string
// TLS configuration for streaming
tlsConfig *rest.TLSClientConfig
}

type attachOptions struct {
// id of container
id string
Expand All @@ -167,6 +171,8 @@ type attachOptions struct {
stdin bool
// transport to be used
transport string
// TLS configuration for streaming
tlsConfig *rest.TLSClientConfig
}

type portforwardOptions struct {
Expand All @@ -176,6 +182,8 @@ type portforwardOptions struct {
ports []string
// transport to be used
transport string
// TLS configuration for streaming
tlsConfig *rest.TLSClientConfig
}

func getSortedKeys(m map[string]string) []string {
Expand Down

0 comments on commit 9c8cb37

Please sign in to comment.