SSH 远程连接与免密登录#
整理了 SSH 连接服务器、生成公钥、配置免密登录,以及日常常用的 SSH / SCP 命令。
1. 生成公钥#
在本地终端(Windows PowerShell / Linux / macOS)执行:
# 推荐 ed25519: 更安全、密钥更短
ssh-keygen -t ed25519 -C "[email protected]"
# 需要兼容老设备时可用 RSA (至少 4096 位)
ssh-keygen -t rsa -b 4096 -C "[email protected]"
# 当然更简单的直接输入也可以
ssh-keygen
一路回车即可,默认生成于 ~/.ssh/:
私钥
id_ed25519:务必保密,不要外传或提交到仓库公钥
id_ed25519.pub:可公开,用于上传到服务器
ssh-keygen 会询问 passphrase(密钥口令),留空即为无口令(纯免密),输入则每次使用私钥需先解口令。
2. 上传公钥到服务器#
下面这段 PowerShell 脚本用于把本地公钥追加到远端 authorized_keys,保持原样不要改动:
将公钥上传到服务器脚本如下:
[CmdletBinding(DefaultParameterSetName = "DeployMode")]
param(
[Parameter(Mandatory = $true, ParameterSetName = "DeployMode", Position = 0)]
[Alias("r")]
[string]$RemoteHost,
[Parameter(Mandatory = $true, ParameterSetName = "DeployMode", Position = 1)]
[Alias("u")]
[string]$User,
[Parameter(ParameterSetName = "DeployMode")]
[ValidateRange(1, 65535)]
[Alias("p")]
[int]$Port = 22,
[Parameter(Mandatory = $true, ParameterSetName = "HelpMode")]
[Alias("h")]
[switch]$Help
)
$scriptName = Split-Path -Leaf $PSCommandPath
function Show-Help {
Write-Host @"
SSH 公钥部署工具 v2.1
使用方法:
$scriptName <主机> <用户> [-Port <端口>]
$scriptName -RemoteHost <主机> -User <用户> [-Port <端口>]
$scriptName -h
参数:
-RemoteHost 目标服务器 IP/域名 (部署模式必需)
-User 目标服务器用户名 (部署模式必需)
-Port SSH 端口 (默认: 22)
-h, -Help 显示帮助信息
示例:
.\$scriptName 114.514.191.9 orz -Port 810
.\$scriptName -RemoteHost 10.0.0.5 -User webuser -Port 2222
注意事项:
1. 本地需已生成密钥对: ssh-keygen -t ed25519
2. 首次连接自动接受主机指纹 (StrictHostKeyChecking=accept-new)
3. 仍需在最后一步输入一次远端密码 (公钥尚未生效时)
"@
exit 0
}
# --- 终端动画 (opencode 风格 braille spinner) ----------------------------
$frameList = @('⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏')
function pause {
$null = Read-Host -Prompt "按 Enter 键退出"
}
function Start-Spinner {
param(
[Parameter(Mandatory = $true)]
[scriptblock]$Action,
[string]$Message = '',
[int]$StepMs = 90
)
# 在后台作业里跑命令: 作业进程不带用户控制台、不继承 stdin,
# 因此不会出现"卡住等回车"的问题
$job = Start-Job -ScriptBlock $Action
$idx = 0
while ($job.State -eq 'Running') {
Write-Host -NoNewline ("`r{0} {1}" -f $frameList[$idx % $frameList.Length], $Message)
Start-Sleep -Milliseconds $StepMs
$idx++
}
# 清掉动画行, 避免残留字符
Write-Host -NoNewline ("`r" + (" " * 80) + "`r")
$result = Receive-Job -Job $job -ErrorAction SilentlyContinue
Remove-Job -Job $job -Force
return $result
}
if ($Help) { Show-Help }
# --- 环境检查 -------------------------------------------------------------
if (-not (Get-Command ssh -ErrorAction SilentlyContinue)) {
Write-Host "[错误] 未找到 ssh 客户端, 请先安装 OpenSSH Client" -ForegroundColor Red
exit 1
}
$sshDir = Join-Path $env:USERPROFILE ".ssh"
if (-not (Test-Path -LiteralPath $sshDir)) {
Write-Host "[错误] SSH 目录不存在: $sshDir 请先执行 ssh-keygen -t ed25519" -ForegroundColor Red
exit 1
}
# --- 公钥选择 -------------------------------------------------------------
$pubKeys = @(Get-ChildItem -LiteralPath $sshDir -Filter "*.pub" -File -ErrorAction SilentlyContinue)
if ($pubKeys.Count -eq 0) {
Write-Host "[错误] 未找到任何 .pub 公钥文件于 $sshDir" -ForegroundColor Red
exit 1
}
if ($pubKeys.Count -gt 1) {
Write-Host "`n检测到多个公钥文件:"
for ($i = 0; $i -lt $pubKeys.Count; $i++) {
Write-Host (" [{0}] {1}" -f ($i + 1), $pubKeys[$i].Name)
}
do {
$selected = Read-Host "`n请选择公钥编号 (1-$($pubKeys.Count))"
} while ($selected -notmatch '^\d+$' -or [int]$selected -lt 1 -or [int]$selected -gt $pubKeys.Count)
$keyFile = $pubKeys[[int]$selected - 1].FullName
} else {
$keyFile = $pubKeys[0].FullName
Write-Host "`n使用唯一公钥: $($pubKeys[0].Name)" -ForegroundColor Cyan
}
# --- 读取公钥 (去掉换行, 保证末尾一个 \n) ---------------------------------
try {
$keyLine = ((Get-Content -LiteralPath $keyFile -Raw -ErrorAction Stop) -replace '\r?\n', ' ').Trim()
} catch {
Write-Host "[错误] 读取公钥文件失败: $($_.Exception.Message)" -ForegroundColor Red
exit 1
}
if ([string]::IsNullOrWhiteSpace($keyLine)) {
Write-Host "[错误] 公钥文件内容为空: $keyFile" -ForegroundColor Red
exit 1
}
# --- 部署 ---------------------------------------------------------------
$target = "$User@$RemoteHost"
$remoteCmd = 'mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys'
# 保证输出为 UTF-8 且不带 BOM, 避免管道编码问题
$OutputEncoding = New-Object System.Text.UTF8Encoding($false)
Write-Host "`n正在部署公钥到 $target (端口 $Port) ..." -ForegroundColor Cyan
try {
$keyLine | & ssh -p $Port -o StrictHostKeyChecking=accept-new $target $remoteCmd
if ($LASTEXITCODE -ne 0) {
throw "ssh 上传失败, 退出码 $LASTEXITCODE (可能是密码错误或端口不通)"
}
} catch {
Write-Host "`n[错误] 部署失败: $($_.Exception.Message)" -ForegroundColor Red
exit 1
}
# --- 验证 (BatchMode: 不再询问密码, 带 spinner) -------------------------
$exitCode = Start-Spinner -Message "正在验证免密登录 $target" -Action {
& ssh -p $using:Port -o BatchMode=yes -o StrictHostKeyChecking=accept-new $using:target "echo OK" 2>$null | Out-Null
return $LASTEXITCODE
}
if ($exitCode -eq 0) {
Write-Host "[成功] 公钥已部署, 免密登录生效: ssh -p $Port $target" -ForegroundColor Green
} else {
Write-Host "[警告] 公钥已写入, 但免密验证未通过 (退出码 $exitCode)" -ForegroundColor Yellow
Write-Host "请检查远端 ~/.ssh 权限、sshd 的 PubkeyAuthentication 与 AuthorizedKeysFile 配置" -ForegroundColor Yellow
}
pause
exit $exitCode
3. 验证免密登录#
上传成功后,直接连接服务器,若不再提示输入密码即表示免密生效:
ssh -p 22 user@host
如果仍要密码,检查远端文件权限(不正确时 sshd 会拒绝使用公钥):
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
并确认 /etc/ssh/sshd_config 中:
PubkeyAuthentication yes
AuthorizedKeysFile .ssh/authorized_keys
修改配置后需重启 sshd:
# Linux
sudo systemctl restart sshd
4. SSH 连接服务器命令#
基本语法:
ssh [选项] [user@]host[:port]
常用形式:
# 最简: 默认端口 22, 使用当前本地用户名
ssh host
# 指定用户与端口
ssh -p 2222 user@host
# 首次连接自动接受主机指纹 (避免交互确认)
ssh -o StrictHostKeyChecking=accept-new user@host
# 指定私钥文件
ssh -i ~/.ssh/id_ed25519 -p 22 user@host
# 本地端口转发 (本机 8080 映射到远端 80)
ssh -L 8080:localhost:80 user@host
# 远程执行单条命令 (常用于脚本/自动化)
ssh user@host "nvidia-smi"
5. 常用命令#
文件传输(SCP)#
# 本地 → 远端
scp -P 22 local_file.txt user@host:/path/to/dest/
# 远端 → 本地
scp -P 22 user@host:/path/to/remote_file.txt ./
# 递归传输整个目录
scp -r -P 22 ./myfolder user@host:/path/to/dest/
注意:scp 用大写
-P指定端口,而 ssh 用小写-p,两者容易混淆。
其他常用#
# 查看 ssh 客户端版本
ssh -V
# 详细诊断 (排查连接失败)
ssh -v user@host
# 连接复用, 加速频繁连接 (ControlMaster)
ssh -o ControlMaster=auto -o ControlPath=~/.ssh/%r@%h-%p -o ControlPersist=600 user@host
配置别名(省去重复输入)#
编辑本地 ~/.ssh/config:
Host myserver
HostName <YOUR_IP>
User <USER_NAME>
Port <PORT>
IdentityFile ~/.ssh/id_ed25519
配置后只需 ssh myserver 即可一键连接。