@ -78,8 +78,7 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
errs = packer . MultiErrorAppend ( errs , err )
}
// Check that the authorized key file exists ( this should really be called the public key )
// Check for either file ( if you specify either file you must specify both files )
// Check that the authorized key file exists
if len ( p . config . SSHAuthorizedKeyFile ) > 0 {
err = validateFileConfig ( p . config . SSHAuthorizedKeyFile , "ssh_authorized_key_file" , true )
if err != nil {
@ -109,107 +108,18 @@ func (p *Provisioner) Prepare(raws ...interface{}) error {
return nil
}
type Keys struct {
//! This is the public key that we will allow to authenticate
public ssh . PublicKey
//! This is the name of the file of the private key that coorlates
//the the public key
filename string
//! This is the servers private key ( Set in case the public key
//is autogenerated )
private ssh . Signer
//! This is the flag to say the server key was generated
generated bool
}
func ( p * Provisioner ) Provision ( ui packer . Ui , comm packer . Communicator ) error {
ui . Say ( "Provisioning with Ansible..." )
keyFactory := func ( pubKeyFile string , privKeyFile string ) ( * Keys , error ) {
var public ssh . PublicKey
var private ssh . Signer
var filename string = ""
var generated bool = false
if len ( pubKeyFile ) > 0 {
pubKeyBytes , err := ioutil . ReadFile ( pubKeyFile )
if err != nil {
return nil , errors . New ( "Failed to read public key" )
}
public , _ , _ , _ , err = ssh . ParseAuthorizedKey ( pubKeyBytes )
if err != nil {
return nil , errors . New ( "Failed to parse authorized key" )
}
} else {
key , err := rsa . GenerateKey ( rand . Reader , 2048 )
if err != nil {
return nil , errors . New ( "Failed to generate key pair" )
}
public , err = ssh . NewPublicKey ( key . Public ( ) )
if err != nil {
return nil , errors . New ( "Failed to extract public key from generated key pair" )
}
// To support Ansible calling back to us we need to write
// this file down
privateKeyDer := x509 . MarshalPKCS1PrivateKey ( key )
privateKeyBlock := pem . Block {
Type : "RSA PRIVATE KEY" ,
Headers : nil ,
Bytes : privateKeyDer ,
}
tf , err := ioutil . TempFile ( "" , "ansible-key" )
if err != nil {
return nil , errors . New ( "failed to create temp file for generated key" )
}
_ , err = tf . Write ( pem . EncodeToMemory ( & privateKeyBlock ) )
if err != nil {
return nil , errors . New ( "failed to write private key to temp file" )
}
err = tf . Close ( )
if err != nil {
return nil , errors . New ( "failed to close private key temp file" )
}
filename = tf . Name ( )
}
if len ( privKeyFile ) > 0 {
privateBytes , err := ioutil . ReadFile ( privKeyFile )
if err != nil {
return nil , errors . New ( "Failed to load private host key" )
}
private , err = ssh . ParsePrivateKey ( privateBytes )
if err != nil {
return nil , errors . New ( "Failed to parse private host key" )
}
} else {
key , err := rsa . GenerateKey ( rand . Reader , 2048 )
if err != nil {
return nil , errors . New ( "Failed to generate server key pair" )
}
private , err = ssh . NewSignerFromKey ( key )
if err != nil {
return nil , errors . New ( "Failed to extract private key from generated key pair" )
}
generated = true
}
return & Keys { public , filename , private , generated } , nil
}
k , err := keyFactory ( p . config . SSHAuthorizedKeyFile , p . config . SSHHostKeyFile )
k , err := newUserKey ( p . config . SSHAuthorizedKeyFile )
if err != nil {
return err
}
hostSigner , err := newSigner ( p . config . SSHHostKeyFile )
// Remove the private key file
if len ( k . filenam e) > 0 {
defer os . Remove ( k . filenam e)
if len ( k . privKeyFile ) > 0 {
defer os . Remove ( k . privKeyFile )
}
keyChecker := ssh . CertChecker {
@ -219,7 +129,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
return nil , errors . New ( "authentication failed" )
}
if ! bytes . Equal ( k . public . Marshal ( ) , pubKey . Marshal ( ) ) {
if ! bytes . Equal ( k . Marshal ( ) , pubKey . Marshal ( ) ) {
ui . Say ( "unauthorized key" )
return nil , errors . New ( "authentication failed" )
}
@ -236,7 +146,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
//NoClientAuth: true,
}
config . AddHostKey ( k. private )
config . AddHostKey ( hostSigner )
localListener , err := func ( ) ( net . Listener , error ) {
port , err := strconv . ParseUint ( p . config . LocalPort , 10 , 16 )
@ -299,7 +209,7 @@ func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) error {
} ( )
}
if err := p . executeAnsible ( ui , comm , k . filename, k . generated ) ; err != nil {
if err := p . executeAnsible ( ui , comm , k . privKeyFile, ! hostSigner . generated ) ; err != nil {
return fmt . Errorf ( "Error executing Ansible: %s" , err )
}
@ -317,20 +227,19 @@ func (p *Provisioner) Cancel() {
os . Exit ( 0 )
}
func ( p * Provisioner ) executeAnsible ( ui packer . Ui , comm packer . Communicator , authToken string , generated bool ) error {
func ( p * Provisioner ) executeAnsible ( ui packer . Ui , comm packer . Communicator , privKeyFile string , checkHostKey bool ) error {
playbook , _ := filepath . Abs ( p . config . PlaybookFile )
inventory := p . config . inventoryFile
args := [ ] string { playbook , "-i" , inventory }
if len ( authToken ) > 0 {
args = append ( args , "--private-key" , authToken )
if len ( privKeyFile ) > 0 {
args = append ( args , "--private-key" , privKeyFile )
}
args = append ( args , p . config . ExtraArguments ... )
cmd := exec . Command ( p . config . Command , args ... )
// If we have autogenerated the key files turn off host key checking
if generated {
if ! checkHostKey {
cmd . Env = os . Environ ( )
cmd . Env = append ( cmd . Env , "ANSIBLE_HOST_KEY_CHECKING=False" )
}
@ -385,6 +294,97 @@ func validateFileConfig(name string, config string, req bool) error {
return nil
}
type userKey struct {
ssh . PublicKey
privKeyFile string
}
func newUserKey ( pubKeyFile string ) ( * userKey , error ) {
userKey := new ( userKey )
if len ( pubKeyFile ) > 0 {
pubKeyBytes , err := ioutil . ReadFile ( pubKeyFile )
if err != nil {
return nil , errors . New ( "Failed to read public key" )
}
userKey . PublicKey , _ , _ , _ , err = ssh . ParseAuthorizedKey ( pubKeyBytes )
if err != nil {
return nil , errors . New ( "Failed to parse authorized key" )
}
return userKey , nil
}
key , err := rsa . GenerateKey ( rand . Reader , 2048 )
if err != nil {
return nil , errors . New ( "Failed to generate key pair" )
}
userKey . PublicKey , err = ssh . NewPublicKey ( key . Public ( ) )
if err != nil {
return nil , errors . New ( "Failed to extract public key from generated key pair" )
}
// To support Ansible calling back to us we need to write
// this file down
privateKeyDer := x509 . MarshalPKCS1PrivateKey ( key )
privateKeyBlock := pem . Block {
Type : "RSA PRIVATE KEY" ,
Headers : nil ,
Bytes : privateKeyDer ,
}
tf , err := ioutil . TempFile ( "" , "ansible-key" )
if err != nil {
return nil , errors . New ( "failed to create temp file for generated key" )
}
_ , err = tf . Write ( pem . EncodeToMemory ( & privateKeyBlock ) )
if err != nil {
return nil , errors . New ( "failed to write private key to temp file" )
}
err = tf . Close ( )
if err != nil {
return nil , errors . New ( "failed to close private key temp file" )
}
userKey . privKeyFile = tf . Name ( )
return userKey , nil
}
type signer struct {
ssh . Signer
generated bool
}
func newSigner ( privKeyFile string ) ( * signer , error ) {
signer := new ( signer )
if len ( privKeyFile ) > 0 {
privateBytes , err := ioutil . ReadFile ( privKeyFile )
if err != nil {
return nil , errors . New ( "Failed to load private host key" )
}
signer . Signer , err = ssh . ParsePrivateKey ( privateBytes )
if err != nil {
return nil , errors . New ( "Failed to parse private host key" )
}
return signer , nil
}
key , err := rsa . GenerateKey ( rand . Reader , 2048 )
if err != nil {
return nil , errors . New ( "Failed to generate server key pair" )
}
signer . Signer , err = ssh . NewSignerFromKey ( key )
if err != nil {
return nil , errors . New ( "Failed to extract private key from generated key pair" )
}
signer . generated = true
return signer , nil
}
// Ui provides concurrency-safe access to packer.Ui.
type Ui struct {
sem chan int