Read the error
| SSH says | Layer | Look at |
|---|---|---|
Connection timed out | Network: packets never arrive | Security group, NACL, route table, public IP, local firewall |
Connection refused | Instance reached, nothing listening on 22 | sshd stopped, wrong port, host firewall REJECT |
Permission denied (publickey) | sshd reached, auth failed | Wrong key, wrong user name, file permissions |
Host key verification failed | The client's known_hosts has an old key | IP was reused. Remove the old entry. |
Timeout: walk the network path
ID=i-0abc123
aws ec2 describe-instances --instance-ids $ID \
--query "Reservations[].Instances[].{state:State.Name,publicIp:PublicIpAddress,subnet:SubnetId,sgs:SecurityGroups[].GroupId}" --output table
- The instance has a public IP (or you connect over VPN or Direct Connect to the private IP).
- The security group allows TCP 22 from the customer's current public IP. Home IPs change.
aws ec2 describe-security-groups --group-ids sg-0123 --query "SecurityGroups[].IpPermissions[?ToPort==\`22\`]" - The subnet's route table has
0.0.0.0/0 → igw-...for public access. - The network ACL allows inbound 22 and outbound ephemeral ports 1024–65535. NACLs are stateless, so the return traffic needs its own rule.
- Reachability Analyzer can test the path from the IGW to the instance ENI on port 22, and names the blocking component.
Refused: the instance side
Use a channel that doesn't need SSH:
aws ssm start-session --target $ID # if the SSM agent and role are in place
aws ec2-instance-connect send-serial-console-ssh-public-key --instance-id $ID --serial-port 0 --ssh-public-key file://~/.ssh/id_ed25519.pub
Then check:
sudo systemctl status sshd
sudo ss -ltnp | grep ':22'
sudo journalctl -u sshd --since "1 hour ago" | tail -30
sudo sshd -t # validates sshd_config syntax
A typo in sshd_config is a common cause after hardening scripts run.
Permission denied
- Use the default user for the AMI:
ec2-user(Amazon Linux, RHEL),ubuntu(Ubuntu),admin(Debian),centos, orrocky. - Make sure the key file is private:
chmod 400 key.pem. - Confirm the key pair matches the instance:
aws ec2 describe-instances --instance-ids $ID --query "Reservations[].Instances[].KeyName" - Push a temporary key with EC2 Instance Connect (valid for 60 seconds):
aws ec2-instance-connect send-ssh-public-key --instance-id $ID --instance-os-user ec2-user --ssh-public-key file://~/.ssh/id_ed25519.pub
Lost key: recovery
- Use SSM Session Manager or the serial console to add a new key to
~/.ssh/authorized_keys. - Without those, run
AWSSupport-ResetAccessautomation, or detach the root volume, attach it to a rescue instance, editauthorized_keys, and reattach (see the EC2 status check runbook).
Tip
Make Session Manager the default access path: no inbound port 22 and no keys to lose, with every session logged.