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 traVấn đề phổ biếnHành động
Security Group (Inbound)Thiếu rule TCP port 22Thêm inbound rule: TCP 22 từ IP của bạn
Network ACLDeny rule chặn port 22 hoặc ephemeral portsKiểm tra inbound và outbound NACL rules
Route TableSubnet không có route ra Internet GatewayGắn IGW route vào route table của subnet
Public IP / Elastic IPInstance không có public IPGán Elastic IP hoặc bật auto-assign public IP
Instance StateInstance đang ở trạng thái stopped hoặc chưa pass status checkKiể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ò.

graph LR Client["Máy tính của bạn
(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
  1. 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.
  2. Internet Gateway: AWS IGW nhận gói và thực hiện NAT từ public IP sang private IP của instance.
  3. Route Table: Subnet của instance phải có route 0.0.0.0/0 → igw-xxxxxxxx để traffic có thể đến được.
  4. 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.
  5. Security Group (Stateful): Lớp firewall stateful ở cấp instance — nếu inbound được phép, return traffic tự động được cho qua.
  6. 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 = runningPublicIP 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 SystemStatusimpaired, đâ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/0GatewayId 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.

graph TD Start(["SSH Connection Timeout"]) SG{"Security Group có
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
  1. Kiểm tra Security Group: Có inbound rule TCP 22 không? Đúng IP nguồn không?
  2. Kiểm tra Public IP: Instance có public IP không? IP có đúng không?
  3. Kiểm tra Route Table: Subnet có route 0.0.0.0/0 → IGW không?
  4. Kiểm tra NACL: Có DENY rule nào chặn port 22 hoặc ephemeral ports không?
  5. Kiểm tra Instance State: Instance đang running và pass cả hai status checks không?
  6. 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.

Glossary — Thuật Ngữ Chính

Thuật ngữGiải thích
Security GroupStateful 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 PortsPort 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.

Related Posts

Nhận xét

Bài đăng phổ biến từ blog này

EC2 Không Có Internet Trong Custom VPC: Cách Gắn Internet Gateway và Cập Nhật Route Table

Route 53 Alias vs CNAME: Khi Nào Dùng Alias Record Cho Zone Apex?