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

Bạn tạo một custom VPC, launch EC2 instance vào public subnet, SSH vào được — nhưng ping google.com trả về timeout hoàn toàn. Đây là một trong những lỗi phổ biến nhất khi làm việc với EC2 không có internet trong custom VPC, và nguyên nhân gần như luôn nằm ở một trong ba lớp: thiếu Internet Gateway, route table chưa có default route, hoặc Security Group/NACL chặn traffic ra ngoài.

TL;DR — EC2 Không Có Internet Trong Custom VPC

Lớp kiểm traTriệu chứngFix
Internet GatewayKhông có IGW hoặc IGW chưa attach vào VPCTạo IGW, attach vào VPC
Route TableKhông có route 0.0.0.0/0 → igw-xxxThêm default route trỏ vào IGW
Subnet AssociationSubnet dùng main route table không có IGW routeAssociate subnet với route table đúng
Public IPEC2 không có public IP hoặc Elastic IPEnable auto-assign public IP hoặc gắn EIP
Security GroupOutbound rule bị xóa hoặc quá hạn chếThêm outbound rule cho port 80/443 hoặc all traffic
Network ACLNACL chặn outbound hoặc inbound ephemeral portsKiểm tra inbound/outbound rules của NACL

Cơ Chế Hoạt Động: Tại Sao EC2 Trong Custom VPC Không Có Internet Mặc Định?

Khi bạn tạo VPC bằng wizard mặc định của AWS (default VPC), AWS tự động tạo Internet Gateway, cấu hình route table, và gán public IP cho instance. Nhưng khi bạn tạo custom VPC, không có gì trong số đó được tạo tự động — bạn phải cấu hình từng lớp thủ công.

Luồng traffic từ EC2 ra internet đi qua các lớp sau, theo thứ tự:

