Bastion Host Deep Dive & Các cách thay thế truy cập EC2
Mechanics SSH sâu, ProxyJump vs Agent Forwarding, ssh-agent, SSM Session Manager, EC2 Instance Connect Endpoint, Client VPN, Teleport/Boundary, decision matrix
Mục tiêu của doc này
Doc này là phần deep dive của bastion-host.md. File kia giải thích "bastion là gì", file này trả lời các câu hỏi sâu hơn:
- 1 lệnh
sshthực sự tạo ra bao nhiêu TCP connection khi đi qua bastion? - Tại sao
-J(ProxyJump) an toàn hơn-A(Agent Forwarding)? ssh-agentgiữ key như thế nào, và vì sao-Acó rủi ro?- Trên production người ta có còn dùng bastion không, hay chuyển sang cái gì?
- Session Manager, EC2 Instance Connect Endpoint, Client VPN khác nhau thế nào, khi nào dùng cái nào?
Đi kèm doc này là diagram minh hoạ packet flow. Trên webapp, diagram được render trực tiếp từ file PNG đã export từ Excalidraw:

File nguồn để chỉnh sửa diagram:
Phần 1: Vì sao cần "secure access" vào EC2
Trong 3-tier VPC, các EC2 nằm ở private subnet thường:
Vậy khi admin cần SSH vào để debug/patch/cài đặt, làm sao truy cập được nếu không có public IP?
Có 2 triết lý trả lời câu hỏi này:
Triết lý 1: Bastion Host (cổ điển)
Đặt 1 EC2 ở public subnet, mở port 22 cho IP của admin. Admin SSH vào bastion, rồi từ bastion SSH tiếp vào private EC2.
Triết lý 2: Bastion-less (hiện đại)
Không cần bastion vật lý. Dùng dịch vụ của AWS làm "identity-aware proxy", auth bằng IAM thay vì SSH key, không mở port 22.
Phần 2–4 đi sâu vào triết lý 1. Phần 5–8 đi sâu vào triết lý 2 và các biến thể.
Phần 2: Bastion Host truyền thống — mechanics sâu
2.1 Kiến trúc tham chiếu
2.2 Nhận diện sai lầm phổ biến: 1 lệnh ssh -J KHÔNG phải 1 TCP connection
Khi bạn chạy:
Thực tế có 2 TCP connections riêng biệt và 2 SSH handshakes riêng biệt:
Hai connection này độc lập. Tắt một cái không tự tắt cái kia (nếu không dùng -J). Mỗi connection bị kiểm bởi bộ NACL/SG riêng của subnet chứa nó.
2.3 Trace packet chi tiết (xem diagram đi kèm)
Diagram dưới đây vẽ đầy đủ 4 luồng traffic chính:

Lưu ý: đây là 4 flow direction khi xét NACL/SG, không phải chỉ đúng 4 packet vật lý. Thực tế TCP/SSH sẽ có nhiều packet: SYN, SYN-ACK, ACK, SSH key exchange, encrypted data, keepalive, v.v.
| Luồng | Packet mẫu | Đi qua checkpoint nào |
|---|---|---|
| C1 Request (Laptop→Bastion) | 59.153.x.x:51544 → 47.129.159.247:22 | IGW → facing NACL IN(22) → bastion-SG IN(22) → Bastion |
| C1 Response (Bastion→Laptop) | 47.129.159.247:22 → 59.153.x.x:51544 | Bastion → bastion-SG OUT(auto) → facing NACL OUT(ephemeral) → IGW |
| C2 Request (Bastion→Private) | 10.0.1.76:45678 → 10.0.3.57:22 | bastion-SG OUT(22) → facing NACL OUT(22) → public RT → VPC router → service NACL IN(22) → service-SG IN(22) → Private EC2 |
| C2 Response (Private→Bastion) | 10.0.3.57:22 → 10.0.1.76:45678 | service-SG OUT(auto) → service NACL OUT(ephemeral) → VPC router → facing NACL IN(ephemeral) → bastion-SG IN(auto) → Bastion |
Vì sao C2 Response không về port 22 của Bastion?
Ở connection 2, Bastion là SSH client, private EC2 là SSH server:
Port 45678 là ephemeral port do Bastion tự chọn khi mở connection. Khi private EC2 trả lời, TCP packet sẽ đảo chiều source/destination:
Vì NACL là stateless, NACL không tự hiểu đây là response hợp lệ. Nó chỉ nhìn packet hiện tại. Do packet đi vào public/facing subnet có destination port = 45678, facing NACL inbound phải allow ephemeral range từ private subnet.
⚠️ Điểm dễ sai: NACL inbound của facing subnet phải mở ephemeral port (ví dụ 32768-65535 hoặc 1024-65535 tùy OS/client) cho return traffic từ private subnet, vì return packet có destination port = ephemeral của Bastion, không phải 22. Xem chi tiết ở
docs/diagrams/nacl-sg-step-by-step-packet-path.png.
2.4 Rule set tối thiểu để 2 connection thông
bastion-SG (gắn vào Bastion):
service-SG (gắn vào Private EC2):
facing NACL (subnet public):
service NACL (subnet private):
Nguồn: Custom network ACLs — VPC User Guide. Phần "Ephemeral ports" giải thích rõ vì sao range 32768-65535 (Amazon Linux) hoặc 1024-65535 (NAT/ELB).
Phần 3: SSH qua Bastion — 4 cách và vì sao khác nhau
Có 4 cách để từ laptop SSH tới private EC2 qua bastion. Tất cả đều tạo 2 TCP connections giống nhau, chỉ khác ở chỗ ai giữ key và ai terminate SSH session 2.
Cách A: Copy key .pem lên bastion (❌ TỆ NHẤT)
- Bastion giữ file key thật trên ổ cứng
- Bastion bị hack → attacker copy
.pem→ lộ key vĩnh viễn - Không bao giờ nên làm vậy ngoài lab
Cách B: -A Agent Forwarding (⚠️ Có rủi ro)
- Key file không trên bastion
- Nhưng socket
/tmp/ssh-XXXX/agent.NNNtồn tại trên bastion trong suốt session - Root trên bastion có thể mượn socket đó để ký challenge → login vào private EC2 (hoặc bất kỳ host nào key valid) mà không cần file key
Cách C: -J ProxyJump (✅ KHUYẾN NGHỊ)
- Bastion chỉ là raw TCP tunnel
- Laptop tự authenticate với private EC2 (handshake đi qua tunnel)
- Bastion chỉ thấy encrypted bytes, không có key, không có agent socket
- An toàn nhất trong 4 cách
Cách D: -L Local Port Forwarding (thủ công)
- Laptop mở port 2222 cục bộ, tunnel qua bastion tới
10.0.3.57:22 - Tương tự
-Jnhưng setup thủ công, 2 terminal - Hữu ích khi cần tunnel nhiều port cùng lúc (DB, JMX, web debug)
Bảng so sánh 4 cách
| Tiêu chí | A: copy key | B: -A | C: -J | D: -L |
|---|---|---|---|---|
| File key trên bastion | ✗ Có | ✗ Không | ✗ Không | ✗ Không |
| Agent socket trên bastion | — | ✓ Có (rủi ro) | ✗ Không | ✗ Không |
| Bastion bị hack → lộ key | Vĩnh viễn | Chỉ trong session | Không | Không |
| Số lệnh | 3 | 3 | 1 | 2 |
| Tốc độ authenticate | Nhanh | Chậm (round trip agent) | Nhanh | Nhanh |
| Đề xuất | Không | Hạn chế | Default | Khi cần multi-port |
Phần 4: ssh-agent, ProxyJump và Agent Forwarding
4.1 ssh-agent là gì
ssh-agent là một background process giữ private key (đã giải mã) trong RAM, ký challenge SSH khi được nhờ, không bao giờ đưa key ra ngoài.
4.2 Vòng đời key trong agent
Trừ khi dùng macOS Keychain (ssh-add --apple-use-keychain) hoặc GNOME Keyring để tự nạp lại sau reboot.
4.3 Lệnh thường dùng
| Lệnh | Tác dụng |
|---|---|
ssh-agent | Khởi động daemon |
ssh-add key.pem | Nạp key vào agent |
ssh-add -l | Xem agent đang giữ key nào |
ssh-add -D | Xóa hết key khỏi agent |
ssh -A host | Forward agent socket sang remote |
ssh -J jump target | Dùng bastion làm tunnel (an toàn hơn) |
4.4 Vì sao -A nguy hiểm hơn -J
-A tạo UNIX socket tunnel từ bastion về agent trên laptop:
Bất kỳ process nào đọc được socket đó đều mượn được agent:
Agent ký challenge vì nó không phân biệt ai request. Key không lộ, nhưng khả năng ký bị mượn trong suốt session.
-J không có rủi ro này vì không có agent socket nào trên bastion.
4.5 Khi nào THỰC SỰ cần -A
Chỉ dùng -A khi bastion cần tự authenticate với bên thứ 3 (GitHub, container registry,Artifact store...) mà không muốn copy key:
Mọi trường hợp SSH vào private EC2 khác → dùng -J.
Phần 5: AWS Systems Manager Session Manager
Đây là cách thay thế bastion phổ biến nhất trên AWS-native infra. KHÔNG cần bastion, KHÔNG cần mở port 22, KHÔNG cần SSH key.
5.1 Cách hoạt động
- EC2 chạy SSM Agent (Amazon Linux cài sẵn)
- Agent mở outbound HTTPS :443 tới SSM service (hoặc qua VPC endpoint)
- Admin auth bằng IAM credentials (không phải SSH key)
- Traffic end-to-end TLS, session data có thể log vào S3/CloudWatch Logs
- Mọi session được log vào CloudTrail
Nguồn: AWS blog "Toward a bastion-less world", Session Manager prerequisites
5.2 Prerequisites
| Yêu cầu | Chi tiết |
|---|---|
| SSM Agent | Có sẵn trên Amazon Linux 2/2023, Windows. Min version 2.3.68.0+ cho Session Manager |
| IAM role cho EC2 | attached instance profile với AmazonSSMManagedInstanceCore (hoặc policy ssmmessages:* inline) |
| Network | Outbound HTTPS :443 tới SSM endpoints, HOẶC SSM VPC endpoints (PrivateLink) nếu private subnet không có NAT |
| (Tuỳ chọn) KMS key | Để mã hóa session data |
| (Tuỳ chọn) S3/CloudWatch | Để log session output |
5.3 Cách dùng
Hoặc dùng ssh quen thuộc với SSM làm transport (qua SSH ProxyCommand):
5.4 Ưu/nhược
| ✅ Ưu điểm | ❌ Nhược điểm |
|---|---|
| Không cần bastion, không mở port 22 | Cần SSM Agent + IAM role đúng |
| Auth bằng IAM, không cần manage SSH key | Instance phải reach SSM endpoint (NAT hoặc VPC endpoint) |
| CloudTrail log built-in | Không phải tool nào cũng tương thích (scp hơi lắt lé) |
| Hỗ trợ port forwarding tới RDS/internal web | IAM quyền ssm:StartSession cần scope kỹ |
| Miễn phí (chỉ tính SSM API call) | Windows session management đôi khi chậm |
Phần 6: Amazon EC2 Instance Connect Endpoint
Ra mắt năm 2023, EIC Endpoint là identity-aware TCP proxy do AWS quản lý. Vẫn dùng ssh client quen thuộc nhưng không cần bastion, không cần public IP, không cần IGW cho VPC.
Nguồn: Connect using EC2 Instance Connect Endpoint, AWS Compute Blog — EIC Endpoint
6.1 Cách hoạt động
- Tạo 1 endpoint trong VPC (chọn subnet + SG)
- AWS push ephemeral SSH key (RSA, TTL 60 giây) vào
authorized_keyscủa instance - Admin SSH qua WebSocket tunnel, IAM auth và authorize trước khi traffic vào VPC
- Mọi attempt (thành công + thất bại) log vào CloudTrail
6.2 Prerequisites
| Yêu cầu | Chi tiết |
|---|---|
| EIC Endpoint | Tạo bằng Console/CLI/Terraform trong 1 subnet của VPC |
| IAM permission | ec2-instance-connect:OpenTunnel, ec2:DescribeInstances, ec2-instance-connect:SendSSHPublicKey |
| Instance | Có thể reach được từ endpoint (route table cho phép), có SG mở 22 cho endpoint |
| KHÔNG cần | Bastion, public IP, IGW, SSM Agent |
6.3 Cách dùng
Vẫn dùng OpenSSH client, PuTTY, WinSCP — tooling không đổi.
6.4 Ưu/nhược
| ✅ Ưu điểm | ❌ Nhược điểm |
|---|---|
| Không bastion, không public IP, không IGW | AWS only (không multi-cloud) |
UX vẫn là ssh quen thuộc | Cần tạo endpoint trước (1 lần/VPC) |
| IAM auth + CloudTrail + ephemeral key | Cross-AZ data transfer có phí |
| Miễn phí (trừ cross-AZ transfer) | Chỉ support SSH/RDP qua TCP, không phải mọi port |
6.5 So sánh SSM Session Manager vs EIC Endpoint
| SSM Session Manager | EIC Endpoint | |
|---|---|---|
| Cài agent trên instance | Có (SSM Agent) | Không |
| Auth | IAM | IAM + ephemeral SSH key |
| UX | aws ssm start-session | ssh/scp quen thuộc |
| Outbound từ instance | HTTPS :443 tới SSM | Không cần (instance bị động) |
| Port forwarding tới host khác | ✓ (RDS, internal web) | ✓ |
| Multi-cloud | Không | Không |
| Phù hợp khi | Đã có SSM Agent, muốn audit mạnh | Muốn UX ssh thuần, zero-agent |
Phần 7: AWS Client VPN
Khác bastion và SSM/EIC, Client VPN là mạng riêng ảo — client kết nối VPN rồi instance private "thấy" client như 1 IP nội bộ.
7.1 Cách hoạt động
- Cài AWS VPN client (hoặc OpenVPN client)
- Auth bằng mutual TLS certificate (ACM) hoặc SAML SSO (IAM Identity Center/Okta)
- Client nhận IP từ CIDR VPN, route tới subnet private
- Mỗi user có certificate riêng → audit được
7.2 Khi nào dùng
- Cả dev team cần truy cập nhiều resource nội bộ (DB, internal web, EC2) cùng lúc
- Cần SSO enterprise (SAML với Okta/Azure AD)
- Muốn một đường hầm "all traffic" thay vì từng port
7.3 Ưu/nhược
| ✅ | ❌ |
|---|---|
| Cả team vào 1 lần, dùng mọi tool nội bộ | Chi phí endpoint theo giờ + connection-hour |
| SAML SSO, MFA dễ tích hợp | Setup phức tạp hơn SSM/EIC |
| Route cả subnet, không cần proxy từng host | Client phải cài VPN client |
Phần 8: Third-party hardened access (Teleport, Boundary, Tailscale)
Khi enterprise cần nhiều hơn IAM audit (VD: session recording, RBAC fine-grained, MFA per-session), người ta dựng bastion hardened bằng tool chuyên dụng.
| Tool | Tính năng nổi bật | Khi nào dùng |
|---|---|---|
| Teleport (Gravitational) | RBAC bằng roles, session recording, short-lived certificate, access plugins (Slack/Jira approval) | Enterprise hybrid, cần approval workflow |
| HashiCorp Boundary | Identity-based access, không cần manage SSH key, dynamic credential, multi-cloud | Đã dùng HashiCorp stack, multi-cloud |
| Tailscale SSH | WireGuard mesh, SSH mà không cần SSH key (client cert từ Tailscale), zero-config | Team quen SSH, muốn zero-trust nhẹ cân |
| AWS Verified Access | AWS-native, verify device posture + identity trước khi cho vào app | Đã dùng AWS Directory/Identity Center |
Đặc điểm chung: thay SSH key dài hạn bằng short-lived certificate + RBAC + audit, từ đó giảm rủi ro "key lộ vĩnh viễn".
Phần 9: Decision matrix — chọn cách nào
9.1 Bảng tổng hợp
| Tiêu chí | Bastion + -J | SSM Session Manager | EIC Endpoint | Client VPN | Teleport/Boundary |
|---|---|---|---|---|---|
| Cần EC2 public | ✓ Bastion | ✗ | ✗ | ✗ | Tùy setup |
| Mở port 22 ra internet | ✓ (cho bastion) | ✗ | ✗ | ✗ | ✗ |
| Auth bằng | SSH key | IAM | IAM + ephemeral key | Cert/SAML | Short-lived cert |
| Cài agent | Không | SSM Agent | Không | VPN client | Agent riêng |
| Audit | Tự setup | CloudTrail built-in | CloudTrail built-in | Connection log | Session recording |
| Cost | Bastion chạy 24/7 | Free | Free (trừ cross-AZ) | Endpoint per-hour + per-connection | License |
| Multi-cloud | ✓ | ✗ AWS | ✗ AWS | ✓ | ✓ |
| UX | ssh quen thuộc | aws ssm | ssh quen thuộc | Mọi tool nội bộ | tsh/boundary |
9.2 Decision tree
9.3 Thực tế ngành 2024-2026
AWS Managed Services (AMS) cho enterprise dùng bastion trong Shared Services account, chỉ reach được qua VPN/Direct Connect, auth bằng Active Directory. Bastion không "chết", nó tiến hóa thành bastion hardened.
Phần 10: Production patterns
10.1 Bastion HA (High Availability)
Đừng để 1 bastion = single point of failure. Pattern:
Khi 1 bastion hỏng, ASG tự launch cái mới. Admin SSH tới DNS name thay vì IP cố định.
10.2 Bastion trong private subnet (pattern của AWS CDK blog)
"Deploy bastion hosts into private subnets rather than public ones, providing temporary and limited access... leveraging AWS Systems Manager Session Manager for secure connectivity." — AWS CDK blog
Đặt bastion trong private subnet, không có public IP, vào qua SSM. Rủi ro attack surface giảm mạnh.
10.3 Hardening checklist
10.4 Session recording
| Tool | Cách record |
|---|---|
tlog (OSS) | Record terminal session, lưu JSON log |
| Teleport | Built-in session recording + playback |
| Boundary | Session recording, có thể disable selective |
| AWS SSM | s3BucketName / cloudWatchLogGroup trong Session preferences |
| auditd + syslog | Log command, không record video terminal |
Compliance (SOC 2, HIPAA, PCI DSS) thường yêu cầu session recording cho mọi admin access.
Phần 11: Common pitfalls
Pitfall 1: Nghĩ -A và -J giống nhau về security
→ Default là -J, chỉ dùng -A khi bastion cần git push/pull.
Pitfall 2: NACL inbound của facing subnet chỉ mở port 22
NACL stateless → phải allow cả return traffic, và return có destination port = ephemeral.
Pitfall 3: Copy .pem lên bastion
→ Không bao giờ. Dùng -J hoặc -A.
Pitfall 4: Quên rằng -J tạo 2 SSH session, mỗi session cần 1 key hợp lệ
Nếu bastion và private EC2 dùng 2 keypair khác nhau, phải config cả 2:
Pitfall 5: SSM không work vì thiếu VPC endpoint
Private subnet không có NAT → SSM Agent không reach SSM service qua internet. Phải tạo SSM VPC endpoints (PrivateLink):
Pitfall 6: EIC Endpoint không reach instance vì route table/SG
Endpoint đặt ở subnet A, instance ở subnet B. Phải:
- Route table của subnet A cho phép tới subnet B (local route auto)
- SG của instance cho phép 22 từ SG của endpoint
Phần 12: Cheatsheet
Mối quan hệ giữa các khái niệm
1 dòng cho mỗi cách
| Cách | 1 dòng tóm tắt |
|---|---|
Bastion + -J | Tunnel TCP qua bastion, laptop tự auth, không lộ key |
Bastion + -A | Forward agent socket sang bastion, root mượn được trong session |
| SSM Session Manager | Không bastion, IAM auth, CloudTrail built-in, cần agent |
| EIC Endpoint | Identity-aware TCP proxy, ssh UX thuần, ephemeral key, không cần agent |
| Client VPN | Cả team vào VPC như nội bộ, cần VPN client + endpoint |
| Teleport/Boundary | Bastion hardened với RBAC, MFA, session recording |
Lệnh nhanh
Gợi ý khi gặp trong đề thi / phỏng vấn
- "How to securely access EC2 in private subnet?" → Bastion host, SSM Session Manager, EIC Endpoint, Client VPN. AWS hiện nay ưu tiên Session Manager vì không cần mở port 22.
- "Bastion vs NAT Gateway?" → Bastion = admin inbound SSH access. NAT Gateway = outbound Internet access cho private subnet. Khác hẳn mục đích.
- "SSM Agent failed to connect?" → Check IAM role (
AmazonSSMManagedInstanceCore), check outbound HTTPS :443 tới SSM endpoints, check SSM VPC endpoints nếu không có NAT. - "No public IP, no bastion, no NAT — how to SSH?" → EIC Endpoint hoặc SSM với VPC endpoints.
- "Why ProxyJump over Agent Forwarding?" →
-Aforward agent socket → root trên bastion mượn được.-Jchỉ tunnel, bastion không thấy key. - "Session recording cho compliance?" → Teleport, Boundary, hoặc SSM session logging vào S3/CloudWatch.
- "NACL inbound của facing subnet cần gì để SSH return traffic?" → Ephemeral port range (32768-65535 cho Amazon Linux) từ private subnet CIDR.
Nguồn AWS chính thức
- Custom network ACLs for your VPC — Ephemeral ports — giải thích vì sao range ephemeral khác nhau theo OS
- Security group rules — VPC User Guide — SG stateful, default outbound all
- Amazon EC2 security group connection tracking — vì sao SG stateful
- Connect using EC2 Instance Connect Endpoint — identity-aware TCP proxy
- AWS Compute Blog — EIC Endpoint (June 2023)
- Session Manager prerequisites — SSM Agent version, IAM, network
- AWS blog "Toward a bastion-less world" — Session Manager thay bastion
- AWS blog "Port Forwarding using SSM Session Manager"
- AWS blog "Deploy bastion hosts into private subnets with CDK"
- Prescriptive Guidance — Access a bastion host by using Session Manager and EC2 Instance Connect
Doc đi kèm diagram render trực tiếp từ /diagrams/vpc-ssh-full-flow-2-connections.png. File nguồn chỉnh sửa: docs/diagrams/vpc-ssh-full-flow-2-connections.excalidraw. Xem thêm bastion-host.md cho phần nền tảng.