AWS Learning
Compute

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 ssh thự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-agent giữ key như thế nào, và vì sao -A có 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:

Full SSH flow qua Bastion gồm 2 TCP connections và 4 luồng traffic

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:

- Không có public IP
- Không mở port 22 ra Internet
- Chỉ nhận traffic nội bộ VPC (từ ALB, app tier, ...)

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.

Internet ──► Bastion (public) ──► 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.

Internet ──► AWS Proxy (SSM / EIC Endpoint) ──► Private EC2

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

┌─────────────────────────── VPC 10.0.0.0/16 ─────────────────────────────┐
│                                                                         │
│  ┌── Public subnet 10.0.1.0/24 ───┐    ┌── Private subnet 10.0.3.0/24 ─┐│
│  │                                │    │                               ││
│  │  ┌──────────────┐              │    │  ┌──────────────┐             ││
│  │  │ Bastion EC2  │              │    │  │ Private EC2  │             ││
│  │  │ 10.0.1.76    │══════════════┼────┼─►│ 10.0.3.57    │             ││
│  │  │ public IP    │              │local  │ no public IP │             ││
│  │  │ SG: 22 open  │              │route  │ SG: 22 only  │             ││
│  │  └──────────────┘              │    │  │ from bastion │             ││
│  │         ▲                      │    │  └──────────────┘             ││
│  └─────────┼──────────────────────┘    └───────────────────────────────┘│
│            │                                                            │
└────────────┼────────────────────────────────────────────────────────────┘

        ┌────┴────┐
        │ Internet│
        │ Gateway │
        └────┬────┘

        Admin laptop (public IP)

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:

ssh -i singapo.pem -J ec2-user@47.129.159.247 ec2-user@10.0.3.57

Thực tế có 2 TCP connections riêng biệt2 SSH handshakes riêng biệt:

Connection 1 (Laptop ↔ Bastion):
  - TCP: laptop:ephemeral ↔ 47.129.159.247:22
  - SSH: authenticate với bastion bằng key

Connection 2 (Bastion ↔ Private EC2):
  - TCP: 10.0.1.76:ephemeral ↔ 10.0.3.57:22
  - SSH: authenticate với private EC2 bằng key

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:

Full SSH flow qua Bastion gồm 2 TCP connections và 4 luồng traffic

2 TCP connections × 2 chiều = 4 luồng traffic

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ồngPacket mẫuĐi qua checkpoint nào
C1 Request (Laptop→Bastion)59.153.x.x:51544 → 47.129.159.247:22IGW → facing NACL IN(22) → bastion-SG IN(22) → Bastion
C1 Response (Bastion→Laptop)47.129.159.247:22 → 59.153.x.x:51544Bastion → bastion-SG OUT(auto) → facing NACL OUT(ephemeral) → IGW
C2 Request (Bastion→Private)10.0.1.76:45678 → 10.0.3.57:22bastion-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:45678service-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:

Bastion:45678  --->  Private EC2:22

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:

Private EC2:22  --->  Bastion:45678

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):

INBOUND  : TCP 22 from <admin-public-ip>/32     ← cho connection 1
OUTBOUND : TCP 22 to service-SG (sg-xxx)        ← cho connection 2 forward

service-SG (gắn vào Private EC2):

INBOUND  : TCP 22 from bastion-SG (sg-xxx)
OUTBOUND : (stateful, auto-allow return)

facing NACL (subnet public):

INBOUND  : TCP 22 from 0.0.0.0/0 (hoặc IP admin)            ← C1 req
           TCP 32768-65535 from 10.0.3.0/24                  ← C2 res (ephemeral!)
OUTBOUND : TCP 32768-65535 to <admin-public-ip>/32          ← C1 res (ephemeral!)
           TCP 22 to 10.0.3.0/24                            ← C2 req

service NACL (subnet private):

INBOUND  : TCP 22 from 10.0.1.0/24                          ← C2 req
OUTBOUND : TCP 32768-65535 to 10.0.1.0/24                   ← C2 res (ephemeral!)

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)

scp singapo.pem ec2-user@47.129.159.247:~/
ssh ec2-user@47.129.159.247
ssh -i singapo.pem ec2-user@10.0.3.57
  • Bastion giữ file key thật trên ổ cứng
  • Bastion bị hack → attacker copy .pemlộ 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)

