Allow caller to specify new line for SSH public key.

The default behavior of the various builders that create SSH key
pairs appears to be to add a trailing new line. This will be the
default behavior, but at least it can be customized if desired.
This commit is contained in:
Stephen Fox 2019-02-03 10:36:06 -05:00
parent ad075ffac3
commit 6824806e6f
2 changed files with 89 additions and 6 deletions

View File

@ -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

View File

@ -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
}