Một VPS mới “public IP” nhận brute force trong vài phút sau khi boot — log SSH đầy request từ các bot quét Internet. Mặc định của OpenSSH khá an toàn, nhưng chỉ khá. Bài này là checklist hardening thực dụng, kèm lý do phía sau để bạn biết điều gì đáng cứng hoá, điều gì chỉ là thuốc an thần.
1. Nền tảng: cách SSH thực sự xác thực
OpenSSH thử lần lượt theo thứ tự cấu hình:
- publickey: client ký challenge bằng private key; server verify bằng public key trong
authorized_keys. - password: client gửi password (qua kênh đã mã hoá); server verify với
/etc/shadow. - keyboard-interactive: PAM-based (có thể là OTP, 2FA).
Hardening xoay quanh: chỉ cho phép phương pháp mạnh, giảm bề mặt tấn công (ai connect được, account nào login được), và ghi lại hành vi.
2. Trước khi bắt đầu: đừng tự khóa mình ra
Sai lầm kinh điển: đổi cấu hình, systemctl restart sshd, mất kết nối vì chính mình. Quy trình an toàn:
Giữ một session SSH đang mở trong suốt quá trình chỉnh.
Test config trước khi reload:
sudo sshd -t # syntax check sudo sshd -T # in cấu hình effectiveSau khi reload, mở session mới trong cửa sổ khác để xác nhận vẫn login được, giữ session cũ.
Nếu sập: VPS provider thường có console ra serial để cứu.
Nếu ở cloud: snapshot disk trước khi chỉnh.
3. Key-based authentication
3.1. Tạo key đúng
Ưu tiên Ed25519 (ngắn, nhanh, an toàn):
ssh-keygen -t ed25519 -a 100 -f ~/.ssh/id_ed25519_prod -C "me@laptop"
-a 100: rounds cho KDF passphrase — chậm tính toán → chống brute offline nếu key bị lấy.- Đặt passphrase. Không passphrase = mất laptop là xong.
3.2. Đưa key lên server
Cách đúng:
ssh-copy-id -i ~/.ssh/id_ed25519_prod.pub user@server
Hoặc thủ công: append vào /home/user/.ssh/authorized_keys với permission chặt:
chmod 700 ~/.ssh
chmod 600 ~/.ssh/authorized_keys
chown -R user:user ~/.ssh
SSH từ chối key nếu permission authorized_keys quá lỏng.
3.3. ssh-agent và agent forwarding
ssh-agentgiữ private key đã unlock để không nhập passphrase mỗi lần.- Agent forwarding (
-A) tiện nhưng rủi ro: trên server đích, root hoặc attacker có thể dùng agent socket để ký — như có key của bạn. Thay bằngProxyJump(xem 3.5) khi có thể.
3.4. .ssh/config phía client
Tạo alias cho dễ dùng:
Host prod-web
HostName web1.prod.example.com
User deploy
IdentityFile ~/.ssh/id_ed25519_prod
IdentitiesOnly yes
AddKeysToAgent yes
ServerAliveInterval 60
IdentitiesOnly yestránh gửi tất cả key trong agent lên server (bị fingerprint theo dõi).ServerAliveIntervalgiữ connection khỏi idle timeout của NAT.
3.5. Jump host / Bastion
Thay vì mở SSH từ Internet tới mọi máy, đặt một bastion ngoài, còn máy trong private network chỉ cho phép SSH từ bastion:
Host app-*.prod
User deploy
ProxyJump bastion.prod.example.com
Khi SSH app-web.prod, client tự tunnel qua bastion. Giới hạn bề mặt tấn công của SSH ra Internet về chỉ 1 host cần giám sát.
4. sshd_config — cấu hình mẫu
File: /etc/ssh/sshd_config (hoặc drop-in /etc/ssh/sshd_config.d/hardening.conf):
# Authentication
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
KbdInteractiveAuthentication no
ChallengeResponseAuthentication no
PermitEmptyPasswords no
AuthenticationMethods publickey
# User control
AllowGroups sshusers
MaxAuthTries 3
LoginGraceTime 30
# Network
Port 22 # xem phần 5 về đổi port
AddressFamily inet # IPv4 only nếu không dùng IPv6
ListenAddress 0.0.0.0
# Session
ClientAliveInterval 300
ClientAliveCountMax 2
MaxSessions 10
MaxStartups 10:30:60
X11Forwarding no
AllowTcpForwarding yes # tắt nếu không dùng port forward
PermitTunnel no
# Logging
LogLevel VERBOSE
Giải thích:
PermitRootLogin no: ai cũng biết userrootcó — bỏ đích trực tiếp của brute force. Login bằng user thường rồisudo.PasswordAuthentication no: chỉ key. Brute force password bot không còn ăn được.AuthenticationMethods publickey: ép phương pháp. Có thể đổi thànhpublickey,passwordhoặcpublickey,keyboard-interactivenếu bắt buộc 2FA trên key.AllowGroups sshusers: chỉ user trong groupsshusersmới SSH được. Tạo group, thêm user hợp pháp. Account hệ thống (postgres, www-data) không login được dù có key.MaxAuthTries 3: giới hạn thử trong một connection.LoginGraceTime 30: client phải login trong 30s — giảm slow connection attack.ClientAliveInterval/CountMax: kick session idle để tránh ghost session.LogLevel VERBOSE: log fingerprint key dùng để login, hữu ích điều tra.
Reload:
sudo sshd -t && sudo systemctl reload sshd
5. Đổi port: bảo mật hay theatre?
Đổi từ 22 sang port khác không tăng bảo mật thực sự với attacker có nmap — họ quét 65535 port trong vài phút. Nhưng:
- Giảm log noise từ bot scan mặc định → log thật sự quan trọng dễ thấy hơn.
- Giảm CPU/BW dùng cho handshake fail.
Nếu đổi, chọn port > 1024 (không cần root sau này) nhưng tránh xung đột với dịch vụ phổ biến. Nhớ:
- Cập nhật firewall.
- Cập nhật client
.ssh/config. - Cập nhật monitoring/SIEM.
- SELinux:
sudo semanage port -a -t ssh_port_t -p tcp <newport>(nếu dùng RHEL/CentOS).
6. Firewall và phân vùng
6.1. ufw (Ubuntu/Debian)
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow from 203.0.113.0/24 to any port 22 proto tcp # chỉ office IP
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw enable
from 203.0.113.0/24 giới hạn SSH về range IP văn phòng/VPN — cực hiệu quả. Nếu IP cố định không có, dùng VPN/jump host.
6.2. firewalld (RHEL/Fedora)
sudo firewall-cmd --permanent --new-zone=ssh-admin
sudo firewall-cmd --permanent --zone=ssh-admin --add-source=203.0.113.0/24
sudo firewall-cmd --permanent --zone=ssh-admin --add-service=ssh
sudo firewall-cmd --reload
6.3. Cloud SG
Cloud (AWS/GCP/Azure) có security group riêng — là firewall trước firewall máy. Giới hạn source CIDR ở đây là bước đầu tiên trước khi chạm OS.
7. fail2ban — giảm noise khi không giới hạn nguồn được
Không phải lúc nào cũng whitelist IP được (user mobile, field engineer). fail2ban theo dõi log:
# /etc/fail2ban/jail.d/sshd.local
[sshd]
enabled = true
port = ssh
filter = sshd
logpath = %(sshd_log)s
maxretry = 5
findtime = 600
bantime = 3600
ignoreip = 127.0.0.1/8 ::1 203.0.113.0/24
maxretryfail trongfindtime→ banbantimegiây.ignoreipwhitelist để tránh tự ban.- Lưu ý: với
PasswordAuthentication no, fail2ban ít việc hơn vì bot không đến đâu; nhưng vẫn hữu ích chặn scanner.
Bản thân fail2ban không thay cho khoá cứng — chỉ giảm ồn ào.
8. 2FA / MFA
Muốn bắt buộc key + OTP:
# sshd_config
AuthenticationMethods publickey,keyboard-interactive
Cài libpam-google-authenticator hoặc tích hợp Duo, YubiKey challenge-response. Với admin critical, MFA là ranh giới mạnh giữa “key bị lộ” và “bị vào”.
Ngoài ra, hardware key (YubiKey) dùng resident ssh-keygen -t ed25519-sk kết hợp PIN/touch — an toàn hơn key phần mềm.
9. Match block: policy theo user/group/host
sshd_config hỗ trợ Match để rule khác nhau tuỳ điều kiện:
Match Group admins
PermitRootLogin no
PasswordAuthentication no
AuthenticationMethods publickey,keyboard-interactive
Match Group sftpusers
ChrootDirectory /srv/sftp/%u
ForceCommand internal-sftp
X11Forwarding no
AllowTcpForwarding no
Match Address 10.0.0.0/8
PermitRootLogin prohibit-password
Ví dụ: user admins bắt buộc 2FA, sftpusers chroot, internal network được nhẹ hơn.
Match phải đặt cuối config — các directive sau nó áp dụng cho match đó.
10. Audit và log
10.1. Log cần theo dõi
/var/log/auth.log(Debian) hoặc/var/log/secure(RHEL): login/logout, key fingerprint (nếuLogLevel VERBOSE).journalctl -u ssh -fđể xem live.
10.2. Tập trung log
Đẩy về SIEM / log aggregator (ELK, Loki, Datadog). Phát hiện pattern:
- Login từ IP lạ ngoài giờ.
- Fail tăng đột biến → brute force.
- User system (postgres, root) bất ngờ login → compromise.
10.3. Thông báo realtime
# /etc/ssh/sshrc (hoặc ~/.ssh/rc)
echo "Login from $SSH_CONNECTION at $(date)" \
| mail -s "SSH login $USER@$(hostname)" [email protected]
Hoặc integration Slack/Telegram cho máy quan trọng.
10.4. last, lastb, w
last -a # login thành công
sudo lastb -a # login thất bại
w # ai đang login
who -u # idle time
11. Rotation và vệ sinh định kỳ
- Audit
authorized_keysđịnh kỳ: ai rời team → xoá key. Script so khớp với danh sách nhân sự. - Rotate host key không cần thường xuyên nhưng thay khi nghi ngờ bị lộ. Thông báo client để update
known_hosts. known_hostsbên client: xoá entry cũ khi reinstall server (ssh-keygen -R host).- Patch OpenSSH: theo dõi CVE (CVE-2024-6387 regreSSHion vừa qua nhắc nhở rằng SSH cũng có 0-day thực). Auto-update security updates (
unattended-upgradesUbuntu).
12. Bonus: bảo vệ thêm
- sshguard: giống fail2ban nhưng nhẹ hơn, viết bằng C.
- CrowdSec: đẩy reputation phân tán — ban IP từ feed cộng đồng.
- Knock/port knocking: “bí mật” sequence port để mở SSH — security theatre thuần; đừng phụ thuộc.
- VPN (WireGuard, Tailscale): ẩn SSH sau VPN, chỉ client VPN mới thấy port 22. Thường là giải pháp sạch nhất.
13. Checklist rút gọn
- Ed25519 key với passphrase; passphrase mạnh.
-
~/.sshvàauthorized_keyspermission đúng. -
PermitRootLogin no,PasswordAuthentication notrongsshd_config. -
AllowGroupsgiới hạn user được SSH. - Firewall whitelist nguồn hoặc VPN/bastion.
-
fail2ban(hoặc sshguard) chạy. - 2FA cho admin account quan trọng.
- Log tập trung, alert khi bất thường.
- Cập nhật OpenSSH kịp thời (auto security updates).
- Quy trình onboard/offboard xoá key khi nhân sự đổi.
14. Tóm tắt
Cứng hoá SSH là tổ hợp của ba tầng: ai được đến (firewall, bastion), đến rồi phải làm gì (key + 2FA, AllowGroups), và bạn thấy được gì sau đó (log, alert). Không có cấu hình “vĩnh viễn an toàn” — chỉ có cấu hình hiện tại an toàn trước các mối đe doạ hiện tại. Review checklist sau mỗi vài tháng, theo dõi CVE OpenSSH, audit key định kỳ. Một server mới boot cần 15 phút để được hardening đúng; phần thưởng là đêm ngủ yên.