Bảo Mật SSH Toàn Diện Trên Linux: OpenSSH 10, Mã Hóa Hậu Lượng Tử Và FIDO2 (2026)

Hướng dẫn thực hành gia cố SSH trên Linux năm 2026: cấu hình OpenSSH 10.0 với mã hóa hậu lượng tử, xác thực FIDO2 bằng khóa phần cứng, SSH Certificate Authority, Fail2Ban và bastion host.

Tại Sao Bảo Mật SSH Phải Là Ưu Tiên Số 1 Trong 2026

SSH — Secure Shell — chính là cánh cửa chính vào mọi máy chủ Linux. Và thật lòng mà nói, kẻ tấn công luôn nhắm vào cánh cửa chính trước tiên. Các cuộc tấn công brute-force nhắm vào SSH vẫn chiếm tỷ lệ lớn kinh ngạc trong tổng số nỗ lực xâm nhập hệ thống Linux. Với hơn 5.530 CVE được ghi nhận trên nhân Linux trong năm 2025 — tăng 28% so với năm trước — và Linux đang gánh gần 50% khối lượng công việc đám mây toàn cầu, việc gia cố SSH không còn là "nên làm" nữa. Nó là bắt buộc.

Nhưng năm 2026 mang đến nhiều thứ mới hơn bạn tưởng. OpenSSH 10.0 đã ra mắt vào tháng 4/2025 với những thay đổi thực sự mang tính bước ngoặt: mã hóa hậu lượng tử trở thành mặc định, DSA bị loại bỏ hoàn toàn, và kiến trúc xác thực được tách biệt để thu hẹp bề mặt tấn công. Bên cạnh đó, xác thực bằng khóa phần cứng FIDO2 đang nhanh chóng trở thành tiêu chuẩn mới cho môi trường doanh nghiệp.

Vậy nên, hãy cùng đi từng bước nhé. Bài viết này sẽ hướng dẫn bạn gia cố SSH trên Linux một cách toàn diện — từ cấu hình cơ bản đến triển khai mã hóa hậu lượng tử và xác thực FIDO2. Dù bạn đang quản lý một máy chủ VPS đơn lẻ hay cả trăm node trong hạ tầng đám mây, những kiến thức này đều áp dụng được ngay.

OpenSSH 10.0: Những Thay Đổi Bạn Cần Biết Ngay

Loại bỏ hoàn toàn DSA — kết thúc một kỷ nguyên

OpenSSH 10.0, phát hành ngày 9/4/2025, đã chính thức loại bỏ hoàn toàn thuật toán chữ ký DSA. Quá trình này bắt đầu từ tận năm 2015 — hơn một thập kỷ chuyển đổi. DSA vốn có những hạn chế cố hữu: giới hạn ở khóa riêng 160-bit, sử dụng SHA-1, chỉ tương đương mức bảo mật đối xứng 80-bit. Nói thẳng ra, trong bối cảnh 2026, mức bảo mật này hoàn toàn không chấp nhận được.

Nếu hệ thống của bạn vẫn đang dùng khóa DSA, thì đây là lúc phải chuyển đổi ngay — không phải tuần sau, không phải tháng sau:

# Kiểm tra xem có khóa DSA nào đang được sử dụng không
find /etc/ssh/ -name "*.pub" -exec grep -l "ssh-dss" {} \;
find ~/.ssh/ -name "*.pub" -exec grep -l "ssh-dss" {} \;

# Tạo khóa Ed25519 thay thế (khuyến nghị)
ssh-keygen -t ed25519 -a 100 -C "[email protected]"

# Hoặc RSA 4096-bit nếu cần tương thích ngược
ssh-keygen -t rsa -b 4096 -C "[email protected]"

Mã hóa hậu lượng tử trở thành mặc định

Đây có lẽ là thay đổi đáng chú ý nhất. OpenSSH 10.0 đưa thuật toán trao đổi khóa lai hậu lượng tử mlkem768x25519-sha256 lên làm mặc định. Thuật toán này kết hợp ML-KEM (Module-Lattice-Based Key Encapsulation Mechanism) — đã được NIST phê chuẩn — với ECDH/X25519 cổ điển. Ý tưởng khá hay: ngay cả khi một trong hai thuật toán bị phá vỡ, kết nối vẫn an toàn.

Tại sao lại cần quan tâm ngay bây giờ? Hai từ: "harvest now, decrypt later" (thu hoạch bây giờ, giải mã sau). Các tác nhân cấp quốc gia rất có thể đang lưu trữ lưu lượng SSH mã hóa ngay lúc này, chờ đến khi máy tính lượng tử đủ mạnh để giải mã. Nhiều chuyên gia dự đoán điều đó có thể xảy ra vào giữa thập niên 2030 — tức là chỉ còn chưa đầy 10 năm nữa thôi.

Kiến trúc tách biệt xác thực — thu hẹp bề mặt tấn công

