VPS yang baru dibuat itu langsung diserang. Bukan “mungkin diserang”, tapi pasti. Coba saja buat VM baru, tunggu 15 menit, lalu jalankan sudo lastb | wc -l: kamu akan lihat puluhan sampai ratusan percobaan login gagal dari bot yang menebak password root sepanjang hari. Selama servermu masih menerima login password, cuma soal waktu sampai salah satu tebakan itu benar.
Kabar baiknya: menutup pintu itu cuma butuh lima langkah dan sekitar sepuluh menit. Panduan ini memakai Ubuntu (22.04/24.04), tapi konsepnya sama untuk hampir semua distro.
Prasyarat penting: kamu harus sudah bisa login pakai SSH key sebelum mematikan login password. Kalau belum, kamu bakal terkunci dari servermu sendiri. Belum setup? Jalankan dari laptop:
ssh-keygen -t ed25519(kalau belum punya key), lalussh-copy-id <user>@<ip-vm>. Tesssh <user>@<ip-vm>: kalau masuk tanpa diminta password, kamu aman lanjut.
1. SSH ke VM
Masuk sebagai user biasa (bukan root):
ssh <user>@<ip-vm-kamu> 2. Masuk ke shell root
Semua perintah berikutnya butuh root, jadi daripada mengetik sudo berulang-ulang:
sudo su - Sekalian update sistem dulu, karena patch keamanan terbaru itu bagian dari hardening juga:
apt update && apt upgrade -y 3. Kunci konfigurasi SSH
Ini langkah paling penting. Edit config SSH:
vim /etc/ssh/sshd_config Cari dan set dua baris ini (hapus tanda # di depannya kalau ada):
PermitRootLogin prohibit-password
PasswordAuthentication no Artinya:
PermitRootLogin prohibit-password: root masih bisa login, tapi hanya dengan SSH key, tidak bisa dengan password. Bot yang menebak password root jadi menebak pintu yang sudah dilas mati.PasswordAuthentication no: mematikan login password untuk semua user. Mulai sekarang satu-satunya jalan masuk adalah SSH key.
Lalu restart SSH supaya config baru berlaku:
systemctl restart ssh ⚠️ Gotcha Ubuntu 24.04: config kamu bisa di-override
Ubuntu modern membaca file tambahan di /etc/ssh/sshd_config.d/*.conf, dan cloud provider sering menaruh file seperti 50-cloud-init.conf berisi PasswordAuthentication yes di sana, dan file itu menang atas editanmu di sshd_config. Cara cek nilai yang benar-benar dipakai:
sshd -T | grep -Ei 'passwordauthentication|permitrootlogin' Kalau outputnya masih passwordauthentication yes, cari biang keroknya:
grep -ri passwordauthentication /etc/ssh/sshd_config.d/ Edit file itu (atau hapus barisnya), lalu systemctl restart ssh lagi.
Tes sebelum menutup sesi
Aturan emas hardening SSH: jangan tutup sesi yang sedang aktif sebelum memastikan kamu masih bisa masuk. Buka terminal baru di laptop dan tes:
ssh <user>@<ip-vm-kamu> Masuk tanpa password? Aman. Ditolak? Perbaiki dulu dari sesi lama yang masih terbuka.
4. Pasang firewall (UFW)
Prinsipnya: tutup semua port, buka hanya yang dipakai. Daftarkan dulu port yang mau dibuka, dan wajib port 22 duluan, karena begitu UFW aktif tanpa rule SSH, sesi berikutnya tidak akan bisa connect:
ufw allow 22/tcp # SSH, WAJIB, kalau tidak kamu terkunci
ufw allow 80/tcp # HTTP
ufw allow 443/tcp # HTTPS
ufw allow 3000/tcp # opsional: panel/app di port 3000, tutup lagi kalau sudah tidak perlu Perintah allow di atas baru mendaftarkan rule, belum menyalakan firewall-nya. Cek dulu statusnya:
ufw status Outputnya harusnya Status: inactive. Sekarang nyalakan:
ufw enable UFW akan memberi peringatan bahwa ini bisa memutus koneksi SSH. Karena port 22 sudah kita allow, jawab y. Cek lagi:
ufw status Sekarang outputnya Status: active plus daftar port yang dibuka:
Status: active
To Action From
-- ------ ----
22/tcp ALLOW Anywhere
80/tcp ALLOW Anywhere
443/tcp ALLOW Anywhere
3000/tcp ALLOW Anywhere Nanti kalau mau menutup port (misal 3000 setelah setup selesai): ufw status numbered untuk lihat nomor rule-nya, lalu ufw delete <nomor>.
Catatan buat pengguna Docker: container yang publish port (misal
-p 5432:5432) mem-bypass UFW, karena trafiknya lewat chainFORWARDmilik Docker, bukan chainINPUTtempat rule UFW berada. Jadi jangan mengandalkan UFW untuk melindungi port container; jangan expose port yang tidak perlu dari container-nya. Cek port yang benar-benar listening denganss -tlnp.
5. Pasang fail2ban
Login password sudah mati, tapi bot tetap akan mengetuk-ngetuk port 22 ribuan kali sehari, memenuhi log dan memakan sedikit resource. fail2ban membaca log login gagal dan otomatis mem-ban IP yang mencoba berulang kali:
apt install -y fail2ban
systemctl enable --now fail2ban Konfigurasi default sudah mengaktifkan jail sshd (ban beberapa menit setelah 5x gagal). Cek kerjanya:
fail2ban-client status sshd Kamu akan lihat jumlah percobaan yang gagal dan daftar IP yang sedang di-ban. Biarkan beberapa jam lalu cek lagi: biasanya daftarnya sudah terisi, dan itu bukti langkah 3 sampai 5 tadi bukan paranoia.
Kalau systemctl status fail2ban menunjukkan error soal log file yang tidak ditemukan (kadang terjadi di instalasi minimal tanpa rsyslog), arahkan fail2ban ke journal systemd:
printf '[sshd]\nbackend = systemd\n' > /etc/fail2ban/jail.d/sshd-systemd.conf
systemctl restart fail2ban Rekap
Sepuluh menit, dan hasilnya:
- SSH masuk sebagai user biasa →
sudo su -untuk kerja sebagai root. - Update sistem: patch keamanan terbaru terpasang.
- Login password mati, root dikunci: satu-satunya jalan masuk adalah SSH key.
- Firewall aktif: hanya port 22/80/443 (dan 3000 sementara) yang terbuka.
- fail2ban jalan: bot yang masih memaksa masuk otomatis di-ban.
Ini hardening dasar, cukup untuk membuat servermu keluar dari kategori sasaran empuk. Kalau mau lapisan berikutnya: ganti port SSH default, aktifkan unattended-upgrades untuk patch otomatis, dan pakai user non-root untuk aplikasi.
Langkah selanjutnya setelah server aman: deploy aplikasimu. Saya sudah tulis panduannya di Deploy Next.js ke VPS IDCloudHost dengan Dokploy, yang bagian hardening-nya persis lima langkah di artikel ini.
Proses seperti ini juga saya praktikkan langsung di channel YouTube Pixel Developer. Kalau kamu lebih suka format nonton sambil praktik, mampir ke arsip episodenya.
