EC2 SSH Connection Timeout: Kiểm Tra Security Group và Các Lớp Mạng Để Sửa Lỗi Kết Nối
Bạn vừa khởi chạy một EC2 instance mới, gõ lệnh ssh ec2-user@<public-ip> và ngồi chờ — rồi terminal trả về Connection timed out. Không phải Connection refused, không phải lỗi xác thực — chỉ là im lặng hoàn toàn rồi timeout. Đây là dấu hiệu điển hình của một vấn đề ở lớp mạng, không phải ở ứng dụng, và Security Group thường là nghi phạm đầu tiên cần kiểm tra khi debug lỗi EC2 SSH connection timeout.
TL;DR — Tóm Tắt Nhanh
| Lớp cần kiểm tra | Vấn đề phổ biến | Hành động |
|---|---|---|
| Security Group (Inbound) | Thiếu rule TCP port 22 | Thêm inbound rule: TCP 22 từ IP của bạn |
| Network ACL | Deny rule chặn port 22 hoặc ephemeral ports | Kiểm tra inbound và outbound NACL rules |
| Route Table | Subnet không có route ra Internet Gateway | Gắn IGW route vào route table của subnet |
| Public IP / Elastic IP | Instance không có public IP | Gán Elastic IP hoặc bật auto-assign public IP |
| Instance State | Instance đang ở trạng thái stopped hoặc chưa pass status check | Kiểm tra instance status trong console |
Cơ Chế Hoạt Động: Tại Sao SSH Timeout Xảy Ra
Lỗi Connection timed out có nghĩa là gói TCP SYN của bạn đã được gửi đi nhưng không nhận được phản hồi nào — không phải RST (từ chối), mà là im lặng hoàn toàn. Điều này xảy ra khi gói tin bị drop ở đâu đó trên đường đi trước khi đến được tiến trình SSH trên instance.
Trong môi trường AWS, một kết nối SSH từ máy tính của bạn đến EC2 instance phải đi qua nhiều lớp kiểm soát mạng theo thứ tự cụ thể. Hiểu đúng thứ tự này giúp bạn debug có hệ thống thay vì đoán mò.
(SSH Client)"] IGW["Internet Gateway"] RT["Route Table
0.0.0.0/0 → IGW"] NACL["Network ACL
(Stateless - Subnet level)"] SG["Security Group
(Stateful - Instance level)"] Instance["EC2 Instance
sshd port 22"] Client -->|"TCP SYN port 22"| IGW IGW -->|"Route lookup"| RT RT -->|"Forward to subnet"| NACL NACL -->|"Inbound ALLOW check"| SG SG -->|"Inbound rule TCP 22"| Instance Instance -->|"Return traffic
(SG: auto-allow)"| NACL NACL -->|"Outbound ALLOW check
(ephemeral ports)"| Client
- Client → Internet: Gói TCP SYN từ máy bạn đi qua mạng công cộng đến địa chỉ public IP của instance.
- Internet Gateway: AWS IGW nhận gói và thực hiện NAT từ public IP sang private IP của instance.
- Route Table: Subnet của instance phải có route
0.0.0.0/0 → igw-xxxxxxxxđể traffic có thể đến được. - Network ACL (Stateless): Lớp firewall stateless ở cấp subnet — kiểm tra cả inbound lẫn outbound rules vì NACL không tự động cho phép return traffic.
- Security Group (Stateful): Lớp firewall stateful ở cấp instance — nếu inbound được phép, return traffic tự động được cho qua.
- Instance OS: Nếu tất cả các lớp trên đều thông, SSH daemon (
sshd) trên instance mới xử lý kết nối.
Security Group hoạt động như một stateful firewall — nó nhớ kết nối. Network ACL thì không — nó kiểm tra từng gói tin độc lập theo chiều. Đây là lý do tại sao NACL cần cả inbound lẫn outbound rules cho cùng một kết nối.
Bước 1: Kiểm Tra Security Group Inbound Rules — Nghi Phạm Số Một
Security Group là lớp phổ biến nhất gây ra SSH timeout trên EC2. Mặc định, một Security Group mới không có inbound rule nào — tất cả traffic inbound đều bị block. Bạn cần thêm rule cho phép TCP port 22 từ địa chỉ IP nguồn của mình.
Trước tiên, lấy Security Group ID đang gắn với instance:
aws ec2 describe-instances \
--instance-ids i-0123456789abcdef0 \
--query 'Reservations[*].Instances[*].SecurityGroups' \
--output table
Sau đó kiểm tra inbound rules của Security Group đó:
aws ec2 describe-security-groups \
--group-ids sg-0123456789abcdef0 \
--query 'SecurityGroups[*].IpPermissions' \
--output table
Nếu không thấy rule nào cho TCP port 22, thêm rule mới. Thay 203.0.113.10/32 bằng địa chỉ IP thực của bạn — không dùng 0.0.0.0/0 trong môi trường production:
aws ec2 authorize-security-group-ingress \
--group-id sg-0123456789abcdef0 \
--protocol tcp \
--port 22 \
--cidr 203.0.113.10/32
Để biết IP public của máy bạn:
curl -s https://checkip.amazonaws.com
Security Group thay đổi có hiệu lực ngay lập tức — không cần restart instance hay bất kỳ thao tác nào khác.
Bước 2: Xác Nhận Instance Có Public IP và Đang Running
Một Security Group đúng cũng vô dụng nếu instance không có địa chỉ public IP để bạn kết nối tới. Đây là điều hay bị bỏ qua khi launch instance trong một subnet mới — nếu subnet không bật 'auto-assign public IPv4 address', instance sẽ chỉ có private IP.
aws ec2 describe-instances \
--instance-ids i-0123456789abcdef0 \
--query 'Reservations[*].Instances[*].{State:State.Name,PublicIP:PublicIpAddress,PrivateIP:PrivateIpAddress}' \
--output table
Kết quả cần cho thấy State = running và PublicIP không phải None. Nếu instance đang ở trạng thái pending hoặc chưa pass cả hai status checks, SSH sẽ không phản hồi dù network hoàn toàn thông.
Kiểm tra status checks:
aws ec2 describe-instance-status \
--instance-ids i-0123456789abcdef0 \
--query 'InstanceStatuses[*].{SystemCheck:SystemStatus.Status,InstanceCheck:InstanceStatus.Status}' \
--output table
Cả hai phải là ok. Nếu SystemStatus là impaired, đây là vấn đề phần cứng phía AWS — thử stop và start lại instance (không phải reboot) để di chuyển sang host khác.
Bước 3: Kiểm Tra Route Table — Subnet Có Thông Ra Internet Không
Nếu Security Group đã đúng nhưng vẫn timeout, bước tiếp theo là kiểm tra xem subnet của instance có route ra Internet Gateway không. Đây là lớp hay bị bỏ qua, đặc biệt khi bạn tạo subnet mới hoặc di chuyển instance sang subnet khác.
Lấy Subnet ID của instance:
aws ec2 describe-instances \
--instance-ids i-0123456789abcdef0 \
--query 'Reservations[*].Instances[*].SubnetId' \
--output text
Tìm Route Table gắn với subnet đó:
aws ec2 describe-route-tables \
--filters Name=association.subnet-id,Values=subnet-0123456789abcdef0 \
--query 'RouteTables[*].Routes' \
--output table
Bạn cần thấy một route có DestinationCidrBlock = 0.0.0.0/0 và GatewayId bắt đầu bằng igw-. Nếu không có route này, subnet đó là private subnet và instance không thể nhận kết nối trực tiếp từ internet.
Bước 4: Kiểm Tra Network ACL — Lớp Stateless Hay Bị Bỏ Qua
Network ACL (NACL) hoạt động ở cấp subnet và là stateless — nghĩa là nó kiểm tra từng gói tin độc lập theo cả hai chiều. Nhiều người chỉ thêm inbound rule cho port 22 nhưng quên mất rằng NACL cũng cần outbound rule để cho phép return traffic trên ephemeral ports.
Lấy NACL gắn với subnet:
aws ec2 describe-network-acls \
--filters Name=association.subnet-id,Values=subnet-0123456789abcdef0 \
--query 'NetworkAcls[*].{InboundRules:Entries[?Egress==`false`],OutboundRules:Entries[?Egress==`true`]}' \
--output json
Kiểm tra hai điều:
- Inbound: Phải có rule ALLOW cho TCP port 22 từ IP nguồn của bạn (hoặc
0.0.0.0/0), với rule number thấp hơn bất kỳ DENY rule nào. - Outbound: Phải có rule ALLOW cho TCP trên ephemeral port range (thường là
1024-65535) để return traffic có thể đi ra. Nếu outbound bị block, client sẽ không nhận được phản hồi và thấy timeout.
Default NACL của AWS cho phép tất cả traffic — vấn đề chỉ xảy ra khi bạn đã tùy chỉnh NACL. Nếu NACL vẫn là default, bạn có thể bỏ qua bước này.
Bước 5: Xác Nhận SSH Daemon Đang Chạy Trên Instance
Nếu tất cả các lớp mạng đều thông nhưng vẫn timeout — không phải Connection refused mà vẫn là timeout — có thể sshd không chạy hoặc đang lắng nghe trên port khác. Trường hợp này ít gặp hơn với AMI chuẩn của AWS, nhưng có thể xảy ra nếu bạn dùng custom AMI hoặc user data script có lỗi.
Cách nhanh nhất để kiểm tra mà không cần SSH là dùng EC2 Instance Connect (nếu instance là Amazon Linux 2 hoặc Ubuntu và region hỗ trợ) hoặc AWS Systems Manager Session Manager:
aws ssm start-session \
--target i-0123456789abcdef0
Nếu Session Manager kết nối được, bạn có thể kiểm tra trạng thái sshd từ bên trong:
sudo systemctl status sshd
Lưu ý: Session Manager yêu cầu instance phải có SSM Agent đang chạy và có IAM instance profile với quyền AmazonSSMManagedInstanceCore. Nếu instance mới launch từ AMI chuẩn của AWS, SSM Agent thường đã được cài sẵn.
Kinh Nghiệm Thực Tế: Sai Lầm Phổ Biến Khi Debug SSH Timeout
Đây là pattern hay gặp nhất: bạn thêm inbound rule TCP 22 vào Security Group, thử lại — vẫn timeout. Bạn kiểm tra lại rule, thấy đúng rồi, bắt đầu nghi ngờ instance. Nhưng thực ra vấn đề nằm ở chỗ bạn đang SSH đến đúng IP nhưng sai instance.
Cụ thể: bạn có hai instance trong cùng một VPC, một cái đã dừng. Instance đang chạy có Security Group đúng, nhưng bạn đang dùng IP của instance đã dừng — IP đó đã được giải phóng và có thể đã được gán cho instance khác, hoặc đơn giản là không còn tồn tại. Terminal cứ timeout vì không có gì ở đầu kia để phản hồi.
Bài học: luôn verify lại public IP từ describe-instances ngay trước khi SSH, đặc biệt sau khi stop/start instance — public IP thay đổi mỗi lần start trừ khi bạn dùng Elastic IP.
inbound TCP 22?"} IP{"Instance có
Public IP không?"} RT{"Route Table có
route tới IGW?"} NACL{"NACL có DENY
port 22 hoặc
ephemeral ports?"} State{"Instance state
= running và
status checks OK?"} SSHD{"sshd đang
chạy không?"} Fix1["Thêm inbound rule
TCP 22"] Fix2["Gán Elastic IP
hoặc bật auto-assign"] Fix3["Thêm route
0.0.0.0/0 → IGW"] Fix4["Sửa NACL rules
inbound và outbound"] Fix5["Stop/Start instance
hoặc kiểm tra hardware"] Fix6["Khởi động lại sshd
qua Session Manager"] Done(["Kết nối SSH thành công"]) Start --> SG SG -->|"Không"| Fix1 --> Done SG -->|"Có"| IP IP -->|"Không"| Fix2 --> Done IP -->|"Có"| RT RT -->|"Không"| Fix3 --> Done RT -->|"Có"| NACL NACL -->|"Có DENY"| Fix4 --> Done NACL -->|"Không có DENY"| State State -->|"Không OK"| Fix5 --> Done State -->|"OK"| SSHD SSHD -->|"Không chạy"| Fix6 --> Done SSHD -->|"Đang chạy"| Done
- Kiểm tra Security Group: Có inbound rule TCP 22 không? Đúng IP nguồn không?
- Kiểm tra Public IP: Instance có public IP không? IP có đúng không?
- Kiểm tra Route Table: Subnet có route
0.0.0.0/0 → IGWkhông? - Kiểm tra NACL: Có DENY rule nào chặn port 22 hoặc ephemeral ports không?
- Kiểm tra Instance State: Instance đang running và pass cả hai status checks không?
- Kiểm tra sshd: SSH daemon có đang chạy và lắng nghe đúng port không?
IAM Policy Tối Thiểu Để Debug SSH Timeout
Để chạy các lệnh CLI trong bài này, IAM user hoặc role của bạn cần các quyền sau. Đây là policy tối thiểu theo nguyên tắc least privilege:
🔽 Xem IAM Policy JSON
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DescribeNetworkResources",
"Effect": "Allow",
"Action": [
"ec2:DescribeInstances",
"ec2:DescribeInstanceStatus",
"ec2:DescribeSecurityGroups",
"ec2:DescribeRouteTables",
"ec2:DescribeNetworkAcls",
"ec2:DescribeSubnets"
],
"Resource": "*"
},
{
"Sid": "ModifySecurityGroup",
"Effect": "Allow",
"Action": [
"ec2:AuthorizeSecurityGroupIngress"
],
"Resource": "arn:aws:ec2:us-east-1:123456789012:security-group/sg-0123456789abcdef0"
},
{
"Sid": "SSMSessionAccess",
"Effect": "Allow",
"Action": [
"ssm:StartSession"
],
"Resource": "arn:aws:ec2:us-east-1:123456789012:instance/i-0123456789abcdef0"
}
]
}
Lưu ý: các action Describe* yêu cầu "Resource": "*" vì chúng không hỗ trợ resource-level permission theo AWS Service Authorization Reference.
Tổng Kết và Bước Tiếp Theo
Lỗi EC2 SSH connection timeout gần như luôn là vấn đề ở lớp mạng, không phải ứng dụng. Debug theo thứ tự từ ngoài vào trong: Security Group → Public IP → Route Table → Network ACL → Instance state → sshd. Phần lớn trường hợp sẽ được giải quyết ở bước 1 hoặc bước 2.
Nếu bạn cần SSH vào instance trong private subnet (không có public IP), hãy cân nhắc dùng AWS Systems Manager Session Manager hoặc thiết lập một bastion host — đây là cách tiếp cận an toàn hơn cho môi trường production so với việc expose port 22 trực tiếp ra internet.
- AWS Documentation: Security Groups for EC2
- AWS Documentation: Network ACLs
- AWS Systems Manager Session Manager
Glossary — Thuật Ngữ Chính
| Thuật ngữ | Giải thích |
|---|---|
| Security Group | Stateful firewall ở cấp instance trong AWS VPC. Chỉ cần định nghĩa inbound rule — return traffic tự động được cho phép. |
| Network ACL (NACL) | Stateless firewall ở cấp subnet. Phải định nghĩa cả inbound lẫn outbound rules vì không có session tracking. |
| Internet Gateway (IGW) | Thành phần VPC cho phép traffic đi ra/vào internet. Phải được gắn vào VPC và có route trong route table. |
| Ephemeral Ports | Port ngẫu nhiên phía client dùng cho return traffic của kết nối TCP. Thường trong range 1024-65535 tùy OS. |
| Elastic IP | Địa chỉ IPv4 tĩnh do AWS cấp, gắn với AWS account. Không thay đổi khi stop/start instance, khác với public IP thông thường. |
Nhận xét
Đăng nhận xét