From 44e3499e41bfe5e0b95b8cd612fe5e15f2a1d95e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Anders=20F=20Bj=C3=B6rklund?= Date: Mon, 2 Nov 2020 11:03:02 +0100 Subject: [PATCH] Add ssh-host command for getting the ssh host keys This is similar to the existing minikube "ssh-key" command, but gets the content of the host key instead of the path to the identity key. The output of this command can be added to the ~/.ssh/known_hosts, for strict host key authentication. For instance when using Docker. --- cmd/minikube/cmd/root.go | 1 + cmd/minikube/cmd/ssh-host.go | 74 +++++++++++++++++++++++ pkg/minikube/machine/ssh.go | 55 +++++++++++++++-- site/content/en/docs/commands/ssh-host.md | 45 ++++++++++++++ 4 files changed, 170 insertions(+), 5 deletions(-) create mode 100644 cmd/minikube/cmd/ssh-host.go create mode 100644 site/content/en/docs/commands/ssh-host.md diff --git a/cmd/minikube/cmd/root.go b/cmd/minikube/cmd/root.go index 2259f03889ce..1ef8e2fadf26 100644 --- a/cmd/minikube/cmd/root.go +++ b/cmd/minikube/cmd/root.go @@ -221,6 +221,7 @@ func init() { Message: translate.T("Troubleshooting Commands:"), Commands: []*cobra.Command{ sshKeyCmd, + sshHostCmd, ipCmd, logsCmd, updateCheckCmd, diff --git a/cmd/minikube/cmd/ssh-host.go b/cmd/minikube/cmd/ssh-host.go new file mode 100644 index 000000000000..efefd4bc9788 --- /dev/null +++ b/cmd/minikube/cmd/ssh-host.go @@ -0,0 +1,74 @@ +/* +Copyright 2016 The Kubernetes Authors All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package cmd + +import ( + "fmt" + "os" + + "github.com/spf13/cobra" + + "k8s.io/minikube/pkg/minikube/config" + "k8s.io/minikube/pkg/minikube/driver" + "k8s.io/minikube/pkg/minikube/exit" + "k8s.io/minikube/pkg/minikube/machine" + "k8s.io/minikube/pkg/minikube/mustload" + "k8s.io/minikube/pkg/minikube/node" + "k8s.io/minikube/pkg/minikube/out" + "k8s.io/minikube/pkg/minikube/reason" +) + +// sshHostCmd represents the sshHostCmd command +var sshHostCmd = &cobra.Command{ + Use: "ssh-host", + Short: "Retrieve the ssh host key of the specified node", + Long: "Retrieve the ssh host key of the specified node.", + Run: func(cmd *cobra.Command, args []string) { + cname := ClusterFlagValue() + co := mustload.Running(cname) + if co.CP.Host.DriverName == driver.None { + exit.Message(reason.Usage, "'none' driver does not support 'minikube ssh-host' command") + } + + var err error + var n *config.Node + if nodeName == "" { + n = co.CP.Node + } else { + n, _, err = node.Retrieve(*co.Config, nodeName) + if err != nil { + exit.Message(reason.GuestNodeRetrieve, "Node {{.nodeName}} does not exist.", out.V{"nodeName": nodeName}) + } + } + + scanArgs := []string{"-t", "rsa"} + + keys, err := machine.RunSSHHostCommand(co.API, *co.Config, *n, "ssh-keyscan", scanArgs) + if err != nil { + // This is typically due to a non-zero exit code, so no need for flourish. + out.ErrLn("ssh-keyscan: %v", err) + // It'd be nice if we could pass up the correct error code here :( + os.Exit(1) + } + + fmt.Printf("%s", keys) + }, +} + +func init() { + sshHostCmd.Flags().StringVarP(&nodeName, "node", "n", "", "The node to ssh into. Defaults to the primary control plane.") +} diff --git a/pkg/minikube/machine/ssh.go b/pkg/minikube/machine/ssh.go index c547ed09d7d2..93b62f288724 100644 --- a/pkg/minikube/machine/ssh.go +++ b/pkg/minikube/machine/ssh.go @@ -17,7 +17,11 @@ limitations under the License. package machine import ( + "fmt" + "os/exec" + "github.com/docker/machine/libmachine" + "github.com/docker/machine/libmachine/host" "github.com/docker/machine/libmachine/ssh" "github.com/docker/machine/libmachine/state" "github.com/pkg/errors" @@ -25,21 +29,30 @@ import ( "k8s.io/minikube/pkg/minikube/driver" ) -// CreateSSHShell creates a new SSH shell / client -func CreateSSHShell(api libmachine.API, cc config.ClusterConfig, n config.Node, args []string, native bool) error { +func getHost(api libmachine.API, cc config.ClusterConfig, n config.Node) (*host.Host, error) { machineName := driver.MachineName(cc, n) host, err := LoadHost(api, machineName) if err != nil { - return errors.Wrap(err, "host exists and load") + return nil, errors.Wrap(err, "host exists and load") } currentState, err := host.Driver.GetState() if err != nil { - return errors.Wrap(err, "state") + return nil, errors.Wrap(err, "state") } if currentState != state.Running { - return errors.Errorf("%q is not running", machineName) + return nil, errors.Errorf("%q is not running", machineName) + } + + return host, nil +} + +// CreateSSHShell creates a new SSH shell / client +func CreateSSHShell(api libmachine.API, cc config.ClusterConfig, n config.Node, args []string, native bool) error { + host, err := getHost(api, cc, n) + if err != nil { + return err } if native { @@ -55,3 +68,35 @@ func CreateSSHShell(api libmachine.API, cc config.ClusterConfig, n config.Node, } return client.Shell(args...) } + +// RunSSHHostCommand runs a command to the SSH host +func RunSSHHostCommand(api libmachine.API, cc config.ClusterConfig, n config.Node, command string, args []string) (string, error) { + host, err := getHost(api, cc, n) + if err != nil { + return "", err + } + + cmdPath, err := exec.LookPath(command) + if err != nil { + return "", err + } + + port, err := host.Driver.GetSSHPort() + if err != nil { + return "", err + } + + args = append(args, "-p") + args = append(args, fmt.Sprintf("%d", port)) + + addr, err := host.Driver.GetSSHHostname() + if err != nil { + return "", err + } + + args = append(args, addr) + + cmd := exec.Command(cmdPath, args...) + output, err := cmd.Output() + return string(output), err +} diff --git a/site/content/en/docs/commands/ssh-host.md b/site/content/en/docs/commands/ssh-host.md new file mode 100644 index 000000000000..0006ee702920 --- /dev/null +++ b/site/content/en/docs/commands/ssh-host.md @@ -0,0 +1,45 @@ +--- +title: "ssh-host" +description: > + Retrieve the ssh host key of the specified node +--- + + +## minikube ssh-host + +Retrieve the ssh host key of the specified node + +### Synopsis + +Retrieve the ssh host key of the specified node. + +``` +minikube ssh-host [flags] +``` + +### Options + +``` + -h, --help help for ssh-host + -n, --node string The node to ssh into. Defaults to the primary control plane. +``` + +### Options inherited from parent commands + +``` + --add_dir_header If true, adds the file directory to the header of the log messages + --alsologtostderr log to standard error as well as files + -b, --bootstrapper string The name of the cluster bootstrapper that will set up the Kubernetes cluster. (default "kubeadm") + --log_backtrace_at traceLocation when logging hits line file:N, emit a stack trace (default :0) + --log_dir string If non-empty, write log files in this directory + --log_file string If non-empty, use this log file + --log_file_max_size uint Defines the maximum size a log file can grow to. Unit is megabytes. If the value is 0, the maximum file size is unlimited. (default 1800) + --logtostderr log to standard error instead of files + -p, --profile string The name of the minikube VM being used. This can be set to allow having multiple instances of minikube independently. (default "minikube") + --skip_headers If true, avoid header prefixes in the log messages + --skip_log_headers If true, avoid headers when opening log files + --stderrthreshold severity logs at or above this threshold go to stderr (default 2) + -v, --v Level number for the log level verbosity + --vmodule moduleSpec comma-separated list of pattern=N settings for file-filtered logging +``` +