长期在 Windows 上折腾 SSH 连 AWS 的人,多少都经历过同一件事:在本地一脸自信地敲下 ssh -i key.pem ec2-user@xxx,结果回车之后不是权限报错就是超时。以前我习惯用 Xshell、MobaXterm 这些图形工具,功能确实全面,但换来换去还是觉得 Windows 自带的 OpenSSH 最轻量,配好之后一条命令进去,配合 VS Code 做远程开发,顺滑程度不比 Mac 差。这篇就把我从零开始用 Windows 原生 SSH 连 AWS EC2 的完整经验写出来,包括密钥处理、免密登录、端口转发、常见报错排查,每一步都带理由,照着做基本能一条龙走通。
1. 为什么用 Windows 自带的 OpenSSH 而不是第三方工具
1.1 其实你已经装好了,只是没注意
Windows 10 1809 之后的版本、Windows 11 全系,默认都带了 OpenSSH 客户端,不需要额外安装。我自己是在一个全新的 Windows 11 笔记本上验证的,打开 PowerShell 输入:
powershell复制ssh -V
输出类似 OpenSSH_for_Windows_8.6p1, LibreSSL 3.4.3,说明客户端已经就绪。如果你用的是老版本的 Windows 10,可以在“设置 → 系统 → 可选功能”里手动添加 OpenSSH 客户端,或者用 winget 装:
powershell复制winget install Microsoft.OpenSSH.Beta
但绝大多数场景没必要,系统自带的版本足够用。
1.2 命令行方案对比图形工具的实际优势
不是说 Xshell、MobaXterm 不好,它们确实在会话管理、文件传输上更直观。但命令行方案有几个不可替代的好处:
- 脚本化:我经常需要批量登录多台 EC2 执行命令,用 ssh 配合循环脚本一次搞定,图形工具做不到这种粒度。
- 轻量:不用常驻一个 GUI 进程,PowerShell 里开个会话,用完关掉,不占资源。
- 原生支持:Git、VS Code、rsync(通过 WSL)、Ansible 这些工具都直接调用系统的 SSH 客户端,配置一次,处处复用。
- 隧道功能顺手:端口转发、跳板机代理,一行参数的事,比在图形界面里点点点效率高。
所以我的建议是,如果你只是偶尔登一次服务器,图形工具完全没问题;但如果你要长期运维 AWS 上的机器,或者做自动化,那原生 SSH 是绕不开的基础能力。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 密钥对准备:AWS 上的 .pem 文件到底怎么处理
2.1 在 AWS 控制台创建密钥对
登录 AWS 管理控制台,进入 EC2 服务,左侧菜单选“密钥对(Key Pairs)”,点击创建。格式选择 PEM,这是 OpenSSH 原生支持的格式。PPK 是 PuTTY 的格式,如果用 Windows 自带的 SSH,根本不需要转换。
创建成功后会自动下载一个 .pem 文件,这个文件就是你的私钥,相当于你服务器的“门钥匙”,一定要保存好,不能丢,也不能让别人拿到。AWS 只在创建那一刻提供下载,后续没地方再下载一次。
2.2 Windows 上最容易被坑的权限问题
.pem 文件下载下来之后,直接放桌面拿去连接,大概率会碰到这个报错:
code复制Permissions for 'key.pem' are too open.
这是因为 OpenSSH 对私钥文件的权限要求非常严格,只有当前用户能读,其他任何账户、任何组权限都会拒绝。解决办法是用 icacls 命令重置权限。
先右键 .pem 文件,在属性里把继承的权限全部禁用,然后给当前用户加上读取权限。命令行操作更直接:
powershell复制# 禁用继承并删除所有权限
icacls .\key.pem /inheritance:r
# 给当前用户添加读取权限
icacls .\key.pem /grant:r "$($env:USERNAME):R"
我习惯把密钥统一放到 C:\Users\你的用户名\.ssh\ 目录下,文件名改成容易识别的,比如 aws-key.pem,然后在同一个目录下创建 SSH 配置文件,管理多个主机很方便。这个习惯后面会细说。
提示:在 Windows 的 PowerShell 里,
~/路径指向的是用户目录,但实际路径通常是C:\Users\你的用户名。如果之前用过 Git Bash,会发现路径风格和 PowerShell 不太一样,容易混淆。建议直接用绝对路径或者$env:USERPROFILE来引用。
2.3 私钥文件放对了位置,后续一切才顺
我见过不少朋友连接失败,是因为把 .pem 文件放在了 NTFS 分区里一个权限特别复杂的位置,比如公司的共享文件夹,或者移动硬盘。除了权限问题,有些文件系统对文件属性的支持也不完整。统一放进 .ssh 目录是个好习惯,至少所有权、权限都是可控的。
3. 第一次连接:从命令到理解的完整过程
3.1 构造第一条 SSH 命令
创建好 EC2 实例后,实例详情页会给出连接信息。最基本的命令是:
bash复制ssh -i C:\Users\你的用户名\.ssh\aws-key.pem ec2-user@你的公网IP
这里 ec2-user 是 Amazon Linux 2 或 Amazon Linux 2023 默认的用户名。不同 AMI 的用户名不一样,最常见的有:
| 操作系统 | 默认用户名 |
|---|---|
| Amazon Linux 2 / 2023 | ec2-user |
| Ubuntu | ubuntu |
| Debian | admin |
| RHEL | ec2-user 或 root |
| CentOS | centos 或 ec2-user |
| Windows Server | Administrator |
如果你不确定,AWS 文档里有一个完整的对照表,也可以去 EC2 实例的“连接”页面看提示,里面会明确告诉你该用什么用户名。
第一次连接时,SSH 会询问是否确认主机的指纹,输入 yes 回车即可。然后把指纹写入 known_hosts 文件,以后再连就不会问了。
3.2 配置 ~/.ssh/config:再也不用记 IP 和密钥路径
连过几次之后你就会发现,每次都要敲完整的 -i 参数和 IP 太烦人了。SSH 支持配置文件,把每个主机的连接参数提前写好,以后只需 ssh my-aws-host 一行。
在 C:\Users\你的用户名\.ssh\ 下创建 config 文件(没有扩展名),写入:
code复制Host my-aws-host
HostName 54.xx.xx.xx
User ec2-user
IdentityFile C:\Users\你的用户名\.ssh\aws-key.pem
ServerAliveInterval 60
解释一下几个参数:
Host:自定义别名,用来在命令行输入的名字。HostName:实际连接的 IP 或域名。User:登录用户名。IdentityFile:私钥文件的路径。ServerAliveInterval:每隔 60 秒发一个心跳包,防止空闲连接被服务器断开。这条对 AWS 特别实用,因为默认的空闲超时可能只有几分钟。
配置好之后,在 PowerShell 里执行:
bash复制ssh my-aws-host
就能直接进入服务器。如果你有多台 AWS 机器,就复制多个 Host 块,管理起来一目了然。
3.3 免密登录为什么安全
SSH 钥匙对是公钥加密体系,公钥放在服务器上的 ~/.ssh/authorized_keys 文件里,私钥留在本地。登录时服务器用公钥来验证你的身份,只有持有对应私钥的客户端才能通过验证。
实际使用中,.pem 文件本身就是一个私钥,AWS 在创建实例时把公钥写进了服务器的 authorized_keys。所以本地只要有 .pem,就能免密登录。这一套机制的好处是,密码永远不会在网络上传输,中间人攻击的难度也大很多。
如果你觉得每次还要带 .pem 文件麻烦,可以把私钥加进系统服务。Windows 上运行:
powershell复制# 启动 ssh-agent 服务并设为自动启动
Get-Service ssh-agent | Set-Service -StartupType Automatic
Start-Service ssh-agent
# 把私钥添加到 agent
ssh-add C:\Users\你的用户名\.ssh\aws-key.pem
加了之后,连服务器时就不用再指定 -i 参数了,SSH 会从 agent 里找匹配的私钥。前提是在 config 里不写 IdentityFile,或者写的路径和 agent 里的密钥能对应上。
4. 连接失败排查链路:从网络到密钥逐个排除
4.1 先看安全组,再看网络 ACL
连接 EC2 超时,百分之八十的问题是安全组没有放行 22 端口。AWS 安全组相当于实例的防火墙,默认情况下 22 端口不对公网开放。
进入 EC2 控制台,找到实例,看“安全”标签页里的安全组,确认入站规则里有:
- 类型:SSH
- 协议:TCP
- 端口范围:22
- 来源:我的 IP 或 0.0.0.0/0
不建议直接把来源设为 0.0.0.0/0,这意味着任何 IP 都能尝试登录你的服务器,容易遭受暴力破解。如果 IP 不固定,可以考虑用安全组引用另一个安全组,或者定期更新规则。更稳妥的做法是配合 AWS 的 Session Manager,不开 22 端口也能管理服务器,这个后面再提。
4.2 PEM 文件报错的排查顺序
远程连接时报错五花八门,我按实际踩坑频率排个序:
| 报错信息 | 原因 | 解决方案 |
|---|---|---|
Permissions for 'xxx.pem' are too open |
文件权限过宽 | icacls 重置权限 |
Load key "xxx.pem": invalid format |
密钥文件格式不对,可能是 PKey 文件 | 转换格式或重新下载正确的 PEM |
unprotected private key file (Git Bash 下) |
同样是权限问题 | chmod 600 |
Host key verification failed |
known_hosts 里的指纹变了,常见于实例重建 | 清理 known_hosts 中对应条目 |
Connection timed out |
网络、安全组或路由问题 | 检查安全组、网络 ACL、互联网网关 |
Permission denied (publickey) |
用户名或密钥不匹配 | 检查用户名、确认用了正确的密钥对 |
Permission denied 是最容易误导人的一个。表面上看是权限不对,实际原因往往是你用的用户名不对,或者实例不是用这把密钥创建的。建议先对照 AMI 默认用户名表确认,再用 ssh -v 看详细调试信息:
bash复制ssh -v my-aws-host
输出里会显示 SSH 尝试了哪些认证方法、哪个密钥被拒了。看到 Offering public key: ... 然后跟着 Authentications that can continue: publickey,基本就是密钥不被接受,要么用户名错,要么密钥不对。
4.3 实例重启后 IP 变了
默认情况下 EC2 的公网 IP 是动态的,实例停止再启动后会变。如果你的 config 里写死了 HostName,下次连不上很正常。
解决方案有两个:
- 创建弹性 IP,绑定到实例上,这样 IP 固定不变。
- 用 AWS CLI 动态获取实例当前公网 IP,在 config 里配合脚本生成,但麻烦。
日常测试环境用弹性 IP 更省心,就是 AWS 弹性 IP 有数量限制,不绑定实例时会收费,注意别浪费。
5. 进阶玩法:端口转发、文件传输和 VS Code 远程开发
5.1 SSH 端口转发让本地服务穿透到服务器
SSH 隧道是我用得最频繁的功能之一。比如服务器上的 Redis 只监听了 127.0.0.1,本地想用可视化工具连上去,就可以用本地端口转发:
bash复制ssh -L 6379:localhost:6379 my-aws-host
这条命令的意思是把本地的 6379 端口映射到服务器的 6379 端口,之后本地直接连 localhost:6379 就能访问服务器上的 Redis。好处是数据是加密的,且不暴露 Redis 服务到公网。
另一个场景是跳板机。有时候服务器在私有子网,只能通过堡垒机访问,这时候用 -J 参数跳转:
bash复制ssh -J jump-user@堡垒机IP target-user@目标IP
config 里也可以配置 ProxyJump,多级跳板也能串起来。
5.2 SCP 传文件:最朴素但最可靠
Windows 自带 OpenSSH 里包含 scp 命令,传文件不比任何图形工具差。从本地上传到服务器:
bash复制scp C:\Users\你的用户名\file.txt my-aws-host:/home/ec2-user/
从服务器下载到本地:
bash复制scp my-aws-host:/home/ec2-user/result.log C:\Users\你的用户名\Downloads\
如果目录多、文件多,我通常先在服务器上打包成 tar.gz 再下载,比一条条传快很多:
bash复制tar czf - /var/log/myapp | ssh my-aws-host "cat > /home/ec2-user/logs.tar.gz"
不过这是 bash 里的写法,Windows PowerShell 下管道行为不太一样,可以直接先打包再 scp。
5.3 VS Code Remote-SSH 插件的无损接入
Windows 下开发,VS Code 配 Remote-SSH 基本是标配。安装 Remote-SSH 插件后,左侧栏会出现远程资源管理器,选择 SSH Targets,找到 config 里配置的主机,一键连接。连接后会重新打开一个窗口,直接在这个窗口里编辑服务器上的文件、运行终端命令、调试代码,体验和在本地一样。
需要注意,第一次连接时 VS Code 会在服务器端下载并安装一个 server 组件,服务器需要有外网访问权限。如果你在私有子网或者网段受限,安装会失败,这时需要手动离线安装或者配置代理。
插件里还支持端口转发,你把 VS Code 的“端口”面板打开,填入服务器上的端口,它会自动帮你建立隧道,开发调试时非常方便。
5.4 如果服务器在私有子网:Session Manager 更稳
这是很多人的盲区。如果 EC2 实例在私有子网里,根本没有公网 IP,从 Windows 上直接 ssh 是连不上的。这时候有两个选择:一是搭堡垒机,通过跳板连;二是用 AWS Session Manager,不需要开 22 端口,不需要公网 IP,通过 SSM Agent 建立会话。
Session Manager 配合 AWS CLI 使用:
bash复制aws ssm start-session --target i-xxxxxx
前提是你在本地装好 AWS CLI 并配置了凭证,实例上装好 SSM Agent 并且有对应的 IAM 角色。登录后是交互式 shell,体验和 SSH 基本一致。如果再配合端口转发功能,可以把私有子网的数据库隧道出来,比 SSH 隧道还安全,因为全程不暴露端口。
6. 把这些能力固化成自己的日常工具箱
6.1 Windows 上推荐配合的工具链
我用到的组合是:
- PowerShell 7:比自带的 Windows PowerShell 5.1 好用太多,命令兼容性更强。
- Windows Terminal:多标签窗口,支持自定义配色和快捷键,日常工作都在里面。
- VS Code + Remote-SSH:写代码、改配置、看日志,替代大部分图形化运维工具。
- AWS CLI v2:配好凭证之后,和 SSH 配合使用,一个查资源一个连机器。
- Git for Windows:自带的 Git Bash 里也有 ssh 命令,有些在 PowerShell 里不太顺畅的管道操作,在 Git Bash 里反而很顺。
6.2 会话管理:把公网 IP 变化的影响降到最低
我之前提过用 config 文件管理多个主机,配合弹性 IP 基本能稳定使用。但如果你不想要弹性 IP 的费用,可以定期用 AWS CLI 刷新 config:
powershell复制aws ec2 describe-instances --instance-id i-xxxxxx --query 'Reservations[0].Instances[0].PublicIpAddress' --output text
然后在脚本里自动更新 config 里的 HostName 字段。这个我目前没有完全自动化,因为频率不高,但思路是通的。
6.3 安全基线:关闭密码登录和 root 登录
新开的 EC2 默认只允许密钥登录,但如果你自己改了 sshd_config,记得确认以下两行:
code复制PasswordAuthentication no
PermitRootLogin no
改完执行 sudo systemctl restart sshd。关闭密码登录是为了防暴力破解,关闭 root 登录是为了限制权限范围。日常操作用 ec2-user 这样的普通用户,需要提权时再 sudo,这样即使密钥泄露,损失也有限。
另外,建议定期用 ssh-keygen -R 主机IP 清理 known_hosts 里旧指纹,尤其是你重新创建过实例以后。
6.4 一条命令直接上服务器,完整示例
最后放一个我在 .ssh/config 里最常用的模板,新开一台 AWS 主机后照着改就能用:
code复制Host aws-dev
HostName 54.190.xx.xx
User ec2-user
IdentityFile C:\Users\YourName\.ssh\aws-dev.pem
ServerAliveInterval 30
ServerAliveCountMax 3
Compression yes
Host aws-prod
HostName 1.2.3.4
User ubuntu
IdentityFile C:\Users\YourName\.ssh\aws-prod.pem
ServerAliveInterval 30
ServerAliveCountMax 3
Compression yes
其中 Compression yes 会在 SSH 传输时启用压缩,平时敲命令感觉不出来,但如果用 scp 传文本类大文件,能明显感觉到流量变小。
7. 从连接走向自动化:SSH 只是起点
7.1 在 Windows 上执行远程命令的几种方式
SSH 不仅用于交互式登录,也可以用来在远程主机上批量执行命令。Windows 下 PowerShell 可以这样:
powershell复制ssh my-aws-host "uptime && df -h && free -m"
如果是在 Git Bash 或 WSL 里,输出效果更接近 Linux 环境。实际做运维巡检时,我可以在一台 Windows 管理机上循环遍历所有主机的 config,批量执行健康检查命令;比如磁盘占用超过 80% 的实例,用一条脚本就能发现。
7.2 配合计划任务实现自动巡检
Windows 的任务计划程序可以定时运行一个 PowerShell 脚本,这个脚本通过 SSH 登录 AWS 主机,执行关键命令,把结果重定向到日志文件。如果发现异常(比如进程挂了),触发邮件告警。这条链路搭建起来后,日常巡检的工作量会大幅下降。
当然,这个自动化深度已经超出了单纯 SSH 的范畴。但正因为 SSH 配置得顺,后面做这些才能顺手,否则基础不通,上层自动化全是空中楼阁。
踩过几次坑之后,我现在新配一台 AWS 主机的时间基本控制在三分钟内:下载密钥、放 .ssh 目录、改权限、写 config、连一次验证。整个流程里的每一步都有原因,不是机械操作。如果你在 Windows 上连 AWS 主机的链路还不顺,建议照着这篇从头理一遍,尤其注意密钥权限和安全组这两个高频问题。配置好之后,日常登录就是一行 ssh xxx 的事,剩下的精力可以放在真正有价值的自动化上。
