From f47c41372e9b3da3cc7fc05ae8212762661b7d45 Mon Sep 17 00:00:00 2001 From: Rickard von Essen Date: Fri, 14 Jul 2017 21:25:20 +0200 Subject: [PATCH] cloudstack: Add support for ssh_agent_auth --- builder/cloudstack/builder.go | 9 ++- builder/cloudstack/ssh.go | 70 +++++++++++++------ builder/cloudstack/step_prepare_config.go | 9 +++ .../source/docs/builders/cloudstack.html.md | 7 ++ 4 files changed, 69 insertions(+), 26 deletions(-) diff --git a/builder/cloudstack/builder.go b/builder/cloudstack/builder.go index 593065537..369fe0cc6 100644 --- a/builder/cloudstack/builder.go +++ b/builder/cloudstack/builder.go @@ -66,9 +66,12 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe }, &stepSetupNetworking{}, &communicator.StepConnect{ - Config: &b.config.Comm, - Host: commHost, - SSHConfig: sshConfig, + Config: &b.config.Comm, + Host: commHost, + SSHConfig: SSHConfig( + b.config.Comm.SSHAgentAuth, + b.config.Comm.SSHUsername, + b.config.Comm.SSHPassword), }, &common.StepProvision{}, &stepShutdownInstance{}, diff --git a/builder/cloudstack/ssh.go b/builder/cloudstack/ssh.go index 83243a407..ab350d053 100644 --- a/builder/cloudstack/ssh.go +++ b/builder/cloudstack/ssh.go @@ -2,12 +2,14 @@ package cloudstack import ( "fmt" - "io/ioutil" + "net" + "os" packerssh "github.com/hashicorp/packer/communicator/ssh" "github.com/mitchellh/multistep" "github.com/xanzy/go-cloudstack/cloudstack" "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/agent" ) func commHost(state multistep.StateBag) (string, error) { @@ -26,32 +28,54 @@ func commHost(state multistep.StateBag) (string, error) { return config.hostAddress, nil } -func sshConfig(state multistep.StateBag) (*ssh.ClientConfig, error) { - config := state.Get("config").(*Config) +func SSHConfig(useAgent bool, username, password string) func(state multistep.StateBag) (*ssh.ClientConfig, error) { + return func(state multistep.StateBag) (*ssh.ClientConfig, error) { + if useAgent { + authSock := os.Getenv("SSH_AUTH_SOCK") + if authSock == "" { + return nil, fmt.Errorf("SSH_AUTH_SOCK is not set") + } - clientConfig := &ssh.ClientConfig{ - User: config.Comm.SSHUsername, - Auth: []ssh.AuthMethod{ - ssh.Password(config.Comm.SSHPassword), - ssh.KeyboardInteractive( - packerssh.PasswordKeyboardInteractive(config.Comm.SSHPassword)), - }, - HostKeyCallback: ssh.InsecureIgnoreHostKey(), - } + sshAgent, err := net.Dial("unix", authSock) + if err != nil { + return nil, fmt.Errorf("Cannot connect to SSH Agent socket %q: %s", authSock, err) + } - if config.Comm.SSHPrivateKey != "" { - privateKey, err := ioutil.ReadFile(config.Comm.SSHPrivateKey) - if err != nil { - return nil, fmt.Errorf("Error loading configured private key file: %s", err) + return &ssh.ClientConfig{ + User: username, + Auth: []ssh.AuthMethod{ + ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers), + }, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + }, nil } - signer, err := ssh.ParsePrivateKey(privateKey) - if err != nil { - return nil, fmt.Errorf("Error setting up SSH config: %s", err) + privateKey, hasKey := state.GetOk("privateKey") + + if hasKey { + signer, err := ssh.ParsePrivateKey([]byte(privateKey.(string))) + if err != nil { + return nil, fmt.Errorf("Error setting up SSH config: %s", err) + } + + return &ssh.ClientConfig{ + User: username, + Auth: []ssh.AuthMethod{ + ssh.PublicKeys(signer), + }, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + }, nil + + } else { + + return &ssh.ClientConfig{ + User: username, + HostKeyCallback: ssh.InsecureIgnoreHostKey(), + Auth: []ssh.AuthMethod{ + ssh.Password(password), + ssh.KeyboardInteractive( + packerssh.PasswordKeyboardInteractive(password)), + }}, nil } - - clientConfig.Auth = []ssh.AuthMethod{ssh.PublicKeys(signer)} } - - return clientConfig, nil } diff --git a/builder/cloudstack/step_prepare_config.go b/builder/cloudstack/step_prepare_config.go index af28a76a8..de397308e 100644 --- a/builder/cloudstack/step_prepare_config.go +++ b/builder/cloudstack/step_prepare_config.go @@ -22,6 +22,15 @@ func (s *stepPrepareConfig) Run(state multistep.StateBag) multistep.StepAction { var err error var errs *packer.MultiError + if config.Comm.SSHPrivateKey != "" { + privateKey, err := ioutil.ReadFile(config.Comm.SSHPrivateKey) + if err != nil { + errs = packer.MultiErrorAppend(errs, fmt.Errorf("Error loading configured private key file: %s", err)) + } + + state.Put("privateKey", privateKey) + } + // First get the project and zone UUID's so we can use them in other calls when needed. if config.Project != "" && !isUUID(config.Project) { config.Project, _, err = client.Project.GetProjectID(config.Project) diff --git a/website/source/docs/builders/cloudstack.html.md b/website/source/docs/builders/cloudstack.html.md index d0a10fb88..a3f701eaf 100644 --- a/website/source/docs/builders/cloudstack.html.md +++ b/website/source/docs/builders/cloudstack.html.md @@ -123,6 +123,13 @@ builder. connecting any provisioners to. If not provided, a temporary public IP address will be associated and released during the Packer run. +- `ssh_agent_auth` (boolean) - If true, the local SSH agent will be used to + authenticate connections to the source instance. No temporary keypair will + be created, and the values of `ssh_password` and `ssh_private_key_file` will + be ignored. To use this option with a key pair already configured in the source + image, leave the `keypair` blank. To associate an existing key pair + with the source instance, set the `keypair` field to the name of the key pair. + - `ssl_no_verify` (boolean) - Set to `true` to skip SSL verification. Defaults to `false`.