11package cmd
22
33import (
4+ "bytes"
45 "fmt"
56 "net"
67 "os"
@@ -10,7 +11,7 @@ import (
1011 "github.com/spf13/cobra"
1112 "golang.org/x/crypto/ssh"
1213 "golang.org/x/crypto/ssh/agent"
13- "golang.org/x/crypto/ssh/terminal "
14+ terminal "golang.org/x/term "
1415)
1516
1617var loginCmd = & cobra.Command {
@@ -23,29 +24,71 @@ var loginCmd = &cobra.Command{
2324 },
2425}
2526
26- func publicKey (path string , skipAgent bool ) (ssh.AuthMethod , func () error ) {
27+ func publicKey (path , publicKeyOverride string , publicKeyIdentities [] string , skipAgent bool ) (ssh.AuthMethod , func () error ) {
2728 noopCloseFunc := func () error { return nil }
2829
2930 if ! skipAgent {
3031 // Connect to SSH agent to ask for unencrypted private keys
3132 if sshAgentConn , err := net .Dial ("unix" , os .Getenv ("SSH_AUTH_SOCK" )); err == nil {
3233 sshAgent := agent .NewClient (sshAgentConn )
33-
34- keys , _ := sshAgent .List ()
35- if len (keys ) > 0 {
36- // There are key(s) in the agent
37- //defer sshAgentConn.Close()
38- return ssh .PublicKeysCallback (sshAgent .Signers ), sshAgentConn .Close
34+ agentSigners , err := sshAgent .Signers ()
35+ handleError (err )
36+ // There are key(s) in the agent
37+ if len (agentSigners ) > 0 {
38+ identities := make (map [string ]ssh.PublicKey )
39+ if publicKeyOverride == "" {
40+ // check for identify files in the current lagoon config context
41+ for _ , identityFile := range publicKeyIdentities {
42+ // append to identityfiles
43+ keybytes , err := os .ReadFile (identityFile )
44+ handleError (err )
45+ pubkey , _ , _ , _ , err := ssh .ParseAuthorizedKey (keybytes )
46+ handleError (err )
47+ identities [identityFile ] = pubkey
48+ }
49+ } else {
50+ // append to identityfiles
51+ keybytes , err := os .ReadFile (publicKeyOverride )
52+ handleError (err )
53+ pubkey , _ , _ , _ , err := ssh .ParseAuthorizedKey (keybytes )
54+ handleError (err )
55+ identities [publicKeyOverride ] = pubkey
56+ }
57+ // check all keys in the agent to see if there is a matching identity file
58+ for _ , signer := range agentSigners {
59+ for file , identity := range identities {
60+ if bytes .Equal (signer .PublicKey ().Marshal (), identity .Marshal ()) {
61+ if verboseOutput {
62+ fmt .Fprintf (os .Stderr , "ssh: attempting connection using identity file public key: %s\n " , file )
63+ }
64+ // only provide this matching key back to the ssh client to use
65+ return ssh .PublicKeys (signer ), noopCloseFunc
66+ }
67+ }
68+ }
69+ if publicKeyOverride != "" {
70+ handleError (fmt .Errorf ("ssh: no key matching %s in agent" , publicKeyOverride ))
71+ }
72+ // if no matching identity files, just return all agent keys like previous behaviour
73+ if verboseOutput {
74+ fmt .Fprintf (os .Stderr , "ssh: attempting connection using any keys in ssh-agent\n " )
75+ }
76+ return ssh .PublicKeysCallback (sshAgent .Signers ), noopCloseFunc
3977 }
4078 }
4179 }
4280
81+ // if no keys in the agent, and a specific private key has been defined, then check the key and use it if possible
82+ if verboseOutput {
83+ fmt .Fprintf (os .Stderr , "ssh: attempting connection using private key: %s\n " , path )
84+ }
4385 key , err := os .ReadFile (path )
4486 handleError (err )
4587
4688 // Try to look for an unencrypted private key
4789 signer , err := ssh .ParsePrivateKey (key )
4890 if err != nil {
91+ // if encrypted, prompt for passphrase or error and ask user to add to their agent
4992 fmt .Printf ("Enter passphrase for %s:" , path )
5093 bytePassword , err := terminal .ReadPassword (int (os .Stdin .Fd ()))
5194 if err != nil {
@@ -60,9 +103,7 @@ func publicKey(path string, skipAgent bool) (ssh.AuthMethod, func() error) {
60103 fmt .Println ("Lagoon CLI could not decode private key, you will need to add your private key to your ssh-agent." )
61104 os .Exit (1 )
62105 }
63- return ssh .PublicKeys (signer ), noopCloseFunc
64106 }
65- // return unencrypted private key
66107 return ssh .PublicKeys (signer ), noopCloseFunc
67108}
68109
@@ -98,7 +139,7 @@ func retrieveTokenViaSsh() (string, error) {
98139 privateKey = cmdSSHKey
99140 skipAgent = true
100141 }
101- authMethod , closeSSHAgent := publicKey (privateKey , skipAgent )
142+ authMethod , closeSSHAgent := publicKey (privateKey , cmdPubkeyIdentity , lagoonCLIConfig . Lagoons [ lagoonCLIConfig . Current ]. PublicKeyIdentities , skipAgent )
102143 config := & ssh.ClientConfig {
103144 User : "lagoon" ,
104145 Auth : []ssh.AuthMethod {
0 commit comments