OpenSSH 10.0 đem đến một thay đổi kiến trúc mà mình đánh giá rất cao: tách mã xác thực người dùng thành tệp nhị phân riêng biệt gọi là sshd-auth. Trước đây, tất cả mã xác thực chạy chung không gian bộ nhớ với phần còn lại. Giờ thì giai đoạn trước xác thực (pre-authentication) — nơi xảy ra phần lớn các cuộc tấn công — có không gian địa chỉ hoàn toàn riêng.

Điều này có nghĩa là gì? Đơn giản thôi: ngay cả khi kẻ tấn công tìm được lỗ hổng trong giai đoạn trước xác thực, chúng cũng không thể dễ dàng leo thang để truy cập phần kết nối đã xác thực. Bài học rút ra từ các CVE nghiêm trọng gần đây, thực sự.

Ưu tiên mật mã mạnh hơn

OpenSSH 10.0 cũng sắp xếp lại thứ tự ưu tiên các thuật toán mật mã:

  • ChaCha20/Poly1305 giữ vị trí ưu tiên cao nhất
  • AES-GCM được đẩy lên trước AES-CTR cho mã hóa dữ liệu
  • ECDH thay thế Diffie-Hellman trường hữu hạn (modp) làm mặc định
  • Thứ tự khóa máy chủ ưu tiên Ed25519 nhờ hiệu quả vượt trội

Các Lỗ Hổng SSH Nghiêm Trọng Gần Đây — Và Bài Học Xương Máu

CVE-2025-26465 — giả mạo máy chủ qua VerifyHostKeyDNS

Vào tháng 2/2025, một lỗi logic nghiêm trọng được phát hiện trong ssh(1), ảnh hưởng các phiên bản từ 6.8p1 đến 9.9p1. Khi tùy chọn VerifyHostKeyDNS được bật, kẻ tấn công nằm trên đường truyền (on-path attacker) có thể giả mạo bất kỳ máy chủ nào. Nghe đã thấy rùng mình rồi — nó phá vỡ hoàn toàn cơ chế xác minh danh tính máy chủ.

Bài học ở đây khá rõ ràng: đừng bao giờ dựa vào VerifyHostKeyDNS như biện pháp bảo mật duy nhất. Luôn xác minh dấu vân tay máy chủ thủ công khi kết nối lần đầu, và cập nhật OpenSSH ngay khi có bản vá.

CVE-2025-26466 — tấn công DoS trước xác thực

Cũng trong tháng 2/2025, sshd(8) phiên bản 9.5p1 đến 9.9p1 bị phát hiện lỗ hổng cho phép tấn công từ chối dịch vụ trước xác thực thông qua gói SSH2_MSG_PING. Kẻ tấn công có thể ngốn hết bộ nhớ và CPU của máy chủ mà chẳng cần xác thực gì cả.

Hãy kiểm tra phiên bản OpenSSH hiện tại ngay:

# Kiểm tra phiên bản sshd
sshd -V 2>&1 || ssh -V

# Kiểm tra gói OpenSSH đã cài đặt
# Trên Ubuntu/Debian
dpkg -l | grep openssh

# Trên RHEL/CentOS/Fedora
rpm -qa | grep openssh

Gia Cố Cấu Hình SSH Cơ Bản — Nền Tảng Không Được Bỏ Qua

Trước khi lao vào những tính năng nâng cao, hãy chắc chắn nền tảng của bạn đã vững. Mình từng thấy không ít người cấu hình hậu lượng tử xong xuôi mà quên... tắt đăng nhập root. Dưới đây là cấu hình sshd_config được tối ưu cho năm 2026.

Vô hiệu hóa đăng nhập root và xác thực bằng mật khẩu

# /etc/ssh/sshd_config — Cấu hình cơ bản

# Vô hiệu hóa đăng nhập root trực tiếp
PermitRootLogin no

# Vô hiệu hóa xác thực bằng mật khẩu
PasswordAuthentication no

# Vô hiệu hóa xác thực challenge-response (bao gồm PAM keyboard-interactive)
KbdInteractiveAuthentication no

# Chỉ cho phép xác thực bằng khóa công khai
PubkeyAuthentication yes
AuthenticationMethods publickey

# Giới hạn số lần thử xác thực
MaxAuthTries 3

# Giới hạn thời gian chờ xác thực
LoginGraceTime 30

# Vô hiệu hóa các phương thức xác thực không cần thiết
HostbasedAuthentication no
PermitEmptyPasswords no

Giới hạn người dùng và nhóm được phép truy cập

# Chỉ cho phép các người dùng cụ thể
AllowUsers admin deployer

# Hoặc chỉ cho phép các nhóm cụ thể
AllowGroups ssh-users sudo

# Giới hạn số phiên đồng thời
MaxSessions 3

# Giới hạn số kết nối chưa xác thực
MaxStartups 10:30:60

Giải thích nhanh về MaxStartups 10:30:60: khi có 10 kết nối chưa xác thực, SSH bắt đầu từ chối ngẫu nhiên (random drop), xác suất tăng tuyến tính đến 30% cho đến 60 kết nối — lúc đó từ chối tất cả. Đây là biện pháp khá hiệu quả chống brute-force quy mô lớn.

