🐛 fix(ssh): 修复非 id_rsa 私钥无法浏览选择与读取

- 修正 OpenFileDialog 默认目录,支持无扩展名密钥路径
- macOS 下取消错误文件名过滤,显示隐藏项以选择 ~/.ssh 密钥
- 私钥读/解析失败返回明确错误,示例改为 id_ed25519
- 补充选钥目录解析与私钥读取失败回归测试

Refs #711
This commit is contained in:
Syngnat
2026-07-24 21:30:40 +08:00
parent e6f39d34d3
commit 2a7ef109fd
5 changed files with 209 additions and 44 deletions

View File

@@ -4,11 +4,13 @@ import (
"context"
"crypto/sha256"
"encoding/hex"
"errors"
"fmt"
"io"
"net"
"os"
"strconv"
"strings"
"sync"
"time"
@@ -59,18 +61,22 @@ func connectSSH(config connection.SSHConfig) (*ssh.Client, error) {
logger.Infof("开始建立 SSH 连接:地址=%s:%d 用户=%s", config.Host, config.Port, config.User)
authMethods := []ssh.AuthMethod{}
if config.KeyPath != "" {
key, err := os.ReadFile(config.KeyPath)
if keyPath := strings.TrimSpace(config.KeyPath); keyPath != "" {
key, err := os.ReadFile(keyPath)
if err != nil {
logger.Warnf("读取 SSH 私钥失败:路径=%s原因%v", config.KeyPath, err)
} else {
signer, err := ssh.ParsePrivateKey(key)
if err != nil {
logger.Warnf("解析 SSH 私钥失败:路径=%s原因%v", config.KeyPath, err)
} else {
authMethods = append(authMethods, ssh.PublicKeys(signer))
}
logger.Warnf("读取 SSH 私钥失败:路径=%s原因%v", keyPath, err)
return nil, fmt.Errorf("failed to read SSH private key %s: %w", keyPath, err)
}
signer, err := ssh.ParsePrivateKey(key)
if err != nil {
logger.Warnf("解析 SSH 私钥失败:路径=%s原因%v", keyPath, err)
var passphraseErr *ssh.PassphraseMissingError
if errors.As(err, &passphraseErr) {
return nil, fmt.Errorf("SSH private key %s is encrypted with a passphrase; passphrase-protected keys are not supported", keyPath)
}
return nil, fmt.Errorf("failed to parse SSH private key %s: %w", keyPath, err)
}
authMethods = append(authMethods, ssh.PublicKeys(signer))
}
if config.Password != "" {

View File

@@ -0,0 +1,58 @@
package ssh
import (
"os"
"path/filepath"
"strings"
"testing"
"GoNavi-Wails/internal/connection"
)
func TestConnectSSHReturnsErrorWhenPrivateKeyMissing(t *testing.T) {
t.Cleanup(CloseAllSSHClients)
missing := filepath.Join(t.TempDir(), "id_ed25519")
_, err := connectSSH(connection.SSHConfig{
Host: "127.0.0.1",
Port: 1,
User: "root",
KeyPath: missing,
})
if err == nil {
t.Fatal("expected error for missing private key")
}
message := err.Error()
if !strings.Contains(message, "failed to read SSH private key") {
t.Fatalf("expected read failure message, got %q", message)
}
if !strings.Contains(message, missing) {
t.Fatalf("expected key path in error, got %q", message)
}
}
func TestConnectSSHReturnsErrorWhenPrivateKeyInvalid(t *testing.T) {
t.Cleanup(CloseAllSSHClients)
keyPath := filepath.Join(t.TempDir(), "custom_deploy_key")
if err := os.WriteFile(keyPath, []byte("not-a-private-key"), 0o600); err != nil {
t.Fatalf("write key: %v", err)
}
_, err := connectSSH(connection.SSHConfig{
Host: "127.0.0.1",
Port: 1,
User: "root",
KeyPath: keyPath,
})
if err == nil {
t.Fatal("expected error for invalid private key")
}
message := err.Error()
if !strings.Contains(message, "failed to parse SSH private key") {
t.Fatalf("expected parse failure message, got %q", message)
}
if !strings.Contains(message, keyPath) {
t.Fatalf("expected key path in error, got %q", message)
}
}