From 69ec0253986f550e1b6b78b18d611eec9aadd9e2 Mon Sep 17 00:00:00 2001 From: Andreas Fritzler Date: Thu, 12 Dec 2024 11:30:07 +0100 Subject: [PATCH] Make host key validation configurable --- cmd/metalctl/app/console.go | 40 +++++++++++++++++++++++++++++----- docs/usage/metalctl.md | 3 +++ internal/console/console.go | 2 +- internal/console/suite_test.go | 2 +- 4 files changed, 40 insertions(+), 7 deletions(-) diff --git a/cmd/metalctl/app/console.go b/cmd/metalctl/app/console.go index 05fa072..3685164 100644 --- a/cmd/metalctl/app/console.go +++ b/cmd/metalctl/app/console.go @@ -10,19 +10,23 @@ import ( "log" "net" "os" + "path/filepath" "github.com/ironcore-dev/metal-operator/internal/console" "github.com/spf13/cobra" "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/knownhosts" _ "k8s.io/client-go/plugin/pkg/client/auth/oidc" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/client/config" ) var ( - kubeconfigPath string - kubeconfig string - serialConsoleNumber int + kubeconfigPath string + kubeconfig string + serialConsoleNumber int + skipHostKeyValidation bool + knownHostsFile string ) func NewConsoleCommand() *cobra.Command { @@ -34,6 +38,8 @@ func NewConsoleCommand() *cobra.Command { consoleCmd.Flags().StringVar(&kubeconfig, "kubeconfig", "", "Path to a kubeconfig.") consoleCmd.Flags().IntVar(&serialConsoleNumber, "serial-console-number", 1, "Serial console number.") + consoleCmd.Flags().BoolVar(&skipHostKeyValidation, "skip-host-key-validation", false, "Skip host key validation.") + consoleCmd.Flags().StringVar(&knownHostsFile, "known-hosts-file", "~/.ssh/known_hosts", "Path to known_hosts file.") return consoleCmd } @@ -69,14 +75,27 @@ func openConsoleStream(ctx context.Context, k8sClient client.Client, serverName return fmt.Errorf("console config is nil") } + var hostKeyCallback ssh.HostKeyCallback + if skipHostKeyValidation { + hostKeyCallback = ssh.InsecureIgnoreHostKey() + } else { + expandedPath, err := expandPath(knownHostsFile) + if err != nil { + return fmt.Errorf("failed to expand known_hosts file path: %w", err) + } + hostKeyCallback, err = knownhosts.New(expandedPath) + if err != nil { + return fmt.Errorf("failed to parse known_hosts file: %w", err) + } + } + // Create SSH client configuration sshConfig := &ssh.ClientConfig{ User: consoleConfig.Username, Auth: []ssh.AuthMethod{ ssh.Password(consoleConfig.Password), }, - // TODO: use proper key verification - HostKeyCallback: ssh.InsecureIgnoreHostKey(), + HostKeyCallback: hostKeyCallback, } // Connect to the BMC @@ -170,3 +189,14 @@ func createClient() (client.Client, error) { } return k8sClient, nil } + +func expandPath(path string) (string, error) { + if len(path) > 0 && path[0] == '~' { + homeDir, err := os.UserHomeDir() + if err != nil { + return "", err + } + return filepath.Join(homeDir, path[1:]), nil + } + return path, nil +} diff --git a/docs/usage/metalctl.md b/docs/usage/metalctl.md index e40aefd..2ff527e 100644 --- a/docs/usage/metalctl.md +++ b/docs/usage/metalctl.md @@ -25,6 +25,9 @@ or set the `KUBECONFIG` environment variable by pointing to an effective `kubeco By default, the serial console on `ttyS1` will be opened. You can override this by setting `--serial-console-number`. +Additionally, you can skip the host validation by providing the `--skip-host-key-validation=true` flag. If set to `false` +it is possible provide a custom `known_hosts` file via the `--known-hosts-file` flag. + ### move The `metalctl move` command allows to move the metal Custom Resources, like e.g. `Endpoint`, `BMC`, `Server`, etc. from one diff --git a/internal/console/console.go b/internal/console/console.go index a13307a..33ea719 100644 --- a/internal/console/console.go +++ b/internal/console/console.go @@ -59,5 +59,5 @@ func GetConfigForServerName(ctx context.Context, c client.Client, serverName str }, nil } - return nil, nil + return nil, fmt.Errorf("failed to create configuration for accessing server %q", serverName) } diff --git a/internal/console/suite_test.go b/internal/console/suite_test.go index f59356f..b673d8c 100644 --- a/internal/console/suite_test.go +++ b/internal/console/suite_test.go @@ -44,7 +44,7 @@ func TestControllers(t *testing.T) { SetDefaultConsistentlyDuration(consistentlyDuration) RegisterFailHandler(Fail) - RunSpecs(t, "Controller Suite") + RunSpecs(t, "Console Suite") } var _ = BeforeSuite(func() {