EC2 SSH Connection Timeout: Which Security Group Rules to Check
An EC2 SSH connection timeout is one of the most common blockers when launching a new instance — the fix is almost always a missing or misconfigured inbound rule in your Security Group.
TL;DR — Diagnostic Steps at a Glance
| Step | What to Check | Common Fix |
|---|---|---|
| 1 | Security Group inbound rules | Add SSH (TCP 22) from your IP |
| 2 | Subnet route table | Ensure 0.0.0.0/0 routes to an Internet Gateway |
| 3 | Network ACL rules | Allow inbound TCP 22 and ephemeral return ports |
| 4 | Instance public IP / Elastic IP | Confirm a public IP is assigned |
| 5 | Key pair and SSH username | Use correct private key and AMI-specific username |
Why SSH Times Out Instead of Refusing
A connection refused error means the TCP handshake reached the host but nothing is listening. A connection timed out error means packets never arrived — or replies never returned. On EC2, that silence is almost always a network-layer block, not an OS-level problem. The Security Group (an instance-level stateful firewall) is the first and most frequent culprit.
Analogy: Think of a Security Group as a bouncer at the door of your instance. If SSH (port 22) isn't on the guest list, your packets queue outside indefinitely — no rejection notice, just silence.
Step 1 — Fix the Security Group Inbound Rule
Security Groups use an implicit deny model: all inbound traffic is blocked unless an explicit allow rule exists. For SSH to work, you need a rule that permits inbound TCP on port 22 from your source IP.
Minimum required inbound rule:
- Type: SSH
- Protocol: TCP
- Port range: 22
- Source: Your public IP address (e.g.,
203.0.113.10/32)
Using 0.0.0.0/0 as the source works but exposes port 22 to the entire internet. For production instances, restrict to your known IP or a bastion host's Security Group ID.
🔽 AWS CLI — Add SSH inbound rule to a Security Group
aws ec2 authorize-security-group-ingress \
--group-id sg-0123456789abcdef0 \
--protocol tcp \
--port 22 \
--cidr 203.0.113.10/32 \
--region us-east-1
After adding the rule, Security Groups apply changes immediately — no instance reboot required.
Step 2 — Verify the Subnet Route Table
Even with a correct inbound rule, SSH will time out if the subnet has no path to the internet. Public subnets require a route that sends 0.0.0.0/0 traffic to an Internet Gateway (IGW).
Check your route table in the VPC console: navigate to VPC → Route Tables → Routes for the subnet hosting your instance. You should see a route like:
- Destination:
0.0.0.0/0 - Target:
igw-xxxxxxxxxxxxxxxxx
If the target is a NAT Gateway or shows local only, the instance is in a private subnet. You cannot SSH directly into a private subnet instance from the internet — you need a bastion host or AWS Systems Manager Session Manager.
Step 3 — Check Network ACLs
Network ACLs (stateless subnet-level firewalls) are evaluated before traffic reaches the instance. Unlike Security Groups, ACLs are stateless — you must explicitly allow both the inbound request and the outbound reply.
Required ACL rules for SSH:
- Inbound: Allow TCP port 22 from your source IP
- Outbound: Allow TCP on the ephemeral port range (the exact range depends on the client OS — check the AWS VPC documentation for current guidance) back to your source IP
Default VPCs ship with a default Network ACL that allows all traffic. Custom ACLs start with a deny-all rule — a common source of confusion when teams migrate to custom VPCs.
Step 4 — Confirm a Public IP Is Assigned
An instance in a public subnet with a correct route table still won't be reachable if it has no public IP. Check the instance details in the EC2 console under Public IPv4 address.
If the field is empty, either:
- Auto-assign public IP was disabled at launch (subnet default or explicit setting)
- No Elastic IP has been associated
You cannot add a public IP to a running instance after launch without using an Elastic IP. Allocate an Elastic IP and associate it with the instance from the EC2 console or CLI.
Step 5 — Key Pair and SSH Username
Once network access is confirmed, a mismatch between the private key or the SSH username will produce an authentication error — but only after the TCP connection succeeds. If you're still timing out, this step isn't the cause. If you've resolved the timeout but now see a permission denied error, check these:
- Private key file: Must be the
.pemfile corresponding to the key pair selected at launch. Permissions must bechmod 400. - SSH username: Varies by AMI. Common defaults for official AMIs include:
- Amazon Linux 2 / Amazon Linux 2023:
ec2-user - Ubuntu:
ubuntu - RHEL:
ec2-userorroot(check AMI description) - SUSE:
ec2-user - Debian: varies by AMI — check the AMI description or provider documentation. Not all Debian images use the same default username.
- Amazon Linux 2 / Amazon Linux 2023:
Only a subset of official AWS AMIs have standardized, well-documented default usernames. For marketplace or community AMIs — including many Debian images — the username is set by the AMI publisher and must be verified in the AMI's description or the publisher's documentation before connecting.
Full Diagnostic Flow
- Start: SSH attempt from your local machine.
- Public IP check: If no public IP exists, the packet has nowhere to go — assign an Elastic IP.
- Route Table check: If no IGW route exists, the subnet is private — use a bastion or Session Manager.
- Network ACL check: If the ACL blocks port 22 or ephemeral return ports, add explicit allow rules.
- Security Group check: If no inbound rule allows TCP 22 from your IP, add one — this is the most common fix.
- Connection established: TCP handshake succeeds; SSH authentication begins.
Production Gotcha — Your IP Changes, the Rule Doesn't
A common pitfall on dynamic home or office connections: you add an SSH rule scoped to your-ip/32, it works, then the next day you get a timeout again. Your ISP rotated your public IP — the Security Group rule still points to the old one. Teams sometimes respond by widening the source to 0.0.0.0/0 permanently, which trades the inconvenience for a real attack surface.
The correct approach: use AWS Systems Manager Session Manager for interactive shell access without opening port 22 at all, or maintain a dedicated bastion host with an Elastic IP whose Security Group you reference as the SSH source.
Common Mistake — Editing the Wrong Security Group
EC2 instances can have multiple Security Groups attached. Teams often add the SSH rule to one group and verify it looks correct — but the instance is actually using a different group that still has no port 22 rule. Before concluding a rule is missing, confirm which Security Groups are attached to the specific instance under EC2 → Instance → Security tab, then verify the inbound rules on each attached group.
Tradeoff — Restricting SSH vs. Operational Convenience
Locking SSH to a single /32 IP is secure but operationally brittle for teams with dynamic IPs or multiple engineers. The architectural tradeoff worth knowing: AWS Systems Manager Session Manager provides browser-based and CLI shell access using IAM authentication, with no inbound Security Group rule required and full session logging to CloudTrail and S3. For new deployments, this eliminates the SSH key management and port exposure problem entirely — at the cost of requiring the SSM Agent to be running and an appropriate IAM instance profile attached.
Wrap-Up
Most EC2 SSH timeouts resolve at Step 1 — a missing TCP 22 inbound rule in the Security Group. Work through the five steps in order: Security Group → Route Table → Network ACL → Public IP → Key/Username. For long-term security, consider replacing direct SSH with Session Manager to eliminate port 22 exposure entirely.
- AWS Docs: Authorize inbound traffic for Linux instances
- AWS Docs: AWS Systems Manager Session Manager
Glossary
| Term | Definition |
|---|---|
| Security Group | A stateful, instance-level virtual firewall that controls inbound and outbound traffic. Implicit deny on all inbound traffic unless explicitly allowed. |
| Network ACL | A stateless, subnet-level firewall evaluated before Security Groups. Requires explicit allow rules for both request and reply directions. |
| Internet Gateway (IGW) | A horizontally scaled VPC component that enables communication between instances in a VPC and the internet. |
| Elastic IP | A static public IPv4 address that can be allocated to your AWS account and associated with an EC2 instance. |
| Session Manager | An AWS Systems Manager capability that provides secure, auditable shell access to EC2 instances without requiring open inbound ports or SSH keys. |
Related Posts
- 📄 Security Group vs. Network ACL: Stateful vs. Stateless Traffic Filtering in AWS VPC
- 📄 Connecting to AWS RDS MySQL from Your Local Machine: Public Access, Security Groups, and Safer Alternatives
- 📄 Ditch SSH: Secure EC2 Access with AWS Systems Manager Session Manager
- 📄 EC2 No Internet Access in Custom VPC: Attaching an Internet Gateway and Fixing Route Tables
Comments
Post a Comment