ssh-add singapo.pem              # nạp key vào agent trên laptop
ssh -A ec2-user@47.129.159.247   # forward agent socket sang bastion
ssh ec2-user@10.0.3.57           # bastion mượn agent laptop để ký
  • Key file không trên bastion
  • Nhưng socket /tmp/ssh-XXXX/agent.NNN tồ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Ị)

ssh -i singapo.pem -J ec2-user@47.129.159.247 ec2-user@10.0.3.57
  • 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)

# Terminal 1: mở tunnel
ssh -L 2222:10.0.3.57:22 ec2-user@47.129.159.247
 
# Terminal 2: dùng tunnel
ssh -p 2222 ec2-user@localhost
  • Laptop mở port 2222 cục bộ, tunnel qua bastion tới 10.0.3.57:22
  • Tương tự -J như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 keyB: -AC: -JD: -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ộ keyVĩnh viễnChỉ trong sessionKhôngKhông
Số lệnh3312
Tốc độ authenticateNhanhChậm (round trip agent)NhanhNhanh
Đề xuấtKhôngHạn chếDefaultKhi 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.

ssh-agent (RAM)
  ├── singapo.pem        ← đã giải mã, sống đến khi logout/reboot
  ├── bastion-key.pem
  └── github-key.pem

4.2 Vòng đời key trong agent

Bật máy

ssh-agent khởi động (thường tự động)
  ↓ RAM trống
ssh-add singapo.pem
  ↓ gõ passphrase 1 LẦN DUY NHẤT
  ↓ key (đã giải mã) vào RAM agent

ssh ec2-user@...   ← không cần gõ passphrase nữa, agent ký hộ

Tắt máy / kill agent
  ↓ RAM bị xóa → key mất, phải ssh-add lại sau reboot

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ệnhTác dụng
ssh-agentKhởi động daemon
ssh-add key.pemNạp key vào agent
ssh-add -lXem agent đang giữ key nào
ssh-add -DXóa hết key khỏi agent
ssh -A hostForward agent socket sang remote
ssh -J jump targetDù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:

/tmp/agent.BASTION (trên bastion, thuộc quyền ec2-user)
       │ tunnel qua SSH connection

/tmp/agent.LAPTOP (trên laptop)


   ssh-agent (RAM laptop) → key

Bất kỳ process nào đọc được socket đó đều mượn được agent:

# Nếu attacker có root trên bastion trong lúc session còn sống:
SSH_AUTH_SOCK=/tmp/ssh-XXXX/agent.NNN ssh ec2-user@10.0.3.57   # mượn agent của BẠN
SSH_AUTH_SOCK=/tmp/.../agent.NNN git clone git@github.com:you/private-repo

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:

ssh -A ec2-user@bastion
git clone git@github.com:myorg/private-app.git   # bastion push/pull GitHub bằng key của bạn

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

Admin laptop                          AWS SSM service
   │                                       ▲
   │ aws ssm start-session                 │
   └───────────────────────────────────────┤

              Private EC2 (SSM Agent) ─────┘
              outbound HTTPS :443 tới SSM endpoint
  • 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ầuChi tiết
SSM AgentCó sẵn trên Amazon Linux 2/2023, Windows. Min version 2.3.68.0+ cho Session Manager
IAM role cho EC2attached instance profile với AmazonSSMManagedInstanceCore (hoặc policy ssmmessages:* inline)
NetworkOutbound 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

# Cài Session Manager plugin cho AWS CLI trước
# Shell tương tác:
aws ssm start-session --target i-0abc123def456
 
# Port forwarding tới service private (VD RDS):
aws ssm start-session \
  --target i-0abc123def456 \
  --document-name AWS-StartPortForwardingSessionToRemoteHost \
  --parameters '{"hostNumber":["10.0.3.57"],"portNumber":["3306"]}'

Hoặc dùng ssh quen thuộc với SSM làm transport (qua SSH ProxyCommand):

# ~/.ssh/config
Host i-*
  ProxyCommand sh -c "aws ssm start-session --target %h --document-name AWS-StartSSHSession --parameters 'portNumber=%p'"
 