Thiết lập thời gian chờ phiên

# Gửi gói keepalive mỗi 300 giây (5 phút)
ClientAliveInterval 300

# Ngắt kết nối sau 3 lần không phản hồi (15 phút tổng cộng)
ClientAliveCountMax 3

# Vô hiệu hóa TCP keepalive (dùng SSH keepalive thay thế)
TCPKeepAlive no

Vô hiệu hóa các tính năng không cần thiết

# Vô hiệu hóa chuyển tiếp X11
X11Forwarding no

# Vô hiệu hóa chuyển tiếp agent (trừ khi thực sự cần)
AllowAgentForwarding no

# Vô hiệu hóa chuyển tiếp TCP (trừ khi dùng SSH tunnel)
AllowTcpForwarding no

# Vô hiệu hóa chuyển tiếp luồng (streamlocal)
AllowStreamLocalForwarding no

# Vô hiệu hóa hiển thị banner hệ thống
PrintMotd no

# Bật phân tách quyền hạn (mặc định trong phiên bản mới)
UsePrivilegeSeparation sandbox

Kiểm tra và áp dụng cấu hình

# Kiểm tra cú pháp cấu hình trước khi khởi động lại
sudo sshd -t

# Nếu không có lỗi, khởi động lại sshd
sudo systemctl restart sshd

# QUAN TRỌNG: Giữ phiên SSH hiện tại mở,
# mở terminal mới để kiểm tra kết nối trước khi đóng phiên cũ
ssh -v user@server-ip

Mẹo từ kinh nghiệm thực tế: Luôn, luôn, luôn giữ ít nhất một phiên SSH đang hoạt động khi thay đổi cấu hình. Mình đã chứng kiến (và thú thật là từng trải qua) tình huống bị khóa khỏi máy chủ vì cấu hình mới có lỗi mà không để ý. Đừng lặp lại sai lầm đó.

Cấu Hình Mã Hóa Hậu Lượng Tử Cho SSH

Kiểm tra hỗ trợ thuật toán

Đầu tiên, hãy xem phiên bản OpenSSH của bạn có hỗ trợ các thuật toán hậu lượng tử hay chưa:

# Liệt kê tất cả thuật toán trao đổi khóa được hỗ trợ
ssh -Q kex

# Kiểm tra cụ thể ML-KEM
ssh -Q kex | grep mlkem

# Kiểm tra sntrup761 (hỗ trợ từ OpenSSH 9.0)
ssh -Q kex | grep sntrup

# Kết quả mong đợi trên OpenSSH 10.0+:
# mlkem768x25519-sha256
# [email protected]

Không thấy kết quả? Vậy là bạn cần nâng cấp OpenSSH. Tin vui là trên Ubuntu 24.04 LTS trở lên, OpenSSH 9.7+ đã có sẵn. Trên RHEL 10, cả hai thuật toán đều được hỗ trợ (dù vẫn ở dạng Technology Preview).

Cấu hình máy chủ với mã hóa hậu lượng tử

# /etc/ssh/sshd_config — Cấu hình mã hóa hậu lượng tử

# Thuật toán trao đổi khóa — ưu tiên hậu lượng tử
KexAlgorithms mlkem768x25519-sha256,[email protected],curve25519-sha256,[email protected]

# Mật mã — ưu tiên ChaCha20 và AES-GCM
Ciphers [email protected],[email protected],[email protected],aes256-ctr,aes192-ctr,aes128-ctr

# MAC — chỉ dùng Encrypt-then-MAC
MACs [email protected],[email protected]

# Thuật toán khóa máy chủ — ưu tiên Ed25519
HostKeyAlgorithms ssh-ed25519,[email protected],rsa-sha2-512,rsa-sha2-256

# Yêu cầu kích thước RSA tối thiểu 3072-bit
RequiredRSASize 3072

Cấu hình máy khách SSH

# ~/.ssh/config — Cấu hình máy khách

Host *
    # Ưu tiên trao đổi khóa hậu lượng tử
    KexAlgorithms mlkem768x25519-sha256,[email protected],curve25519-sha256

    # Mật mã mạnh
    Ciphers [email protected],[email protected],[email protected]

    # Chỉ dùng Encrypt-then-MAC
    MACs [email protected],[email protected]

    # Thuật toán khóa máy chủ ưa thích
    HostKeyAlgorithms ssh-ed25519,[email protected],rsa-sha2-512,rsa-sha2-256

# Ngoại lệ cho máy chủ không hỗ trợ hậu lượng tử (ví dụ: GitHub)
Host github.com
    KexAlgorithms +curve25519-sha256,[email protected],diffie-hellman-group16-sha512

Xác minh kết nối hậu lượng tử

# Kết nối thử với thuật toán hậu lượng tử cụ thể
ssh -o KexAlgorithms=mlkem768x25519-sha256 user@server-ip

# Xác minh thuật toán đang được sử dụng
ssh -v user@server-ip 2>&1 | grep "kex:"
# Kết quả mong đợi: debug1: kex: algorithm: mlkem768x25519-sha256

