VPC và Security Group: Mạng và Tường Lửa Cơ Bản trên AWS
Ở Bài 2 ta đã mở port 22 và 80 cho instance mà chưa hiểu rõ phần mạng phía sau. Bài này giải thích VPC và Security Group: nơi instance của bạn sống và thứ quyết định ai truy cập được vào nó.
Phần đầu thiên về khái niệm vì mạng là thứ cần hiểu trước khi làm. Phần sau ta thực hành kiểm tra mạng có sẵn và siết lại quyền truy cập cho một instance.
Mục tiêu
- Hiểu VPC, subnet, route table, internet gateway là gì và liên hệ với nhau ra sao.
- Phân biệt subnet public và private.
- Hiểu Security Group hoạt động thế nào và khác gì với tường lửa thông thường.
- Thực hành: kiểm tra VPC mặc định và tạo một Security Group siết chặt cho EC2.
Chi phí dự kiến
Các thành phần mạng trong bài (VPC, subnet, route table, internet gateway, Security Group) đều miễn phí. Phần thực hành có tạo lại một EC2 t2.micro để thử, tính tiền như Bài 2 và sẽ được dọn ở cuối.
Một lưu ý: NAT Gateway (sẽ nhắc tới khi nói về subnet private) là dịch vụ có tính tiền theo giờ, kể cả khi không dùng. Bài này chỉ giải thích chứ không tạo NAT Gateway, nên bạn không phát sinh chi phí từ nó.
VPC là gì
VPC (Virtual Private Cloud) là một mạng riêng của bạn bên trong AWS. Mọi resource cần mạng — như EC2, RDS — đều nằm trong một VPC. Hãy hình dung VPC như một khu đất có hàng rào: bên trong là mạng của bạn, AWS đảm bảo nó tách biệt với mạng của tài khoản khác.
Khi tạo tài khoản, AWS đã tạo sẵn cho mỗi region một VPC mặc định (default VPC). Instance ở Bài 2 chạy trong VPC này mà bạn không phải cấu hình gì. Mỗi VPC có một dải địa chỉ IP riêng (CIDR block), ví dụ 172.31.0.0/16.
Kiểm tra VPC mặc định bằng CLI:
aws ec2 describe-vpcs \
--query "Vpcs[].{ID:VpcId,CIDR:CidrBlock,Default:IsDefault}" \
--output table
Subnet: chia nhỏ VPC
Một VPC được chia thành nhiều subnet (mạng con). Mỗi subnet nằm trong một Availability Zone — một trung tâm dữ liệu vật lý của AWS trong region. Chia theo nhiều AZ giúp hệ thống chịu được sự cố: một AZ hỏng thì AZ khác vẫn chạy.
Điểm quan trọng nhất với người mới là phân biệt hai loại subnet:
- Public subnet: có đường ra Internet trực tiếp. Resource ở đây (như web server) có thể nhận truy cập từ Internet và tự đi ra Internet. Instance Bài 2 nằm trong public subnet, nên nó mới có Public IP và bạn mới SSH vào được.
- Private subnet: không có đường ra Internet trực tiếp. Resource ở đây (như database) không thể bị truy cập thẳng từ bên ngoài, an toàn hơn. Database nên nằm ở private subnet — ý này quay lại ở Bài 5.
Cái quyết định một subnet là public hay private không nằm ở tên gọi, mà ở route table.
Route table và Internet Gateway
Route table là bảng định tuyến, quy định lưu lượng trong subnet đi đâu. Mỗi subnet gắn với một route table.
Internet Gateway (IGW) là cửa nối VPC ra Internet. Một subnet trở thành public khi route table của nó có một dòng: "mọi địa chỉ ngoài VPC (0.0.0.0/0) thì đi qua Internet Gateway". Không có dòng đó, subnet là private.
Tóm lại chuỗi liên hệ: instance nằm trong subnet, subnet gắn route table, route table có (hoặc không có) đường ra qua Internet Gateway. Đó là cái khiến một máy "ra Internet được" hay không.
Vậy private subnet làm sao cập nhật phần mềm, tải gói cài đặt nếu không ra được Internet? Câu trả lời là NAT Gateway: cho phép resource trong private subnet đi ra Internet (để tải về) nhưng không cho Internet đi vào. NAT Gateway tính tiền theo giờ, nên ta không tạo trong series này. Cứ biết là nó tồn tại để giải bài toán đó.
Security Group: tường lửa của instance
Security Group (SG) là tường lửa gắn ở mức từng instance, quyết định lưu lượng nào được vào và ra. Ở Bài 2, khi ta mở port 22 và 80, ta đang thêm rule vào một Security Group.
Vài đặc điểm cần nắm:
- SG hoạt động theo kiểu chỉ cho phép (allow), không có rule chặn (deny). Nếu không có rule cho phép, lưu lượng bị chặn mặc định. Bạn liệt kê những gì được vào, phần còn lại tự động bị chặn.
- SG có trạng thái (stateful): nếu cho một kết nối đi vào, lưu lượng phản hồi tự động được đi ra, không cần thêm rule ra riêng.
- Mỗi rule gồm: protocol (TCP/UDP), port, và source (đối với inbound) — source có thể là một dải IP, hoặc một Security Group khác.
Điểm cuối rất hữu ích: source của một rule có thể là một Security Group khác thay vì dải IP. Ví dụ "chỉ instance thuộc SG của web server mới được kết nối tới database" — ta sẽ dùng đúng kiểu này ở Bài 5.
Một sai lầm phổ biến của người mới là để SSH (port 22) mở cho 0.0.0.0/0. Khi đó mọi địa chỉ trên Internet đều có thể thử đăng nhập, và các bot quét cổng sẽ liên tục dò mật khẩu. Quy tắc: SSH chỉ mở cho IP của bạn, còn các port phục vụ web (80, 443) thì mở rộng vì đó là mục đích của chúng.
Thực hành: tạo Security Group siết chặt và gắn vào instance
Ta tạo một Security Group bằng CLI cho rõ từng rule, rồi dùng nó khi tạo instance.
Trước hết lấy ID của VPC mặc định và IP công khai hiện tại của bạn:
# Lấy VPC id mặc định
VPC_ID=$(aws ec2 describe-vpcs --filters Name=isDefault,Values=true \
--query "Vpcs[0].VpcId" --output text)
echo $VPC_ID
# Lấy IP công khai hiện tại của máy bạn
MY_IP=$(curl -s https://checkip.amazonaws.com)
echo $MY_IP
Tạo Security Group:
SG_ID=$(aws ec2 create-security-group \
--group-name web-ssh-restricted \
--description "SSH chi cho IP cua toi, HTTP cho moi nguoi" \
--vpc-id $VPC_ID \
--query "GroupId" --output text)
echo $SG_ID
Thêm rule SSH chỉ cho IP của bạn (chú ý /32 nghĩa là đúng một địa chỉ đó):
aws ec2 authorize-security-group-ingress \
--group-id $SG_ID \
--protocol tcp --port 22 --cidr ${MY_IP}/32
Thêm rule HTTP cho mọi nơi:
aws ec2 authorize-security-group-ingress \
--group-id $SG_ID \
--protocol tcp --port 80 --cidr 0.0.0.0/0
Xem lại các rule vừa tạo:
aws ec2 describe-security-groups --group-ids $SG_ID \
--query "SecurityGroups[0].IpPermissions" --output json
Bạn sẽ thấy hai rule: port 22 giới hạn theo IP của bạn, port 80 mở rộng. Đây chính là cấu hình ta muốn cho một web server.
Để kiểm chứng, tạo nhanh một instance dùng đúng SG này (cần một subnet public — lấy subnet đầu tiên trong VPC mặc định):
SUBNET_ID=$(aws ec2 describe-subnets --filters Name=vpc-id,Values=$VPC_ID \
--query "Subnets[0].SubnetId" --output text)
# AMI Amazon Linux 2023 mới nhất (qua SSM Parameter Store)
AMI_ID=$(aws ssm get-parameters \
--names /aws/service/ami-amazon-linux-latest/al2023-ami-kernel-default-x86_64 \
--query "Parameters[0].Value" --output text)
aws ec2 run-instances \
--image-id $AMI_ID \
--instance-type t2.micro \
--key-name devops-key \
--security-group-ids $SG_ID \
--subnet-id $SUBNET_ID \
--associate-public-ip-address \
--tag-specifications 'ResourceType=instance,Tags=[{Key=Name,Value=vpc-test}]' \
--query "Instances[0].InstanceId" --output text
Chờ instance Running, lấy Public IP và thử SSH như Bài 2. Vì IP nhà có thể đổi theo thời gian, nếu sau này SSH không vào được, chạy lại lệnh authorize-security-group-ingress với IP mới.
🧹 Dọn dẹp
Bài này tạo một instance (tính tiền) và một Security Group (không tính tiền). Xóa instance trước, vì Security Group đang gắn vào instance thì không xóa được.
# Lấy id instance vp-test rồi terminate
INSTANCE_ID=$(aws ec2 describe-instances \
--filters Name=tag:Name,Values=vpc-test Name=instance-state-name,Values=running \
--query "Reservations[0].Instances[0].InstanceId" --output text)
aws ec2 terminate-instances --instance-ids $INSTANCE_ID
Chờ instance ở trạng thái terminated (kiểm tra bằng describe-instances như Bài 2), rồi xóa Security Group:
aws ec2 delete-security-group --group-id $SG_ID
Nếu lệnh xóa SG báo lỗi "resource in use", nghĩa là instance chưa terminate xong; chờ thêm rồi thử lại.
Kiểm tra lần cuối: không còn instance đang chạy, và SG web-ssh-restricted đã biến mất khỏi danh sách:
aws ec2 describe-security-groups \
--query "SecurityGroups[].GroupName" --output table
Tổng kết
Giờ bạn đã hiểu instance của mình sống ở đâu (VPC, subnet) và cái gì quyết định nó ra Internet được hay không (route table, Internet Gateway), cũng như cách Security Group kiểm soát truy cập. Quy tắc quan trọng nhất rút ra: SSH chỉ mở cho IP của bạn, và database nên nằm ở private subnet thay vì phơi ra Internet.
Bài 4 ta chuyển sang lưu trữ với S3: nơi cất file và host một trang web tĩnh mà không cần server nào chạy liên tục.