# Rồi:
ssh i-0abc123def456

5.4 Ưu/nhược

✅ Ưu điểm❌ Nhược điểm
Không cần bastion, không mở port 22Cần SSM Agent + IAM role đúng
Auth bằng IAM, không cần manage SSH keyInstance phải reach SSM endpoint (NAT hoặc VPC endpoint)
CloudTrail log built-inKhô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 webIAM 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

Admin laptop
   │ WebSocket tunnel (auth bằng IAM credentials)

EC2 Instance Connect Endpoint  ← AWS-managed, đặt trong subnet của VPC
   │ private IP, không expose ra internet

Private EC2 (port 22 nội bộ VPC)
  • Tạo 1 endpoint trong VPC (chọn subnet + SG)
  • AWS push ephemeral SSH key (RSA, TTL 60 giây) vào authorized_keys củ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ầuChi tiết
EIC EndpointTạo bằng Console/CLI/Terraform trong 1 subnet của VPC
IAM permissionec2-instance-connect:OpenTunnel, ec2:DescribeInstances, ec2-instance-connect:SendSSHPublicKey
InstanceCó thể reach được từ endpoint (route table cho phép), có SG mở 22 cho endpoint
KHÔNG cầnBastion, public IP, IGW, SSM Agent

6.3 Cách dùng

# Tạo endpoint
aws ec2 create-instance-connect-endpoint \
  --subnet-id subnet-0abc123 \
  --security-group-id sg-0xyz789
 
# SSH qua endpoint (CLI tự push ephemeral key):
aws ec2-instance-connect ssh --instance-id i-0abc123def456
 
# Hoặc scp:
scp -o ProxyCommand="aws ec2-instance-connect open-tunnel --instance-id i-0abc123def456" \
    file.txt i-0abc123def456:/tmp/

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 IGWAWS only (không multi-cloud)
UX vẫn là ssh quen thuộcCần tạo endpoint trước (1 lần/VPC)
IAM auth + CloudTrail + ephemeral keyCross-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 ManagerEIC Endpoint
Cài agent trên instance (SSM Agent)Không
AuthIAMIAM + ephemeral SSH key
UXaws ssm start-sessionssh/scp quen thuộc
Outbound từ instanceHTTPS :443 tới SSMKhông cần (instance bị động)
Port forwarding tới host khác✓ (RDS, internal web)
Multi-cloudKhôngKhông
Phù hợp khiĐã có SSM Agent, muốn audit mạnhMuố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

Admin laptop ──VPN tunnel──► AWS Client VPN endpoint (ngang hàng VPC)

                                  ▼ authorize tới private subnet
                              Private EC2 (port 22 nội bộ)
  • 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ợpSetup phức tạp hơn SSM/EIC
Route cả subnet, không cần proxy từng hostClient 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.

ToolTính năng nổi bậtKhi 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 BoundaryIdentity-based access, không cần manage SSH key, dynamic credential, multi-cloudĐã dùng HashiCorp stack, multi-cloud
Tailscale SSHWireGuard mesh, SSH mà không cần SSH key (client cert từ Tailscale), zero-configTeam quen SSH, muốn zero-trust nhẹ cân
AWS Verified AccessAWS-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 + -JSSM Session ManagerEIC EndpointClient VPNTeleport/Boundary
Cần EC2 public✓ BastionTùy setup
Mở port 22 ra internet✓ (cho bastion)
Auth bằngSSH keyIAMIAM + ephemeral keyCert/SAMLShort-lived cert
Cài agentKhôngSSM AgentKhôngVPN clientAgent riêng
AuditTự setupCloudTrail built-inCloudTrail built-inConnection logSession recording
CostBastion chạy 24/7FreeFree (trừ cross-AZ)Endpoint per-hour + per-connectionLicense
Multi-cloud✗ AWS✗ AWS
UXssh quen thuộcaws ssmssh quen thuộcMọi tool nội bộtsh/boundary

9.2 Decision tree