# Kiểm tra toàn bộ thông tin mật mã của kết nối
ssh -vv user@server-ip 2>&1 | grep -E "kex:|cipher:|mac:|compression:"

Một lưu ý nhỏ: từ OpenSSH 10.1, bạn sẽ nhận được cảnh báo khi kết nối đến máy chủ không hỗ trợ trao đổi khóa hậu lượng tử. Có thể tắt bằng WarnWeakCrypto no trong ssh_config, nhưng thay vì tắt cảnh báo, tốt hơn hết là nâng cấp máy chủ đích.

Xác Thực FIDO2 — Bảo Mật Phần Cứng Cho SSH

Tại sao FIDO2 vượt trội hơn khóa SSH truyền thống

Xác thực SSH truyền thống dựa trên khóa riêng lưu dưới dạng tệp trên máy tính. Dù có passphrase bảo vệ, khóa riêng vẫn có thể bị đánh cắp qua malware, phishing, hoặc truy cập trái phép. FIDO2 giải quyết triệt để vấn đề này:

  • Khóa riêng không bao giờ rời khỏi thiết bị phần cứng — ngay cả khi máy tính bị compromised, kẻ tấn công cũng không thể sao chép khóa
  • Yêu cầu xác minh vật lý: phải chạm vào thiết bị (User Presence) hoặc nhập PIN/vân tay (User Verification)
  • Kháng phishing: khóa được ràng buộc với nguồn gốc (origin-bound), không thể bị lừa dùng cho máy chủ giả mạo
  • Đa yếu tố tích hợp sẵn: kết hợp thứ bạn có (khóa phần cứng) + thứ bạn biết (PIN) hoặc thứ bạn là (vân tay)

Điều kiện tiên quyết

Để sử dụng FIDO2 với SSH, bạn cần chuẩn bị:

  • OpenSSH phiên bản 8.2 trở lên (cả client lẫn server)
  • Thiết bị FIDO2 tương thích (YubiKey 5 series, SoloKeys, Nitrokey FIDO2, Titan Security Key...)
  • Thư viện libfido2 đã cài đặt
# Cài đặt libfido2 trên Ubuntu/Debian
sudo apt update
sudo apt install libfido2-1 libfido2-dev libfido2-doc fido2-tools -y

# Trên RHEL/Fedora
sudo dnf install libfido2 libfido2-devel fido2-tools -y

# Kiểm tra thiết bị FIDO2 đã được nhận diện
fido2-token -L
# Kết quả mẫu: /dev/hidraw3: vendor=0x1050, product=0x0407 (Yubico YubiKey)

Tạo khóa SSH FIDO2

OpenSSH hỗ trợ hai loại khóa FIDO2: ecdsa-sked25519-sk (sk = security key). Khuyến nghị dùng ed25519-sk vì bảo mật tốt hơn và hiệu quả cao hơn.

# Tạo khóa ed25519-sk cơ bản (yêu cầu chạm thiết bị)
ssh-keygen -t ed25519-sk -C "fido2-key-$(hostname)-$(date +%Y%m%d)"

# Tạo khóa resident (lưu trên thiết bị — dùng được trên nhiều máy)
ssh-keygen -t ed25519-sk -O resident -C "resident-key-$(hostname)"

# Tạo khóa với yêu cầu xác minh PIN (bảo mật cao nhất)
ssh-keygen -t ed25519-sk -O resident -O verify-required -C "secure-key-$(hostname)"

# Khi được hỏi, chạm vào thiết bị FIDO2 và nhập PIN nếu cần

Giải thích nhanh các tùy chọn quan trọng:

  • -O resident: Lưu khóa trực tiếp trên thiết bị FIDO2 — bạn có thể dùng khóa từ bất kỳ máy nào mà không cần mang theo tệp
  • -O verify-required: Yêu cầu xác minh người dùng (PIN hoặc vân tay) mỗi lần sử dụng, không chỉ chạm đơn thuần
  • -O no-touch-required: Bỏ qua yêu cầu chạm — chỉ nên dùng cho tự động hóa, không khuyến nghị cho truy cập tương tác

Triển khai khóa FIDO2 lên máy chủ

# Sao chép khóa công khai lên máy chủ
ssh-copy-id -i ~/.ssh/id_ed25519_sk.pub user@server-ip

# Hoặc thêm thủ công
cat ~/.ssh/id_ed25519_sk.pub | ssh user@server-ip "mkdir -p ~/.ssh && chmod 700 ~/.ssh && cat >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"

# Kiểm tra kết nối (sẽ yêu cầu chạm thiết bị)
ssh -i ~/.ssh/id_ed25519_sk user@server-ip

Khôi phục khóa resident từ thiết bị FIDO2

Một trong những ưu điểm lớn của khóa resident là khả năng khôi phục trên máy mới — rất tiện khi bạn đổi laptop hoặc cần làm việc từ máy khác:

# Tải tất cả khóa resident từ thiết bị về máy hiện tại
ssh-keygen -K

