Skip to content

Commit 403d4b9

Browse files
refactor(cmd): extract SSH_KEY parsing, add tests
This extracts the SSH_KEY parsing code in cmd.ConfigSet into its own function to make it easier to test and adds tests. It also changes the order of evaluation to prefer the quickest and safest path. The code also changes the private key regex to a stricter regex which also matches new format OPENSSH keys and is required to support keys of the ed25519 type.
1 parent 46002f4 commit 403d4b9

2 files changed

Lines changed: 67 additions & 27 deletions

File tree

cmd/config.go

Lines changed: 35 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -76,34 +76,11 @@ func (d *DeisCmd) ConfigSet(appID string, configVars []string) error {
7676
return err
7777
}
7878

79-
value, ok := configMap["SSH_KEY"]
80-
81-
if ok {
82-
sshKey := value.(string)
83-
sshRegex := regexp.MustCompile("^-.+ .SA PRIVATE KEY-*")
84-
85-
if _, err = os.Stat(value.(string)); err == nil {
86-
contents, err := ioutil.ReadFile(value.(string))
87-
88-
if err != nil {
89-
return err
90-
}
91-
92-
sshKey = string(contents)
93-
} else {
94-
// NOTE(felixbuenemann): check if the current value is already a base64 encoded key.
95-
// This is the case if it was fetched using "deis config:pull".
96-
contents, err := base64.StdEncoding.DecodeString(sshKey)
97-
98-
if err == nil && sshRegex.MatchString(string(contents)) {
99-
sshKey = string(contents)
100-
}
101-
}
102-
103-
if !sshRegex.MatchString(sshKey) {
104-
return fmt.Errorf("Could not parse SSH private key:\n %s", sshKey)
79+
if value, ok := configMap["SSH_KEY"]; ok {
80+
sshKey, err := parseSSHKey(value.(string))
81+
if err != nil {
82+
return err
10583
}
106-
10784
configMap["SSH_KEY"] = base64.StdEncoding.EncodeToString([]byte(sshKey))
10885
}
10986

@@ -298,6 +275,37 @@ func parseConfig(configVars []string) (map[string]interface{}, error) {
298275
return configMap, nil
299276
}
300277

278+
func parseSSHKey(value string) (string, error) {
279+
sshRegex := regexp.MustCompile("^-----BEGIN (DSA|RSA|EC|OPENSSH) PRIVATE KEY-----")
280+
281+
if sshRegex.MatchString(value) {
282+
return value, nil
283+
}
284+
285+
// NOTE(felixbuenemann): check if the current value is already a base64 encoded key.
286+
// This is the case if it was fetched using "deis config:pull".
287+
contents, err := base64.StdEncoding.DecodeString(value)
288+
289+
if err == nil && sshRegex.MatchString(string(contents)) {
290+
return string(contents), nil
291+
}
292+
293+
// NOTE(felixbuenemann): check if the value is a path to a private key.
294+
if _, err := os.Stat(value); err == nil {
295+
contents, err := ioutil.ReadFile(value)
296+
297+
if err != nil {
298+
return "", err
299+
}
300+
301+
if sshRegex.MatchString(string(contents)) {
302+
return string(contents), nil
303+
}
304+
}
305+
306+
return "", fmt.Errorf("Could not parse SSH private key:\n %s", value)
307+
}
308+
301309
func formatConfig(configVars map[string]interface{}) string {
302310
var formattedConfig string
303311

cmd/config_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ package cmd
33
import (
44
"bytes"
55
"fmt"
6+
"io/ioutil"
67
"net/http"
8+
"os"
79
"testing"
810

911
"github.com/arschles/assert"
@@ -26,6 +28,36 @@ func TestParseConfig(t *testing.T) {
2628
assert.Equal(t, actual, map[string]interface{}{"FOO": ""}, "map")
2729
}
2830

31+
func TestParseSSHKey(t *testing.T) {
32+
t.Parallel()
33+
34+
_, err := parseSSHKey("foobar")
35+
assert.ExistsErr(t, err, "bogus key")
36+
37+
validSSHKey := "-----BEGIN OPENSSH PRIVATE KEY-----"
38+
39+
actual, err := parseSSHKey(validSSHKey)
40+
assert.NoErr(t, err)
41+
assert.Equal(t, actual, validSSHKey, "plain key")
42+
43+
encodedSSHKey := "LS0tLS1CRUdJTiBPUEVOU1NIIFBSSVZBVEUgS0VZLS0tLS0="
44+
45+
actual, err = parseSSHKey(encodedSSHKey)
46+
assert.NoErr(t, err)
47+
assert.Equal(t, actual, validSSHKey, "base64 key")
48+
49+
keyFile, err := ioutil.TempFile("", "deis-cli-unit-test-sshkey")
50+
assert.NoErr(t, err)
51+
defer os.Remove(keyFile.Name())
52+
_, err = keyFile.Write([]byte(validSSHKey))
53+
assert.NoErr(t, err)
54+
keyFile.Close()
55+
56+
actual, err = parseSSHKey(keyFile.Name())
57+
assert.NoErr(t, err)
58+
assert.Equal(t, actual, validSSHKey, "key path")
59+
}
60+
2961
func TestFormatConfig(t *testing.T) {
3062
t.Parallel()
3163

0 commit comments

Comments
 (0)