graph LR EC2["EC2 Instance
Private IP"] --> SG["Security Group
Outbound Rules"] SG --> NACL["Network ACL
Stateless Filter"] NACL --> RT["Route Table
0.0.0.0/0 → IGW"] RT --> IGW["Internet Gateway
1-to-1 NAT"] IGW --> NET["Internet"] style EC2 fill:#FF9900,color:#fff style IGW fill:#232F3E,color:#fff style NET fill:#1a73e8,color:#fff
  1. EC2 Instance gửi packet ra ngoài (ví dụ: DNS query hoặc HTTP request).
  2. Security Group kiểm tra outbound rules — nếu không có rule cho phép, packet bị drop ngay tại đây.
  3. Network ACL kiểm tra outbound rules ở cấp subnet — stateless, nên cần cả inbound rule cho ephemeral ports (1024-65535) để nhận response.
  4. Route Table tra cứu destination IP — nếu không có route 0.0.0.0/0 trỏ vào IGW, packet không biết đi đâu và bị drop.
  5. Internet Gateway thực hiện NAT: dịch private IP của EC2 sang public IP trước khi gửi ra internet.
  6. Nếu EC2 không có public IP (hoặc Elastic IP), IGW không thể thực hiện NAT — packet bị drop tại đây.
Internet Gateway hoạt động như một cổng biên của VPC — nó không chỉ là một router, mà còn thực hiện 1-to-1 NAT giữa private IP và public IP của instance. Thiếu public IP đồng nghĩa với việc IGW không có địa chỉ đích để NAT sang.

Hướng Dẫn Từng Bước: Gắn Internet Gateway và Cập Nhật Route Table

Bước 1: Xác Minh Trạng Thái Hiện Tại Của VPC

Trước khi sửa bất cứ thứ gì, kiểm tra toàn bộ cấu hình hiện tại. Nhiều người nhảy thẳng vào tạo IGW mới trong khi VPC đã có IGW nhưng chưa được attach — tạo thêm sẽ không giải quyết được gì.

# Lấy VPC ID của bạn
aws ec2 describe-vpcs \
  --filters "Name=isDefault,Values=false" \
  --query 'Vpcs[*].{VpcId:VpcId,CidrBlock:CidrBlock,State:State}' \
  --output table

# Kiểm tra IGW đã tồn tại chưa và trạng thái attach
aws ec2 describe-internet-gateways \
  --filters "Name=attachment.vpc-id,Values=vpc-0123456789abcdef0" \
  --query 'InternetGateways[*].{IGW_ID:InternetGatewayId,State:Attachments[0].State}' \
  --output table

Nếu output rỗng — VPC chưa có IGW nào. Nếu có IGW nhưng Statedetached, bạn chỉ cần attach lại.

Bước 2: Tạo và Attach Internet Gateway

Nếu bước 1 xác nhận chưa có IGW, tạo mới và attach vào VPC. Một VPC chỉ có thể attach một IGW tại một thời điểm — đây là hard limit của AWS.

# Tạo Internet Gateway
aws ec2 create-internet-gateway \
  --tag-specifications 'ResourceType=internet-gateway,Tags=[{Key=Name,Value=my-custom-igw}]' \
  --query 'InternetGateway.InternetGatewayId' \
  --output text

# Kết quả ví dụ: igw-0abc123def456789a
# Attach IGW vào VPC
aws ec2 attach-internet-gateway \
  --internet-gateway-id igw-0abc123def456789a \
  --vpc-id vpc-0123456789abcdef0

Không có output nghĩa là thành công. Verify lại bằng lệnh describe ở bước 1 — State phải là available.

Bước 3: Kiểm Tra Route Table Của Public Subnet

Đây là nơi hầu hết mọi người bỏ sót. Attach IGW vào VPC chỉ là điều kiện cần — route table của subnet phải có default route trỏ vào IGW đó. Nếu subnet đang dùng main route table và main route table không có IGW route, traffic vẫn không đi được.

# Tìm subnet ID của EC2 instance
aws ec2 describe-instances \
  --instance-ids i-0123456789abcdef0 \
  --query 'Reservations[0].Instances[0].{SubnetId:SubnetId,PublicIp:PublicIpAddress}' \
  --output table

# Kiểm tra route table được associate với subnet
aws ec2 describe-route-tables \
  --filters "Name=association.subnet-id,Values=subnet-0123456789abcdef0" \
  --query 'RouteTables[*].{RouteTableId:RouteTableId,Routes:Routes}' \
  --output json

Trong output, tìm route có DestinationCidrBlock: 0.0.0.0/0. Nếu không có — đó là vấn đề. Nếu có nhưng GatewayIdlocal hoặc trỏ vào NAT Gateway — subnet đó đang được cấu hình như private subnet.

Bước 4: Thêm Default Route Vào Route Table

Nếu route table của subnet chưa có default route trỏ vào IGW, thêm vào. Nếu subnet đang dùng main route table và bạn không muốn ảnh hưởng đến các subnet khác, hãy tạo route table riêng cho public subnet.

🔽 Click để xem: Tạo route table mới và associate với subnet
# Tạo route table mới cho public subnet
aws ec2 create-route-table \
  --vpc-id vpc-0123456789abcdef0 \
  --tag-specifications 'ResourceType=route-table,Tags=[{Key=Name,Value=public-rt}]' \
  --query 'RouteTable.RouteTableId' \
  --output text

# Kết quả ví dụ: rtb-0abc123def456789a

# Thêm default route trỏ vào IGW
aws ec2 create-route \
  --route-table-id rtb-0abc123def456789a \
  --destination-cidr-block 0.0.0.0/0 \
  --gateway-id igw-0abc123def456789a

# Associate route table với public subnet
aws ec2 associate-route-table \
  --route-table-id rtb-0abc123def456789a \
  --subnet-id subnet-0123456789abcdef0

Nếu bạn muốn thêm route vào route table hiện có (không tạo mới):

aws ec2 create-route \
  --route-table-id rtb-0existingtable123 \
  --destination-cidr-block 0.0.0.0/0 \
  --gateway-id igw-0abc123def456789a

Bước 5: Xác Minh EC2 Có Public IP

IGW thực hiện 1-to-1 NAT — nó cần một public IP để map với private IP của instance. Nếu EC2 không có public IP hoặc Elastic IP, traffic sẽ không ra được dù route table đã đúng.

# Kiểm tra public IP của instance
aws ec2 describe-instances \
  --instance-ids i-0123456789abcdef0 \
  --query 'Reservations[0].Instances[0].{PrivateIP:PrivateIpAddress,PublicIP:PublicIpAddress,PublicDNS:PublicDnsName}' \
  --output table

Nếu PublicIPNone, bạn có hai lựa chọn: cấp Elastic IP và associate vào instance, hoặc stop/start instance sau khi bật auto-assign public IP trên subnet.

# Bật auto-assign public IP cho subnet (áp dụng cho instance mới)
aws ec2 modify-subnet-attribute \
  --subnet-id subnet-0123456789abcdef0 \
  --map-public-ip-on-launch

# Hoặc: Cấp Elastic IP và associate vào instance hiện tại
aws ec2 allocate-address \
  --domain vpc \
  --query 'AllocationId' \
  --output text
# Kết quả ví dụ: eipalloc-0abc123def456789a

aws ec2 associate-address \
  --instance-id i-0123456789abcdef0 \
  --allocation-id eipalloc-0abc123def456789a

Bước 6: Kiểm Tra Security Group Outbound Rules

Security Group là stateful — nếu outbound rule bị xóa hoặc quá hạn chế, response từ internet sẽ không vào được dù inbound rule đúng. Đây là lớp hay bị bỏ qua vì mặc định AWS tạo outbound rule cho phép all traffic, nhưng ai đó có thể đã xóa nó.

# Lấy Security Group ID của instance
aws ec2 describe-instances \
  --instance-ids i-0123456789abcdef0 \
  --query 'Reservations[0].Instances[0].SecurityGroups' \
  --output table

# Kiểm tra outbound rules
aws ec2 describe-security-groups \
  --group-ids sg-0123456789abcdef0 \
  --query 'SecurityGroups[0].IpPermissionsEgress' \
  --output json

Outbound rule tối thiểu cần thiết để ping và HTTP/HTTPS hoạt động:

# Thêm outbound rule cho HTTPS (443)
aws ec2 authorize-security-group-egress \
  --group-id sg-0123456789abcdef0 \
  --protocol tcp \
  --port 443 \
  --cidr 0.0.0.0/0

# Thêm outbound rule cho HTTP (80)
aws ec2 authorize-security-group-egress \
  --group-id sg-0123456789abcdef0 \
  --protocol tcp \
  --port 80 \
  --cidr 0.0.0.0/0

# Thêm outbound rule cho ICMP (ping)
aws ec2 authorize-security-group-egress \
  --group-id sg-0123456789abcdef0 \
  --protocol icmp \
  --port -1 \
  --cidr 0.0.0.0/0

Bước 7: Kiểm Tra Network ACL

NACL là stateless — không giống Security Group, nó không tự động cho phép response traffic. Bạn phải có cả outbound rule lẫn inbound rule cho ephemeral ports (1024-65535) để TCP handshake hoàn chỉnh. Đây là lớp hay gây nhầm lẫn nhất vì ping thất bại dù Security Group đã đúng.

# Tìm NACL associate với subnet
aws ec2 describe-network-acls \
  --filters "Name=association.subnet-id,Values=subnet-0123456789abcdef0" \
  --query 'NetworkAcls[*].{NaclId:NetworkAclId,Entries:Entries}' \
  --output json

Trong output, kiểm tra:

  • Outbound rules: Phải có rule cho phép 0.0.0.0/0 hoặc ít nhất port 80, 443, và ICMP.
  • Inbound rules: Phải có rule cho phép ephemeral ports 1024-65535 từ 0.0.0.0/0 để nhận TCP response.
  • Rule số thấp hơn có priority cao hơn. Nếu có DENY rule số nhỏ trước ALLOW rule — traffic bị chặn.
graph TD START["ping google.com timeout"] --> Q1{"IGW attached to VPC?"} Q1 -->|"No"| FIX1["Tạo và attach IGW"] Q1 -->|"Yes"| Q2{"Route table có 0.0.0.0/0 → IGW?"} FIX1 --> Q2 Q2 -->|"No"| FIX2["Thêm default route vào route table"] Q2 -->|"Yes"| Q3{"EC2 có Public IP?"} FIX2 --> Q3 Q3 -->|"No"| FIX3["Gắn Elastic IP hoặc bật auto-assign"] Q3 -->|"Yes"| Q4{"SG outbound cho phép ICMP/HTTP?"} FIX3 --> Q4 Q4 -->|"No"| FIX4["Thêm outbound rule vào Security Group"] Q4 -->|"Yes"| Q5{"NACL inbound cho phép ephemeral ports?"} FIX4 --> Q5 Q5 -->|"No"| FIX5["Thêm NACL inbound rule cho port 1024-65535"] Q5 -->|"Yes"| DONE["Kiểm tra DNS attributes của VPC"] FIX5 --> DONE style START fill:#d32f2f,color:#fff style DONE fill:#388e3c,color:#fff

Kinh Nghiệm Thực Tế: Lỗi Hay Gặp Và Cách Nhận Diện

Tình huống thực tế: Route table đã có IGW route, Security Group đúng, nhưng curl https://google.com vẫn timeout. Kiểm tra NACL thấy có outbound rule ALLOW ALL — có vẻ ổn. Nhưng inbound rule chỉ có ALLOW port 22DENY ALL. Response từ google.com về qua ephemeral port 443+ bị NACL chặn ở chiều inbound trước khi vào Security Group.

Chẩn đoán sai ban đầu: Nghĩ Security Group outbound bị thiếu, thêm rule nhưng không có tác dụng. Nguyên nhân thực: NACL inbound không có rule cho ephemeral ports. Fix: Thêm inbound rule ALLOW TCP 1024-65535 from 0.0.0.0/0 vào NACL.

NACL stateless là điểm khác biệt quan trọng nhất so với Security Group — một rule outbound ALLOW không tự động tạo inbound ALLOW cho response.

Checklist Xác Minh Cuối Cùng

# Test kết nối từ bên trong EC2 instance sau khi fix
# SSH vào instance trước
ssh -i your-key.pem ec2-user@

# Test DNS resolution
nslookup google.com

# Test ICMP
ping -c 4 google.com

# Test HTTP/HTTPS
curl -I https://google.com

Nếu DNS không resolve được nhưng ping IP trực tiếp hoạt động — kiểm tra DNS settings của VPC (enableDnsSupportenableDnsHostnames phải là true).

# Kiểm tra DNS attributes của VPC
aws ec2 describe-vpc-attribute \
  --vpc-id vpc-0123456789abcdef0 \
  --attribute enableDnsSupport

aws ec2 describe-vpc-attribute \
  --vpc-id vpc-0123456789abcdef0 \
  --attribute enableDnsHostnames

# Bật nếu cần
aws ec2 modify-vpc-attribute \
  --vpc-id vpc-0123456789abcdef0 \
  --enable-dns-support '{"Value":true}'

aws ec2 modify-vpc-attribute \
  --vpc-id vpc-0123456789abcdef0 \
  --enable-dns-hostnames '{"Value":true}'

IAM Permissions Cần Thiết

Để thực hiện toàn bộ các bước trên, IAM user hoặc role cần có các permissions sau theo nguyên tắc least privilege:

🔽 Click để xem: IAM Policy tối thiểu
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "DescribeNetworkResources",
      "Effect": "Allow",
      "Action": [
        "ec2:DescribeVpcs",
        "ec2:DescribeInternetGateways",
        "ec2:DescribeRouteTables",
        "ec2:DescribeSubnets",
        "ec2:DescribeInstances",
        "ec2:DescribeSecurityGroups",
        "ec2:DescribeNetworkAcls",
        "ec2:DescribeVpcAttribute",
        "ec2:DescribeAddresses"
      ],
      "Resource": "*"
    },
    {
      "Sid": "ManageInternetGateway",
      "Effect": "Allow",
      "Action": [
        "ec2:CreateInternetGateway",
        "ec2:AttachInternetGateway",
        "ec2:CreateTags"
      ],
      "Resource": "*"
    },
    {
      "Sid": "ManageRouteTable",
      "Effect": "Allow",
      "Action": [
        "ec2:CreateRouteTable",
        "ec2:CreateRoute",
        "ec2:AssociateRouteTable"
      ],
      "Resource": "*"
    },
    {
      "Sid": "ManagePublicIP",
      "Effect": "Allow",
      "Action": [
        "ec2:AllocateAddress",
        "ec2:AssociateAddress",
        "ec2:ModifySubnetAttribute"
      ],
      "Resource": "*"
    },
    {
      "Sid": "ManageSecurityGroup",
      "Effect": "Allow",
      "Action": [
        "ec2:AuthorizeSecurityGroupEgress"
      ],
      "Resource": "*"
    },
    {
      "Sid": "ManageVpcAttributes",
      "Effect": "Allow",
      "Action": [
        "ec2:ModifyVpcAttribute"
      ],
      "Resource": "*"
    }
  ]
}

