diff --git a/builder/amazon/common/ssh.go b/builder/amazon/common/ssh.go index 64479e841..3057bf171 100644 --- a/builder/amazon/common/ssh.go +++ b/builder/amazon/common/ssh.go @@ -3,12 +3,15 @@ package common import ( "errors" "fmt" + "net" + "os" "time" "github.com/aws/aws-sdk-go/service/ec2" "github.com/mitchellh/multistep" packerssh "github.com/mitchellh/packer/communicator/ssh" "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/agent" ) type ec2Describer interface { @@ -67,8 +70,26 @@ func SSHHost(e ec2Describer, private bool) func(multistep.StateBag) (string, err // SSHConfig returns a function that can be used for the SSH communicator // config for connecting to the instance created over SSH using the private key // or password. -func SSHConfig(username, password string) func(multistep.StateBag) (*ssh.ClientConfig, error) { +func SSHConfig(useAgent bool, username, password string) func(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") + } + + sshAgent, err := net.Dial("unix", authSock) + if err != nil { + return nil, fmt.Errorf("Cannot connect to SSH Agent socket %q: %s", authSock, err) + } + + return &ssh.ClientConfig{ + User: username, + Auth: []ssh.AuthMethod{ + ssh.PublicKeysCallback(agent.NewClient(sshAgent).Signers), + }, + }, nil + } privateKey, hasKey := state.GetOk("privateKey") if hasKey { diff --git a/builder/amazon/common/step_key_pair.go b/builder/amazon/common/step_key_pair.go index 0a3cb1cb5..5f3e536de 100644 --- a/builder/amazon/common/step_key_pair.go +++ b/builder/amazon/common/step_key_pair.go @@ -13,6 +13,7 @@ import ( type StepKeyPair struct { Debug bool + SSHAgentAuth bool DebugKeyPath string TemporaryKeyPairName string KeyPairName string @@ -24,6 +25,11 @@ type StepKeyPair struct { func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction { ui := state.Get("ui").(packer.Ui) + if s.SSHAgentAuth { + ui.Say("Using SSH Agent") + return multistep.ActionContinue + } + if s.PrivateKeyFile != "" { ui.Say("Using existing ssh private key") privateKeyBytes, err := ioutil.ReadFile(s.PrivateKeyFile) diff --git a/builder/amazon/ebs/builder.go b/builder/amazon/ebs/builder.go index 5d9cced79..904e004c0 100644 --- a/builder/amazon/ebs/builder.go +++ b/builder/amazon/ebs/builder.go @@ -148,6 +148,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe ec2conn, b.config.SSHPrivateIp), SSHConfig: awscommon.SSHConfig( + b.config.RunConfig.Comm.SSHAgentAuth, b.config.RunConfig.Comm.SSHUsername, b.config.RunConfig.Comm.SSHPassword), }, diff --git a/builder/amazon/instance/builder.go b/builder/amazon/instance/builder.go index c057f9f58..bbf4e4f18 100644 --- a/builder/amazon/instance/builder.go +++ b/builder/amazon/instance/builder.go @@ -230,6 +230,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe ec2conn, b.config.SSHPrivateIp), SSHConfig: awscommon.SSHConfig( + b.config.RunConfig.Comm.SSHAgentAuth, b.config.RunConfig.Comm.SSHUsername, b.config.RunConfig.Comm.SSHPassword), }, diff --git a/helper/communicator/config.go b/helper/communicator/config.go index 7c36e70d9..66e4e96fa 100644 --- a/helper/communicator/config.go +++ b/helper/communicator/config.go @@ -23,6 +23,7 @@ type Config struct { SSHPrivateKey string `mapstructure:"ssh_private_key_file"` SSHPty bool `mapstructure:"ssh_pty"` SSHTimeout time.Duration `mapstructure:"ssh_timeout"` + SSHAgentAuth bool `mapstructure:"ssh_agent_auth"` SSHDisableAgent bool `mapstructure:"ssh_disable_agent"` SSHHandshakeAttempts int `mapstructure:"ssh_handshake_attempts"` SSHBastionHost string `mapstructure:"ssh_bastion_host"` diff --git a/website/source/docs/builders/amazon-ebs.html.md b/website/source/docs/builders/amazon-ebs.html.md index 9667e931f..841f464ef 100644 --- a/website/source/docs/builders/amazon-ebs.html.md +++ b/website/source/docs/builders/amazon-ebs.html.md @@ -194,6 +194,12 @@ builder. [`ssh_private_key_file`](/docs/templates/communicator.html#ssh_private_key_file) must be specified with this. +- `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. This is suitable for use if the source AMI already has authorized + keys configured. + - `ssh_private_ip` (boolean) - If true, then SSH will always use the private IP if available. diff --git a/website/source/docs/builders/amazon-instance.html.md b/website/source/docs/builders/amazon-instance.html.md index e80f2a773..474197ea8 100644 --- a/website/source/docs/builders/amazon-instance.html.md +++ b/website/source/docs/builders/amazon-instance.html.md @@ -212,6 +212,12 @@ builder. [`ssh_private_key_file`](/docs/templates/communicator.html#ssh_private_key_file) must be specified when `ssh_keypair_name` is utilized. +- `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. This is suitable for use if the source AMI already has authorized + keys configured. + - `ssh_private_ip` (boolean) - If true, then SSH will always use the private IP if available.