diff --git a/builder/virtualbox/common/sshkeypair.go b/builder/virtualbox/common/sshkeypair.go index 8e323aee9..dfaa4208a 100644 --- a/builder/virtualbox/common/sshkeypair.go +++ b/builder/virtualbox/common/sshkeypair.go @@ -4,6 +4,7 @@ package common // Perhaps through 'helper/ssh'? import ( + "bytes" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" @@ -35,6 +36,29 @@ func (o sshKeyPairType) String() string { return string(o) } +const ( + // unixNewLine is a unix new line. + unixNewLine newLineOption = "\n" + + // windowsNewLine is a Windows new line. + windowsNewLine newLineOption = "\r\n" + + // noNewLine will not append a new line. + noNewLine newLineOption = "" +) + +// newLineOption specifies the type of new line to append to a string. +// See the 'const' block for choices. +type newLineOption string + +func (o newLineOption) String() string { + return string(o) +} + +func (o newLineOption) Bytes() []byte { + return []byte(o) +} + // sshKeyPairBuilder builds SSH key pairs. type sshKeyPairBuilder interface { // SetType sets the key pair type. @@ -99,9 +123,9 @@ type sshKeyPair interface { PrivateKeyPemBlock() []byte // PublicKeyAuthorizedKeysFormat returns a slice of bytes - // representing the public key in OpenSSH authorized_keys - // format with a trailing new line. - PublicKeyAuthorizedKeysFormat() []byte + // representing the public key in OpenSSH authorized_keys format + // with the specified new line. + PublicKeyAuthorizedKeysFormat(newLineOption) []byte } type defaultSshKeyPair struct { @@ -148,8 +172,26 @@ func (o defaultSshKeyPair) PrivateKeyPemBlock() []byte { }) } -func (o defaultSshKeyPair) PublicKeyAuthorizedKeysFormat() []byte { - return ssh.MarshalAuthorizedKey(o.publicKey) +func (o defaultSshKeyPair) PublicKeyAuthorizedKeysFormat(nl newLineOption) []byte { + result := ssh.MarshalAuthorizedKey(o.publicKey) + + switch nl { + case noNewLine: + result = bytes.TrimSuffix(result, unixNewLine.Bytes()) + case windowsNewLine: + result = bytes.TrimSuffix(result, unixNewLine.Bytes()) + result = append(result, nl.Bytes()...) + 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()...) + } + } + + return result } // newEcdsaSshKeyPair returns a new ECDSA SSH key pair for the given bits diff --git a/builder/virtualbox/common/sshkeypair_test.go b/builder/virtualbox/common/sshkeypair_test.go index b92fa385f..e976b655e 100644 --- a/builder/virtualbox/common/sshkeypair_test.go +++ b/builder/virtualbox/common/sshkeypair_test.go @@ -45,7 +45,12 @@ func (o expected) matches(kp sshKeyPair) error { expDescription + "' - got '" + kp.Description() + "'") } - err := verifySshKeyPair(kp) + err := verifyPublickeyAuthorizedKeysFormat(kp) + if err != nil { + return err + } + + err = verifySshKeyPair(kp) if err != nil { return err } @@ -121,3 +126,39 @@ func verifySshKeyPair(kp sshKeyPair) error { return nil } + +func verifyPublickeyAuthorizedKeysFormat(kp sshKeyPair) error { + newLines := []newLineOption{ + unixNewLine, + noNewLine, + windowsNewLine, + } + + for _, nl := range newLines { + publicKeyAk := kp.PublicKeyAuthorizedKeysFormat(nl) + + if len(publicKeyAk) < 2 { + return errors.New("expected public key in authorized keys format to be at least 2 bytes") + } + + switch nl { + 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: + 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() { + 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() { + return errors.New("public key in authorized keys format does not have windows new line when windows was specified") + } + } + } + + return nil +}