Lưu ý: Nhiều Describe actions trong EC2 yêu cầu "Resource": "*" vì chúng không hỗ trợ resource-level permissions. Kiểm tra AWS Service Authorization Reference cho EC2 để xác nhận trước khi áp dụng vào production.

Wrap-Up: EC2 Không Có Internet Trong Custom VPC — Tóm Tắt Quy Trình

Vấn đề EC2 không có internet trong custom VPC luôn nằm ở một trong sáu lớp: IGW, route table, subnet association, public IP, Security Group, hoặc NACL. Không có lớp nào tự động cấu hình khi bạn tạo custom VPC — đây là điểm khác biệt cốt lõi so với default VPC.

Quy trình debug hiệu quả nhất là đi từ ngoài vào trong: IGW → route table → public IP → Security Group → NACL. Đừng bỏ qua NACL chỉ vì nó ít được nhắc đến — stateless behavior của NACL là nguyên nhân của nhiều lỗi timeout khó giải thích nhất.

Tài liệu tham khảo chính thức:

Glossary — Thuật Ngữ Chính

Thuật ngữGiải thích
Internet Gateway (IGW)Thành phần VPC cho phép traffic giữa VPC và internet. Thực hiện 1-to-1 NAT giữa private IP và public IP của instance.
Route TableTập hợp các routing rules xác định traffic từ subnet đi đến đâu. Mỗi subnet phải được associate với một route table.
Network ACL (NACL)Tường lửa stateless ở cấp subnet. Khác Security Group ở chỗ phải cấu hình cả inbound lẫn outbound rules cho mỗi luồng traffic.
Ephemeral PortsDải port tạm thời (1024-65535) mà client sử dụng để nhận response từ server. NACL phải cho phép inbound traffic trên dải này.
Elastic IP (EIP)Public IPv4 address tĩnh có thể associate với EC2 instance, thay thế cho auto-assigned public IP vốn thay đổi mỗi lần stop/start.

Nhận xét

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

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