refactor: optimize setting view
This commit is contained in:
@@ -385,7 +385,14 @@ func shellQuote(s string) string {
|
||||
}
|
||||
|
||||
// buildAuthMethods chooses authentication methods given the supplied
|
||||
// configuration. See Dial's docstring for the priority order.
|
||||
// configuration.
|
||||
//
|
||||
// The method set is gated by cfg.AuthMethod:
|
||||
// - SSHAuthPassword → password only.
|
||||
// - SSHAuthKey → ssh-agent + ~/.ssh/id_* key files only.
|
||||
// - "" / SSHAuthBuiltin (legacy) → password (if set) + agent + key files.
|
||||
//
|
||||
// (Kerberos never reaches here; it uses the system ssh binary instead.)
|
||||
//
|
||||
// Returns a human-readable summary alongside the method slice so the
|
||||
// caller can include "password+agent+ed25519" (and similar) in its dial
|
||||
@@ -393,47 +400,58 @@ func shellQuote(s string) string {
|
||||
func buildAuthMethods(cfg domain.SSHConfig) ([]ssh.AuthMethod, string, error) {
|
||||
var methods []ssh.AuthMethod
|
||||
var sources []string
|
||||
if cfg.Password != "" {
|
||||
|
||||
// Decide which families are allowed for the selected method. An empty /
|
||||
// "builtin" value preserves the original combined behaviour so configs
|
||||
// written before the split keep working.
|
||||
allowPassword := cfg.AuthMethod == domain.SSHAuthPassword ||
|
||||
cfg.AuthMethod == domain.SSHAuthBuiltin || cfg.AuthMethod == ""
|
||||
allowKey := cfg.AuthMethod == domain.SSHAuthKey ||
|
||||
cfg.AuthMethod == domain.SSHAuthBuiltin || cfg.AuthMethod == ""
|
||||
|
||||
if allowPassword && cfg.Password != "" {
|
||||
methods = append(methods, ssh.Password(cfg.Password))
|
||||
sources = append(sources, "password")
|
||||
}
|
||||
if sock := os.Getenv("SSH_AUTH_SOCK"); sock != "" {
|
||||
if conn, err := net.Dial("unix", sock); err == nil {
|
||||
// 关键: 仅在 agent 真正持有 key 时才把它加入 auth methods.
|
||||
//
|
||||
// macOS 的 launchd ssh-agent 即便没有任何 identity 也会响应,
|
||||
// 此时若仍注册 PublicKeysCallback, x/crypto 会把它当作一次空的
|
||||
// publickey 尝试. 配合服务器的 MaxAuthTries 计数, 这次"空尝试"
|
||||
// 会挤占后续基于磁盘私钥的认证机会, 最终导致
|
||||
// "[none publickey] no supported methods remain" —— 即便磁盘上
|
||||
// 的 key 本身完全可用. 因此空 agent 必须跳过.
|
||||
ag := agent.NewClient(conn)
|
||||
if keys, lerr := ag.List(); lerr == nil && len(keys) > 0 {
|
||||
methods = append(methods, ssh.PublicKeysCallback(ag.Signers))
|
||||
sources = append(sources, fmt.Sprintf("agent(%d)", len(keys)))
|
||||
if allowKey {
|
||||
if sock := os.Getenv("SSH_AUTH_SOCK"); sock != "" {
|
||||
if conn, err := net.Dial("unix", sock); err == nil {
|
||||
// 关键: 仅在 agent 真正持有 key 时才把它加入 auth methods.
|
||||
//
|
||||
// macOS 的 launchd ssh-agent 即便没有任何 identity 也会响应,
|
||||
// 此时若仍注册 PublicKeysCallback, x/crypto 会把它当作一次空的
|
||||
// publickey 尝试. 配合服务器的 MaxAuthTries 计数, 这次"空尝试"
|
||||
// 会挤占后续基于磁盘私钥的认证机会, 最终导致
|
||||
// "[none publickey] no supported methods remain" —— 即便磁盘上
|
||||
// 的 key 本身完全可用. 因此空 agent 必须跳过.
|
||||
ag := agent.NewClient(conn)
|
||||
if keys, lerr := ag.List(); lerr == nil && len(keys) > 0 {
|
||||
methods = append(methods, ssh.PublicKeysCallback(ag.Signers))
|
||||
sources = append(sources, fmt.Sprintf("agent(%d)", len(keys)))
|
||||
} else {
|
||||
sshLog().Debug("ssh-agent has no identities; skipping",
|
||||
"sock", sock, "list_err", lerr)
|
||||
}
|
||||
} else {
|
||||
sshLog().Debug("ssh-agent has no identities; skipping",
|
||||
"sock", sock, "list_err", lerr)
|
||||
sshLog().Debug("ssh-agent dial failed", "sock", sock, "err", err)
|
||||
}
|
||||
}
|
||||
home, err := os.UserHomeDir()
|
||||
if err == nil {
|
||||
// Probe the common default keys; any unreadable / missing file is
|
||||
// silently skipped so the user never sees noise about keys they did
|
||||
// not set up.
|
||||
for _, name := range []string{"id_ed25519", "id_rsa", "id_ecdsa"} {
|
||||
signer, err := loadPrivateKey(path.Join(home, ".ssh", name))
|
||||
if err == nil && signer != nil {
|
||||
methods = append(methods, ssh.PublicKeys(signer))
|
||||
sources = append(sources, name)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sshLog().Debug("ssh-agent dial failed", "sock", sock, "err", err)
|
||||
sshLog().Debug("home dir unavailable for ssh keys", "err", err)
|
||||
}
|
||||
}
|
||||
home, err := os.UserHomeDir()
|
||||
if err == nil {
|
||||
// Probe the common default keys; any unreadable / missing file is
|
||||
// silently skipped so the user never sees noise about keys they did
|
||||
// not set up.
|
||||
for _, name := range []string{"id_ed25519", "id_rsa", "id_ecdsa"} {
|
||||
signer, err := loadPrivateKey(path.Join(home, ".ssh", name))
|
||||
if err == nil && signer != nil {
|
||||
methods = append(methods, ssh.PublicKeys(signer))
|
||||
sources = append(sources, name)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
sshLog().Debug("home dir unavailable for ssh keys", "err", err)
|
||||
}
|
||||
if len(sources) == 0 {
|
||||
return methods, "none", nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user