Instance nằm 100% trong AWS?
├─ KHÔNG (hybrid/multi-cloud)
│   └─→ Dùng Bastion hardened (Teleport/Boundary) hoặc Client VPN
└─ CÓ
    ├─ Đã có SSM Agent + IAM role + (NAT hoặc VPC endpoint)?
    │   └─→ SSM Session Manager  ← default, audit mạnh nhất
    ├─ Muốn UX ssh thuần, không muốn cài agent?
    │   └─→ EC2 Instance Connect Endpoint
    ├─ Cả team cần vào nhiều resource cùng lúc?
    │   └─→ Client VPN
    └─ Lab / học fundamentals / interview?
        └─→ Bastion truyền thống + `-J`

9.3 Thực tế ngành 2024-2026

- Startup nhỏ / lab:           Raw EC2 bastion + ssh -J
- Scale-up AWS-native:         Chuyển sang SSM Session Manager
- Enterprise / regulated:      Teleport / Boundary / Client VPN
- Legacy / on-prem hybrid:     Vẫn bastion truyền thống, dần migrate

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:

- 2 bastion ở 2 AZ khác nhau
- Đặt trong Auto Scaling Group (min=2, max=2)
- Phía trước đặt NLB (Network Load Balancer) với listener :22
- DNS record (Route 53) trỏ tới NLB

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

□ SG inbound chỉ mở 22 cho IP admin cụ thể (KHÔNG 0.0.0.0/0)
□ Dùng SSH key Ed25519 (mạnh hơn RSA), có passphrase
□ Tắt password authentication: /etc/ssh/sshd_config -> PasswordAuthentication no
□ Tắt root login: PermitRootLogin no
□ Cài fail2ban hoặc AWS GuardDuty
□ Bật session recording (auditd, tlog, Teleport)
□ Bắt buộc MFA (PAM + Duo, hoặc Teleport/Boundary)
□ Patch định kỳ qua SSM Patch Manager
□ IAM instance profile least-privilege (không AmazonS3FullAccess)
□ CloudWatch Logs thu thump /var/log/secure
□ Auto-terminate sau N giờ không hoạt động (lambda + CloudWatch)

10.4 Session recording

ToolCách record
tlog (OSS)Record terminal session, lưu JSON log
TeleportBuilt-in session recording + playback
BoundarySession recording, có thể disable selective
AWS SSMs3BucketName / cloudWatchLogGroup trong Session preferences
auditd + syslogLog 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-J giống nhau về security

-J : bastion chỉ là tunnel, không có agent socket → an toàn
-A : bastion có agent socket → root mượn được trong session

→ 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

SAI: facing NACL IN : TCP 22 from 10.0.3.0/24
      → return traffic từ private EC2 có dst port ephemeral → bị DROP

ĐÚNG: facing NACL IN : TCP 32768-65535 from 10.0.3.0/24

NACL stateless → phải allow cả return traffic, và return có destination port = ephemeral.

Pitfall 3: Copy .pem lên bastion

Bastion bị hack 1 phút → scp .pem ra ngoài → lộ key vĩnh viễn

→ 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:

# ~/.ssh/config
Host bastion
  HostName 47.129.159.247
  IdentityFile ~/.ssh/bastion-key.pem

Host private-ec2
  HostName 10.0.3.57
  IdentityFile ~/.ssh/private-ec2-key.pem
  ProxyJump bastion

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):

ssm.<region>.amazonaws.com
ssmmessages.<region>.amazonaws.com
ec2messages.<region>.amazonaws.com

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

Private EC2 (không public IP)

    │ "Làm sao vào được?"
    ├─ Bastion truyền thống ─── ssh -J bastion ────► cần key, mở port 22 cho bastion
    ├─ SSM Session Manager ──── aws ssm ─────────► cần SSM Agent + IAM + (NAT hoặc VPC endpoint)
    ├─ EIC Endpoint ─────────── aws ec2-... ssh ──► cần endpoint trong VPC, IAM, ephemeral key
    ├─ Client VPN ───────────── VPN tunnel ───────► cần VPN endpoint + cert/SAML
    └─ Teleport/Boundary ────── tsh/boundary ────► cần tool riêng, RBAC + recording

1 dòng cho mỗi cách

