如何在 Go 中安全高效地将 SSH 公钥复制到远程服务器
本文介绍使用 Go 标准库 os/exec 直接将本地 SSH 公钥写入远程服务器 ~/.ssh/authorized_keys 的正确方法避免 shell 字符串拼接风险兼容 macOS/Linux 环境且不依赖 ssh-copy-id。 本文介绍使用 go 标准库 os/exec 直接将本地 ssh 公钥写入远程服务器 ~/.ssh/authorized_keys 的正确方法避免 shell 字符串拼接风险兼容 macos/linux 环境且不依赖 ssh-copy-id。在 Go 中通过命令行方式如 cat key.pub | ssh userhost cat ~/.ssh/authorized_keys实现公钥分发看似直观但原代码存在多个关键问题 ? 错误调用 exec.Command(identity, address)将两个独立命令cat ... 和 ssh ...作为参数传给单个 exec.Command实际会尝试执行名为 cat /home/foo/.ssh/foobar.pub 的二进制文件显然不存在 ? 未正确构建管道逻辑Go 的 exec.Command 不会自动解析 shell 管道符 |需显式设置 Stdin ? 路径硬编码与拼接风险~/.shh/authorized_keys注意 typoshh 应为 ssh和字符串格式化易引入路径注入或权限错误 ? 忽略 SSH 连接前置条件如目标用户 .ssh 目录权限应为 700、authorized_keys 文件权限推荐 600否则 OpenSSH 会拒绝加载。? 正确做法是用 Go 原生读取公钥文件内容并将其作为 stdin 流式传递给 ssh 进程。这样既规避了 shell 解析的不确定性又提升安全性与可移植性。以下是完整、健壮的实现import ( io log os os/exec path/filepath)func copySSHPubKeyToServer(username, hostname, keyName string) error { // 1. 构建公钥文件路径使用 filepath.Join 避免路径拼接漏洞 homeDir : os.Getenv(HOME) if homeDir { return fmt.Errorf(HOME environment variable not set) } keyPath : filepath.Join(homeDir, .ssh, keyName.pub) // 2. 打开公钥文件 keyFile, err : os.Open(keyPath) if err ! nil { return fmt.Errorf(failed to open public key %s: %w, keyPath, err) } defer keyFile.Close() // 3. 构建 ssh 命令注意目标路径必须为 ~/.ssh/authorized_keys修正 typo // 使用绝对路径更可靠如 $HOME/.ssh/authorized_keys但 ~ 在 ssh 上下文中通常可被服务端 shell 展开 sshTarget : username hostname cmd : exec.Command(ssh, sshTarget, mkdir -p ~/.ssh chmod 700 ~/.ssh cat ~/.ssh/authorized_keys) // 4. 将公钥文件设为命令标准输入 cmd.Stdin keyFile // 5. 捕获输出以便调试 output, err : cmd.CombinedOutput() if err ! nil { log.Printf(SSH command failed (output: %s), string(output)) return fmt.Errorf(failed to copy SSH key to %s: %w, sshTarget, err) } log.Printf(Successfully added %s.pub to %ss authorized_keys, keyName, sshTarget) return nil}? 关键注意事项 RedClaw 百度推出的手机端万能AI Agent助手