Skip to content

Commit

Permalink
Add ssh-host command for getting the ssh host keys
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
afbjorklund committed Dec 9, 2020
1 parent 877f155 commit fe44cf2
Show file tree
Hide file tree
Showing 4 changed files with 171 additions and 5 deletions.
1 change: 1 addition & 0 deletions cmd/minikube/cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,7 @@ func init() {
Message: translate.T("Troubleshooting Commands:"),
Commands: []*cobra.Command{
sshKeyCmd,
sshHostCmd,
ipCmd,
logsCmd,
updateCheckCmd,
Expand Down
74 changes: 74 additions & 0 deletions cmd/minikube/cmd/ssh-host.go
Original file line number Diff line number Diff line change
@@ -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.")
}
55 changes: 50 additions & 5 deletions pkg/minikube/machine/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,29 +17,42 @@ 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"
"k8s.io/minikube/pkg/minikube/config"
"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 {
Expand All @@ -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
}
46 changes: 46 additions & 0 deletions site/content/en/docs/commands/ssh-host.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
---
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.

```shell
minikube ssh-host [flags]
```

### Options

```
-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")
-h, --help
--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
--one_output If true, only write logs to their native severity level (vs also writing to each lower severity level
-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
```

0 comments on commit fe44cf2

Please sign in to comment.