Cách1 dòng tóm tắt
Bastion + -JTunnel TCP qua bastion, laptop tự auth, không lộ key
Bastion + -AForward agent socket sang bastion, root mượn được trong session
SSM Session ManagerKhông bastion, IAM auth, CloudTrail built-in, cần agent
EIC EndpointIdentity-aware TCP proxy, ssh UX thuần, ephemeral key, không cần agent
Client VPNCả team vào VPC như nội bộ, cần VPN client + endpoint
Teleport/BoundaryBastion hardened với RBAC, MFA, session recording

Lệnh nhanh

# Bastion -J (default cho lab)
ssh -i singapo.pem -J ec2-user@47.129.159.247 ec2-user@10.0.3.57
 
# Bastion -A (chỉ khi cần git từ bastion)
ssh-add singapo.pem
ssh -A ec2-user@47.129.159.247
 
# SSM Session Manager
aws ssm start-session --target i-0abc123def456
 
# EIC Endpoint
aws ec2-instance-connect ssh --instance-id i-0abc123def456
 
# Port forwarding tới RDS qua SSM
aws ssm start-session --target i-0abc123def456 \
  --document-name AWS-StartPortForwardingSessionToRemoteHost \
  --parameters '{"hostNumber":["10.0.3.57"],"portNumber":["3306"]}'

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?"-A forward agent socket → root trên bastion mượn được. -J chỉ 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


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.

On this page

Mục tiêu của doc nàyPhần 1: Vì sao cần "secure access" vào EC2Triết lý 1: Bastion Host (cổ điển)Triết lý 2: Bastion-less (hiện đại)Phần 2: Bastion Host truyền thống — mechanics sâu2.1 Kiến trúc tham chiếu2.2 Nhận diện sai lầm phổ biến: 1 lệnh ssh -J KHÔNG phải 1 TCP connection2.3 Trace packet chi tiết (xem diagram đi kèm)Vì sao C2 Response không về port 22 của Bastion?2.4 Rule set tối thiểu để 2 connection thôngPhần 3: SSH qua Bastion — 4 cách và vì sao khác nhauCách A: Copy key .pem lên bastion (❌ TỆ NHẤT)Cách B: -A Agent Forwarding (⚠️ Có rủi ro)Cách C: -J ProxyJump (✅ KHUYẾN NGHỊ)Cách D: -L Local Port Forwarding (thủ công)Bảng so sánh 4 cáchPhần 4: ssh-agent, ProxyJump và Agent Forwarding4.1 ssh-agent là gì4.2 Vòng đời key trong agent4.3 Lệnh thường dùng4.4 Vì sao -A nguy hiểm hơn -J4.5 Khi nào THỰC SỰ cần -APhần 5: AWS Systems Manager Session Manager5.1 Cách hoạt động5.2 Prerequisites5.3 Cách dùng5.4 Ưu/nhượcPhần 6: Amazon EC2 Instance Connect Endpoint6.1 Cách hoạt động6.2 Prerequisites6.3 Cách dùng6.4 Ưu/nhược6.5 So sánh SSM Session Manager vs EIC EndpointPhần 7: AWS Client VPN7.1 Cách hoạt động7.2 Khi nào dùng7.3 Ưu/nhượcPhần 8: Third-party hardened access (Teleport, Boundary, Tailscale)Phần 9: Decision matrix — chọn cách nào9.1 Bảng tổng hợp9.2 Decision tree9.3 Thực tế ngành 2024-2026Phần 10: Production patterns10.1 Bastion HA (High Availability)10.2 Bastion trong private subnet (pattern của AWS CDK blog)10.3 Hardening checklist10.4 Session recordingPhần 11: Common pitfallsPitfall 1: Nghĩ -A-J giống nhau về securityPitfall 2: NACL inbound của facing subnet chỉ mở port 22Pitfall 3: Copy .pem lên bastionPitfall 4: Quên rằng -J tạo 2 SSH session, mỗi session cần 1 key hợp lệPitfall 5: SSM không work vì thiếu VPC endpointPitfall 6: EIC Endpoint không reach instance vì route table/SGPhần 12: CheatsheetMối quan hệ giữa các khái niệm1 dòng cho mỗi cáchLệnh nhanhGợi ý khi gặp trong đề thi / phỏng vấnNguồn AWS chính thức