builder/amazon: Allow use of local SSH Agent
This commit adds an option to use the local SSH Agent to authenticate connections to source instances started by the the EBS and Instance Store builders. This is of use when the source AMI _already_ has configuration for authorized SSH keys - for example if one uses an SSH certificate authority. A further extension (not implemented in this commit) is to allow SSH agent use with a pre-defined key pair, in order to allow keys with passphrases to be used without giving the passphrase to Packer.
This commit is contained in:
parent
8e7f3778b2
commit
7425fef2c7
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
@ -24,6 +25,11 @@ type StepKeyPair struct {
|
||||||
func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction {
|
func (s *StepKeyPair) Run(state multistep.StateBag) multistep.StepAction {
|
||||||
ui := state.Get("ui").(packer.Ui)
|
ui := state.Get("ui").(packer.Ui)
|
||||||
|
|
||||||
|
if s.SSHAgentAuth {
|
||||||
|
ui.Say("Using SSH Agent")
|
||||||
|
return multistep.ActionContinue
|
||||||
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
|
|
@ -148,6 +148,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),
|
||||||
},
|
},
|
||||||
|
|
|
@ -230,6 +230,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),
|
||||||
},
|
},
|
||||||
|
|
|
@ -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"`
|
||||||
|
|
|
@ -194,6 +194,12 @@ builder.
|
||||||
[`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.
|
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
|
- `ssh_private_ip` (boolean) - If true, then SSH will always use the private
|
||||||
IP if available.
|
IP if available.
|
||||||
|
|
||||||
|
|
|
@ -212,6 +212,12 @@ builder.
|
||||||
[`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.
|
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
|
- `ssh_private_ip` (boolean) - If true, then SSH will always use the private
|
||||||
IP if available.
|
IP if available.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue