
本文介绍如何在 go 中绕过 shell 命令拼接,直接通过 os.open 读取本地公钥文件并以标准输入(stdin)方式传输至远程服务器的 authorized_keys,避免 shell 注入、路径错误与跨平台兼容性问题。
本文介绍如何在 go 中绕过 shell 命令拼接,直接通过 os.open 读取本地公钥文件并以标准输入(stdin)方式传输至远程服务器的 authorized_keys,避免 shell 注入、路径错误与跨平台兼容性问题。
在自动化部署或基础设施管理场景中,经常需要通过 Go 程序将本地生成的 SSH 公钥安全地追加到远程 Linux/macOS 服务器的 ~/.ssh/authorized_keys 文件中。许多开发者最初尝试用 exec.Command 模拟终端管道命令(如 cat key.pub | ssh user@host 'cat >> ~/.ssh/authorized_keys'),但这种方式存在严重缺陷:不仅因字符串拼接易引发 shell 注入和路径解析错误(如示例中误写为 ~/.shh/authorized_keys),更因 exec.Command 的参数传递机制不支持 Unix 管道符 | —— 它只会把整个字符串当作单一命令名执行,导致运行失败。
正确的做法是解耦「读取」与「传输」逻辑:由 Go 直接打开公钥文件,再将其作为 stdin 注入 ssh 子进程。这样既规避了 shell 解析依赖,又确保了二进制安全传输(无换行截断、编码失真风险),且天然兼容 macOS、Linux 等不同系统。
以下为推荐实现方案:
import (
"log"
"os"
"os/exec"
"path/filepath"
)
func copySSHPubKeyToServer(pubKeyPath, userAtHost string) error {
// 1. 安全读取公钥文件(自动处理路径)
absPath, err := filepath.Abs(pubKeyPath)
if err != nil {
return err
}
file, err := os.Open(absPath)
if err != nil {
return err
}
defer file.Close()
// 2. 构建 ssh 命令:注意目标路径应为 ~/.ssh/authorized_keys(非 .shh!)
cmd := exec.Command("ssh", userAtHost, "mkdir -p ~/.ssh && cat >> ~/.ssh/authorized_keys")
cmd.Stdin = file
// 3. 执行并捕获输出(便于调试)
output, err := cmd.CombinedOutput()
if err != nil {
log.Printf("SSH command failed: %s", string(output))
return err
}
return nil
}
// 使用示例
func main() {
homeDir := fileUtil.FindUserHomeDir() // 假设该工具函数已定义
pubKeyPath := filepath.Join(homeDir, ".ssh", "foobar.pub")
userAtHost := "alice@example.com"
if err := copySSHPubKeyToServer(pubKeyPath, userAtHost); err != nil {
log.Fatal("Failed to install SSH key:", err)
}
log.Println("SSH public key successfully installed.")
}✅ 关键优势说明:
- 无 Shell 依赖:不调用 /bin/sh,彻底规避 ssh-copy-id 缺失或 shell 版本差异问题;
- 路径安全:filepath.Abs 和 filepath.Join 自动处理跨平台路径分隔符(/ vs \);
- 原子写入保障:命令中内联 mkdir -p ~/.ssh 确保目录存在,避免因权限或缺失目录导致写入失败;
- 错误可追溯:使用 CombinedOutput() 获取完整 stderr/stdout,便于定位连接拒绝、权限不足等常见问题。
⚠️ 注意事项:
- 确保本地私钥已正确配置(如 ~/.ssh/id_rsa),且远程服务器 SSH 服务允许公钥认证(PubkeyAuthentication yes);
- 首次连接时若需交互确认 host key,建议预置 ssh-keyscan 或设置 StrictHostKeyChecking=no(仅限可信网络);
- 生产环境应校验公钥格式(如以 ssh-rsa/ssh-ed25519 开头),防止恶意内容注入;
- 若需幂等操作(避免重复追加),可先通过 ssh ... 'grep -q ... authorized_keys' 检查是否存在,再决定是否写入。
通过此方法,你获得了一个轻量、可靠、跨平台的 SSH 密钥分发能力,为构建 CI/CD 工具链或云资源编排系统打下坚实基础。


















