ssh.dial必须配net.dialer超时,否则tcp握手无响应会卡死几十秒;ssh.clientconfig.timeout仅控制认证阶段,不影响底层连接建立。

ssh.Dial 必须配 net.Dialer 超时,否则卡死无日志
Go 的 ssh.Dial 默认不设 TCP 层超时,哪怕目标 IP 完全不通、防火墙 DROP 了 22 端口,也会静默 hang 住几十秒。这不是认证失败,是底层 net.Dial 在等 SYN-ACK。ssh.ClientConfig.Timeout 只管握手和认证阶段,对 TCP 连接本身无效。
- 用
&net.Dialer{Timeout: 5 * time.Second}显式传入 dialer 参数 - 不要依赖
context.WithTimeout包裹ssh.Dial—— 它无法中断正在阻塞的系统调用 - 若需 DNS 解析超时,还得加
KeepAlive: 30 * time.Second和Resolver(小众但必要)
HostKeyCallback 是强制字段,不是可选项
编译会直接报错:missing required field HostKeyCallback。这不是警告,是结构体定义强制要求。填 ssh.InsecureIgnoreHostKey() 能过编译,但等于关掉服务器身份校验。
- 本地调试可用,CI/CD 或容器环境严禁使用
- 生产必须用
ssh.KnownHosts(knownHostsFile),路径自己拼:比如filepath.Join(home, ".ssh", "known_hosts"),别指望~自动展开 - 想预置某台机器公钥?先
ssh-keyscan -t rsa host拿到 raw key,再用ssh.ParsePublicKey解析后传给ssh.FixedHostKey
*ssh.Session 不能复用,每次命令都得 NewSession()
*ssh.Session 不是连接池资源,也不是线程安全对象。复用它执行多个命令,常见问题包括:
write tcp: broken pipe命令静默丢弃(尤其并发场景)
session.Run("ls")后再调session.Wait()会 panic(内部已自动 Wait)每次命令前调
client.NewSession(),用完defer session.Close()需交互(如输密码、yes/no)才用
session.Start()+session.Wait(),且必须提前配好session.StdinPipe()和session.StdoutPipe()单行命令优先用
session.CombinedOutput(),比session.Output()+session.StderrPipe()更省心
SFTP 上传必须绕开 session.Run("scp")
session.Run("scp ...") 看似简单,但实际极度脆弱:远端没 scp 命令、shell 被设为 /bin/false、路径含空格或中文,全都会崩,错误信息还模糊(比如只报 exit status 1)。
- 改用
sftp.NewClient(conn),走 SSH 内置子系统,不依赖 shell - 上传后记得
file.Chmod(0644)——client.Create()默认权限是0600,常导致后续脚本读不了 - 目标目录不存在?
client.MkdirAll()在老旧 OpenSSH 版本上可能不递归,得手动拆路径逐级Mkdir()
私钥格式错误是最隐蔽的坑,unable to parse private key 往往不是密码错
crypto/ssh 只认 PEM 格式私钥(以 -----BEGIN RSA PRIVATE KEY----- 开头),不支持新版 OpenSSH 默认的 -----BEGIN OPENSSH PRIVATE KEY-----。
- 用
ssh-keygen -p -m PEM -f id_rsa转换格式(会提示输原密码) - 加载时用
os.ReadFile,别用已弃用的ioutil.ReadFile - 解析必须用
ssh.ParsePrivateKey;若私钥加密,得先pem.Decode,再根据类型选x509.ParsePKCS1PrivateKey或ParsePKCS8PrivateKey
跳板机(Bastion)连接不能靠 ProxyCommand,得用 Client.Dial() 手动建隧道
OpenSSH 的 -o ProxyCommand=nc 是 shell 层代理,Go 里没法复用。真正可控的方式是:先连跳板机,再调用其 client.Dial("tcp", "inner-host:22"),在远端发起第二层 TCP 连接——本质是 “SSH over SSH”。
- 这种方式不依赖
nc、socat等外部工具 - 隧道建立后,后续所有
ssh.Dial都走这个连接,而非直连内网地址 - 注意:跳板机上的
AllowTcpForwarding yes必须开启,否则Dial会返回connection refused
真正的难点不在连接,而在错误归因——ssh: handshake failed 可能是密钥错、hostkey 拒绝、TCP 超时、甚至跳板机转发被禁,得一层层剥开看日志。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











