Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 3 additions & 38 deletions pkg/asset/installconfig/ssh.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,13 @@ import (
"io/ioutil"
"os"
"path/filepath"
"regexp"
"sort"
"strings"

"github.com/pkg/errors"
survey "gopkg.in/AlecAivazis/survey.v1"

"github.com/openshift/installer/pkg/asset"
"github.com/openshift/installer/pkg/validate"
)

const (
Expand All @@ -38,7 +37,7 @@ func readSSHKey(path string) (string, error) {

key := string(keyAsBytes)

err = validateOpenSSHPublicKey(key)
err = validate.SSHPublicKey(key)
if err != nil {
return "", err
}
Expand All @@ -50,7 +49,7 @@ func readSSHKey(path string) (string, error) {
func (a *sshPublicKey) Generate(asset.Parents) error {
if value, ok := os.LookupEnv("OPENSHIFT_INSTALL_SSH_PUB_KEY"); ok {
if value != "" {
if err := validateOpenSSHPublicKey(value); err != nil {
if err := validate.SSHPublicKey(value); err != nil {
return errors.Wrap(err, "failed to validate public key")
}
}
Expand Down Expand Up @@ -121,37 +120,3 @@ func (a *sshPublicKey) Generate(asset.Parents) error {
func (a sshPublicKey) Name() string {
return "SSH Key"
}

// validateOpenSSHPublicKey checks if the given string is a valid OpenSSH public key and returns an error if not.
// Ignores leading and trailing whitespace.
func validateOpenSSHPublicKey(v string) error {
trimmed := strings.TrimSpace(v)

// Don't let users hang themselves
if isMatch(`-BEGIN [\w-]+ PRIVATE KEY-`, trimmed) {
return errors.New("invalid SSH public key (appears to be a private key)")
}

if strings.Contains(trimmed, "\n") {
return errors.New("invalid SSH public key (should not contain any newline characters)")
}

invalidError := errors.New("invalid SSH public key")

keyParts := regexp.MustCompile(`\s+`).Split(trimmed, -1)
if len(keyParts) < 2 {
return invalidError
}

keyType := keyParts[0]
keyBase64 := keyParts[1]
if !isMatch(`^[\w-]+$`, keyType) || !isMatch(`^[A-Za-z0-9+\/]+={0,2}$`, keyBase64) {
return invalidError
}

return nil
}

func isMatch(re string, v string) bool {
return regexp.MustCompile(re).MatchString(v)
}
38 changes: 0 additions & 38 deletions pkg/asset/installconfig/ssh_test.go

This file was deleted.

30 changes: 30 additions & 0 deletions pkg/validate/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,3 +232,33 @@ func lastIP(cidr *net.IPNet) net.IP {
}
return last
}

// SSHPublicKey checks if the given string is a valid OpenSSH public key
// and returns an error if not.
func SSHPublicKey(v string) error {
trimmed := strings.TrimSpace(v)

// Don't let users hang themselves
if isMatch(`-BEGIN [\w-]+ PRIVATE KEY-`, trimmed) {
return errors.New("invalid SSH public key (appears to be a private key)")
}

if strings.Contains(trimmed, "\n") {
return errors.New("invalid SSH public key (should not contain any newline characters)")
}

invalidError := errors.New("invalid SSH public key")

keyParts := regexp.MustCompile(`\s+`).Split(trimmed, -1)
if len(keyParts) < 2 {
return invalidError
}

keyType := keyParts[0]
keyBase64 := keyParts[1]
if !isMatch(`^[\w-]+$`, keyType) || !isMatch(`^[A-Za-z0-9+\/]+={0,2}$`, keyBase64) {
return invalidError
}

return nil
}
33 changes: 33 additions & 0 deletions pkg/validate/validate_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,3 +268,36 @@ func TestCIDRsDontOverlap(t *testing.T) {
})
}
}

func TestOpenSSHPublicKey(t *testing.T) {
const invalidMsg = "invalid SSH public key"
const multiLineMsg = "invalid SSH public key (should not contain any newline characters)"
const privateKeyMsg = "invalid SSH public key (appears to be a private key)"
tests := []struct {
in string
expected string
}{
{"a", invalidMsg},
{".", invalidMsg},
{"日本語", invalidMsg},
{"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDxL", ""},
{"ssh-rsa \t AAAAB3NzaC1yc2EAAAADAQABAAACAQDxL", ""},
{"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDxL [email protected]", ""},
{"\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDxL [email protected]", ""},
{"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDxL [email protected]\n", ""},
{"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDxL\nssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDxL", multiLineMsg},
{"ssh-rsa\nAAAAB3NzaC1yc2EAAAADAQABAAACAQDxL [email protected]", multiLineMsg},
{"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDxL\[email protected]", multiLineMsg},
{"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAACAQDxL", ""},
{"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCt3BebCHqnSsgpLjo4kVvyfY/z2BS8t27r/7du+O2pb4xYkr7n+KFpbOz523vMTpQ+o1jY4u4TgexglyT9nqasWgLOvo1qjD1agHme8LlTPQSk07rXqOB85Uq5p7ig2zoOejF6qXhcc3n1c7+HkxHrgpBENjLVHOBpzPBIAHkAGaZcl07OCqbsG5yxqEmSGiAlh/IiUVOZgdDMaGjCRFy0wk0mQaGD66DmnFc1H5CzcPjsxr0qO65e7lTGsE930KkO1Vc+RHCVwvhdXs+c2NhJ2/3740Kpes9n1/YullaWZUzlCPDXtRuy6JRbFbvy39JUgHWGWzB3d+3f8oJ/N4qZ cardno:000603633110", ""},
{"-----BEGIN CERTIFICATE-----abcd-----END CERTIFICATE-----", invalidMsg},
{"-----BEGIN RSA PRIVATE KEY-----\nabc\n-----END RSA PRIVATE KEY-----", privateKeyMsg},
}

for _, test := range tests {
err := SSHPublicKey(test.in)
if (err == nil && test.expected != "") || (err != nil && err.Error() != test.expected) {
t.Errorf("For %q, expected %q, got %q", test.in, test.expected, err)
}
}
}