EC2 Ubuntu SSH “Permission denied (publickey)”:2026 恢复手册

2026 年维护说明:Permission denied (publickey) 是认证结果,不是根因诊断。本手册从只读取证开始,区分客户端、网络和服务器端原因,并在离线磁盘修复前优先使用 AWS 支持的恢复路径。仅可用于你获授权管理的实例。

安全边界与恢复门槛

  • 变更前记录实例 ID、区域、可用区、AMI ID、预期系统用户、密钥对名称、当前地址、根卷 ID、块设备映射、加密/KMS 状态和 DeleteOnTermination 值。
  • 建立维护窗口并确定回滚负责人。任何离线写入前都要为 EBS 根卷创建快照;只有弄清恢复路径和 KMS 访问权限后,快照才算可用备份。
  • 不要把私钥、口令、完整 authorized_keys、会话令牌或未经脱敏的 ssh -vvv 日志粘贴到工单或聊天中。不要关闭主机密钥校验。
  • 每次只做一个边界明确的改动,用第二个 SSH 会话验证,并在无需回滚前保持恢复通道开启。
  • 如果目标实例、账户、区域、卷、文件系统、预期用户、密钥指纹或 KMS 权限存在疑问,立即停止。

这条错误能证明什么,不能证明什么

Permission denied (publickey) 通常表示 TCP 已连接到 SSH 服务,但服务端没有为该用户接受客户端提供的任何公钥。超时、DNS 失败、Connection refused、主机密钥验证失败或 EC2 状态检查失败属于其他分支。在分支明确前,不要轮换密钥或编辑服务器文件。

先在不接触秘密的情况下记录 AWS 端身份:

aws ec2 describe-instances 
  --instance-ids i-REPLACE_WITH_INSTANCE_ID 
  --query 'Reservations[0].Instances[0].{State:State.Name,ImageId:ImageId,KeyName:KeyName,AZ:Placement.AvailabilityZone,PublicIp:PublicIpAddress,PrivateIp:PrivateIpAddress,SubnetId:SubnetId,VpcId:VpcId,SecurityGroups:SecurityGroups[*].GroupId,RootDevice:RootDeviceName}' 
  --output yaml

EC2 KeyName 只记录启动时选择的密钥,不能证明当前 authorized_keys 未被更改。镜像、配置管理、user-data 脚本或管理员都可能在之后修改它。

1. 在改动 AWS 前核对客户端身份

官方 Ubuntu AMI 的初始用户通常是 ubuntu;其他 AMI 或后建账户可能不同。应核对 AMI 和预期账户,不要逐个猜用户名。确认 -i 指向私钥而不是 .pub 文件,并按 AWS 要求限制本地私钥权限:

chmod 400 /secure/path/to/private-key.pem
ssh-keygen -lf /secure/path/to/private-key.pem
ssh-keygen -y -f /secure/path/to/private-key.pem | ssh-keygen -lf -

最后一条命令只在内存中导出公钥部分并打印指纹,不会暴露私钥。通过已经可信的恢复通道,把该指纹与目标用户预期授权行的指纹比较:

sudo -u ubuntu ssh-keygen -lf /home/ubuntu/.ssh/authorized_keys

算法名称相同并不够,指纹必须与某一条授权记录完全匹配。AWS 密钥对的指纹算法因来源和算法而异;与 EC2 控制台比较时应使用 AWS 官方指纹流程。如果私钥已经丢失,EC2 不能再次下载它;应转入受支持的恢复路径,不要自行拼凑替代身份。

2. 核对服务器主机密钥

主机密钥用于验证服务器,与用于验证你的用户密钥不同。通过可信 AWS 控制平面获取实例指纹,并与 SSH 提示比较:

aws ec2 get-console-output 
  --instance-id i-REPLACE_WITH_INSTANCE_ID 
  --query Output 
  --output text

在系统日志中查找 BEGIN SSH HOST KEY FINGERPRINTS。控制台输出可能含主机名、地址、启动消息或 user-data 输出,共享前必须脱敏。如果主机密钥意外变化,应停止操作:确认 DNS/IP 仍指向该实例,并调查实例是否被替换或重建。不要通过关闭严格主机密钥检查或丢弃 known-hosts 记录来绕过警告。

