Skip to content
This repository has been archived by the owner on Sep 26, 2021. It is now read-only.

Commit

Permalink
Merge pull request #1299 from hairyhenderson/ssh-agent-support-1291
Browse files Browse the repository at this point in the history
Allowing generic driver to use ssh-agent to get identities
  • Loading branch information
nathanleclaire committed Dec 12, 2015
2 parents 104b04d + cf6136f commit d29a42e
Show file tree
Hide file tree
Showing 5 changed files with 70 additions and 35 deletions.
23 changes: 17 additions & 6 deletions docs/drivers/generic.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,20 @@ Options:
Environment variables and default values:

| CLI option | Environment variable | Default |
| -------------------------- | -------------------- | ------------------- |
| **`--generic-ip-address`** | `GENERIC_IP_ADDRESS` | - |
| `--generic-ssh-key` | `GENERIC_SSH_KEY` | `$HOME/.ssh/id_rsa` |
| `--generic-ssh-user` | `GENERIC_SSH_USER` | `root` |
| `--generic-ssh-port` | `GENERIC_SSH_PORT` | `22` |
| CLI option | Environment variable | Default |
|----------------------------|----------------------|---------------------------|
| **`--generic-ip-address`** | `GENERIC_IP_ADDRESS` | - |
| `--generic-ssh-key` | `GENERIC_SSH_KEY` | _(defers to `ssh-agent`)_ |
| `--generic-ssh-user` | `GENERIC_SSH_USER` | `root` |
| `--generic-ssh-port` | `GENERIC_SSH_PORT` | `22` |

##### Interaction with SSH Agents

When an SSH identity is not provided (with the `--generic-ssh-key` flag),
the SSH agent (if running) will be consulted. This makes it possible to
easily use password-protected SSH keys.

Note that this usage is _only_ supported if you're using the external SSH client,
which is the default behaviour when the `ssh` binary is available. If you're
using the native client (with `--native-ssh`), using the SSH agent is not yet
supported.
53 changes: 30 additions & 23 deletions drivers/generic/generic.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"fmt"
"net"
"os"
"path/filepath"
"strconv"
"time"

Expand All @@ -25,10 +24,6 @@ const (
defaultTimeout = 1 * time.Second
)

var (
defaultSourceSSHKey = filepath.Join(mcnutils.GetHomeDir(), ".ssh", "id_rsa")
)

// GetCreateFlags registers the flags this driver adds to
// "docker hosts create"
func (d *Driver) GetCreateFlags() []mcnflag.Flag {
Expand All @@ -46,8 +41,8 @@ func (d *Driver) GetCreateFlags() []mcnflag.Flag {
},
mcnflag.StringFlag{
Name: "generic-ssh-key",
Usage: "SSH private key path",
Value: defaultSourceSSHKey,
Usage: "SSH private key path (if not provided, identities in ssh-agent will be used)",
Value: "",
EnvVar: "GENERIC_SSH_KEY",
},
mcnflag.IntFlag{
Expand All @@ -66,7 +61,6 @@ func NewDriver(hostName, storePath string) drivers.Driver {
MachineName: hostName,
StorePath: storePath,
},
SSHKey: defaultSourceSSHKey,
}
}

Expand All @@ -83,6 +77,17 @@ func (d *Driver) GetSSHUsername() string {
return d.SSHUser
}

func (d *Driver) GetSSHKeyPath() string {
if d.SSHKey == "" {
return ""
}

if d.SSHKeyPath == "" {
d.SSHKeyPath = d.ResolveStorePath("id_rsa")
}
return d.SSHKeyPath
}

func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
d.IPAddress = flags.String("generic-ip-address")
d.SSHUser = flags.String("generic-ssh-user")
Expand All @@ -93,31 +98,33 @@ func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
return errors.New("generic driver requires the --generic-ip-address option")
}

if d.SSHKey == "" {
return errors.New("generic driver requires the --generic-ssh-key option")
}

return nil
}

