diff --git a/builder/null/builder.go b/builder/null/builder.go index 08eb363b5..1abc83398 100644 --- a/builder/null/builder.go +++ b/builder/null/builder.go @@ -32,6 +32,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe Config: &b.config.CommConfig, Host: CommHost(b.config.CommConfig.Host()), SSHConfig: SSHConfig( + b.config.CommConfig.SSHAgentAuth, b.config.CommConfig.SSHUsername, b.config.CommConfig.SSHPassword, b.config.CommConfig.SSHPrivateKey), diff --git a/builder/null/config.go b/builder/null/config.go index 17d4925a1..37287b182 100644 --- a/builder/null/config.go +++ b/builder/null/config.go @@ -41,14 +41,16 @@ func NewConfig(raws ...interface{}) (*Config, []string, error) { fmt.Errorf("a Username must be specified, please reference your communicator documentation")) } - if c.CommConfig.Password() == "" && c.CommConfig.SSHPrivateKey == "" { + if !c.CommConfig.SSHAgentAuth && c.CommConfig.Password() == "" && c.CommConfig.SSHPrivateKey == "" { errs = packer.MultiErrorAppend(errs, fmt.Errorf("one authentication method must be specified, please reference your communicator documentation")) } - if c.CommConfig.SSHPassword != "" && c.CommConfig.SSHPrivateKey != "" { + if (c.CommConfig.SSHAgentAuth && + (c.CommConfig.SSHPassword != "" || c.CommConfig.SSHPrivateKey != "")) || + (c.CommConfig.SSHPassword != "" && c.CommConfig.SSHPrivateKey != "") { errs = packer.MultiErrorAppend(errs, - fmt.Errorf("only one of ssh_password and ssh_private_key_file must be specified")) + fmt.Errorf("only one of ssh_agent_auth, ssh_password, and ssh_private_key_file must be specified")) } if errs != nil && len(errs.Errors) > 0 { diff --git a/builder/null/ssh.go b/builder/null/ssh.go index 7af2b55d7..1cbf4ceac 100644 --- a/builder/null/ssh.go +++ b/builder/null/ssh.go @@ -2,10 +2,14 @@ package null import ( "fmt" + "io/ioutil" + "net" + "os" + "github.com/hashicorp/packer/communicator/ssh" "github.com/mitchellh/multistep" gossh "golang.org/x/crypto/ssh" - "io/ioutil" + "golang.org/x/crypto/ssh/agent" ) func CommHost(host string) func(multistep.StateBag) (string, error) { @@ -17,8 +21,26 @@ func CommHost(host string) func(multistep.StateBag) (string, error) { // SSHConfig returns a function that can be used for the SSH communicator // config for connecting to the specified host via SSH // private_key_file has precedence over password! -func SSHConfig(username string, password string, privateKeyFile string) func(multistep.StateBag) (*gossh.ClientConfig, error) { +func SSHConfig(useAgent bool, username string, password string, privateKeyFile string) func(multistep.StateBag) (*gossh.ClientConfig, error) { return func(state multistep.StateBag) (*gossh.ClientConfig, error) { + if useAgent { + authSock := os.Getenv("SSH_AUTH_SOCK") + if authSock == "" { + return nil, fmt.Errorf("SSH_AUTH_SOCK is not set") + } + + sshAgent, err := net.Dial("unix", authSock) + if err != nil { + return nil, fmt.Errorf("Cannot connect to SSH Agent socket %q: %s", authSock, err) + } + + return &gossh.ClientConfig{ + User: username, + Auth: []gossh.AuthMethod{ + gossh.PublicKeysCallback(agent.NewClient(sshAgent).Signers), + }, + }, nil + } if privateKeyFile != "" { // key based auth