diff --git a/builtin/provisioners/file/resource_provisioner.go b/builtin/provisioners/file/resource_provisioner.go index bb95c0860330..8b9e14570f1d 100644 --- a/builtin/provisioners/file/resource_provisioner.go +++ b/builtin/provisioners/file/resource_provisioner.go @@ -60,6 +60,7 @@ func (p *ResourceProvisioner) copyFiles(conf *helper.SSHConfig, src, dst string) if err != nil { return err } + defer config.CleanupConfig() // Wait and retry until we establish the SSH connection var comm *helper.SSHCommunicator diff --git a/builtin/provisioners/remote-exec/resource_provisioner.go b/builtin/provisioners/remote-exec/resource_provisioner.go index b3f0d0c0e962..046e0e860cfb 100644 --- a/builtin/provisioners/remote-exec/resource_provisioner.go +++ b/builtin/provisioners/remote-exec/resource_provisioner.go @@ -172,16 +172,20 @@ func (p *ResourceProvisioner) runScripts( if err != nil { return err } + defer config.CleanupConfig() o.Output(fmt.Sprintf( "Connecting to remote host via SSH...\n"+ " Host: %s\n"+ " User: %s\n"+ " Password: %v\n"+ - " Private key: %v", + " Private key: %v"+ + " SSH Agent: %v", conf.Host, conf.User, conf.Password != "", - conf.KeyFile != "")) + conf.KeyFile != "", + conf.Agent, + )) // Wait and retry until we establish the SSH connection var comm *helper.SSHCommunicator diff --git a/helper/ssh/communicator.go b/helper/ssh/communicator.go index 817f37368de9..f908de97dfa3 100644 --- a/helper/ssh/communicator.go +++ b/helper/ssh/communicator.go @@ -97,6 +97,10 @@ type Config struct { // NoPty, if true, will not request a pty from the remote end. NoPty bool + + // SSHAgentConn is a pointer to the UNIX connection for talking with the + // ssh-agent. + SSHAgentConn net.Conn } // New creates a new packer.Communicator implementation over SSH. This takes diff --git a/helper/ssh/provisioner.go b/helper/ssh/provisioner.go index 2d60d893477b..bf8f526373d6 100644 --- a/helper/ssh/provisioner.go +++ b/helper/ssh/provisioner.go @@ -5,12 +5,15 @@ import ( "fmt" "io/ioutil" "log" + "net" + "os" "time" - "golang.org/x/crypto/ssh" "github.com/hashicorp/terraform/terraform" "github.com/mitchellh/go-homedir" "github.com/mitchellh/mapstructure" + "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/agent" ) const ( @@ -37,6 +40,7 @@ type SSHConfig struct { KeyFile string `mapstructure:"key_file"` Host string Port int + Agent bool Timeout string ScriptPath string `mapstructure:"script_path"` TimeoutVal time.Duration `mapstructure:"-"` @@ -99,9 +103,32 @@ func safeDuration(dur string, defaultDur time.Duration) time.Duration { // PrepareConfig is used to turn the *SSHConfig provided into a // usable *Config for client initialization. func PrepareConfig(conf *SSHConfig) (*Config, error) { + var conn net.Conn + var err error + sshConf := &ssh.ClientConfig{ User: conf.User, } + if conf.Agent { + sshAuthSock := os.Getenv("SSH_AUTH_SOCK") + + if sshAuthSock == "" { + return nil, fmt.Errorf("SSH Requested but SSH_AUTH_SOCK not-specified") + } + + conn, err = net.Dial("unix", sshAuthSock) + if err != nil { + return nil, fmt.Errorf("Error connecting to SSH_AUTH_SOCK: %v", err) + } + // I need to close this but, later after all connections have been made + // defer conn.Close() + signers, err := agent.NewClient(conn).Signers() + if err != nil { + return nil, fmt.Errorf("Error getting keys from ssh agent: %v", err) + } + + sshConf.Auth = append(sshConf.Auth, ssh.PublicKeys(signers...)) + } if conf.KeyFile != "" { fullPath, err := homedir.Expand(conf.KeyFile) if err != nil { @@ -140,8 +167,17 @@ func PrepareConfig(conf *SSHConfig) (*Config, error) { } host := fmt.Sprintf("%s:%d", conf.Host, conf.Port) config := &Config{ - SSHConfig: sshConf, - Connection: ConnectFunc("tcp", host), + SSHConfig: sshConf, + Connection: ConnectFunc("tcp", host), + SSHAgentConn: conn, } return config, nil } + +func (c *Config) CleanupConfig() error { + if c.SSHAgentConn != nil { + return c.SSHAgentConn.Close() + } + + return nil +} diff --git a/website/source/docs/provisioners/connection.html.markdown b/website/source/docs/provisioners/connection.html.markdown index af55fb2e433e..6d289c6dadb7 100644 --- a/website/source/docs/provisioners/connection.html.markdown +++ b/website/source/docs/provisioners/connection.html.markdown @@ -46,6 +46,8 @@ The following arguments are supported: * `key_file` - The SSH key to use for the connection. This takes preference over the password if provided. +* `agent` - Set to true to enable using ssh-agent to authenticate. + * `host` - The address of the resource to connect to. This is provided by the provider. * `port` - The port to connect to. This defaults to 22.