func (d *Driver) PreCreateCheck() error {
if _, err := os.Stat(d.SSHKey); os.IsNotExist(err) {
return fmt.Errorf("Ssh key does not exist: %q", d.SSHKey)
if d.SSHKey != "" {
if _, err := os.Stat(d.SSHKey); os.IsNotExist(err) {
return fmt.Errorf("Ssh key does not exist: %q", d.SSHKey)
}
}

return nil
}

func (d *Driver) Create() error {
log.Info("Importing SSH key...")

// TODO: validate the key is a valid key
if err := mcnutils.CopyFile(d.SSHKey, d.GetSSHKeyPath()); err != nil {
return fmt.Errorf("unable to copy ssh key: %s", err)
}

if err := os.Chmod(d.GetSSHKeyPath(), 0600); err != nil {
return fmt.Errorf("unable to set permissions on the ssh key: %s", err)
if d.SSHKey == "" {
log.Info("No SSH key specified. Connecting to this machine now and in the" +
" future will require the ssh agent to contain the appropriate key.")
} else {
log.Info("Importing SSH key...")
// TODO: validate the key is a valid key
if err := mcnutils.CopyFile(d.SSHKey, d.GetSSHKeyPath()); err != nil {
return fmt.Errorf("unable to copy ssh key: %s", err)
}

if err := os.Chmod(d.GetSSHKeyPath(), 0600); err != nil {
return fmt.Errorf("unable to set permissions on the ssh key: %s", err)
}
}

log.Debugf("IP: %s", d.IPAddress)
Expand Down
9 changes: 7 additions & 2 deletions libmachine/drivers/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,13 @@ func GetSSHClientFromDriver(d Driver) (ssh.Client, error) {
return nil, err
}

auth := &ssh.Auth{
Keys: []string{d.GetSSHKeyPath()},
var auth *ssh.Auth
if d.GetSSHKeyPath() == "" {
auth = &ssh.Auth{}
} else {
auth = &ssh.Auth{
Keys: []string{d.GetSSHKeyPath()},
}
}

client, err := ssh.NewClient(d.GetSSHUsername(), address, port, auth)
Expand Down
9 changes: 7 additions & 2 deletions libmachine/host/host.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,8 +70,13 @@ func (h *Host) CreateSSHClient() (ssh.Client, error) {
return ssh.ExternalClient{}, err
}

auth := &ssh.Auth{
Keys: []string{h.Driver.GetSSHKeyPath()},
var auth *ssh.Auth
if h.Driver.GetSSHKeyPath() == "" {
auth = &ssh.Auth{}
} else {
auth = &ssh.Auth{
Keys: []string{h.Driver.GetSSHKeyPath()},
}
}

return ssh.NewClient(h.Driver.GetSSHUsername(), addr, port, auth)
Expand Down
11 changes: 9 additions & 2 deletions libmachine/ssh/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ const (
var (
baseSSHArgs = []string{
"-o", "PasswordAuthentication=no",
"-o", "IdentitiesOnly=yes",
"-o", "StrictHostKeyChecking=no",
"-o", "UserKnownHostsFile=/dev/null",
"-o", "LogLevel=quiet", // suppress "Warning: Permanently added '[localhost]:2022' (ECDSA) to the list of known hosts."
Expand Down Expand Up @@ -262,9 +261,17 @@ func NewExternalClient(sshBinaryPath, user, host string, port int, auth *Auth) (

args := append(baseSSHArgs, fmt.Sprintf("%s@%s", user, host))

// If no identities are explicitly provided, also look at the identities
// offered by ssh-agent
if len(auth.Keys) > 0 {
args = append(args, "-o", "IdentitiesOnly=yes")
}

// Specify which private keys to use to authorize the SSH request.
for _, privateKeyPath := range auth.Keys {
args = append(args, "-i", privateKeyPath)
if privateKeyPath != "" {
args = append(args, "-i", privateKeyPath)
}
}

// Set which port to use for SSH.
Expand Down

0 comments on commit d29a42e

Please sign in to comment.