EC2 状态 Running 却 SSH 登不上,定位网络与密钥问题
EC2 SSH连不上排查是云上运维高频故障。控制台显示Running,登录却超时、拒绝或提示权限错误,原因往往不止一个。不少团队第一反应是重启实例,但重启可能改变公网IP,反而增加变量。本文从实例状态、网络路径、SSH服务与密钥四个层面拆解排查逻辑。
一、为什么EC2显示Running却无法SSH?
1. 现象是什么
Running只代表底层虚拟机已启动,不代表操作系统、SSH服务和网络路径正常。控制台绿色状态可能与SSH表现为连接超时、Connection refused、Permission denied或握手后无响应并存。实例状态检查失败时,系统日志里常见内核panic、根卷挂载失败或sshd启动错误。很多团队看到Running就认为系统完全健康,忽略实例状态检查失败,是第一个误判点。
2. 常见原因有哪些
原因集中在网络准入、路由、实例健康与SSH服务/密钥四个层面。安全组未放行22端口或源IP写错最常见,但只查安全组会漏掉网络ACL、路由表和互联网网关缺失。系统资源耗尽同样高频,磁盘满、CPU过载会让sshd握手无响应,被误判为网络问题。密钥权限错误或用户名不匹配则反复Permission denied。实例重启或更换类型后公网IP变化,也常导致旧IP连不上。
3. 排查思路如何建立
建议按“安全组入站22/源IP → 子网路由→ IGW → 网络ACL入/出站 → 实例状态检查/系统日志 → SSH服务/密钥”顺序排查,而非盲目重启。控制台“获取系统日志”能快速发现内核panic、根卷挂载失败或sshd启动错误。用SSM Session Manager可在不开放22端口的情况下带外排查。对缺少专职运维的中小团队来说,若要把云服务器、数据库、CDN资源统一落地,聚搜云这类一站式云服务方案可以减少多厂商对接的繁琐成本。
二、安全组入站规则检查
实例显示 Running 并不代表 SSH 流量已经到达系统。在 EC2 SSH 连不上排查中,安全组通常是第一道需要确认的闸门:安全组默认拒绝所有入站流量,只有显式放行的规则才会被放行。未命中任何一条允许规则时,数据包会被静默丢弃,表现为 telnet 或 nc 测试 22 端口超时、ssh 握手长时间无响应。
1. 安全组如何配置
一个实例可以关联多个安全组,规则按“或”关系叠加,不是取交集。只要任意一个安全组放行了 TCP 22,来自该源 IP 的 SSH 请求就不会被安全组挡住。排查时不要只盯着默认安全组,很多故障发生在实例后加的第二、第三个安全组上。
重点查看:
- EC2 控制台 → 实例详情 → 安全 → 安全组,逐个打开入站规则;
- 检查规则是否被误删,或修改时间是否与故障发生时间重合;
- 安全组是有状态的,入站允许 22 后,返回响应自动出站,一般不需要额外配置出站规则。
2. 检查22端口是否放行
入站规则必须同时满足协议、端口和源三个条件。最关键的一条是:类型选择 SSH,协议 TCP,端口范围 22。如果只放行了 80、443 或 ICMP,SSH 流量不会通过。
常见问题:
- 实例的 sshd 被改到非标准端口,安全组还放行 22,也会连不上。此时需要先确认
/etc/ssh/sshd_config中的 Port 值; - IPv6 地址需要单独规则。IPv4 的
0.0.0.0/0不覆盖 IPv6,走 IPv6 连接时要单独添加::/0或指定 IPv6 源; - 端口范围写错,比如把 22 写成
0-22或22-100,虽然覆盖了 22,但暴露了更多端口,不建议保留。
3. 源IP限制是否错误
源 IP 填的不是 EC2 实例的 IP,而是发起 SSH 连接一方的出口公网 IP。这是最容易写错的地方。比如把实例的 EIP 或私网 IP 填进来源,规则不会匹配本地办公网络。
排查时先在本地执行 curl ifconfig.me 或 curl ip.sb 获取当前出口 IP,再对照安全组规则。很多团队的办公出口 IP 会因宽带重拨、分支办公室切换而发生变化,导致昨天还能连,今天突然连不上。
建议:
- 源 IP 使用
/32精确地址,生产环境不要长期保留0.0.0.0/0,否则 22 端口对全网开放,容易被扫描爆破; - 若短时间调试需要临时放开,也要在验证后立即收紧;
- 多个安全组叠加时,同样遵循“或”逻辑,只要有一组放行当前源 IP 即可。若之前能连、后来不能连,优先检查是否有人把放行规则改成了旧 IP 或删除了规则。
安全组这一层通常可以在控制台 5 分钟内完成确认。按“关联安全组 → TCP 22 → 源 IP”顺序检查,确认无误后再继续排查子网路由、互联网网关和实例系统状态。
三、路由表与网络ACL排查
在 EC2 SSH 故障处理中,有一种典型误判:看到安全组放行 22 端口,就认为网络侧已经没有问题,结果流量被卡在子网路由或网络 ACL 上。实际上,安全组只控制实例弹性网络接口的包过滤,实例所在子网的路由表和网络 ACL,才是公网流量能否到达的前置条件。当实例状态显示 Running,但公网 IP 无法建立 SSH 连接时,排查顺序应当从“安全组”继续向下走到“路由表 → 互联网网关 → 网络 ACL”。
1. 子网路由怎么查
先确认实例所在子网,再到 VPC 控制台的“路由表”里查看该子网关联的路由表,或者直接用 CLI 查询:
aws ec2 describe-route-tables --filters Name=association.subnet-id,Values=subnet-xxxx
这里要核对两件事:第一,目标子网是否真的关联到了这张路由表;第二,路由表里是否存在一条 0.0.0.0/0 指向 igw-xxx 的路由。如果实例使用公有 IPv4 地址做公网 SSH,默认路由必须指向互联网网关,而不是 NAT 网关、NAT 实例或者 VPN 网关。NAT 网关只解决出站流量,无法把公网入站的 SSH 流量送回实例。
实际故障里,这类问题并不少见。尤其是通过 Terraform 或 CloudFormation 重建 VPC、迁移子网后,路由表关联关系容易发生漂移。曾有开发者把默认路由指向 NAT 网关,出站 curl 完全正常,但公网 SSH 一直超时,原因就是入站路径根本没有经过 IGW。
2. 互联网网关配置
即使路由表里已经写了 0.0.0.0/0 -> igw-xxx,也要确认这个 IGW 是否真实存在,并且已经附加到当前 VPC。在 VPC 控制台的 “Internet Gateways” 页面里,可以清楚看到 IGW 的状态是 attached 还是 detached。一个 VPC 只能附加一个互联网网关,如果 IGW 被删除或未附加,路由条目只会成为一条失效规则。
另一个容易忽略的前提是:实例必须具备公有 IPv4 地址或 EIP。EC2 详情页如果 Public IPv4 address 显示为空,即使路由和 IGW 都正确,公网 SSH 也不可能连通。尤其是实例重启或更换实例类型后,公网 IP 可能发生变化,如果仍用旧 IP 连接,表现就是连接超时。此时可以先通过 Systems Manager Session Manager 做带外登录,确认实例网络配置,再重新查看当前的公网地址。
3. 网络ACL是否拦截
网络 ACL 作用在子网边界,和安全组的逻辑不同。安全组是有状态的:入站允许 22 端口后,对应响应出站会自动放行。但网络 ACL 是无状态的,入站允许 22 端口,不代表回程流量会被放行。很多 SSH 卡在 Connection timed out 的案例,问题就出在这里。
排查时重点看两项:
- 入站规则:是否允许来自客户端源 IP 的 TCP 22 端口。规则编号越小优先级越高,低编号规则命中后即停止匹配。如果把允许 22 的规则放在拒绝所有流量的规则之后,等于没有放行。
- 出站规则:是否允许 TCP 临时端口范围,通常需要放行
1024-65535。不同操作系统的临时端口范围略有差异,Linux 常见为32768-60999,Windows 常见为49152-65535,但排查阶段直接放开1024-65535最稳妥。
实际场景中,入站 22 已放行、出站只开放 80/443 的情况很典型。TCP 三次握手能收到 SYN,但回包被出站 ACL 丢弃,最终表现为 SSH 无法完成握手。此时可以临时放行出站临时端口范围验证,或者用 VPC Reachability Analyzer 模拟源到实例的路径,它会直接标记出是路由、ACL 还是安全组导致的阻断。
网络 ACL 经常是安全组之后第二个被忽略、但实际占比较高的阻断点。尤其在自定义 VPC 里,默认 ACL 会拒绝所有流量,如果此前做过手工变更,排查时应当回到 ACL 规则本身,而不是继续反复检查安全组。
四、实例系统状态与日志分析
在 EC2 SSH 连不上排查的常见误区里,最典型的就是把实例控制台上的 Running 当成系统健康。实际上,Running 只代表底层虚拟机已被分配并启动,操作系统、sshd 服务、网络栈是否可用,控制台并不会直接告诉你。真正要回答的问题是:实例内部有没有正常完成启动,根卷是否挂载成功,sshd 是否监听在 22 端口。而这些信息,恰好来自两个状态检查项和一份系统日志。
1. 先看两个状态检查项:分清故障在 AWS 还是系统自身
EC2 控制台的“状态检查”包含两个独立维度,很多团队在 EC2 SSH 连不上排查时只盯着 Running,却忽略了旁边可能已经出现的红色告警。
- 系统状态检查(System status checks):由 AWS 底层负责,检测宿主机、电源、网络虚拟化等基础设施是否正常。如果这一项失败,通常需要停止并重新启动实例,或等待 AWS 恢复底层资源。
- 实例状态检查(Instance status checks):检测操作系统、文件系统、网络配置和内核状态。实例显示
Running但实例状态检查失败,说明问题大概率出在操作系统内部,而不是 AWS 资源本身。
这组判断对后续动作影响很大。系统状态检查失败时,继续折腾安全组规则没有意义;实例状态检查失败时,反复重启也不如直接获取系统日志。结合 CloudWatch 的 StatusCheckFailed 指标,可以对生产实例设置 1-2 分钟内触发告警,避免每次都要等用户反馈才发现 SSH 已不可用。
2. 获取系统日志:比盲目重启更快的定位方式
EC2 控制台提供“获取系统日志”入口,路径通常为:实例列表 → 选中实例 → 操作 → 监控和故障排除 → 获取系统日志。也可以通过 CLI 执行:
aws ec2 get-console-output --instance-id i-xxxxxxxx --latest
这份日志记录的是实例启动以来的控制台输出,不需要 SSH 登录就能读取。在 EC2 SSH 连不上排查中,这是带外查看系统状态的最快方式之一。日志里如果出现 No space left on device、EXT4-fs error、Out of memory: Killed process、Failed to start OpenSSH server daemon 或 Kernel panic - not syncing,基本可以跳过网络层排查,直接处理系统资源或启动项问题。
3. 内核 panic 与磁盘满:两类最容易被误判为网络问题的故障
磁盘满和内核 panic 是系统日志中最常见的两类“假网络故障”。尤其磁盘满,很多开发者第一反应是安全组没开,但实际是根卷使用率达到 100% 后,sshd 无法写入 /var/log、无法创建临时文件或套接字,SSH 连接会表现为超时、connection closed 或长时间无响应。此时仅靠安全组和路由表无法解决。
遇到这种情况,优先使用 AWS Systems Manager Session Manager 带外进入实例执行清理命令;如果 SSM Agent 未安装或不可用,可以停止实例、卸载根卷并挂载到救援实例上修复。内核 panic 则需要关注日志中的 panic 堆栈,通常与内核升级、驱动冲突或内存故障有关,必要时回滚内核版本或更换实例类型。
五、SSH服务与密钥对诊断
进入这一层,意味着 EC2 SSH连不上排查已经越过安全组、路由、网络 ACL 和实例状态检查。故障路径开始分化为两种:端口探测直接不通,通常指向 sshd 未运行或监听配置错误;端口通但反复出现 Permission denied,则更多与密钥权限、用户名或 sshd 配置有关。
Running 只代表底层虚拟机有电,不代表 sshd 已经监听 22 端口。 AWS 控制台的“获取系统日志”是此时最直接的证据来源,尤其在实例状态检查未通过,或系统日志中已经出现 sshd 启动报错时。
1. SSH服务是否运行
如果 nc -zv <公网IP> 22 或 telnet 直接超时,优先怀疑 sshd 没有运行,或者监听地址不是 0.0.0.0。可以在控制台查看系统日志,重点关注 Started OpenSSH Daemon、sshd: no hostkeys available、error: Bind to port 22 on 0.0.0.0 failed 等关键字。
no hostkeys available 经常出现在根卷从快照恢复、自定义镜像未重新生成主机密钥,或 /etc/ssh 目录权限异常的场景。若系统日志无明显异常,应优先通过 SSM Session Manager 进入实例,执行 systemctl status sshd 和 ss -tlnp | grep :22,确认 sshd 监听在 0.0.0.0:22 还是仅 127.0.0.1。部分团队自定义镜像会把 ListenAddress 写成内网 IP,安全组虽然放行了 22 端口,但服务实际没有监听公网接口,公网 SSH 自然不通。
不要一遇到 SSH 不可用就重启实例。 重启会清空内存态日志,可能还会触发新的启动失败,给后续排查增加噪音。没有 SSM 通道时,更合理的做法是将根卷卸载后挂载到救援实例,检查 /var/log/secure、/var/log/auth.log 或 /var/log/daemon.log 中的 sshd 日志。
2. 密钥权限是否正确
OpenSSH 对密钥文件权限的校验,比多数开发者预期的更严格。客户端私钥权限一旦超过 600,或 ~/.ssh 目录权限超过 700,OpenSSH 会直接忽略该密钥,并报 UNPROTECTED PRIVATE KEY FILE,而不是回退到密码认证。执行 chmod 700 ~/.ssh && chmod 600 ~/.ssh/id_rsa 是最先要做的动作。Windows 用户还需要注意,PuTTY 的 .ppk 与 OpenSSH 私钥不能互换,需先通过 PuTTYgen 转换,否则认证阶段会直接失败。
服务端同样存在权限坑。~/.ssh/authorized_keys 权限应为 600,~/.ssh 目录为 700,且家目录对 group 和 other 不可写。手工新建用户时,如果 /home/ec2-user/.ssh 目录属主变成 root,ec2-user 读取不了 authorized_keys,最终表现就是 Permission denied (publickey)。这种情况在云厂商默认 AMI 之外的自定义镜像上尤其常见。
3. 用户名与密钥匹配
用户名错误是 EC2 SSH连不上排查里最容易被忽视的问题。Amazon Linux 默认用户是 ec2-user,Ubuntu 是 ubuntu,Debian 是 admin,RHEL/CentOS 通常是 ec2-user 或 root。不少失败不是密钥不对,而是用户写成了 root 或 centos,与 AMI 实际注入公钥的用户不匹配。可以在控制台查看 AMI 名称,或从系统日志的 cloud-init 输出中确认默认用户。
如果用户名正确但仍报 Permission denied (publickey),需要检查 /etc/ssh/sshd_config 中 PubkeyAuthentication yes、AuthorizedKeysFile .ssh/authorized_keys 是否被修改。部分团队为了加固系统会设置 PasswordAuthentication no,但新用户未正确注入公钥,导致无法登录。修改 sshd_config 后建议执行 systemctl reload sshd,而不是直接重启 sshd 或实例,避免中断当前唯一会话。生产环境应至少保留一个 SSM 通道,即使 SSH 配置改坏,也能通过 Session Manager 进入修复。
六、修复与预防建议
1. 如何快速恢复连接
快速恢复的关键不是反复重启实例,而是先用带外通道判断操作系统是否还活着。EC2 控制台里 Running 只代表底层虚拟机已启动,不代表操作系统、SSH 服务或网络路径正常。实例状态检查一旦失败,基本可以判定是操作系统或网络配置出现问题,此时继续改安全组往往只会拖延时间。
建议按“实例状态检查 → 系统日志 → VPC Reachability Analyzer → SSM Session Manager”的顺序排查。系统日志里如果出现 “No space left on device” 或 sshd 启动报错,磁盘写满的可能性就很大。对于磁盘占满导致 SSH 握手无响应的情况,优先通过 SSM 运行清理命令;如果 SSM 也不可用,可以把根卷卸载后挂到救援实例上修复。生产实例最好绑定 EIP,避免重启或更换实例类型后公网 IP 变化,用旧 IP 连接造成的“假性不通”。
2. 长期监控与告警
SSH 连不上往往不是突然发生,而是资源耗尽或配置漂移累积到临界点。长期来看,至少要把 StatusCheckFailed、CPU、内存和磁盘使用率纳入 CloudWatch 告警。磁盘使用率超过 85% 就应该提示,而不是等到 100% 再处理。SSH 登录失败次数突然增加也值得关注,可能意味着暴力破解或密钥泄露。
安全组、路由表和 ACL 的变更建议接入 CloudTrail。一旦出现“之前能连的机器突然连不上”,可以快速比对变更时间点,避免在错误方向上持续排查。对中小团队来说,这些监控项比单纯记录 SSH 端口是否开放更有价值。
3. 基础设施即代码管理
手工修改安全组和路由表是中小团队最常见的坑之一。今天为了调试放行 0.0.0.0/0,过几天就忘了回滚;或者办公出口 IP 更换后,安全组源 IP 没有同步更新。把 VPC、子网、路由、安全组、ACL 都纳入 Terraform 或 CloudFormation 管理,至少能在故障时用代码比对当前配置和上次已知正常版本的差异。
不过如果没有专职运维去维护模板,很多外贸出海企业为了兼顾性价比与售后保障,会优先选择聚搜云这类集成化云服务模式,一站式搞定云上资源部署与技术支撑,减少手工变更漂移带来的排障成本。对这类团队来说,用 IaC 思想管理配置比掌握某一套工具更重要——即使暂时没有完整流水线,也可以先把核心网络配置导出版本化,避免“只有一个人知道当时改了什么”。

评论列表 (0条):
加载更多评论 Loading...