fix: use SSH agent for authentication
Try SSH_AUTH_SOCK agent first (handles macOS Keychain, ssh-add, and custom key paths configured in the shell). Fall back to common key file names (id_ed25519, id_ecdsa, id_rsa) if no agent is available. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c6657daa96
commit
1501b15a9d
1 changed files with 32 additions and 8 deletions
|
|
@ -3,11 +3,13 @@ package sshclient
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"net"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
"golang.org/x/crypto/ssh"
|
||||||
|
"golang.org/x/crypto/ssh/agent"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Client struct {
|
type Client struct {
|
||||||
|
|
@ -140,17 +142,23 @@ func parseTarget(target string) (user, host string, err error) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func buildAuthMethods(keyPath string) ([]ssh.AuthMethod, error) {
|
func buildAuthMethods(keyPath string) ([]ssh.AuthMethod, error) {
|
||||||
candidates := []string{}
|
var methods []ssh.AuthMethod
|
||||||
|
|
||||||
|
// SSH agent — uses whatever keys are loaded in the agent (macOS Keychain, ssh-add, etc.)
|
||||||
|
if m := agentAuth(); m != nil {
|
||||||
|
methods = append(methods, m)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explicit key file or common defaults
|
||||||
|
var candidates []string
|
||||||
if keyPath != "" {
|
if keyPath != "" {
|
||||||
candidates = append(candidates, keyPath)
|
candidates = append(candidates, keyPath)
|
||||||
} else {
|
} else {
|
||||||
home, _ := os.UserHomeDir()
|
home, _ := os.UserHomeDir()
|
||||||
candidates = append(candidates,
|
for _, name := range []string{"id_ed25519", "id_ecdsa", "id_rsa"} {
|
||||||
filepath.Join(home, ".ssh", "id_ed25519"),
|
candidates = append(candidates, filepath.Join(home, ".ssh", name))
|
||||||
filepath.Join(home, ".ssh", "id_rsa"),
|
}
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, p := range candidates {
|
for _, p := range candidates {
|
||||||
data, err := os.ReadFile(p)
|
data, err := os.ReadFile(p)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
@ -160,10 +168,26 @@ func buildAuthMethods(keyPath string) ([]ssh.AuthMethod, error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return []ssh.AuthMethod{ssh.PublicKeys(signer)}, nil
|
methods = append(methods, ssh.PublicKeys(signer))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, fmt.Errorf("no usable SSH private key found (tried: %v)", candidates)
|
if len(methods) == 0 {
|
||||||
|
return nil, fmt.Errorf("no SSH auth method available — no agent (SSH_AUTH_SOCK) and no key files found in %v", candidates)
|
||||||
|
}
|
||||||
|
return methods, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// agentAuth returns an auth method backed by the SSH agent if SSH_AUTH_SOCK is set.
|
||||||
|
func agentAuth() ssh.AuthMethod {
|
||||||
|
sock := os.Getenv("SSH_AUTH_SOCK")
|
||||||
|
if sock == "" {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
conn, err := net.Dial("unix", sock)
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return ssh.PublicKeysCallback(agent.NewClient(conn).Signers)
|
||||||
}
|
}
|
||||||
|
|
||||||
func parseMeminfo(raw string) (totalMB, usedMB int64) {
|
func parseMeminfo(raw string) (totalMB, usedMB int64) {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue