Khi có nhiều VPS, việc mở SSH public trên từng máy làm tăng bề mặt tấn công và khiến quản lý IP, firewall, SSH key trở nên phức tạp. Mô hình bastion host tạo một điểm truy cập trung gian: máy quản trị SSH tới bastion, sau đó dùng ProxyJump để đi tiếp tới các VPS nội bộ. Bài viết này hướng dẫn cấu hình OpenSSH `-J`/`ProxyJump`, file `~/.ssh/config`, firewall và SSH key theo nguyên tắc least privilege, đồng thời lưu ý các sai lầm dễ khiến bạn tự khóa khỏi hạ tầng.
Khi chỉ có một VPS, mở SSH public và giới hạn bằng SSH key/firewall có thể vẫn dễ quản lý. Nhưng khi số máy tăng, mô hình “mỗi VPS mở port 22 ra Internet” bắt đầu tạo ra nhiều điểm cần bảo vệ.
Bastion host — còn gọi là jump host — là một máy trung gian duy nhất cho phép quản trị viên đi vào mạng server bên trong.
Mô hình:
Laptop quản trị
↓ SSH
Bastion
↓ SSH nội bộ
┌────┼────┐
web api db
OpenSSH hỗ trợ ProxyJump, giúp client kết nối tới host đích bằng cách SSH qua jump host trước rồi thiết lập TCP forwarding tới máy cuối.
Nếu bạn đã dùng mạng riêng giữa các VPS, bài Cài Tailscale trên VPS Ubuntu là lựa chọn khác hoặc có thể kết hợp với bastion tùy kiến trúc.
Bastion host giải quyết vấn đề gì?
Một thiết kế phổ biến:
- chỉ bastion có SSH public;
- web/db/app server chỉ cho SSH từ private network hoặc IP bastion;
- admin dùng một entry point;
- logging và access control tập trung hơn.
Lợi ích:
- giảm số port SSH public;
- dễ allowlist IP quản trị;
- dễ thu hồi quyền;
- dễ đặt MFA/SSO ở lớp ngoài nếu dùng công cụ hỗ trợ;
- topology nội bộ ít lộ hơn.
Nhưng bastion trở thành tài sản rất nhạy cảm. Nếu bastion bị compromise, attacker có thể tiến sâu vào hệ thống nếu quyền nội bộ quá rộng.
ProxyJump hoạt động thế nào?
OpenSSH hỗ trợ tham số:
ssh -J user@bastion user@target
-J là shortcut của ProxyJump.
Ví dụ:
ssh -J admin@203.0.113.10 ubuntu@10.10.0.21
Client sẽ:
- SSH tới bastion
203.0.113.10; - từ kết nối đó tạo TCP forward tới
10.10.0.21:22; - xác thực SSH với target.
Điểm quan trọng: bastion không nhất thiết phải chứa private key của target.
Không copy private key vào bastion
Một sai lầm phổ biến là:
laptop → bastion có private key → target
Nếu bastion bị chiếm, private key trên đó cũng bị lộ.
Với ProxyJump, private key có thể nằm trên laptop và client thực hiện xác thực end-to-end với target.
Ví dụ:
ssh -i ~/.ssh/id_ed25519 \
-J admin@203.0.113.10 \
ubuntu@10.10.0.21
Nếu bastion dùng key khác:
ssh -J bastion-admin@203.0.113.10 ubuntu@10.10.0.21
và cấu hình key riêng trong ~/.ssh/config.
Cấu hình ~/.ssh/config
Thay vì gõ lệnh dài:
Host bastion
HostName 203.0.113.10
User admin
IdentityFile ~/.ssh/id_ed25519_bastion
Host web01
HostName 10.10.0.21
User ubuntu
IdentityFile ~/.ssh/id_ed25519_prod
ProxyJump bastion
Host db01
HostName 10.10.0.31
User ubuntu
IdentityFile ~/.ssh/id_ed25519_prod
ProxyJump bastion
Sau đó:
ssh web01
ssh db01
OpenSSH đọc config theo thứ tự và dùng block Host phù hợp.
Nhiều jump host
OpenSSH hỗ trợ nhiều proxy nối tiếp bằng dấu phẩy.
Ví dụ:
ssh -J user1@jump1,user2@jump2 admin@target
Nhưng nhiều hop làm troubleshooting khó hơn. Chỉ dùng khi topology thực sự cần.
Chuẩn bị private network giữa bastion và target
Bastion phải route tới target.
Kiểm tra từ bastion:
ip route
ping -c 3 10.10.0.21
Ping có thể bị chặn nên quan trọng hơn là kiểm tra TCP:
nc -vz 10.10.0.21 22
Nếu không có nc, có thể dùng:
ssh -v ubuntu@10.10.0.21
Firewall trên target
Mục tiêu là chỉ cho phép SSH từ bastion/private subnet.
Ví dụ UFW, nếu bastion có private IP 10.10.0.10:
sudo ufw allow from 10.10.0.10 to any port 22 proto tcp
Sau khi test bastion thành công mới cân nhắc xóa rule SSH public cũ.
Xem rule:
sudo ufw status numbered
Luôn giữ phiên SSH hiện tại và có console/KVM/rescue mode trước khi chỉnh firewall.
Nếu cần nền tảng UFW, xem bài Cài UFW Firewall trên VPS Ubuntu.
Firewall trên bastion
Bastion là entry point nên cần chặt hơn.
Ví dụ:
- chỉ allow SSH từ IP văn phòng/VPN;
- không chạy dịch vụ không cần thiết;
- dùng SSH key;
- tắt password login sau khi test;
- cập nhật hệ thống;
- Fail2Ban nếu SSH vẫn public.
Tham khảo bài Dùng SSH key và tắt password trên VPS Ubuntu và Cài Fail2Ban trên VPS Ubuntu.
Tắt SSH public trên target như thế nào?
Đừng sửa sshd_config để bind sai interface trước khi test.
Cách ít rủi ro hơn thường là firewall:
- giữ SSH daemon listen bình thường;
- allow private IP bastion;
- test ProxyJump từ laptop;
- mở terminal mới và SSH lại;
- sau đó mới xóa public SSH rule.
Ví dụ kiểm tra listener:
sudo ss -tulnp | grep ':22'
Kiểm tra host key
Khi kết nối qua bastion, client vẫn xác minh host key của target.
Lần đầu:
The authenticity of host ... can't be established
Đừng gõ yes theo thói quen trên hệ thống quan trọng. Xác minh fingerprint qua console/provider hoặc kênh tin cậy.
Không dùng:
StrictHostKeyChecking no
như cấu hình mặc định để “đỡ lỗi”. Điều đó làm yếu chống man-in-the-middle.
SSH agent forwarding có cần không?
ProxyJump không yêu cầu agent forwarding cho use case thông thường.
Tránh bật:
ForwardAgent yes
trừ khi bạn hiểu rủi ro và thực sự cần SSH tiếp từ shell trên bastion.
Agent forwarding cho phép process có quyền phù hợp trên bastion sử dụng agent socket để yêu cầu ký. Nó không copy private key file, nhưng vẫn mở thêm bề mặt tấn công.
ProxyJump trực tiếp từ client thường an toàn và đơn giản hơn.
Dùng ssh -v để debug
Khi lỗi:
ssh -vvv web01
Log debug giúp xác định:
- key nào được thử;
- host config nào được áp dụng;
- kết nối tới bastion có thành công không;
- forwarding tới target có lỗi không;
- target từ chối auth hay network.
Không paste full debug log công khai nếu có hostname/IP nhạy cảm.
SCP qua bastion
Với config ProxyJump trong ~/.ssh/config, scp cũng có thể dùng host alias:
scp app.tar.gz web01:/tmp/
Hoặc command line:
scp -o ProxyJump=admin@203.0.113.10 app.tar.gz ubuntu@10.10.0.21:/tmp/
Rsync qua bastion
rsync -av -e 'ssh -J admin@203.0.113.10' ./build/ ubuntu@10.10.0.21:/var/www/app/
Với automation production, CI/CD nên dùng deploy user riêng thay vì key cá nhân.
Xem bài Deploy VPS bằng GitHub Actions.
Ansible qua bastion
Ansible dùng OpenSSH nên có thể tận dụng ProxyJump.
Ví dụ inventory/group var có thể cấu hình SSH common args phù hợp, hoặc đơn giản hơn dùng ~/.ssh/config trên control node.
Nếu bạn quản lý nhiều VPS, xem Quản lý nhiều VPS bằng Ansible.
Có nên dùng bastion hay Tailscale?
Bastion phù hợp khi
- muốn một entry point rõ ràng;
- target nằm trong private subnet;
- sử dụng OpenSSH thuần;
- cần audit/central control theo jump host.
Tailscale phù hợp khi
- muốn mesh network giữa nhiều thiết bị;
- admin cần truy cập nhiều service private;
- muốn identity-based network access;
- không muốn phụ thuộc một jump host duy nhất.
Có thể kết hợp: bastion chỉ reachable qua Tailscale.
Bastion có phải single point of failure không?
Có thể.
Nếu chỉ có một bastion và nó down, admin mất đường SSH dù ứng dụng vẫn chạy.
Với hạ tầng quan trọng, cân nhắc:
- console out-of-band;
- hai bastion ở failure domain khác nhau;
- private VPN path dự phòng;
- automation không phụ thuộc một host duy nhất.
Đừng mở lại SSH public trên toàn fleet chỉ để “dự phòng” mà không có kiểm soát.
Logging trên bastion
Nên theo dõi:
sudo journalctl -u ssh --since '24 hours ago'
hoặc log auth tùy distro.
Centralized logging giúp bạn tìm:
- ai đăng nhập;
- từ IP nào;
- auth fail tăng bất thường;
- thời điểm bastion bị scan.
Có thể xem bài Thu thập log tập trung bằng Grafana Loki và Alloy.
Hardening bastion
Checklist tối thiểu:
- SSH key only;
- disable root SSH trực tiếp;
- allowlist source IP/VPN nếu có thể;
- package update định kỳ;
- không chạy Docker/app/database trên bastion nếu không cần;
- filesystem/log monitoring;
- MFA thông qua lớp truy cập ngoài nếu kiến trúc hỗ trợ;
- key rotation;
- backup config;
- console recovery.
Sai lầm thường gặp
Copy private key production vào bastion
Nếu bastion bị compromise, attacker lấy được key.
Mở bastion SSH cho toàn Internet nhưng không harden
Bạn chỉ chuyển bề mặt tấn công từ nhiều máy về một máy cực kỳ quan trọng.
Bật agent forwarding mặc định
Không cần với ProxyJump và tăng rủi ro.
Xóa public SSH target trước khi test
Có thể tự khóa khỏi server.
Bastion dùng chung account root
Khó audit và tăng blast radius.
Quy trình triển khai an toàn
- tạo bastion;
- cấu hình SSH key;
- thiết lập private network;
- test SSH bastion → target;
- test
ssh -Jtừ laptop; - thêm
~/.ssh/config; - allow SSH target chỉ từ bastion;
- test lại từ terminal mới;
- mới xóa rule SSH public target;
- thiết lập monitoring/logging.
FAQ
ProxyJump có copy private key lên bastion không?
Không. OpenSSH client có thể dùng key trên máy local để xác thực target qua kết nối proxy.
Bastion có cần public IP không?
Không bắt buộc. Bastion có thể chỉ reachable qua VPN/Tailscale hoặc corporate network.
Có thể dùng nhiều bastion không?
Có. ProxyJump hỗ trợ nhiều jump host nối tiếp, nhưng topology phức tạp hơn.
Bastion có thay firewall không?
Không. Target vẫn nên firewall chỉ cho nguồn cần thiết; bastion cũng cần firewall riêng.
Có nên dùng agent forwarding?
Không mặc định. ProxyJump thường không cần. Chỉ bật khi use case thực sự yêu cầu và hiểu rủi ro.
Kết luận
SSH bastion + ProxyJump là cách đơn giản để giảm số VPS phải mở SSH trực tiếp ra Internet. Bạn vẫn dùng OpenSSH quen thuộc nhưng gom quyền truy cập về một entry point có thể kiểm soát và giám sát tốt hơn.
Điểm quan trọng nhất là không biến bastion thành “server chứa mọi private key”. Hãy giữ key ở client, giới hạn firewall, dùng account riêng và luôn có console recovery trước khi đóng public SSH trên target.
Nếu bạn đang xây dựng cụm nhiều VPS cho web, API hoặc database, có thể tham khảo các dịch vụ VPS tại HUONG CLOUD và thiết kế private network/access model phù hợp ngay từ đầu.
Nguồn tham khảo
- OpenBSD Manual — ssh_config / ProxyJump: https://man.openbsd.org/OpenBSD-current/man/ssh_config
- OpenBSD Manual — ssh / -J: https://man.openbsd.org/ssh
Kết luận
Nếu chưa chắc cấu hình hoặc loại dịch vụ nào phù hợp, hãy gửi nhu cầu thực tế để đội ngũ hỗ trợ tư vấn trước khi mua.
Chưa biết nên chọn gói nào?
Xem bảng giá VPS hoặc liên hệ đội ngũ hỗ trợ để chọn cấu hình phù hợp.
Xem bảng giá VPS →