gongdear

gongdear的技术博客

欢迎大家参观我的博客
  menu
119 文章
89355 浏览
7 当前访客
ღゝ◡╹)ノ❤️

在 Windows PowerShell 中优雅实现 ssh-copy-id:告别手动复制公钥

在 Windows PowerShell 中优雅实现 ssh-copy-id:告别手动复制公钥

前言:Windows 开发者的痛点

对于习惯了 Linux 环境的开发者来说,Windows 下的 SSH 体验曾一度令人抓狂。虽然 Windows 10/11 已经内置了 OpenSSH Client,提供了完整的 sshscpsftp **等基础命令,但唯独缺少了那个让无数运维和开发感到安心的命令——ssh-copy-id

每次配置新服务器,我们要么手动 cat 公钥再 scp 上传,要么小心翼翼地复制粘贴 authorized_keys,稍有不慎就会因为换行符或空格导致免密登录失败。

既然 Windows 的 PowerShell 拥有强大的脚本能力,且底层的 SSH 协议栈是完整的,我们完全可以用 PowerShell 重新实现 ssh-copy-id 。本文将带你手写一个支持多种密钥类型(RSA、ED25519)的 PowerShell 函数,让你从此告别手动复制公钥的烦恼。


核心思路

Linux 原生的 ssh-copy-id 本质上做了三件事:

  1. 读取本地公钥文件内容。
  2. 通过 SSH 连接到远程服务器。
  3. 将公钥内容追加到远程服务器的~/.ssh/authorized_keys文件中,并确保目录和文件权限正确。

在 PowerShell 中,我们可以利用管道 | 将本地文件内容直接传递给远程 SSH 命令,从而一步到位。


代码实现:支持多密钥类型的 ssh-copy-id

考虑到现代 SSH 密钥不仅限于 RSA,ED25519 因其高安全性和小体积正变得越来越流行。下面是一个增强版的 PowerShell 函数,支持自动检测或指定密钥类型。

将以下代码保存为 ssh-copy-id.ps1 ,或者直接粘贴到你的 PowerShell 终端中:

function ssh-copy-id {
    [CmdletBinding()]
    param(
        [Parameter(Mandatory=$true, Position=0)]
        [string]$userAtMachine,

        [Parameter(Position=1)]
        [ValidateSet("rsa", "ed25519", "ecdsa")]
        [string]$KeyType = "rsa",

        [Parameter(ValueFromRemainingArguments=$true)]
        [string[]]$SshArgs
    )

    # 1. 构建公钥路径
    $keyPath = Join-Path $ENV:USERPROFILE ".ssh\id_$KeyType.pub"
  
    # 2. 检查本地公钥是否存在
    if (!(Test-Path $keyPath)) {
        Write-Error "ERROR: 找不到公钥文件 '$keyPath'。请先使用 ssh-keygen -t $KeyType 生成密钥。"
        return
    }

    Write-Host "正在将公钥 ($KeyType) 复制到 $userAtMachine ..." -ForegroundColor Cyan

    # 3. 核心逻辑:读取公钥并通过 SSH 追加到远程 authorized_keys
    # umask 077: 确保创建的文件/目录权限安全
    # test -d .ssh || mkdir .ssh: 确保 .ssh 目录存在
    # cat >> .ssh/authorized_keys: 追加公钥
    try {
        Get-Content $keyPath | ssh $SshArgs $userAtMachine `
            "umask 077; test -d .ssh || mkdir .ssh; cat >> .ssh/authorized_keys"
      
        if ($LASTEXITCODE -eq 0) {
            Write-Host "✅ 成功!现在可以使用 'ssh $userAtMachine' 进行免密登录了。" -ForegroundColor Green
        } else {
            Write-Warning "⚠️ 远程命令执行返回非零状态码,请检查远程服务器权限或网络。"
        }
    }
    catch {
        Write-Error "❌ 发生错误: $_"
    }
}

id_rsa.pub

function ssh-copy-id([string]$userAtMachine, $args){   
    $publicKey = "$ENV:USERPROFILE" + "/.ssh/id_rsa.pub"
    if (!(Test-Path "$publicKey")){
        Write-Error "ERROR: failed to open ID file '$publicKey': No such file"            
    }
    else {
        & cat "$publicKey" | ssh $args $userAtMachine "umask 077; test -d .ssh || mkdir .ssh ; cat >> .ssh/authorized_keys || exit 1"      
    }
}

id_ed25519.pub

function ssh-copy-id([string]$userAtMachine, $args){   
    $publicKey = "$ENV:USERPROFILE" + "/.ssh/id_ed25519.pub"
    if (!(Test-Path "$publicKey")){
        Write-Error "ERROR: failed to open ID file '$publicKey': No such file"            
    }
    else {
        & cat "$publicKey" | ssh $args $userAtMachine "umask 077; test -d .ssh || mkdir .ssh ; cat >> .ssh/authorized_keys || exit 1"      
    }
}

代码深度解析

1. 参数设计

  • $userAtMachine : 格式为user@hostname,支持端口指定(如-p 2222 user@host)。
  • $KeyType : 默认rsa,支持ed25519 ecdsa。这解决了你提供的原始脚本中硬编码路径的问题。
  • $SshArgs : 使用ValueFromRemainingArguments,允许你透传任何 SSH 参数,例如-p 2222 -i ~/.ssh/custom_key

2. 路径处理

使用 Join-Path $ENV:USERPROFILE ".ssh\id_$KeyType.pub" 代替字符串拼接,这是 Windows 路径处理的最佳实践,避免了正斜杠/反斜杠的兼容性问题。

3. 远程命令的原子性

umask 077; test -d .ssh || mkdir .ssh; cat >> .ssh/authorized_keys
  • umask 077:这是安全的关键 。它确保新创建的.ssh目录权限为700authorized_keys文件权限为 600。如果权限过大,OpenSSH 服务端会拒绝密钥认证。
  • test -d ... || mkdir ...: 防止因目录不存在导致cat写入失败。

如何使用?

场景 1:默认 RSA 密钥

ssh-copy-id root@192.168.1.100

场景 2:使用 ED25519 密钥

ssh-copy-id root@192.168.1.100 -KeyType ed25519

场景 3:非标准端口 (2222)

ssh-copy-id root@192.168.1.100 -SshArgs "-p", "2222"
# 或者利用 ValueFromRemainingArguments 特性:
ssh-copy-id root@192.168.1.100 -p 2222

进阶:如何永久生效?

每次打开终端都要粘贴函数太麻烦了。你可以将其加入 PowerShell 配置文件:

  1. 打开配置文件:
    notepad $PROFILE
    
  2. 将上面的function ssh-copy-id { ... }代码粘贴进去并保存。
  3. 重启 PowerShell。

现在,你的 Windows PowerShell 拥有了和 Linux 一样丝滑的 ssh-copy-id 体验!


常见问题排查 (Troubleshooting)

问题现象可能原因解决方案
ERROR: 找不到公钥文件本地未生成密钥或路径错误运行 ssh-keygen -t ed25519 生成密钥
免密登录失败 (Permission denied)远程权限不正确登录服务器执行 chmod 700 ~/.ssh && chmod 600 ~/.ssh/authorized_keys
远程命令报错 cat: ... Permission deniedSELinux 或 ACL 限制检查服务器 SELinux 状态或 Windows 用户目录 ACL
提示 ssh 不是内部命令OpenSSH Client 未安装设置 -> 应用 -> 可选功能 -> 添加 OpenSSH 客户端

总结

PowerShell 的强大之处在于它可以无缝桥接 Windows 与 Linux 的工具链。通过短短几十行代码,我们不仅补齐了 Windows SSH 工具链的短板,还实现了对多密钥类型的支持。

下一步建议: 如果你经常管理多台服务器,可以考虑将这个脚本进一步封装为 PowerShell Module,或者结合 ssh config 实现更高级的自动化运维。

安全提示 :永远不要将私钥 (id_rsa) 上传到服务器,ssh-copy-id仅传输公钥 (.pub)。

希望这篇博客能帮到你!如果觉得有用,欢迎点赞收藏。

宝剑锋从磨砺出,梅花香自苦寒来.