
VPS mới khởi tạo là một con server "trần như nhộng": SSH cho phép password root, không firewall, không backup, không monitoring. Bài này là checklist 10 bước hardening cho VPS Ubuntu chạy production, copy-paste là xong, không lý thuyết suông. Khác với bài hướng dẫn beginner: ở đây mình focus security depth + automation cho server chạy 24/7.
Tại sao cần hardening ngay từ ngày đầu
Sự thật phũ: chỉ vài phút sau khi VPS có IP public, bot sẽ scan SSH/HTTP. Log auth.log trên 1 VPS để default sau 1 giờ có thể có vài trăm attempt login. Nếu bạn để root + password yếu, server bị compromise trước khi kịp deploy app.
Checklist dưới mất khoảng 30-45 phút lần đầu, sau đó scriptable cho mọi VPS mới. Mình áp dụng pattern này cho tất cả node của TND và khách thuê Cloud VPS tại TND.
Bước 1: SSH key + disable password
Phòng tuyến đầu tiên. Cách 90% bot không vào được dù brute-force.
# Trên máy local
ssh-keygen -t ed25519 -C "admin@laptop"
ssh-copy-id root@your-vps-ip
# Trên VPS, edit /etc/ssh/sshd_config
sudo nano /etc/ssh/sshd_configSửa các dòng sau:
PasswordAuthentication no
PermitRootLogin prohibit-password
PubkeyAuthentication yes
ChallengeResponseAuthentication no
UsePAM yes
X11Forwarding no
ClientAliveInterval 300
ClientAliveCountMax 2Reload SSH:
sudo systemctl reload sshBước 2: Non-root user + sudo, đổi SSH port (optional)
adduser deploy
usermod -aG sudo deploy
# Copy key sang user mới
rsync --archive --chown=deploy:deploy ~/.ssh /home/deployNếu bạn paranoid hơn, đổi SSH port để giảm noise scan:
# /etc/ssh/sshd_config
Port 2222
# /etc/ufw/applications.d/openssh-server (hoặc dùng allow trực tiếp)
sudo ufw allow 2222/tcp
sudo systemctl reload sshĐổi port không tăng security thật sự (security through obscurity), nhưng giảm 95% bot scan random port 22, log sạch hơn.
Bước 3: ufw firewall với default deny
Mặc định Ubuntu mở mọi port outbound, inbound chỉ chặn khi có firewall rule. Bật ufw deny mọi inbound trừ whitelist:
sudo ufw default deny incoming
sudo ufw default allow outgoing
sudo ufw allow 22/tcp # hoặc 2222 nếu đổi port
sudo ufw allow 80/tcp
sudo ufw allow 443/tcp
sudo ufw limit ssh # rate limit SSH brute force
sudo ufw enable
sudo ufw status numberedufw limit ssh tự block IP nào thử quá 6 connection trong 30 giây. Combo với fail2ban là 2 layer phòng thủ.
Bước 4: fail2ban với jail tùy chỉnh
sudo apt install fail2ban -y
sudo cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
sudo nano /etc/fail2ban/jail.localTìm và sửa section [DEFAULT]:
bantime = 3600 # ban 1 giờ
findtime = 600 # cửa sổ 10 phút
maxretry = 3 # 3 lần fail = ban
ignoreip = 127.0.0.1/8 ::1 YOUR_HOME_IPEnable jail SSH (auto enabled), restart:
sudo systemctl enable --now fail2ban
sudo fail2ban-client status sshdSau vài ngày check lại: sudo fail2ban-client status sshd sẽ thấy danh sách IP bị ban (thường vài chục đến vài trăm tùy mức độ scan).
Bước 5: Swap file (cứu OOM)
VPS gói nhỏ RAM 2-4GB rất dễ OOM khi build code hoặc spike traffic. Swap không thay RAM nhưng cứu được:
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab
# Tune swappiness (default 60, set 10 cho server)
echo 'vm.swappiness=10' | sudo tee -a /etc/sysctl.conf
sudo sysctl -pSwappiness 10 = ưu tiên giữ data trong RAM, chỉ dùng swap khi gần đầy. Server thường set vậy, desktop default 60.
Bước 6: Automatic security updates (unattended-upgrades)
VPS không cập nhật security patch là quả bom hẹn giờ. Bật auto-update chỉ cho security patch (không touch package khác để tránh break):
sudo apt install unattended-upgrades apt-listchanges -y
sudo dpkg-reconfigure -plow unattended-upgradesChọn Yes. Verify config:
cat /etc/apt/apt.conf.d/50unattended-upgrades | grep -A2 'Allowed-Origins'Phải có dòng "${distro_id}:${distro_codename}-security";. Test dry-run:
sudo unattended-upgrades --dry-run --debug 2>&1 | tail -20Nếu app nhạy cảm với restart, edit /etc/apt/apt.conf.d/50unattended-upgrades set:
Unattended-Upgrade::Automatic-Reboot "true";
Unattended-Upgrade::Automatic-Reboot-Time "03:00";Reboot 3h sáng (timezone server) sau khi kernel update.
Bước 7: Log rotation tránh đầy disk
Mặc định Ubuntu có logrotate, nhưng app/nginx/custom service thường log không rotate, phình ra GB sau vài tháng.
cat /etc/logrotate.d/nginxNếu chưa có, tạo:
sudo tee /etc/logrotate.d/nginx <<'EOF'
/var/log/nginx/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
create 0640 www-data adm
sharedscripts
postrotate
if [ -f /var/run/nginx.pid ]; then
kill -USR1 `cat /var/run/nginx.pid`
fi
endscript
}
EOFCleanup journal log (systemd):
sudo journalctl --vacuum-time=14d
# Set giới hạn vĩnh viễn
sudo nano /etc/systemd/journald.conf
# SystemMaxUse=500M
sudo systemctl restart systemd-journaldBước 8: Monitoring nhẹ (Netdata hoặc Uptime Kuma)
Không có monitoring = không biết server chết tới khi khách hàng report. Hai option nhẹ:
Netdata (metrics real-time)
bash <(curl -Ss https://my-netdata.io/kickstart.sh) --dont-waitMở port 19999, vào http://your-ip:19999 xem CPU, RAM, disk, network, processes real-time. Cẩn thận: nên đặt sau Nginx reverse proxy + basic auth, đừng để 19999 mở public.
Uptime Kuma (uptime monitor)
docker run -d --restart=always -p 3001:3001 \
-v uptime-kuma:/app/data --name uptime-kuma louislam/uptime-kuma:1Kuma ping/HTTP check endpoint mỗi N giây, ping Telegram/Discord/Email khi down. Self-hosted free, gọn.
Bước 9: Daily backup script + offsite
Snapshot provider tốt nhưng nếu account bị mất, snapshot cũng mất. Backup offsite (S3, B2, Wasabi) là bảo hiểm thật.
sudo apt install restic -y
# Init repo (B2/S3/local)
export RESTIC_REPOSITORY="b2:my-bucket:vps-backup"
export RESTIC_PASSWORD="strong-pass-here"
restic init
# Backup script /usr/local/bin/backup.sh
sudo tee /usr/local/bin/backup.sh <<'EOF'
#!/bin/bash
export RESTIC_REPOSITORY="b2:my-bucket:vps-backup"
export RESTIC_PASSWORD="strong-pass-here"
export B2_ACCOUNT_ID="..."
export B2_ACCOUNT_KEY="..."
# DB dump trước
mysqldump --all-databases | gzip > /tmp/db-$(date +%F).sql.gz
# Backup folder quan trọng
restic backup /etc /var/www /home/deploy /tmp/db-*.sql.gz
# Giữ 7 daily + 4 weekly + 6 monthly
restic forget --keep-daily 7 --keep-weekly 4 --keep-monthly 6 --prune
rm /tmp/db-*.sql.gz
EOF
sudo chmod +x /usr/local/bin/backup.sh
# Cron 3h sáng mỗi ngày
echo "0 3 * * * root /usr/local/bin/backup.sh >> /var/log/backup.log 2>&1" | sudo tee /etc/cron.d/backupRestic dedupe + encrypt mặc định, phục hồi từng file riêng được. B2 storage giá $6/TB/tháng, rẻ hơn S3.
restic snapshots liệt kê, restic restore latest --target /tmp/restore-test kéo về kiểm. Backup không test = backup không tồn tại.Bước 10: Snapshot provider (cứu khi nâng cấp lỗi)
Snapshot là backup full disk ở phía provider, restore 1 click. Khác backup file: snapshot phục hồi nguyên trạng OS + data trong vài phút.
Cách dùng đúng:
- Tạo snapshot trước mỗi lần upgrade kernel/major package
- Tạo snapshot trước khi đổi config quan trọng (Nginx, MySQL my.cnf)
- Tạo snapshot trước khi chạy migration DB
- Giữ tối thiểu 1 snapshot gần nhất + 1 snapshot tuần trước
Trên TND Cloud VPS, snapshot tạo từ panel, mất 30 giây đến vài phút tùy size disk.
Bonus: 5 thứ thường quên
1. Set hostname đúng
sudo hostnamectl set-hostname web01.example.com
sudo nano /etc/hosts
# Thêm: 127.0.1.1 web01.example.com web01Tránh log MTA, mail bounce vì hostname không hợp lệ.
2. NTP sync
sudo timedatectl set-timezone Asia/Ho_Chi_Minh
sudo apt install systemd-timesyncd -y
timedatectl statusThời gian sai = SSL fail, log lệch, JWT token invalid.
3. Disable IPv6 nếu không dùng
Mở port qua ufw chỉ áp IPv4, IPv6 vẫn dùng default. Nếu app không listen IPv6, disable cho gọn:
echo "net.ipv6.conf.all.disable_ipv6=1" | sudo tee -a /etc/sysctl.conf
sudo sysctl -pHoặc tốt hơn: configure ufw cho cả IPv6 trong /etc/default/ufw set IPV6=yes.
4. Auditd cho log security
sudo apt install auditd -y
sudo systemctl enable --now auditd
sudo ausearch -ts today -i | tail5. SSH banner để cảnh báo legal
sudo tee /etc/ssh/banner <<'EOF'
*****************************************************
* AUTHORIZED ACCESS ONLY *
* Unauthorized access prohibited and prosecuted. *
*****************************************************
EOF
# Trong /etc/ssh/sshd_config thêm:
# Banner /etc/ssh/bannerChecklist tổng kết (in ra dán cạnh máy)
| # | Bước | Thời gian |
|---|---|---|
| 1 | SSH key + disable password | 5 phút |
| 2 | Non-root user + (optional) đổi SSH port | 3 phút |
| 3 | ufw default deny + allow port | 2 phút |
| 4 | fail2ban jail SSH | 3 phút |
| 5 | Swap file + swappiness | 2 phút |
| 6 | Auto security updates | 3 phút |
| 7 | Log rotation + journal limit | 3 phút |
| 8 | Netdata / Uptime Kuma | 5 phút |
| 9 | Restic backup cron + test restore | 10 phút |
| 10 | Snapshot baseline | 2 phút |
Tổng ~40 phút. Sau lần đầu, viết shell script + Ansible playbook chạy 1 lệnh cho VPS sau là 5 phút xong.
Anti-pattern hay gặp
- Disable ufw "tạm thời" để debug, quên bật lại tuần sau. Luôn dùng
ufw allow from 1.2.3.4 to any port 9000tạm, đừng tắt firewall. - Chạy app bằng root: tạo user riêng cho từng service (www-data, nodeapp, postgres). Lỡ RCE thì attacker chỉ có quyền user đó.
- Backup nhưng không test restore: tới lúc cần phục hồi mới biết file backup corrupted.
- Mở MySQL/Redis port public: bind
127.0.0.1hoặc internal network, tuyệt đối không 0.0.0.0 cho DB. - Để default port 22 + password root: vài tiếng là bị brute force compromised, đặc biệt với password yếu.
Khi nào checklist này không đủ
10 bước trên đủ cho 95% web app, blog, API thông thường. Nếu chạy fintech, e-commerce có CC, healthcare data thì cần thêm: WAF (ModSecurity/Cloudflare), IDS (OSSEC/Wazuh), SIEM, encrypted disk (LUKS), HSM cho key. Đó là thế giới khác.
Với baseline web/API thông thường trên VPS TND với Ceph SSD + RAM ECC + DC VN, áp dụng 10 bước này là server bạn an toàn hơn 90% VPS production ngoài kia.
Bài viết liên quan
VPS mới, hardening trong 30 phút
TND Cloud VPS từ 199k/tháng: Ubuntu/Debian fresh install, snapshot 1 click, hỗ trợ apply checklist hardening. SSD Ceph Enterprise, RAM ECC, datacenter VN.
Xem bảng giá Cloud VPS- Self-host Plane.so trên VPS: Jira alternative cho startup
- CI/CD pipeline GitLab CE self-host trên VPS đầy đủ
- GPU VPS cho AI startup: ROI 2026
- Docker Swarm vs K3s cho startup 3 dev: chọn cái nào?
- Antidetect browser cho Etsy: Multilogin vs GoLogin vs Dolphin
- Claude Code là gì? Cách chạy Claude Code 24/7 trên VPS không cần để máy bật



