Vấn đề: Quá nhiều Key, Quá nhiều Lần Nhập Mật khẩu
Nếu bạn đã từng quản lý hơn hai server Linux, bạn sẽ hiểu nỗi khổ này. Một SSH key cho GitHub, một key cho production, một key cho staging, rồi có thể thêm một key cho VPS của khách hàng. Mỗi lần kết nối lại phải nhập passphrase — hoặc tệ hơn, bạn dùng chung một key cho tất cả mọi nơi và cứ mong là không có gì xảy ra.
Mỗi kỹ sư xử lý vấn đề này theo cách khác nhau. Các cách tiếp cận khá đa dạng đến mức nếu chọn bừa, bạn sẽ phải trả giá sau này.
Bốn Cách Quản lý Nhiều SSH Key
Cách 1: Một Key cho Tất cả
Cách đơn giản nhất — một cặp key dùng cho tất cả server và dịch vụ. Hầu hết mọi người bắt đầu từ đây vì không cần cấu hình gì cả.
Cách 2: Key Riêng Từng Host với SSH Config
Tạo cặp key riêng cho từng server hoặc dịch vụ, rồi chỉ định key nào dùng cho host nào trong ~/.ssh/config:
Host github.com
IdentityFile ~/.ssh/github_ed25519
Host production
HostName 203.0.113.10
User ubuntu
IdentityFile ~/.ssh/prod_ed25519
Cách 3: ssh-agent với Key Loading
Khởi động một tiến trình ssh-agent, nạp các key vào đó một lần (chỉ nhập passphrase một lần duy nhất), và SSH tự động tái sử dụng chúng cho suốt phiên làm việc.
Cách 4: ssh-agent + SSH Key Forwarding
Cách cấu hình mạnh nhất. Agent của bạn chạy trên máy local. Khi bạn SSH vào bastion hoặc jump host, kết nối chuyển tiếp socket agent local sang máy đó — cho phép bạn xác thực đến tận các server nội bộ mà private key không bao giờ chạm vào ổ đĩa remote.
Ưu và Nhược Điểm
Cách 1: Một Key cho Tất cả
- Ưu: Không cần cấu hình, dùng được ngay
- Nhược: Một key bị lộ là mất quyền truy cập vào tất cả server
- Nhược: Nếu bỏ passphrase cho tiện, private key trên ổ đĩa không được bảo vệ
Cách 2: Key Riêng Từng Host với SSH Config
- Ưu: Cách ly tốt hơn — một key bị lộ, các key khác vẫn an toàn
- Ưu: Config rõ ràng, dễ đọc, tự ghi chú key nào dùng ở đâu
- Nhược: Vẫn phải nhập lại passphrase mỗi phiên, cho từng key
- Nhược: Vô dụng khi cần nhảy từ server A sang server B mà không muốn sao chép private key lên A
Cách 3: Chỉ ssh-agent
- Ưu: Nhập passphrase một lần mỗi phiên thay vì mỗi lần kết nối
- Ưu: Private key vẫn được mã hóa trên ổ đĩa — agent chỉ giữ chúng trong bộ nhớ
- Nhược: Agent mất khi đóng terminal trừ khi bạn cấu hình persistence
- Nhược: Vẫn không thể kết nối chuỗi server mà không đặt key lên máy trung gian
Cách 4: ssh-agent + SSH Key Forwarding
- Ưu: Private key không bao giờ rời khỏi máy local, kể cả kết nối nhiều chặng
- Ưu: Một terminal, cả chuỗi: laptop → bastion → server nội bộ → database
- Nhược: Bastion host phải đáng tin cậy — root bị chiếm trên đó có thể tạm thời lợi dụng socket được chuyển tiếp
- Nhược: Cần cài đặt ban đầu nhiều hơn so với các cách khác
Cấu hình Được Khuyến nghị
Với hầu hết kỹ sư quản lý một vài server, Cách 4 xứng đáng để bỏ thời gian cài đặt. Key riêng từng host nạp vào ssh-agent, với forwarding chỉ bật trên các bastion host cụ thể mà bạn kiểm soát.
Giữ Cách 2 (SSH config với IdentityFile) làm nền tảng. Chồng ssh-agent lên trên để tiện nhập passphrase. Chỉ bật agent forwarding khi thực sự cần — không bao giờ bật toàn cục.
Trên server Ubuntu 22.04 production của tôi (4GB RAM), sự khác biệt với Ansible là rõ ngay lập tức. Các playbook kết nối đến 10+ node nội bộ qua một bastion duy nhất trước đây liên tục hỏi passphrase từng host — hoặc phải để key không mã hóa ngay trên bastion. Với agent forwarding, cả playbook chạy hoàn toàn tự động. Private key không bao giờ chạm vào filesystem remote.
Hướng dẫn Triển khai
Bước 1: Tạo Key Riêng Theo Mục đích
Dùng Ed25519 — nhanh hơn và mạnh hơn về mật mã so với RSA trong hầu hết trường hợp:
# Key cho GitHub
ssh-keygen -t ed25519 -C "github" -f ~/.ssh/github_ed25519
# Key cho server production
ssh-keygen -t ed25519 -C "prod-servers" -f ~/.ssh/prod_ed25519
# Key cho VPS của khách hàng
ssh-keygen -t ed25519 -C "client-vps" -f ~/.ssh/client_ed25519
Luôn đặt passphrase mạnh khi được hỏi. Không có passphrase, file key trên ổ đĩa chỉ là một file text chờ bị đánh cắp.
Bước 2: Khởi động ssh-agent
Desktop GNOME và KDE thường tự khởi động SSH agent khi bắt đầu phiên làm việc. Kiểm tra trước:
echo $SSH_AUTH_SOCK
# Nếu có agent, sẽ in ra đường dẫn như: /run/user/1000/keyring/ssh
Nếu biến rỗng, khởi động thủ công:
eval "$(ssh-agent -s)"
# Agent pid 12345
Để tự khởi động trong mỗi phiên shell, thêm đoạn này vào ~/.bashrc hoặc ~/.zshrc:
if [ -z "$SSH_AUTH_SOCK" ]; then
eval "$(ssh-agent -s)"
fi
Bước 3: Thêm Key vào Agent
ssh-add ~/.ssh/github_ed25519
# Nhập passphrase: (nhập một lần — xong cho cả phiên)
ssh-add ~/.ssh/prod_ed25519
ssh-add ~/.ssh/client_ed25519
# Xác nhận các key đã nạp
ssh-add -l
# 256 SHA256:abc123... github (ED25519)
# 256 SHA256:def456... prod-servers (ED25519)
Trên máy dùng chung hoặc ít tin cậy, hãy đặt giới hạn thời gian. Key tự xóa khi hết hạn:
# Tự xóa sau 4 giờ (14400 giây)
ssh-add -t 14400 ~/.ssh/prod_ed25519
Bước 4: Cấu hình ~/.ssh/config
Gán key cho từng host và chỉ bật forwarding trên các bastion host đáng tin cậy:
Host github.com
IdentityFile ~/.ssh/github_ed25519
AddKeysToAgent yes
Host bastion
HostName 203.0.113.5
User ubuntu
IdentityFile ~/.ssh/prod_ed25519
ForwardAgent yes # Chỉ bật cho host đáng tin cậy này
AddKeysToAgent yes
Host internal-db
HostName 10.0.1.20
User ubuntu
ProxyJump bastion # Kết nối qua bastion — không cần IdentityFile ở đây
Host client-vps
HostName 198.51.100.10
User root
IdentityFile ~/.ssh/client_ed25519
AddKeysToAgent yes
# ForwardAgent cố ý bỏ qua — không forward đến host bên ngoài
Bước 5: Kiểm tra Chuỗi Kết nối
Với cấu hình này, kết nối đến database nội bộ từ laptop của bạn chỉ cần một lệnh duy nhất:
ssh internal-db
SSH thực sự làm những bước sau:
- Kết nối đến bastion bằng prod key local của bạn qua agent được chuyển tiếp
- Mở kết nối thứ hai từ bastion đến
10.0.1.20 - Server nội bộ xác thực với agent local được chuyển tiếp của bạn
- File private key không bao giờ chạm vào ổ đĩa của bastion
Để xác nhận forwarding đang hoạt động ở đầu remote:
# Sau khi kết nối vào bastion:
echo $SSH_AUTH_SOCK
# /tmp/ssh-XXXXXX/agent.NNNN ← socket được chuyển tiếp đã có mặt
ssh-add -l
# Liệt kê các key từ máy local của bạn
Bước 6: Duy trì Agent Qua Các Phiên Terminal
Một điều bất tiện còn lại: đóng terminal và agent mất, kéo theo tất cả key đã nạp. Tiện ích keychain giải quyết vấn đề này bằng cách giữ một agent duy nhất sống qua các phiên làm việc:
sudo apt install keychain
# Thêm vào ~/.bashrc:
eval "$(keychain --eval --quiet ~/.ssh/prod_ed25519 ~/.ssh/github_ed25519)"
Lần đăng nhập đầu tiên sau khi khởi động lại máy, bạn nhập passphrase một lần. Mọi terminal mở sau đó — tab mới, kết nối lại, pane tmux — đều tái sử dụng agent đang chạy. Không còn phải nhập passphrase nữa.
Checklist Bảo mật Trước khi Triển khai Thực tế
- Chỉ đặt
ForwardAgent yestrên các bastion host bạn hoàn toàn kiểm soát - Không bao giờ thêm
ForwardAgent yesvào blockHost *(áp dụng cho tất cả) - Dùng
ssh-add -t <giây>cho key nạp trên máy dùng chung - Kiểm tra những gì đang được nạp bằng
ssh-add -ltrước các thao tác nhạy cảm - Giới hạn quyền truy cập
/tmptrên bastion và tắt SSH login cho root
Rủi ro thực sự của agent forwarding không phải là key bị sao chép — byte private key của bạn thực sự vẫn ở máy local. Điểm dễ bị khai thác là bản thân socket đó. Một root user trên bastion có thể tạm thời dùng socket đó trong khi phiên SSH của bạn đang mở. Hãy đối xử với bastion host nghiêm túc như với chính laptop của bạn.
Cài đặt một lần và việc xác thực không còn là thứ bạn phải bận tâm nữa. Gõ ssh internal-db là vào được ngay. Key của bạn ở đúng chỗ của nó — trên máy local.