3. 收集最小化详细日志

强制使用预期身份,避免 ssh-agent 中多个密钥掩盖结果:

ssh -vvv 
  -o IdentitiesOnly=yes 
  -i /secure/path/to/private-key.pem 
  ubuntu@REPLACE_WITH_VERIFIED_HOSTNAME

只在本地解读日志:

证据可能分支
在 SSH 标识交换前无路由、超时或拒绝地址、路由、安全组、NACL、本地/主机防火墙或 sshd 可用性
主机密钥不匹配错误端点、重建主机、过期可信记录或潜在拦截;先核验再继续
从未提供预期密钥错误的 -i、密钥不可读/不受支持、SSH 配置或 agent 选择
提供预期密钥但被拒绝系统用户错误、授权密钥缺失/不匹配、所有权/模式、有效 sshd 策略或外部密钥命令

详细日志和指纹输出可能暴露用户名、本地路径、IP、主机和公钥指纹、密钥注释、代理命令与配置。请脱敏这些字段,绝不要包含私钥材料或口令。

4. 独立证明网络路径

先确认实例为 running,系统和实例状态检查都健康。然后检查真实路径,不要为了测试而放宽它:

aws ec2 describe-instance-status 
  --instance-ids i-REPLACE_WITH_INSTANCE_ID 
  --include-all-instances
aws ec2 describe-security-groups 
  --group-ids sg-REPLACE_WITH_GROUP_ID
aws ec2 describe-network-acls 
  --filters Name=association.subnet-id,Values=subnet-REPLACE_WITH_SUBNET_ID
aws ec2 describe-route-tables 
  --filters Name=association.subnet-id,Values=subnet-REPLACE_WITH_SUBNET_ID

检查目标地址、子网路由、互联网网关/NAT/VPN/堡垒机或 EC2 Instance Connect Endpoint 路径、安全组来源、NACL 返回流量、企业/本地出口和主机防火墙。SSH 入站只应允许必要来源或 AWS 托管前缀列表;不要把 22 端口对全世界开放来诊断。VPC 路径健康不能证明 sshd 正在监听,而公钥被拒通常说明网络路径已经可用。

5. 优先使用 AWS 支持的恢复通道

选择前置条件已经具备的第一条路径。不要为了建立恢复通道而削弱 SSH。

路径适用条件重要门槛
EC2 Instance ConnectAMI/软件包、IAM 权限、用户名和网络路径均受支持仍依赖实例端服务/配置以及对应 SSH 路径或端点
Systems Manager Session Manager实例已经是托管节点,具备 SSM Agent、合适的实例配置文件和服务连通性不要临时附加宽泛 IAM 权限;遵循组织的 Session Manager 访问策略
EC2 Serial Console账户/区域、实例类型、IAM 和系统登录前置条件满足它绕过 VPC 数据路径,但 Linux 交互排障通常需要预先配置的密码型系统用户
AWSSupport-TroubleshootSSH具有 Systems Manager Automation 权限,并希望使用 AWS EC2Rescue 检查从默认只读的 CheckAll 开始;在 FixAll 或离线修复前审查范围

通过可信路径进入后,先收集服务器端证据再修改:

id ubuntu
namei -l /home/ubuntu/.ssh/authorized_keys
stat -c '%n owner=%U:%G uid=%u gid=%g mode=%a type=%F' 
  /home/ubuntu 
  /home/ubuntu/.ssh 
  /home/ubuntu/.ssh/authorized_keys
sudo -u ubuntu ssh-keygen -lf /home/ubuntu/.ssh/authorized_keys
sudo sshd -t
sudo sshd -T 
  -C user=ubuntu,host=REPLACE_WITH_INSTANCE_HOSTNAME,addr=REPLACE_WITH_CLIENT_IP 
  | grep -E '^(pubkeyauthentication|authorizedkeysfile|authorizedkeyscommand|strictmodes|allowusers|denyusers|allowgroups|denygroups) '
sudo systemctl status ssh --no-pager

同时检查与本次尝试相关、经过脱敏的 journalctl -u ssh 记录。有效策略可能来自 sshd_config、包含的片段、Match 块或 EC2 Instance Connect 之类的 AuthorizedKeysCommand;不要假设只有一个文件生效。