# Lệnh này sẽ tạo các tệp id_ed25519_sk_rk* trong thư mục hiện tại
# Di chuyển chúng vào ~/.ssh/
mv id_ed25519_sk_rk* ~/.ssh/
chmod 600 ~/.ssh/id_ed25519_sk_rk*

Cấu hình máy chủ để hỗ trợ FIDO2

# /etc/ssh/sshd_config — Hỗ trợ FIDO2

# Cho phép xác thực khóa công khai (bao gồm FIDO2)
PubkeyAuthentication yes

# Chấp nhận các loại khóa FIDO2
PubkeyAcceptedAlgorithms ssh-ed25519,[email protected],[email protected],rsa-sha2-512,rsa-sha2-256

# Yêu cầu xác minh người dùng cho khóa FIDO2
PubkeyAuthOptions verify-required

SSH Certificate Authentication — Vượt Qua Giới Hạn Khóa Công Khai

Vấn đề với authorized_keys truyền thống

Nếu bạn đang quản lý SSH bằng authorized_keys cho hơn vài chục máy chủ, chắc bạn đã biết đó là cơn ác mộng thế nào. Cụ thể:

  • Không có thời hạn: Khóa SSH truyền thống không hết hạn — nếu bị rò rỉ, chúng hữu dụng mãi mãi
  • Khó quản lý quy mô lớn: Với hàng trăm máy chủ và hàng nghìn người dùng, việc phân phối và thu hồi khóa trở thành nỗi khổ
  • Không có danh tính tập trung: Mỗi máy chủ quản lý authorized_keys riêng
  • Thiếu audit trail: Rất khó theo dõi ai đã truy cập vào đâu, khi nào

SSH certificates giải quyết tất cả các vấn đề trên bằng Certificate Authority (CA) tập trung.

Thiết lập SSH Certificate Authority

# Tạo CA cho khóa người dùng (user CA)
ssh-keygen -t ed25519 -f /etc/ssh/ca/user_ca -C "User CA $(date +%Y)"

# Tạo CA riêng cho khóa máy chủ (host CA)
ssh-keygen -t ed25519 -f /etc/ssh/ca/host_ca -C "Host CA $(date +%Y)"

# Đặt quyền truy cập nghiêm ngặt
chmod 600 /etc/ssh/ca/user_ca /etc/ssh/ca/host_ca
chmod 644 /etc/ssh/ca/user_ca.pub /etc/ssh/ca/host_ca.pub

Quan trọng: Luôn dùng CA riêng biệt cho người dùng và máy chủ. Nếu user CA bị rò rỉ, kẻ tấn công chỉ có thể tạo chứng chỉ người dùng giả — nhưng không thể giả mạo máy chủ. Phân tách rủi ro là nguyên tắc cốt lõi.

Ký chứng chỉ cho người dùng

# Ký chứng chỉ ngắn hạn cho người dùng (hết hạn sau 8 giờ)
ssh-keygen -s /etc/ssh/ca/user_ca \
    -I "[email protected]$(date +%Y%m%d)" \
    -n admin,deployer \
    -V +8h \
    -z $(date +%s) \
    /home/admin/.ssh/id_ed25519.pub

# Giải thích các tùy chọn:
# -s: Khóa CA dùng để ký
# -I: Định danh chứng chỉ (cho audit log)
# -n: Danh sách principal (tên người dùng được phép)
# -V: Thời hạn hiệu lực (+8h = 8 giờ từ bây giờ)
# -z: Số serial (dùng timestamp để đảm bảo duy nhất)

# Xem thông tin chứng chỉ
ssh-keygen -L -f /home/admin/.ssh/id_ed25519-cert.pub

Ký chứng chỉ cho máy chủ

# Ký chứng chỉ cho khóa máy chủ (hết hạn sau 1 năm)
ssh-keygen -s /etc/ssh/ca/host_ca \
    -I "webserver01.company.com" \
    -h \
    -n webserver01.company.com,10.0.1.10 \
    -V +52w \
    /etc/ssh/ssh_host_ed25519_key.pub

# -h: Đánh dấu đây là chứng chỉ máy chủ (không phải người dùng)

Cấu hình máy chủ tin tưởng User CA

# /etc/ssh/sshd_config — Tin tưởng CA người dùng

# Chỉ định khóa công khai của User CA
TrustedUserCAKeys /etc/ssh/ca/user_ca.pub

# Chỉ định chứng chỉ máy chủ
HostCertificate /etc/ssh/ssh_host_ed25519_key-cert.pub

# Tùy chọn: Giới hạn principal được phép
AuthorizedPrincipalsFile /etc/ssh/auth_principals/%u
# Tạo tệp principal cho mỗi người dùng
echo -e "admin\ndeployer" | sudo tee /etc/ssh/auth_principals/admin
echo "deployer" | sudo tee /etc/ssh/auth_principals/deployer

Cấu hình máy khách tin tưởng Host CA

# Thêm vào ~/.ssh/known_hosts hoặc /etc/ssh/ssh_known_hosts
echo "@cert-authority *.company.com $(cat /etc/ssh/ca/host_ca.pub)" >> ~/.ssh/known_hosts

