Chuyện xảy ra lúc 2 giờ sáng
Khoảng 6 tháng trước, mình nhận được chuỗi alert từ Fail2ban — server bị brute-force SSH với tần suất chưa từng thấy. Bật laptop lúc 2 giờ sáng, remote vào xử lý, chặn IP, kiểm tra log từng dòng xem có dấu hiệu gì bị xâm nhập không. May mắn là không ai vào được. Nhưng cái cảm giác ngồi kiểm tra từng file lúc nửa đêm… khó chịu không tả được. Từ đó mình học được bài học đắt giá: setup bảo mật ngay từ đầu, đừng đợi đến khi có chuyện.
Sau hôm đó mình ngồi nghĩ lại: giả sử attacker vào được với quyền root thì sao? Private key của mình nằm ở ~/.ssh/id_rsa — dù có passphrase nhưng file vẫn ở đó, trên disk, có thể copy đi bất cứ lúc nào. Một khi file key bị lấy về, attacker có thể crack offline không giới hạn thời gian, không cần kết nối gì đến server của mình nữa.
Vấn đề cốt lõi: SSH Key lưu trên disk là điểm yếu chết người
Cách lưu SSH key thông thường có một lỗ hổng cơ bản: private key là một file trên disk. Dù bạn đặt permission 600, dù có passphrase mạnh, thì với quyền root, attacker vẫn có thể:
- Copy file
~/.ssh/id_rsavề máy và crack offline bằnghashcat— với GPU RTX 4090, passphrase yếu có thể sụp trong vài phút - Dump memory của process
ssh-agentkhi key đang được load (key nằm plaintext trong RAM) - Hook vào OpenSSH shared library để intercept key lúc đang ký
- Đọc key qua
/proc/<pid>/memvới quyền root
Không phải vì passphrase yếu hay permission sai. Điểm chết là private key có thể di chuyển ra khỏi server. File copy được, memory dump được — không có passphrase nào vá được lỗ hổng này.
Các cách giải quyết
Cách 1: Hardware Security Key (YubiKey, Nitrokey)
YubiKey lưu private key bên trong chip, không thể extract. Mỗi lần SSH cần cắm vật lý vào máy. Giá tầm $50–60 cho YubiKey 5 Series, Nitrokey rẻ hơn một chút (~$35–45). Cách này hợp với laptop cá nhân, nhưng với server headless chạy 24/7 thì không thực tế — bạn không thể cắm YubiKey vào VPS đang nằm ở data center.
Cách 2: SSH Certificate Authority với TTL ngắn
Dùng CA ký certificate có thời hạn ngắn (ví dụ 8 giờ). Key bị lộ cũng chỉ dùng được trong window đó. Nhược điểm là cần infrastructure khá phức tạp — HashiCorp Vault SSH Secrets Engine hoặc tự build CA — và key vẫn tồn tại dưới dạng file trong khoảng thời gian đó.
Cách 3: TPM 2.0 — Chip tích hợp sẵn, không cần mua thêm phần cứng
TPM (Trusted Platform Module) là chip bảo mật tích hợp sẵn trên hầu hết server và laptop ra từ 2016 trở đi. Chip này tạo và lưu key pair bên trong phần cứng — private key không bao giờ rời khỏi chip dù attacker có quyền root, dù dump toàn bộ RAM hay ổ đĩa.
Cách tốt nhất: SSH Key với TPM 2.0 bằng tpm2-pkcs11
Bước 1: Kiểm tra server có TPM 2.0 không
# Kiểm tra TPM device có tồn tại không
ls /dev/tpm*
# Hoặc kiểm tra version
cat /sys/class/tpm/tpm0/tpm_version_major
# Output "2" = TPM 2.0
# Cài các gói cần thiết (Ubuntu/Debian)
sudo apt install tpm2-tools tpm2-abrmd tpm2-pkcs11 tpm2-pkcs11-tools
# Khởi động TPM resource manager daemon
sudo systemctl enable --now tpm2-abrmd
Thấy /dev/tpm0 hoặc /dev/tpmrm0 là server có TPM. Phần lớn VPS thông thường không có TPM hardware, nhưng một số cloud provider đã hỗ trợ vTPM — AWS Nitro, Google Cloud Confidential VMs, Azure Gen 2 VMs.
Bước 2: Khởi tạo PKCS#11 token store trong TPM
Thư viện tpm2-pkcs11 cho phép OpenSSH giao tiếp với TPM thông qua PKCS#11 — chuẩn interface phổ biến cho hardware security module.
# Khởi tạo store
tpm2_ptool init
# Tạo token với tên label và hai loại PIN:
# sopin = Security Officer PIN (dùng để reset userpin)
# userpin = PIN dùng hàng ngày khi SSH
tpm2_ptool addtoken \
--pid=1 \
--label=ssh-keys \
--sopin=SuperAdminPin123 \
--userpin=MyUserPin456
Bước 3: Tạo SSH Key trong TPM
# Tạo EC key P-256 (khuyến nghị — nhỏ, nhanh, TPM 2.0 hỗ trợ tốt)
tpm2_ptool addkey \
--label=ssh-keys \
--userpin=MyUserPin456 \
--algorithm=ecc256
# Hoặc RSA 2048 nếu cần tương thích với hệ thống cũ
tpm2_ptool addkey \
--label=ssh-keys \
--userpin=MyUserPin456 \
--algorithm=rsa2048
# Kiểm tra key đã tạo trong TPM
tpm2_ptool listobjects --label=ssh-keys
Bước 4: Lấy Public Key và đưa lên server
# Lấy public key từ TPM để copy lên server đích
ssh-keygen -D /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so
# Output dạng:
# ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAI...
# Copy thẳng lên server đích
ssh-keygen -D /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so | \
ssh user@remote-server "cat >> ~/.ssh/authorized_keys"
Cấu hình ~/.ssh/config để SSH tự động dùng TPM key:
Host production-server
HostName your-server.com
User ubuntu
PKCS11Provider /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so
Bước 5: Kết nối SSH qua TPM
# SSH sẽ hỏi PIN thay vì passphrase
ssh production-server
# Enter PIN for 'ssh-keys': ****
# Hoặc chỉ định PKCS11 provider trực tiếp
ssh -I /usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so user@remote-server
Kiểm chứng: Key thực sự không thể bị copy không?
Mình tự test để chắc chắn. Với quyền root, thử mọi cách tìm private key material:
# Tìm toàn bộ private key files trên hệ thống
sudo find / -name "*.pem" -o -name "id_rsa" -o -name "id_ecdsa" 2>/dev/null
# → Không có file private key nào liên quan đến TPM key
# Xem objects trong TPM
tpm2_ptool listobjects --label=ssh-keys
# Chỉ thấy metadata: CKA_ID, CKA_CLASS, CKA_LABEL...
# Không có raw private key material
# Không tồn tại lệnh export private key trong tpm2_ptool
# Không phải do thiếu permission — TPM hardware đơn giản không
# cung cấp interface này theo thiết kế
Cơ chế thực tế: thay vì load private key vào RAM rồi ký, OpenSSH gửi request “hãy ký data này” vào TPM chip. TPM thực hiện signing bên trong chip rồi trả về chữ ký. Private key không bao giờ rời khỏi chip dưới dạng plaintext — kể cả kernel cũng không đọc được.
Những điều học được sau 6 tháng dùng thực tế
- Backup PIN ngay lập tức: Lưu cả userpin lẫn sopin vào password manager ngay sau khi tạo. Quên cả hai thì key mất vĩnh viễn, không có cách recovery nào.
- Reinstall OS làm mất token store: TPM bị clear khi reinstall một số distro. Phải tạo lại key pair và update authorized_keys ở tất cả server. Ghi lại quy trình để làm lại nhanh.
- Path thư viện khác nhau theo distro: Ubuntu dùng
/usr/lib/x86_64-linux-gnu/libtpm2_pkcs11.so, Arch Linux dùng/usr/lib/libtpm2_pkcs11.so, Fedora dùng/usr/lib64/pkcs11/libtpm2_pkcs11.so. Tìm bằng:find /usr -name "libtpm2_pkcs11.so" - Hiệu năng không đáng lo: Signing qua TPM chậm hơn ~50–100ms so với software key. Trong thực tế SSH connection setup thêm chút thời gian nhưng không cảm nhận được.
- VPS không có TPM: Phần lớn VPS thông thường không có TPM hardware. Lúc này cân nhắc SSH Certificate với TTL ngắn hoặc YubiKey nếu SSH từ máy cá nhân.
Kết hợp các lớp bảo mật
TPM key bịt một attack vector cụ thể: attacker có root nhưng không thể lấy private key để lateral movement sang server khác trong infrastructure. Còn đây là những thứ mình đang chạy song song:
- SSH key lưu trong TPM — root cũng không extract được
- Fail2ban chặn brute-force theo IP
- Đổi port SSH khỏi 22 để giảm noise trong log
AllowUserstrong/etc/ssh/sshd_configgiới hạn user được phép SSH- Tắt
PasswordAuthentication, chỉ cho phép key auth
Từ cái đêm brute-force lúc 2 giờ sáng đó, mình không bao giờ để private key trần trên disk nữa. TPM 2.0 có sẵn trên phần lớn phần cứng hiện đại — setup một lần, dùng mãi, và root cũng không thể lấy key đi được.

