Merge pull request #4050 from jen20/ssh-agent

builder/amazon: Allow use of local SSH Agent
This commit is contained in:
Matthew Hooker 2016-11-01 17:27:37 -07:00 committed by GitHub
commit 970b37077e
7 changed files with 58 additions and 5 deletions

View File

@ -3,12 +3,15 @@ package common
import ( import (
"errors" "errors"
"fmt" "fmt"
"net"
"os"
"time" "time"
"github.com/aws/aws-sdk-go/service/ec2" "github.com/aws/aws-sdk-go/service/ec2"
"github.com/mitchellh/multistep" "github.com/mitchellh/multistep"
packerssh "github.com/mitchellh/packer/communicator/ssh" packerssh "github.com/mitchellh/packer/communicator/ssh"
"golang.org/x/crypto/ssh" "golang.org/x/crypto/ssh"
"golang.org/x/crypto/ssh/agent"
) )
type ec2Describer interface { 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 // 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 // config for connecting to the instance created over SSH using the private key
// or password. // 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) { 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") privateKey, hasKey := state.GetOk("privateKey")
if hasKey { if hasKey {

View File

@ -13,6 +13,7 @@ import (
type StepKeyPair struct { type StepKeyPair struct {
Debug bool Debug bool
SSHAgentAuth bool
DebugKeyPath string DebugKeyPath string
TemporaryKeyPairName string TemporaryKeyPairName string
KeyPairName string KeyPairName string
@ -25,7 +26,7 @@ func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction {
ui := state.Get("ui").(packer.Ui) ui := state.Get("ui").(packer.Ui)
if s.PrivateKeyFile != "" { if s.PrivateKeyFile != "" {
ui.Say("Using existing ssh private key") ui.Say("Using existing SSH private key")
privateKeyBytes, err := ioutil.ReadFile(s.PrivateKeyFile) privateKeyBytes, err := ioutil.ReadFile(s.PrivateKeyFile)
if err != nil { if err != nil {
state.Put("error", fmt.Errorf( state.Put("error", fmt.Errorf(
@ -39,6 +40,17 @@ func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction {
return multistep.ActionContinue return multistep.ActionContinue
} }
if s.SSHAgentAuth && s.KeyPairName == "" {
ui.Say("Using SSH Agent with key pair in Source AMI")
return multistep.ActionContinue
}
if s.SSHAgentAuth && s.KeyPairName != "" {
ui.Say(fmt.Sprintf("Using SSH Agent for existing key pair %s", s.KeyPairName))
state.Put("keyPair", s.KeyPairName)
return multistep.ActionContinue
}
if s.TemporaryKeyPairName == "" { if s.TemporaryKeyPairName == "" {
ui.Say("Not using temporary keypair") ui.Say("Not using temporary keypair")
state.Put("keyPair", "") state.Put("keyPair", "")

View File

@ -149,6 +149,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
ec2conn, ec2conn,
b.config.SSHPrivateIp), b.config.SSHPrivateIp),
SSHConfig: awscommon.SSHConfig( SSHConfig: awscommon.SSHConfig(
b.config.RunConfig.Comm.SSHAgentAuth,
b.config.RunConfig.Comm.SSHUsername, b.config.RunConfig.Comm.SSHUsername,
b.config.RunConfig.Comm.SSHPassword), b.config.RunConfig.Comm.SSHPassword),
}, },

View File

@ -231,6 +231,7 @@ func (b *Builder) Run(ui packer.Ui, hook packer.Hook, cache packer.Cache) (packe
ec2conn, ec2conn,
b.config.SSHPrivateIp), b.config.SSHPrivateIp),
SSHConfig: awscommon.SSHConfig( SSHConfig: awscommon.SSHConfig(
b.config.RunConfig.Comm.SSHAgentAuth,
b.config.RunConfig.Comm.SSHUsername, b.config.RunConfig.Comm.SSHUsername,
b.config.RunConfig.Comm.SSHPassword), b.config.RunConfig.Comm.SSHPassword),
}, },

View File

@ -23,6 +23,7 @@ type Config struct {
SSHPrivateKey string `mapstructure:"ssh_private_key_file"` SSHPrivateKey string `mapstructure:"ssh_private_key_file"`
SSHPty bool `mapstructure:"ssh_pty"` SSHPty bool `mapstructure:"ssh_pty"`
SSHTimeout time.Duration `mapstructure:"ssh_timeout"` SSHTimeout time.Duration `mapstructure:"ssh_timeout"`
SSHAgentAuth bool `mapstructure:"ssh_agent_auth"`
SSHDisableAgent bool `mapstructure:"ssh_disable_agent"` SSHDisableAgent bool `mapstructure:"ssh_disable_agent"`
SSHHandshakeAttempts int `mapstructure:"ssh_handshake_attempts"` SSHHandshakeAttempts int `mapstructure:"ssh_handshake_attempts"`
SSHBastionHost string `mapstructure:"ssh_bastion_host"` SSHBastionHost string `mapstructure:"ssh_bastion_host"`

View File

@ -217,11 +217,20 @@ builder.
`Linux/UNIX (Amazon VPC)`, `SUSE Linux (Amazon VPC)`, `Windows (Amazon VPC)` `Linux/UNIX (Amazon VPC)`, `SUSE Linux (Amazon VPC)`, `Windows (Amazon VPC)`
- `ssh_keypair_name` (string) - If specified, this is the key that will be - `ssh_keypair_name` (string) - If specified, this is the key that will be
used for SSH with the machine. By default, this is blank, and Packer will used for SSH with the machine. The key must match a key pair name loaded
up into Amazon EC2. By default, this is blank, and Packer will
generate a temporary keypair unless generate a temporary keypair unless
[`ssh_password`](/docs/templates/communicator.html#ssh_password) is used. [`ssh_password`](/docs/templates/communicator.html#ssh_password) is used.
[`ssh_private_key_file`](/docs/templates/communicator.html#ssh_private_key_file) [`ssh_private_key_file`](/docs/templates/communicator.html#ssh_private_key_file)
must be specified with this. or `ssh_agent_auth` 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. To use this option with a key pair already configured in the source
AMI, leave the `ssh_keypair_name` blank. To associate an existing key pair
in AWS with the source instance, set the `ssh_keypair_name` field to the name
of the key pair.
- `ssh_private_ip` (boolean) - If true, then SSH will always use the private - `ssh_private_ip` (boolean) - If true, then SSH will always use the private
IP if available. IP if available.

View File

@ -238,8 +238,16 @@ builder.
generate a temporary keypair unless generate a temporary keypair unless
[`ssh_password`](/docs/templates/communicator.html#ssh_password) is used. [`ssh_password`](/docs/templates/communicator.html#ssh_password) is used.
[`ssh_private_key_file`](/docs/templates/communicator.html#ssh_private_key_file) [`ssh_private_key_file`](/docs/templates/communicator.html#ssh_private_key_file)
must be specified when `ssh_keypair_name` is utilized. or `ssh_agent_auth` 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. To use this option with a key pair already configured in the source
AMI, leave the `ssh_keypair_name` blank. To associate an existing key pair
in AWS with the source instance, set the `ssh_keypair_name` field to the name
of the key pair.
- `ssh_private_ip` (boolean) - If true, then SSH will always use the private - `ssh_private_ip` (boolean) - If true, then SSH will always use the private
IP if available. IP if available.