6. 执行一次边界明确的权限修复

当 home、.sshauthorized_keys 可被其他用户修改时,OpenSSH StrictModes 会拒绝密钥认证。所有者必须是预期账户。前面的 nameistat 输出是基线;如果已经安装 getfacl,也记录 ACL。对于使用以下路径的常规 Ubuntu 账户,只修复这三个对象;绝不要对整个 home 树递归执行 chmodchown

sudo getfacl -p 
  /home/ubuntu 
  /home/ubuntu/.ssh 
  /home/ubuntu/.ssh/authorized_keys
sudo chown ubuntu:ubuntu /home/ubuntu
sudo chmod go-w /home/ubuntu
sudo chown ubuntu:ubuntu 
  /home/ubuntu/.ssh 
  /home/ubuntu/.ssh/authorized_keys
sudo chmod 700 /home/ubuntu/.ssh
sudo chmod 600 /home/ubuntu/.ssh/authorized_keys

这些是常用的限制性模式,不是适用于所有部署的通用指令。如果有效 AuthorizedKeysFile、账户数据库、ACL/SELinux/AppArmor 策略、NFS 身份映射或镜像布局不同,应停止并按对应文档处理。如果缺少正确的公钥指纹,应把它作为受控密钥替换:保留当前文件、批准新公钥指纹、按 AWS 官方新增/替换密钥流程操作,并在移除任何旧记录前测试新密钥。

保持恢复会话开启,从第二个终端验证:

sudo sshd -t
sudo systemctl is-active ssh
ssh -o IdentitiesOnly=yes 
  -i /secure/path/to/private-key.pem 
  ubuntu@REPLACE_WITH_VERIFIED_HOSTNAME

如果失败,对照已记录的所有者/模式/ACL,只恢复改过的对象。仅修改文件元数据时无需重载或重启 ssh。如果修改了 SSH 配置,应先以 sshd -t 验证,再受控重载并保留恢复通道。

7. 根卷救援是最后的维护路径

仅在根卷由 EBS 支持且在线恢复路径都不可用时进行离线修复。停止前记录停机批准、依赖服务、Auto Scaling 行为、公网地址行为、所有卷映射、DeleteOnTermination 和任何 instance-store 数据。AWS 说明 stop/start 可能改变非 Elastic IP 的公网 IPv4,并会清除 instance-store 数据。为根卷创建快照,并确认辅助实例位于同一可用区且有权限使用该卷的 KMS 密钥。

原实例停止并安全分离根 EBS 卷后,将它作为数据卷附加到受控辅助实例。不要猜 /dev/sda2:Nitro 实例可能以会变化的 NVMe 名称枚举 EBS 设备。把 EC2 卷 ID 与设备序列号对应,并以只读方式识别文件系统和分区:

lsblk -o NAME,SERIAL,SIZE,FSTYPE,UUID,MOUNTPOINTS
sudo blkid
sudo file -s /dev/REPLACE_WITH_VERIFIED_DEVICE

不要运行 mkfs、自动修复或强制文件系统检查。只有在控制台卷 ID、NVMe 序列号、分区表、文件系统和预期根目录内容一致后,才设置 RESCUE_PARTITION。首先只读挂载:

RESCUE_ROOT='/mnt/ec2-root'
RESCUE_PARTITION='/dev/REPLACE_WITH_VERIFIED_ROOT_PARTITION'
sudo install -d -m 700 "$RESCUE_ROOT"
sudo mount -o ro "$RESCUE_PARTITION" "$RESCUE_ROOT"
findmnt "$RESCUE_ROOT"
sudo test -f "$RESCUE_ROOT/etc/os-release"
sudo test -d "$RESCUE_ROOT/home/ubuntu/.ssh"

应从离线系统自身的账户数据库解析所有权,而不是使用辅助机的 ubuntu 账户;写入前记录元数据:

TARGET_USER='ubuntu'
TARGET_UID="$(awk -F: -v u="$TARGET_USER" '$1==u {print $3}' "$RESCUE_ROOT/etc/passwd")"
TARGET_GID="$(awk -F: -v u="$TARGET_USER" '$1==u {print $4}' "$RESCUE_ROOT/etc/passwd")"
printf 'target uid=%s gid=%sn' "$TARGET_UID" "$TARGET_GID"
sudo stat -c '%n uid=%u gid=%g mode=%a type=%F' 
  "$RESCUE_ROOT/home/$TARGET_USER" 
  "$RESCUE_ROOT/home/$TARGET_USER/.ssh" 
  "$RESCUE_ROOT/home/$TARGET_USER/.ssh/authorized_keys"
sudo ssh-keygen -lf "$RESCUE_ROOT/home/$TARGET_USER/.ssh/authorized_keys"

如果预期用户、UID/GID、指纹或路径不匹配,不要重新读写挂载,直接升级处理。如果全部匹配且已经证明元数据是根因,才批准以下一次边界明确的写入:

sudo mount -o remount,rw "$RESCUE_ROOT"
sudo chown "$TARGET_UID:$TARGET_GID" "$RESCUE_ROOT/home/$TARGET_USER"
sudo chmod go-w "$RESCUE_ROOT/home/$TARGET_USER"
sudo chown "$TARGET_UID:$TARGET_GID" 
  "$RESCUE_ROOT/home/$TARGET_USER/.ssh" 
  "$RESCUE_ROOT/home/$TARGET_USER/.ssh/authorized_keys"
sudo chmod 700 "$RESCUE_ROOT/home/$TARGET_USER/.ssh"
sudo chmod 600 "$RESCUE_ROOT/home/$TARGET_USER/.ssh/authorized_keys"
sync
sudo mount -o remount,ro "$RESCUE_ROOT"

验证最终数字所有者、模式、指纹和挂载状态。干净卸载,从辅助机分离,并按维护前记录的原始块设备映射重新附加到原实例。启动前确认卷状态和附加关系。直到状态检查、主机指纹、SSH 登录、应用健康和监控都通过前,保留快照和元数据记录;回滚是按相同变更控制再次停止,并恢复已知良好的卷/快照。

遇到以下情况应停止并升级处理

  • 错误分支发生变化、无法独立验证主机指纹,或预期的私钥/公钥指纹不匹配;
  • 实例不是 EBS 根卷、instance-store 数据有风险,或目标/根设备不明确;
  • 无法用批准的 KMS 权限附加加密卷;
  • 怀疑文件系统损坏、根卷出现意外挂载,或写入会超出批准的账户路径;
  • 托管策略、集中身份、配置管理或 Auto Scaling 会覆盖修复。

官方资料

2017 年原文档案(非现行操作说明)

以下完整保留 source_export 的可见正文,仅规范行尾空白。其中一个不安全的递归权限命令被窄化替换为 [REDACTED: unsafe recursive chmod command],其他文字和命令均未改动。档案含猜测的设备名和宽泛权限建议,不可作为当前操作手册。


N.B. There are dozens of reasons to lead to this problem, for example, wrong permission of your pem file, incorrect username(e.g., ec2-user, ubuntu),wrong spelling in your command, etc..

The reason which causes my problem, if I am right, is that I run command `[REDACTED: unsafe recursive chmod command]` under the wrong directory, namely, my home folder.

The solution is just setting your home folder permissions back.

1. Stop your problematic instance.
2. Create a new instance and stop the new problem-free instance.
3. The newly created instance should be in the same `Availability Zone` like ‘us-west-2c’ which can be set on the ‘Network’ step under which the menu is ‘Subnet’.
4. Detach your ‘ebs volume’ from the problematic instances and attach it on your new problem-free instance.
5. Your need input your instance id as well as the mount point which looks like ‘/dev/sda2’.
6. Start your new instance and mount the second drive that you just attached.

[ubuntu ~]$ lsblk
NAME MAJ:MIN RM SIZE RO TYPE MOUNTPOINT
xvdf 202:80 0 100G 0 disk
xvda1 202:1 0 8G 0 disk /
[ubuntu ~]$ sudo mount /dev/xvdf/ /mnt


8. Change directory to your mounted point and restore the permission of your home directory with permission 755.

[ubuntu ~]$ cd /mnt/home/
[ubuntu ~]$ chmod 755 yourusername


9. Stop your new instance and detach the volume owned by the problematic instance.
10. Reattach the just detached volume to the default problematic instance.

Leave a Reply