Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TLS support for crictl exec, portforward and attach #1668

Merged
merged 1 commit into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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: flagTLSSNI,
Usage: "Server name used in the TLS client to check server certificates against",
Aliases: []string{"tls-server-name"},
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: flagTLSSNI,
Usage: "Server name used in the TLS client to check server certificates against",
Aliases: []string{"tls-server-name"},
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 (
flagTLSSNI = "tls-sni"
flagTLSCA = "tls-ca"
flagTLSCert = "tls-cert"
flagTLSKey = "tls-key"
)

func tlsConfigFromFlags(ctx *cli.Context) (*rest.TLSClientConfig, error) {
cfg := &rest.TLSClientConfig{
ServerName: ctx.String(flagTLSSNI),
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: flagTLSSNI,
Usage: "Server name used in the TLS client to check server certificates against",
Aliases: []string{"tls-server-name"},
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
Loading