mirror of https://github.com/hashicorp/packer
Merge pull request #10111 from hashicorp/azr_selectable_temp_keygen_type_gcp
GCP: Allow to select algo when generating temporary SSH keypairpull/10190/head
commit
69312458c4
@ -1,88 +0,0 @@
|
||||
package googlecompute
|
||||
|
||||
import (
|
||||
"context"
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
"golang.org/x/crypto/ssh"
|
||||
)
|
||||
|
||||
// StepCreateSSHKey represents a Packer build step that generates SSH key pairs.
|
||||
type StepCreateSSHKey struct {
|
||||
Debug bool
|
||||
DebugKeyPath string
|
||||
}
|
||||
|
||||
// Run executes the Packer build step that generates SSH key pairs.
|
||||
// The key pairs are added to the ssh config
|
||||
func (s *StepCreateSSHKey) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
config := state.Get("config").(*Config)
|
||||
|
||||
if config.Comm.SSHPrivateKeyFile != "" {
|
||||
ui.Say("Using existing SSH private key")
|
||||
privateKeyBytes, err := config.Comm.ReadSSHPrivateKeyFile()
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
config.Comm.SSHPrivateKey = privateKeyBytes
|
||||
config.Comm.SSHPublicKey = nil
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
ui.Say("Creating temporary SSH key for instance...")
|
||||
priv, err := rsa.GenerateKey(rand.Reader, 2048)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating temporary ssh key: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
priv_blk := pem.Block{
|
||||
Type: "RSA PRIVATE KEY",
|
||||
Headers: nil,
|
||||
Bytes: x509.MarshalPKCS1PrivateKey(priv),
|
||||
}
|
||||
|
||||
pub, err := ssh.NewPublicKey(&priv.PublicKey)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating temporary ssh key: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
config.Comm.SSHPrivateKey = pem.EncodeToMemory(&priv_blk)
|
||||
config.Comm.SSHPublicKey = ssh.MarshalAuthorizedKey(pub)
|
||||
|
||||
if s.Debug {
|
||||
ui.Message(fmt.Sprintf("Saving key for debug purposes: %s", s.DebugKeyPath))
|
||||
f, err := os.Create(s.DebugKeyPath)
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("Error saving debug key: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
// Write out the key
|
||||
err = pem.Encode(f, &priv_blk)
|
||||
f.Close()
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("Error saving debug key: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
}
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// Nothing to clean up. SSH keys are associated with a single GCE instance.
|
||||
func (s *StepCreateSSHKey) Cleanup(state multistep.StateBag) {}
|
||||
@ -1,86 +0,0 @@
|
||||
package googlecompute
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStepCreateSSHKey_impl(t *testing.T) {
|
||||
var _ multistep.Step = new(StepCreateSSHKey)
|
||||
}
|
||||
|
||||
func TestStepCreateSSHKey_privateKey(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCreateSSHKey)
|
||||
cfg := state.Get("config").(*Config)
|
||||
cfg.Comm.SSHPrivateKeyFile = "test-fixtures/fake-key"
|
||||
defer step.Cleanup(state)
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify that we have a public/private key
|
||||
if len(cfg.Comm.SSHPrivateKey) == 0 {
|
||||
t.Fatal("should have key")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateSSHKey(t *testing.T) {
|
||||
state := testState(t)
|
||||
step := new(StepCreateSSHKey)
|
||||
cfg := state.Get("config").(*Config)
|
||||
defer step.Cleanup(state)
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify that we have a public/private key
|
||||
if len(cfg.Comm.SSHPrivateKey) == 0 {
|
||||
t.Fatal("should have key")
|
||||
}
|
||||
if len(cfg.Comm.SSHPublicKey) == 0 {
|
||||
t.Fatal("should have key")
|
||||
}
|
||||
}
|
||||
|
||||
func TestStepCreateSSHKey_debug(t *testing.T) {
|
||||
tf, err := ioutil.TempFile("", "packer")
|
||||
if err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
defer os.Remove(tf.Name())
|
||||
tf.Close()
|
||||
|
||||
state := testState(t)
|
||||
step := new(StepCreateSSHKey)
|
||||
cfg := state.Get("config").(*Config)
|
||||
step.Debug = true
|
||||
step.DebugKeyPath = tf.Name()
|
||||
|
||||
defer step.Cleanup(state)
|
||||
|
||||
// run the step
|
||||
if action := step.Run(context.Background(), state); action != multistep.ActionContinue {
|
||||
t.Fatalf("bad action: %#v", action)
|
||||
}
|
||||
|
||||
// Verify that we have a public/private key
|
||||
if len(cfg.Comm.SSHPrivateKey) == 0 {
|
||||
t.Fatal("should have key")
|
||||
}
|
||||
if len(cfg.Comm.SSHPublicKey) == 0 {
|
||||
t.Fatal("should have key")
|
||||
}
|
||||
if _, err := os.Stat(tf.Name()); err != nil {
|
||||
t.Fatalf("err: %s", err)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package communicator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
// StepDumpSSHKey is a multistep Step implementation that writes the ssh
|
||||
// keypair somewhere.
|
||||
type StepDumpSSHKey struct {
|
||||
Path string
|
||||
SSH *SSH
|
||||
}
|
||||
|
||||
func (s *StepDumpSSHKey) Run(_ context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
|
||||
ui.Message(fmt.Sprintf("Saving key for debug purposes: %s", s.Path))
|
||||
|
||||
err := ioutil.WriteFile(s.Path, s.SSH.SSHPrivateKey, 0700)
|
||||
if err != nil {
|
||||
state.Put("error", fmt.Errorf("Error saving debug key: %s", err))
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
func (s *StepDumpSSHKey) Cleanup(state multistep.StateBag) {}
|
||||
@ -0,0 +1,65 @@
|
||||
package communicator
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/hashicorp/packer/helper/communicator/sshkey"
|
||||
"github.com/hashicorp/packer/helper/multistep"
|
||||
"github.com/hashicorp/packer/packer"
|
||||
)
|
||||
|
||||
// StepSSHKeyGen is a Packer build step that generates SSH key pairs.
|
||||
type StepSSHKeyGen struct {
|
||||
CommConf *Config
|
||||
SSHTemporaryKeyPair
|
||||
}
|
||||
|
||||
// Run executes the Packer build step that generates SSH key pairs.
|
||||
// The key pairs are added to the ssh config
|
||||
func (s *StepSSHKeyGen) Run(ctx context.Context, state multistep.StateBag) multistep.StepAction {
|
||||
ui := state.Get("ui").(packer.Ui)
|
||||
comm := s.CommConf
|
||||
|
||||
if comm.SSHPrivateKeyFile != "" {
|
||||
ui.Say("Using existing SSH private key")
|
||||
privateKeyBytes, err := comm.ReadSSHPrivateKeyFile()
|
||||
if err != nil {
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
comm.SSHPrivateKey = privateKeyBytes
|
||||
comm.SSHPublicKey = nil
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
algorithm := s.SSHTemporaryKeyPair.SSHTemporaryKeyPairType
|
||||
if algorithm == "" {
|
||||
algorithm = sshkey.RSA.String()
|
||||
}
|
||||
a, err := sshkey.AlgorithmString(algorithm)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("%w: possible algorithm types are `dsa` | `ecdsa` | `ed25519` | `rsa` ( the default )", err)
|
||||
state.Put("error", err)
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
ui.Say(fmt.Sprintf("Creating temporary %s SSH key for instance...", a.String()))
|
||||
pair, err := sshkey.GeneratePair(a, nil, s.SSHTemporaryKeyPairBits)
|
||||
if err != nil {
|
||||
err := fmt.Errorf("Error creating temporary ssh key: %s", err)
|
||||
state.Put("error", err)
|
||||
ui.Error(err.Error())
|
||||
return multistep.ActionHalt
|
||||
}
|
||||
|
||||
comm.SSHPrivateKey = pair.Private
|
||||
comm.SSHPublicKey = pair.Public
|
||||
|
||||
return multistep.ActionContinue
|
||||
}
|
||||
|
||||
// Nothing to clean up. SSH keys are associated with a single GCE instance.
|
||||
func (s *StepSSHKeyGen) Cleanup(state multistep.StateBag) {}
|
||||
@ -0,0 +1,9 @@
|
||||
package multistep
|
||||
|
||||
// if returns step only if on is true.
|
||||
func If(on bool, step Step) Step {
|
||||
if on == false {
|
||||
return nil
|
||||
}
|
||||
return step
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
<!-- Code generated from the comments of the SSHTemporaryKeyPair struct in helper/communicator/config.go; DO NOT EDIT MANUALLY -->
|
||||
|
||||
- `temporary_key_pair_type` (string) - `dsa` | `ecdsa` | `ed25519` | `rsa` ( the default )
|
||||
|
||||
Specifies the type of key to create. The possible values are 'dsa',
|
||||
'ecdsa', 'ed25519', or 'rsa'.
|
||||
|
||||
- `temporary_key_pair_bits` (int) - Specifies the number of bits in the key to create. For RSA keys, the
|
||||
minimum size is 1024 bits and the default is 4096 bits. Generally, 3072
|
||||
bits is considered sufficient. DSA keys must be exactly 1024 bits as
|
||||
specified by FIPS 186-2. For ECDSA keys, bits determines the key length
|
||||
by selecting from one of three elliptic curve sizes: 256, 384 or 521
|
||||
bits. Attempting to use bit lengths other than these three values for
|
||||
ECDSA keys will fail. Ed25519 keys have a fixed length and bits will be
|
||||
ignored.
|
||||
@ -0,0 +1,5 @@
|
||||
<!-- Code generated from the comments of the SSHTemporaryKeyPair struct in helper/communicator/config.go; DO NOT EDIT MANUALLY -->
|
||||
|
||||
When no ssh credentials are specified, Packer will generate a temporary SSH
|
||||
keypair for the instance, you can change the algorithm type and bits
|
||||
settings.
|
||||
Loading…
Reference in new issue