github.com/secman-team/gh-api@v1.8.2/pkg/cmd/auth/shared/ssh_keys.go (about) 1 package shared 2 3 import ( 4 "fmt" 5 "net/http" 6 "os" 7 "os/exec" 8 "path/filepath" 9 "runtime" 10 11 "github.com/AlecAivazis/survey/v2" 12 "github.com/secman-team/gh-api/core/config" 13 "github.com/secman-team/gh-api/core/run" 14 "github.com/secman-team/gh-api/pkg/cmd/ssh-key/add" 15 "github.com/secman-team/gh-api/pkg/prompt" 16 "github.com/cli/safeexec" 17 ) 18 19 type sshContext struct { 20 configDir string 21 keygenExe string 22 } 23 24 func (c *sshContext) sshDir() (string, error) { 25 if c.configDir != "" { 26 return c.configDir, nil 27 } 28 dir, err := config.HomeDirPath(".ssh") 29 if err == nil { 30 c.configDir = dir 31 } 32 return dir, err 33 } 34 35 func (c *sshContext) localPublicKeys() ([]string, error) { 36 sshDir, err := c.sshDir() 37 if err != nil { 38 return nil, err 39 } 40 41 return filepath.Glob(filepath.Join(sshDir, "*.pub")) 42 } 43 44 func (c *sshContext) findKeygen() (string, error) { 45 if c.keygenExe != "" { 46 return c.keygenExe, nil 47 } 48 49 keygenExe, err := safeexec.LookPath("ssh-keygen") 50 if err != nil && runtime.GOOS == "windows" { 51 // We can try and find ssh-keygen in a Git for Windows install 52 if gitPath, err := safeexec.LookPath("git"); err == nil { 53 gitKeygen := filepath.Join(filepath.Dir(gitPath), "..", "usr", "bin", "ssh-keygen.exe") 54 if _, err = os.Stat(gitKeygen); err == nil { 55 return gitKeygen, nil 56 } 57 } 58 } 59 60 if err == nil { 61 c.keygenExe = keygenExe 62 } 63 return keygenExe, err 64 } 65 66 func (c *sshContext) generateSSHKey() (string, error) { 67 keygenExe, err := c.findKeygen() 68 if err != nil { 69 // give up silently if `ssh-keygen` is not available 70 return "", nil 71 } 72 73 var sshChoice bool 74 err = prompt.SurveyAskOne(&survey.Confirm{ 75 Message: "Generate a new SSH key to add to your GitHub account?", 76 Default: true, 77 }, &sshChoice) 78 if err != nil { 79 return "", fmt.Errorf("could not prompt: %w", err) 80 } 81 if !sshChoice { 82 return "", nil 83 } 84 85 sshDir, err := c.sshDir() 86 if err != nil { 87 return "", err 88 } 89 keyFile := filepath.Join(sshDir, "id_ed25519") 90 if _, err := os.Stat(keyFile); err == nil { 91 return "", fmt.Errorf("refusing to overwrite file %s", keyFile) 92 } 93 94 if err := os.MkdirAll(filepath.Dir(keyFile), 0711); err != nil { 95 return "", err 96 } 97 98 var sshLabel string 99 var sshPassphrase string 100 err = prompt.SurveyAskOne(&survey.Password{ 101 Message: "Enter a passphrase for your new SSH key (Optional)", 102 }, &sshPassphrase) 103 if err != nil { 104 return "", fmt.Errorf("could not prompt: %w", err) 105 } 106 107 keygenCmd := exec.Command(keygenExe, "-t", "ed25519", "-C", sshLabel, "-N", sshPassphrase, "-f", keyFile) 108 return keyFile + ".pub", run.PrepareCmd(keygenCmd).Run() 109 } 110 111 func sshKeyUpload(httpClient *http.Client, hostname, keyFile string) error { 112 f, err := os.Open(keyFile) 113 if err != nil { 114 return err 115 } 116 defer f.Close() 117 118 return add.SSHKeyUpload(httpClient, hostname, f, "GitHub CLI") 119 }