Skip to content

Commit d4deb13

Browse files
feat: Add an option to let the user provide the entire key PEM data as the answer (#1078)
* add an option to let the user provide the entire key PEM data as the answer Signed-off-by: Parthiba-Hazra <[email protected]>
1 parent 31f36fe commit d4deb13

File tree

1 file changed

+74
-14
lines changed

1 file changed

+74
-14
lines changed

common/sshkeys/sshkeys.go

+74-14
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@ import (
3333
"golang.org/x/crypto/ssh"
3434
)
3535

36+
const (
37+
userProvidedKey = "user_provided_key"
38+
)
39+
3640
var (
3741
// DomainToPublicKeys maps domains to public keys gathered with known-hosts/get-public-keys.sh
3842
DomainToPublicKeys = map[string][]string{
@@ -101,37 +105,67 @@ func loadSSHKeysOfCurrentUser() {
101105
privateKeyDir = filepath.Join(home, ".ssh")
102106
logrus.Debugf("Looking in ssh directory at path %q for keys.", privateKeyDir)
103107

104-
// Ask if we should look at the private keys
108+
// Ask whether to load private keys or provide own key
109+
options := []string{
110+
"Load private ssh keys from " + privateKeyDir,
111+
"Provide your own key",
112+
"No, I will add them later if necessary.",
113+
}
105114
message := `The CI/CD pipeline needs access to the git repos in order to clone, build and push.
106115
If any of the repos require ssh keys you will need to provide them.
107-
Do you want to load the private ssh keys from [%s]?:`
108-
ans := qaengine.FetchBoolAnswer(common.ConfigRepoLoadPrivKey, fmt.Sprintf(message, privateKeyDir), []string{"No, I will add them later if necessary."}, false, nil)
109-
if !ans {
116+
Select an option:`
117+
selectedOption := qaengine.FetchSelectAnswer(common.ConfigRepoLoadPrivKey, message, nil, "", options, nil)
118+
119+
switch selectedOption {
120+
case options[0]:
121+
if err := loadKeysFromDirectory(privateKeyDir); err != nil {
122+
logrus.Warn("Can't load keys from directory. Error:", err)
123+
return
124+
}
125+
126+
case options[1]:
127+
privateKeysToConsider = []string{userProvidedKey}
128+
129+
default:
110130
logrus.Debug("Don't read private keys. They will be added later if necessary.")
111131
return
112132
}
113133

114-
// Ask which keys we should consider
115-
finfos, err := os.ReadDir(privateKeyDir)
134+
}
135+
136+
func loadKeysFromDirectory(directory string) error {
137+
finfos, err := os.ReadDir(directory)
116138
if err != nil {
117-
logrus.Errorf("Failed to read the ssh directory at path %q Error: %q", privateKeyDir, err)
118-
return
139+
return fmt.Errorf("failed to read the SSH directory at path %q: %w", directory, err)
119140
}
141+
120142
if len(finfos) == 0 {
121-
logrus.Warn("No key files where found in", privateKeyDir)
122-
return
143+
logrus.Warn("No key files were found in", directory)
144+
return nil
123145
}
146+
124147
filenames := []string{}
125148
for _, finfo := range finfos {
126149
filenames = append(filenames, finfo.Name())
127150
}
128-
filenames = qaengine.FetchMultiSelectAnswer(common.ConfigRepoKeyPathsKey, fmt.Sprintf("These are the files we found in %q . Which keys should we consider?", privateKeyDir), []string{"Select all the keys that give access to git repos."}, filenames, filenames, nil)
129-
if len(filenames) == 0 {
151+
152+
selectedFilenames := qaengine.FetchMultiSelectAnswer(
153+
common.ConfigRepoKeyPathsKey,
154+
fmt.Sprintf("These are the files found in %q. Select the keys to consider:", directory),
155+
[]string{"Select all the keys that give access to git repos."},
156+
filenames,
157+
filenames,
158+
nil,
159+
)
160+
161+
if len(selectedFilenames) == 0 {
130162
logrus.Info("All key files ignored.")
131-
return
163+
return nil
132164
}
165+
133166
// Save the filenames for now. We will decrypt them if and when we need them.
134-
privateKeysToConsider = filenames
167+
privateKeysToConsider = selectedFilenames
168+
return nil
135169
}
136170

137171
func marshalRSAIntoPEM(key *rsa.PrivateKey) string {
@@ -195,6 +229,18 @@ func GetSSHKey(domain string) (string, bool) {
195229
if len(privateKeysToConsider) == 0 {
196230
return "", false
197231
}
232+
if privateKeysToConsider[0] == userProvidedKey {
233+
key := qaengine.FetchStringAnswer(common.ConfigRepoPrivKey, "Provide your own PEM-formatted private key:", []string{"Should not be empty"}, "", nil)
234+
if key == "" {
235+
logrus.Error("User-provided private key is empty.")
236+
return "", false
237+
}
238+
if err := validatePEMPrivateKey(key); err != nil {
239+
logrus.Error("Can't validate the PEM-formatted private key. Error:", err)
240+
return "", false
241+
}
242+
return key, true
243+
}
198244

199245
filenames := privateKeysToConsider
200246
noAnswer := "none of the above"
@@ -216,3 +262,17 @@ func GetSSHKey(domain string) (string, bool) {
216262
}
217263
return key, true
218264
}
265+
266+
func validatePEMPrivateKey(key string) error {
267+
block, _ := pem.Decode([]byte(key))
268+
if block == nil || block.Type != "RSA PRIVATE KEY" {
269+
return fmt.Errorf("invalid PEM private key format")
270+
}
271+
272+
_, err := x509.ParsePKCS1PrivateKey(block.Bytes)
273+
if err != nil {
274+
return err
275+
}
276+
277+
return nil
278+
}

0 commit comments

Comments
 (0)