refactor: optimize setting view

This commit is contained in:
2026-06-12 11:05:04 +08:00
parent 4576653b4e
commit 3cd92945ac
4 changed files with 81 additions and 48 deletions
+52 -34
View File
@@ -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
}