diff --git a/builder/virtualbox/common/step_ssh_key_pair.go b/builder/virtualbox/common/step_ssh_key_pair.go index 77e00b1b3..89cd58ea9 100644 --- a/builder/virtualbox/common/step_ssh_key_pair.go +++ b/builder/virtualbox/common/step_ssh_key_pair.go @@ -9,6 +9,7 @@ import ( "github.com/hashicorp/packer/common/uuid" "github.com/hashicorp/packer/helper/communicator" "github.com/hashicorp/packer/helper/multistep" + "github.com/hashicorp/packer/helper/ssh" "github.com/hashicorp/packer/packer" ) @@ -48,7 +49,7 @@ func (s *StepSshKeyPair) Run(_ context.Context, state multistep.StateBag) multis ui.Say("Creating ephemeral key pair for SSH communicator...") - kp, err := newSshKeyPairBuilder(). + kp, err := ssh.NewKeyPairBuilder(). SetName(fmt.Sprintf("packer_%s", uuid.TimeOrderedUUID())). Build() if err != nil { @@ -59,7 +60,7 @@ func (s *StepSshKeyPair) Run(_ context.Context, state multistep.StateBag) multis s.Comm.SSHKeyPairName = kp.Name() s.Comm.SSHTemporaryKeyPairName = kp.Name() s.Comm.SSHPrivateKey = kp.PrivateKeyPemBlock() - s.Comm.SSHPublicKey = kp.PublicKeyAuthorizedKeysFormat(unixNewLine) + s.Comm.SSHPublicKey = kp.PublicKeyAuthorizedKeysFormat(ssh.UnixNewLine) s.Comm.SSHClearAuthorizedKeys = true ui.Say(fmt.Sprintf("Created ephemeral SSH key pair of type %s", kp.Description())) diff --git a/builder/virtualbox/common/sshkeypair.go b/helper/ssh/sshkeypair.go similarity index 56% rename from builder/virtualbox/common/sshkeypair.go rename to helper/ssh/sshkeypair.go index 7b45b9381..d65b33641 100644 --- a/builder/virtualbox/common/sshkeypair.go +++ b/helper/ssh/sshkeypair.go @@ -1,7 +1,4 @@ -package common - -// TODO: Make this available to other packer APIs. -// Perhaps through 'helper/ssh'? +package ssh import ( "bytes" @@ -22,55 +19,55 @@ const ( // That's a lot of bits. defaultRsaBits = 4096 - // rsaSsh is a SSH key pair of RSA type. - rsaSsh sshKeyPairType = "rsa" + // RsaSsh is a SSH key pair of RSA type. + RsaSsh KeyPairType = "rsa" - // ecdsaSsh is a SSH key pair of ECDSA type. - ecdsaSsh sshKeyPairType = "ecdsa" + // EcdsaSsh is a SSH key pair of ECDSA type. + EcdsaSsh KeyPairType = "ecdsa" ) -// sshKeyPairType represents different types of SSH key pairs. +// KeyPairType represents different types of SSH key pairs. // For example, RSA. -type sshKeyPairType string +type KeyPairType string -func (o sshKeyPairType) String() string { +func (o KeyPairType) String() string { return string(o) } const ( - // unixNewLine is a unix new line. - unixNewLine newLineOption = "\n" + // UnixNewLine is a unix new line. + UnixNewLine NewLineOption = "\n" - // windowsNewLine is a Windows new line. - windowsNewLine newLineOption = "\r\n" + // WindowsNewLine is a Windows new line. + WindowsNewLine NewLineOption = "\r\n" - // noNewLine will not append a new line. - noNewLine newLineOption = "" + // NoNewLine will not append a new line. + NoNewLine NewLineOption = "" ) -// newLineOption specifies the type of new line to append to a string. +// NewLineOption specifies the type of new line to append to a string. // See the 'const' block for choices. -type newLineOption string +type NewLineOption string -func (o newLineOption) String() string { +func (o NewLineOption) String() string { return string(o) } -func (o newLineOption) Bytes() []byte { +func (o NewLineOption) Bytes() []byte { return []byte(o) } -// sshKeyPairBuilder builds SSH key pairs. -type sshKeyPairBuilder interface { +// KeyPairBuilder builds SSH key pairs. +type KeyPairBuilder interface { // SetType sets the key pair type. - SetType(sshKeyPairType) sshKeyPairBuilder + SetType(KeyPairType) KeyPairBuilder // SetBits sets the key pair's bits of entropy. - SetBits(int) sshKeyPairBuilder + SetBits(int) KeyPairBuilder // SetName sets the name of the key pair. This is primarily used // to identify the public key in the authorized_keys file. - SetName(string) sshKeyPairBuilder + SetName(string) KeyPairBuilder // Build returns a SSH key pair. // @@ -80,12 +77,12 @@ type sshKeyPairBuilder interface { // - RSA: 4096 // - ECDSA: 521 // Default name: (empty string) - Build() (sshKeyPair, error) + Build() (KeyPair, error) } -type defaultSshKeyPairBuilder struct { +type defaultKeyPairBuilder struct { // kind describes the resulting key pair's type. - kind sshKeyPairType + kind KeyPairType // bits is the resulting key pair's bits of entropy. bits int @@ -94,35 +91,34 @@ type defaultSshKeyPairBuilder struct { name string } -func (o *defaultSshKeyPairBuilder) SetType(kind sshKeyPairType) sshKeyPairBuilder { +func (o *defaultKeyPairBuilder) SetType(kind KeyPairType) KeyPairBuilder { o.kind = kind return o } -func (o *defaultSshKeyPairBuilder) SetBits(bits int) sshKeyPairBuilder { +func (o *defaultKeyPairBuilder) SetBits(bits int) KeyPairBuilder { o.bits = bits return o } -func (o *defaultSshKeyPairBuilder) SetName(name string) sshKeyPairBuilder { +func (o *defaultKeyPairBuilder) SetName(name string) KeyPairBuilder { o.name = name return o } -func (o *defaultSshKeyPairBuilder) Build() (sshKeyPair, error) { +func (o *defaultKeyPairBuilder) Build() (KeyPair, error) { switch o.kind { - case rsaSsh: + case RsaSsh: return o.newRsaSshKeyPair() - case ecdsaSsh: + case EcdsaSsh: // Default case. } return o.newEcdsaSshKeyPair() } -// newEcdsaSshKeyPair returns a new ECDSA SSH key pair for the given bits -// of entropy. -func (o *defaultSshKeyPairBuilder) newEcdsaSshKeyPair() (sshKeyPair, error) { +// newEcdsaSshKeyPair returns a new ECDSA SSH key pair. +func (o *defaultKeyPairBuilder) newEcdsaSshKeyPair() (KeyPair, error) { var curve elliptic.Curve switch o.bits { @@ -137,30 +133,30 @@ func (o *defaultSshKeyPairBuilder) newEcdsaSshKeyPair() (sshKeyPair, error) { elliptic.P256() case 224: // Not supported by "golang.org/x/crypto/ssh". - return &defaultSshKeyPair{}, errors.New("golang.org/x/crypto/ssh does not support " + + return &defaultKeyPair{}, errors.New("golang.org/x/crypto/ssh does not support " + strconv.Itoa(o.bits) + " bits") default: - return &defaultSshKeyPair{}, errors.New("crypto/elliptic does not support " + + return &defaultKeyPair{}, errors.New("crypto/elliptic does not support " + strconv.Itoa(o.bits) + " bits") } privateKey, err := ecdsa.GenerateKey(curve, rand.Reader) if err != nil { - return &defaultSshKeyPair{}, err + return &defaultKeyPair{}, err } sshPublicKey, err := ssh.NewPublicKey(&privateKey.PublicKey) if err != nil { - return &defaultSshKeyPair{}, err + return &defaultKeyPair{}, err } raw, err := x509.MarshalECPrivateKey(privateKey) if err != nil { - return &defaultSshKeyPair{}, err + return &defaultKeyPair{}, err } - return &defaultSshKeyPair{ - kind: ecdsaSsh, + return &defaultKeyPair{ + kind: EcdsaSsh, bits: o.bits, name: o.name, privateKeyDerBytes: raw, @@ -168,25 +164,24 @@ func (o *defaultSshKeyPairBuilder) newEcdsaSshKeyPair() (sshKeyPair, error) { }, nil } -// newRsaSshKeyPair returns a new RSA SSH key pair for the given bits -// of entropy. -func (o *defaultSshKeyPairBuilder) newRsaSshKeyPair() (sshKeyPair, error) { +// newRsaSshKeyPair returns a new RSA SSH key pair. +func (o *defaultKeyPairBuilder) newRsaSshKeyPair() (KeyPair, error) { if o.bits == 0 { o.bits = defaultRsaBits } privateKey, err := rsa.GenerateKey(rand.Reader, o.bits) if err != nil { - return &defaultSshKeyPair{}, err + return &defaultKeyPair{}, err } sshPublicKey, err := ssh.NewPublicKey(&privateKey.PublicKey) if err != nil { - return &defaultSshKeyPair{}, err + return &defaultKeyPair{}, err } - return &defaultSshKeyPair{ - kind: rsaSsh, + return &defaultKeyPair{ + kind: RsaSsh, bits: o.bits, name: o.name, privateKeyDerBytes: x509.MarshalPKCS1PrivateKey(privateKey), @@ -194,10 +189,10 @@ func (o *defaultSshKeyPairBuilder) newRsaSshKeyPair() (sshKeyPair, error) { }, nil } -// sshKeyPair represents a SSH key pair. -type sshKeyPair interface { +// KeyPair represents a SSH key pair. +type KeyPair interface { // Type returns the key pair's type. - Type() sshKeyPairType + Type() KeyPairType // Bits returns the bits of entropy. Bits() int @@ -218,12 +213,12 @@ type sshKeyPair interface { // PublicKeyAuthorizedKeysFormat returns a slice of bytes // representing the public key in OpenSSH authorized_keys format // with the specified new line. - PublicKeyAuthorizedKeysFormat(newLineOption) []byte + PublicKeyAuthorizedKeysFormat(NewLineOption) []byte } -type defaultSshKeyPair struct { +type defaultKeyPair struct { // kind is the key pair's type. - kind sshKeyPairType + kind KeyPairType // bits is the key pair's bits of entropy. bits int @@ -239,29 +234,29 @@ type defaultSshKeyPair struct { publicKey ssh.PublicKey } -func (o defaultSshKeyPair) Type() sshKeyPairType { +func (o defaultKeyPair) Type() KeyPairType { return o.kind } -func (o defaultSshKeyPair) Bits() int { +func (o defaultKeyPair) Bits() int { return o.bits } -func (o defaultSshKeyPair) Name() string { +func (o defaultKeyPair) Name() string { return o.name } -func (o defaultSshKeyPair) Description() string { +func (o defaultKeyPair) Description() string { return o.kind.String() + " " + strconv.Itoa(o.bits) } -func (o defaultSshKeyPair) PrivateKeyPemBlock() []byte { +func (o defaultKeyPair) PrivateKeyPemBlock() []byte { t := "UNKNOWN PRIVATE KEY" switch o.kind { - case ecdsaSsh: + case EcdsaSsh: t = "EC PRIVATE KEY" - case rsaSsh: + case RsaSsh: t = "RSA PRIVATE KEY" } @@ -272,36 +267,36 @@ func (o defaultSshKeyPair) PrivateKeyPemBlock() []byte { }) } -func (o defaultSshKeyPair) PublicKeyAuthorizedKeysFormat(nl newLineOption) []byte { +func (o defaultKeyPair) PublicKeyAuthorizedKeysFormat(nl NewLineOption) []byte { result := ssh.MarshalAuthorizedKey(o.publicKey) if len(strings.TrimSpace(o.name)) > 0 { // Awful, but the go ssh library automatically appends // a unix new line. - result = bytes.TrimSuffix(result, unixNewLine.Bytes()) + result = bytes.TrimSuffix(result, UnixNewLine.Bytes()) result = append(result, ' ') result = append(result, o.name...) } switch nl { - case noNewLine: - result = bytes.TrimSuffix(result, unixNewLine.Bytes()) - case windowsNewLine: - result = bytes.TrimSuffix(result, unixNewLine.Bytes()) + case NoNewLine: + result = bytes.TrimSuffix(result, UnixNewLine.Bytes()) + case WindowsNewLine: + result = bytes.TrimSuffix(result, UnixNewLine.Bytes()) result = append(result, nl.Bytes()...) - case unixNewLine: + case UnixNewLine: fallthrough default: // This is how all the other "SSH key pair" code works in // the different builders. - if !bytes.HasSuffix(result, unixNewLine.Bytes()) { - result = append(result, unixNewLine.Bytes()...) + if !bytes.HasSuffix(result, UnixNewLine.Bytes()) { + result = append(result, UnixNewLine.Bytes()...) } } return result } -func newSshKeyPairBuilder() sshKeyPairBuilder { - return &defaultSshKeyPairBuilder{} +func NewKeyPairBuilder() KeyPairBuilder { + return &defaultKeyPairBuilder{} } diff --git a/builder/virtualbox/common/sshkeypair_test.go b/helper/ssh/sshkeypair_test.go similarity index 78% rename from builder/virtualbox/common/sshkeypair_test.go rename to helper/ssh/sshkeypair_test.go index a9f7c8de7..83049a265 100644 --- a/builder/virtualbox/common/sshkeypair_test.go +++ b/helper/ssh/sshkeypair_test.go @@ -1,4 +1,4 @@ -package common +package ssh import ( "bytes" @@ -13,14 +13,14 @@ import ( // expected contains the data that the key pair should contain. type expected struct { - kind sshKeyPairType + kind KeyPairType bits int desc string name string data []byte } -func (o expected) matches(kp sshKeyPair) error { +func (o expected) matches(kp KeyPair) error { if o.kind.String() == "" { return errors.New("expected kind's value cannot be empty") } @@ -63,7 +63,7 @@ func (o expected) matches(kp sshKeyPair) error { return err } - err = o.verifySshKeyPair(kp) + err = o.verifyKeyPair(kp) if err != nil { return err } @@ -71,11 +71,11 @@ func (o expected) matches(kp sshKeyPair) error { return nil } -func (o expected) verifyPublicKeyAuthorizedKeysFormat(kp sshKeyPair) error { - newLines := []newLineOption{ - unixNewLine, - noNewLine, - windowsNewLine, +func (o expected) verifyPublicKeyAuthorizedKeysFormat(kp KeyPair) error { + newLines := []NewLineOption{ + UnixNewLine, + NoNewLine, + WindowsNewLine, } for _, nl := range newLines { @@ -86,19 +86,19 @@ func (o expected) verifyPublicKeyAuthorizedKeysFormat(kp sshKeyPair) error { } switch nl { - case noNewLine: + case NoNewLine: if publicKeyAk[len(publicKeyAk) - 1] == '\n' { return errors.New("public key in authorized keys format has trailing new line when none was specified") } - case unixNewLine: + case UnixNewLine: if publicKeyAk[len(publicKeyAk) - 1] != '\n' { return errors.New("public key in authorized keys format does not have unix new line when unix was specified") } - if string(publicKeyAk[len(publicKeyAk) - 2:]) == windowsNewLine.String() { + if string(publicKeyAk[len(publicKeyAk) - 2:]) == WindowsNewLine.String() { return errors.New("public key in authorized keys format has windows new line when unix was specified") } - case windowsNewLine: - if string(publicKeyAk[len(publicKeyAk) - 2:]) != windowsNewLine.String() { + case WindowsNewLine: + if string(publicKeyAk[len(publicKeyAk) - 2:]) != WindowsNewLine.String() { return errors.New("public key in authorized keys format does not have windows new line when windows was specified") } } @@ -121,7 +121,7 @@ func (o expected) verifyPublicKeyAuthorizedKeysFormat(kp sshKeyPair) error { return nil } -func (o expected) verifySshKeyPair(kp sshKeyPair) error { +func (o expected) verifyKeyPair(kp KeyPair) error { signer, err := ssh.ParsePrivateKey(kp.PrivateKeyPemBlock()) if err != nil { return errors.New("failed to parse private key during verification - " + err.Error()) @@ -140,14 +140,14 @@ func (o expected) verifySshKeyPair(kp sshKeyPair) error { return nil } -func TestDefaultSshKeyPairBuilder_Build_Default(t *testing.T) { - kp, err := newSshKeyPairBuilder().Build() +func TestDefaultKeyPairBuilder_Build_Default(t *testing.T) { + kp, err := NewKeyPairBuilder().Build() if err != nil { t.Fatal(err.Error()) } err = expected{ - kind: ecdsaSsh, + kind: EcdsaSsh, bits: 521, desc: "ecdsa 521", data: []byte(uuid.TimeOrderedUUID()), @@ -157,16 +157,16 @@ func TestDefaultSshKeyPairBuilder_Build_Default(t *testing.T) { } } -func TestDefaultSshKeyPairBuilder_Build_EcdsaDefault(t *testing.T) { - kp, err := newSshKeyPairBuilder(). - SetType(ecdsaSsh). +func TestDefaultKeyPairBuilder_Build_EcdsaDefault(t *testing.T) { + kp, err := NewKeyPairBuilder(). + SetType(EcdsaSsh). Build() if err != nil { t.Fatal(err.Error()) } err = expected{ - kind: ecdsaSsh, + kind: EcdsaSsh, bits: 521, desc: "ecdsa 521", data: []byte(uuid.TimeOrderedUUID()), @@ -176,16 +176,16 @@ func TestDefaultSshKeyPairBuilder_Build_EcdsaDefault(t *testing.T) { } } -func TestDefaultSshKeyPairBuilder_Build_RsaDefault(t *testing.T) { - kp, err := newSshKeyPairBuilder(). - SetType(rsaSsh). +func TestDefaultKeyPairBuilder_Build_RsaDefault(t *testing.T) { + kp, err := NewKeyPairBuilder(). + SetType(RsaSsh). Build() if err != nil { t.Fatal(err.Error()) } err = expected{ - kind: rsaSsh, + kind: RsaSsh, bits: 4096, desc: "rsa 4096", data: []byte(uuid.TimeOrderedUUID()), @@ -195,11 +195,11 @@ func TestDefaultSshKeyPairBuilder_Build_RsaDefault(t *testing.T) { } } -func TestDefaultSshKeyPairBuilder_Build_NamedEcdsa(t *testing.T) { +func TestDefaultKeyPairBuilder_Build_NamedEcdsa(t *testing.T) { name := uuid.TimeOrderedUUID() - kp, err := newSshKeyPairBuilder(). - SetType(ecdsaSsh). + kp, err := NewKeyPairBuilder(). + SetType(EcdsaSsh). SetName(name). Build() if err != nil { @@ -207,7 +207,7 @@ func TestDefaultSshKeyPairBuilder_Build_NamedEcdsa(t *testing.T) { } err = expected{ - kind: ecdsaSsh, + kind: EcdsaSsh, bits: 521, desc: "ecdsa 521", data: []byte(uuid.TimeOrderedUUID()), @@ -218,11 +218,11 @@ func TestDefaultSshKeyPairBuilder_Build_NamedEcdsa(t *testing.T) { } } -func TestDefaultSshKeyPairBuilder_Build_NamedRsa(t *testing.T) { +func TestDefaultKeyPairBuilder_Build_NamedRsa(t *testing.T) { name := uuid.TimeOrderedUUID() - kp, err := newSshKeyPairBuilder(). - SetType(rsaSsh). + kp, err := NewKeyPairBuilder(). + SetType(RsaSsh). SetName(name). Build() if err != nil { @@ -230,7 +230,7 @@ func TestDefaultSshKeyPairBuilder_Build_NamedRsa(t *testing.T) { } err = expected{ - kind: rsaSsh, + kind: RsaSsh, bits: 4096, desc: "rsa 4096", data: []byte(uuid.TimeOrderedUUID()),