# Giờ đây kết nối SSH sẽ tự động xác minh chứng chỉ máy chủ
# Không còn cảnh báo "unknown host key" cho các máy chủ có chứng chỉ hợp lệ

Fail2Ban Và Giám Sát SSH Nâng Cao

Cấu hình Fail2Ban cho SSH

Fail2Ban tự động chặn IP sau nhiều lần đăng nhập thất bại — tuyến phòng thủ đầu tiên chống brute-force. Cài đặt và cấu hình khá nhanh:

# Cài đặt Fail2Ban
sudo apt install fail2ban -y    # Ubuntu/Debian
sudo dnf install fail2ban -y    # RHEL/Fedora

# Tạo cấu hình cục bộ (không chỉnh sửa tệp gốc)
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
# /etc/fail2ban/jail.local — Cấu hình SSH jail

[DEFAULT]
# Thời gian cấm mặc định: 1 giờ
bantime = 3600

# Cửa sổ theo dõi: 10 phút
findtime = 600

# Số lần thử tối đa trước khi cấm
maxretry = 3

# Bật chế độ cấm tăng dần
bantime.increment = true
bantime.factor = 2
bantime.maxtime = 86400

[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
bantime = 3600

# Chế độ phát hiện nâng cao (phát hiện nhiều kiểu tấn công hơn)
mode = aggressive
# Khởi động và kích hoạt Fail2Ban
sudo systemctl enable --now fail2ban

# Kiểm tra trạng thái jail SSH
sudo fail2ban-client status sshd

# Xem danh sách IP bị cấm
sudo fail2ban-client get sshd banip

# Gỡ cấm một IP cụ thể (khi cần)
sudo fail2ban-client set sshd unbanip 192.168.1.100

Giám sát SSH với auditd

Để có audit trail đầy đủ, auditd là công cụ không thể thiếu. Cấu hình sau sẽ giám sát mọi thay đổi liên quan đến SSH:

# /etc/audit/rules.d/ssh.rules — Quy tắc audit cho SSH

# Giám sát tệp cấu hình SSH
-w /etc/ssh/sshd_config -p wa -k sshd_config_change
-w /etc/ssh/ -p wa -k ssh_dir_change

# Giám sát tệp authorized_keys
-w /root/.ssh/authorized_keys -p wa -k ssh_authorized_keys
-w /home/ -p wa -k ssh_user_home

# Giám sát đăng nhập SSH
-a always,exit -F arch=b64 -S accept -F a0=22 -k ssh_connection

# Giám sát lệnh ssh-keygen
-w /usr/bin/ssh-keygen -p x -k ssh_keygen_exec
# Tải lại quy tắc audit
sudo augenrules --load

# Tìm kiếm sự kiện SSH trong audit log
sudo ausearch -k sshd_config_change --interpret
sudo ausearch -k ssh_authorized_keys --interpret

Bastion Host — Kiến Trúc Truy Cập An Toàn

Tại sao cần Bastion Host

Bastion host (hay jump server) là máy chủ trung gian duy nhất được phép nhận kết nối SSH từ Internet. Mọi kết nối đến máy chủ nội bộ đều phải đi qua đây. Nghe có vẻ bất tiện, nhưng lợi ích thì rất rõ ràng:

  • Giảm bề mặt tấn công: Chỉ một điểm tiếp xúc với Internet thay vì hàng chục máy chủ
  • Tập trung giám sát: Mọi kết nối SSH được ghi nhật ký tại một điểm duy nhất
  • Kiểm soát truy cập: Áp dụng MFA và chính sách nghiêm ngặt tại bastion
  • Phân đoạn mạng: Máy chủ nội bộ không cần IP công khai

Cấu hình SSH Jump Host

# ~/.ssh/config — Cấu hình bastion host

# Bastion host
Host bastion
    HostName bastion.company.com
    User admin
    Port 22
    IdentityFile ~/.ssh/id_ed25519_sk
    ForwardAgent no

# Máy chủ nội bộ — truy cập qua bastion
Host internal-web
    HostName 10.0.1.10
    User deployer
    ProxyJump bastion
    IdentityFile ~/.ssh/id_ed25519

Host internal-db
    HostName 10.0.2.20
    User dbadmin
    ProxyJump bastion
    IdentityFile ~/.ssh/id_ed25519

# Mẫu cho tất cả máy chủ nội bộ
Host 10.0.*
    ProxyJump bastion
    User admin
# Kết nối thẳng đến máy chủ nội bộ (tự động qua bastion)
ssh internal-web

# Hoặc dùng cú pháp ProxyJump trực tiếp
ssh -J bastion.company.com [email protected]

Script Tự Động Kiểm Tra Cấu Hình SSH

Đây là script mình hay dùng để kiểm tra nhanh cấu hình SSH trên máy chủ. Nó không thay thế được các công cụ chuyên dụng như ssh-audit, nhưng đủ để có cái nhìn tổng quan:

#!/bin/bash
# ssh-hardening-check.sh — Kiểm tra và báo cáo cấu hình SSH
# Tương thích: Ubuntu 22.04+, RHEL 8+, Debian 12+

set -euo pipefail

RED="\033[0;31m"
GREEN="\033[0;32m"
YELLOW="\033[1;33m"
NC="\033[0m"

SSHD_CONFIG="/etc/ssh/sshd_config"
SCORE=0
TOTAL=0
WARNINGS=()

check() {
    local description="$1"
    local result="$2"
    TOTAL=$((TOTAL + 1))

    if [ "$result" = "PASS" ]; then
        echo -e "  ${GREEN}[PASS]${NC} $description"
        SCORE=$((SCORE + 1))
    elif [ "$result" = "WARN" ]; then
        echo -e "  ${YELLOW}[WARN]${NC} $description"
        WARNINGS+=("$description")
    else
        echo -e "  ${RED}[FAIL]${NC} $description"
    fi
}

echo "========================================="
echo "  SSH Hardening Check — $(date +%Y-%m-%d)"
echo "========================================="
echo ""

# Kiểm tra phiên bản OpenSSH
SSH_VERSION=$(ssh -V 2>&1 | grep -oP "OpenSSH_\K[0-9]+\.[0-9]+")
echo "OpenSSH Version: $SSH_VERSION"
echo ""

# 1. Kiểm tra xác thực
echo "[Xác thực]"
PASS_AUTH=$(sshd -T 2>/dev/null | grep -i "^passwordauthentication" | awk "{print \$2}")
[ "$PASS_AUTH" = "no" ] && check "Password authentication disabled" "PASS" || check "Password authentication disabled" "FAIL"

ROOT_LOGIN=$(sshd -T 2>/dev/null | grep -i "^permitrootlogin" | awk "{print \$2}")
[ "$ROOT_LOGIN" = "no" ] && check "Root login disabled" "PASS" || check "Root login disabled" "FAIL"

MAX_AUTH=$(sshd -T 2>/dev/null | grep -i "^maxauthtries" | awk "{print \$2}")
[ "$MAX_AUTH" -le 3 ] 2>/dev/null && check "MaxAuthTries <= 3 (current: $MAX_AUTH)" "PASS" || check "MaxAuthTries <= 3 (current: $MAX_AUTH)" "FAIL"

echo ""

# 2. Kiểm tra mật mã
echo "[Mật mã]"
if ssh -Q kex 2>/dev/null | grep -q "mlkem768x25519"; then
    check "Post-quantum KEX available (mlkem768x25519)" "PASS"
else
    check "Post-quantum KEX available (mlkem768x25519)" "WARN"
fi

if ! sshd -T 2>/dev/null | grep -i "^ciphers" | grep -q "3des\|blowfish\|arcfour\|cast128"; then
    check "No weak ciphers enabled" "PASS"
else
    check "No weak ciphers enabled" "FAIL"
fi

echo ""

# 3. Kiểm tra tính năng bổ sung
echo "[Tính năng]"
X11_FWD=$(sshd -T 2>/dev/null | grep -i "^x11forwarding" | awk "{print \$2}")
[ "$X11_FWD" = "no" ] && check "X11 forwarding disabled" "PASS" || check "X11 forwarding disabled" "WARN"

AGENT_FWD=$(sshd -T 2>/dev/null | grep -i "^allowagentforwarding" | awk "{print \$2}")
[ "$AGENT_FWD" = "no" ] && check "Agent forwarding disabled" "PASS" || check "Agent forwarding disabled" "WARN"

echo ""

# 4. Kiểm tra Fail2Ban
echo "[Phòng thủ]"
if systemctl is-active --quiet fail2ban 2>/dev/null; then
    check "Fail2Ban is running" "PASS"
else
    check "Fail2Ban is running" "FAIL"
fi

echo ""
echo "========================================="
echo -e "  Điểm: ${SCORE}/${TOTAL}"
PERCENT=$((SCORE * 100 / TOTAL))
if [ "$PERCENT" -ge 80 ]; then
    echo -e "  Đánh giá: ${GREEN}TỐT${NC} (${PERCENT}%)"
elif [ "$PERCENT" -ge 60 ]; then
    echo -e "  Đánh giá: ${YELLOW}TRUNG BÌNH${NC} (${PERCENT}%)"
else
    echo -e "  Đánh giá: ${RED}CẦN CẢI THIỆN${NC} (${PERCENT}%)"
fi
echo "========================================="

Tích Hợp Vào Pipeline DevSecOps

Kiểm tra SSH trong CI/CD

Nếu bạn đang làm DevSecOps (hoặc đang muốn bắt đầu), tích hợp kiểm tra SSH vào pipeline CI/CD là bước hợp lý tiếp theo. Mục tiêu là đảm bảo mọi máy chủ mới triển khai đều tuân thủ chính sách bảo mật từ đầu:

# .github/workflows/ssh-audit.yml
name: SSH Security Audit
on:
  schedule:
    - cron: "0 6 * * 1"  # Mỗi thứ Hai lúc 6:00 UTC
  workflow_dispatch:

jobs:
  ssh-audit:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        server:
          - name: "web-prod-01"
            host: "10.0.1.10"
          - name: "db-prod-01"
            host: "10.0.2.20"
    steps:
      - name: Install ssh-audit
        run: pip install ssh-audit

      - name: Audit SSH configuration
        run: |
          ssh-audit --json ${{ matrix.server.host }} > audit-${{ matrix.server.name }}.json

          # Kiểm tra lỗi nghiêm trọng
          if jq -e ".errors | length > 0" audit-${{ matrix.server.name }}.json; then
            echo "FAIL: SSH audit found critical issues on ${{ matrix.server.name }}"
            jq ".errors" audit-${{ matrix.server.name }}.json
            exit 1
          fi

      - name: Upload audit results
        uses: actions/upload-artifact@v4
        with:
          name: ssh-audit-${{ matrix.server.name }}
          path: audit-${{ matrix.server.name }}.json

Ansible playbook cho gia cố SSH hàng loạt

# ssh-hardening.yml — Ansible Playbook
---
- name: SSH Hardening
  hosts: all
  become: true
  vars:
    ssh_port: 22
    ssh_max_auth_tries: 3
    ssh_client_alive_interval: 300
    ssh_client_alive_count_max: 3

  tasks:
    - name: Ensure OpenSSH is up to date
      ansible.builtin.package:
        name: openssh-server
        state: latest

    - name: Deploy hardened sshd_config
      ansible.builtin.template:
        src: templates/sshd_config.j2
        dest: /etc/ssh/sshd_config
        owner: root
        group: root
        mode: "0600"
        validate: "sshd -t -f %s"
      notify: Restart sshd

    - name: Ensure only Ed25519 and RSA host keys exist
      ansible.builtin.file:
        path: "/etc/ssh/ssh_host_{{ item }}_key"
        state: absent
      loop:
        - dsa
        - ecdsa
      notify: Restart sshd

    - name: Configure Fail2Ban for SSH
      ansible.builtin.template:
        src: templates/fail2ban-sshd.j2
        dest: /etc/fail2ban/jail.d/sshd.local
        owner: root
        group: root
        mode: "0644"
      notify: Restart fail2ban

  handlers:
    - name: Restart sshd
      ansible.builtin.service:
        name: sshd
        state: restarted

    - name: Restart fail2ban
      ansible.builtin.service:
        name: fail2ban
        state: restarted

Checklist Bảo Mật SSH 2026 — Lưu Lại Để Dùng Dần

Tổng hợp lại tất cả, đây là checklist bạn nên kiểm tra định kỳ cho mọi máy chủ SSH:

Hạng mụcKiểm traMức độ
Phiên bảnOpenSSH 10.0+ đã cài đặtQuan trọng
Xác thựcVô hiệu hóa mật khẩu, chỉ dùng khóa/chứng chỉQuan trọng
RootPermitRootLogin noQuan trọng
Mã hóaHậu lượng tử KEX làm mặc địnhCao
Khóa máy chủChỉ Ed25519, loại bỏ DSA/ECDSACao
CipherChaCha20/AES-GCM, không có cipher yếuCao
FIDO2Triển khai cho tài khoản quản trịKhuyến nghị
Chứng chỉSSH CA cho môi trường 10+ máy chủKhuyến nghị
Fail2BanĐang chạy với chế độ aggressiveQuan trọng
Auditauditd giám sát thay đổi cấu hình SSHCao
BastionJump host cho truy cập nội bộKhuyến nghị
Tự động hóaKiểm tra SSH trong CI/CD pipelineKhuyến nghị

Lời Kết

Bảo mật SSH không phải là việc cấu hình một lần rồi quên đi — mình muốn nhấn mạnh điều này. Đó là quá trình liên tục đòi hỏi cập nhật, giám sát và thích ứng với các mối đe dọa mới. Năm 2026 đánh dấu bước ngoặt quan trọng khi mã hóa hậu lượng tử chính thức trở thành tiêu chuẩn trong OpenSSH 10.0.

Đừng chờ đến khi máy tính lượng tử trở thành hiện thực mới hành động.

Nếu bạn chỉ có thể làm ba việc ngay bây giờ, hãy làm ba việc này: nâng cấp lên OpenSSH 10.0+, vô hiệu hóa xác thực bằng mật khẩu, và bật Fail2Ban. Ba bước đơn giản này sẽ loại bỏ phần lớn các cuộc tấn công SSH phổ biến. Từ đó, dần dần triển khai FIDO2, SSH certificates, và tích hợp kiểm tra bảo mật vào pipeline DevSecOps để đạt mức bảo mật toàn diện hơn.

Bài viết này nối tiếp tự nhiên loạt bài về bảo mật Linux — sau khi đã nắm vững các công cụ kiểm tra với Lynis, OpenSCAP và CIS Benchmarks, giờ bạn đã có đủ kiến thức để gia cố cánh cửa quan trọng nhất vào hệ thống của mình.

Về Tác Giả Editorial Team

Our team of expert writers and editors.