Lewati ke konten

Cara Menyiapkan VPS untuk Pemula, dari Seorang Pemula (Bagian 1: Mengamankan Sistem Dasar)

Bagian 1 dari penyiapan VPS pertama saya di Debian 13. Update, memberi nama server, satu pengguna dengan kunci SSH dan sudo, dan konfigurasi SSH yang hanya mengizinkan pengguna itu masuk.

Seri 5 bagian.

Pendahuluan

Ini VPS pertama saya. Mesin Debian 13 kecil, dan saya mau menjalankan proyek-proyek saya sendiri di sana, mulai dari situs ini.

Server baru belum dikonfigurasi apa-apa, jadi belum ada juga yang melindunginya. Saya mau paham setiap langkahnya sebelum menaruh apa pun di sana, jadi semuanya saya kerjakan manual dan saya catat sambil jalan. Itulah seri ini.

Di akhir, semuanya saya jadikan satu skrip, vps-setup.sh. Kalau cuma mau hasilnya, Bagian 5 menunjukkan cara menjalankannya. Kalau mau tahu apa yang dilakukannya ke server kita, mulai dari sini.

Akses Pertama

Provider memberi IP dan login root. Sambungkan:

ssh root@IP_VPS
Output
The authenticity of host 'IP_VPS (IP_VPS)' can't be established.
ED25519 key fingerprint is SHA256:qL3nR8vT1wX5yZ0aB4cD7eF9gH2jK6mN8pQ1rS3tU5v.
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes

Linux vps 6.12.48+deb13-amd64 #1 SMP Debian 6.12.48-1 x86_64
root@vps:~#

Pertanyaan fingerprint itu cuma muncul sekali. Kalau suatu saat muncul lagi untuk server yang sama, berarti ada yang berubah di sisi sana, dan saya mau tahu apa sebelum mengetik yes.

Saya akan pakai root beberapa menit ke depan, lalu tidak pernah login sebagai root lagi.

Update

Server dibuat dari image milik provider, dan image itu tidak pernah benar-benar terbaru:

apt update && apt full-upgrade -y
Output
Hit:1 http://deb.debian.org/debian trixie InRelease
Get:2 http://security.debian.org/debian-security trixie-security InRelease [48.0 kB]
...
14 upgraded, 0 newly installed, 0 to remove and 0 not upgraded.

Saya pakai full-upgrade, bukan upgrade, karena full-upgrade boleh memasang atau menghapus package kalau update-nya butuh, misalnya package kernel baru. upgrade biasa malah menahannya.

Lalu beberapa yang dasar:

apt install -y sudo curl ca-certificates nano less

sudo saya taruh paling depan karena ada alasannya. Image minimal Debian tidak membawa sudo, tapi grup sudo tetap ada, jadi menambahkan pengguna ke grup itu berhasil tanpa pesan apa pun. Kalau login root lalu dimatikan padahal perintah sudo-nya tidak ada, tidak ada lagi akun yang bisa membereskan apa pun.

Bagian-bagian berikutnya memasang yang mereka butuhkan masing-masing.

Nama dan Waktu

Dua hal kecil. Hostname muncul di prompt dan di setiap baris log, dan vps atau debian tidak akan berarti apa-apa buat saya nanti:

hostnamectl set-hostname my-vps
nano /etc/hosts
127.0.1.1	my-vps

Baris di /etc/hosts itu harus sama dengan nama baru, kalau tidak sudo akan mengeluh tidak bisa menemukan host-nya.

Beberapa provider mengatur hostname di setiap boot lewat cloud-init. Kalau provider kita begitu, ini mencegah namanya dikembalikan:

echo 'preserve_hostname: true' > /etc/cloud/cloud.cfg.d/99-hostname.cfg

Lalu zona waktu, supaya waktu di log pakai jam saya, bukan UTC:

timedatectl set-timezone Asia/Jakarta
timedatectl set-ntp true
timedatectl status
Output
               Local time: Wed 2026-05-20 16:20:33 WIB
           Universal time: Wed 2026-05-20 09:20:33 UTC
                 Time zone: Asia/Jakarta (WIB, +0700)
System clock synchronized: yes
              NTP service: active

Yang saya cari System clock synchronized: yes. Jam yang salah bikin sertifikat HTTPS gagal dengan cara yang membingungkan. Kalau NTP service bilang n/a, image-nya datang tanpa klien waktu, dan apt install -y systemd-timesyncd membereskannya.

Kunci SSH

Password bisa ditebak. Kunci tidak, setidaknya tidak dengan cara yang masuk akal. Jadi sebelum yang lain, saya buat kunci di laptop (bukan di server):

ssh-keygen -t ed25519 -C "vps-personal"
Output
Generating public/private ed25519 key pair.
Enter file in which to save the key (/home/you/.ssh/id_ed25519):
Enter passphrase for "/home/you/.ssh/id_ed25519" (empty for no passphrase):
Your identification has been saved in /home/you/.ssh/id_ed25519
Your public key has been saved in /home/you/.ssh/id_ed25519.pub

Hasilnya dua berkas. id_ed25519 itu privat dan tidak pernah keluar dari laptop. id_ed25519.pub yang saya berikan ke server:

cat ~/.ssh/id_ed25519.pub
Output
ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIH8kR2pQ7mZvN1xW4tYbL9cD0eF3gH5jK7lM2nP4qR6s vps-personal

Kalau yang muncul diawali -----BEGIN OPENSSH PRIVATE KEY-----, itu berkas yang salah.

Saya pilih Ed25519 karena kuncinya pendek dan cepat, dan semua OpenSSH modern mendukungnya. RSA juga bisa, tapi minimal harus 2048 bit supaya ada gunanya.

Satu Pengguna untuk Semuanya

Saya tidak mau bekerja sebagai root. Saya juga tidak mau punya banyak akun. Jadi server ini dapat tepat satu pengguna biasa, dan pengguna itu yang mengerjakan semuanya: login, pakai sudo, dan nanti memiliki semua kontainer.

Kembali ke server, sebagai root:

useradd --create-home --shell /bin/bash admin
usermod -aG sudo,adm,systemd-journal admin
passwd admin
Output
New password:
Retype new password:
passwd: password updated successfully

Grupnya:

  • sudo untuk menjalankan perintah admin.
  • adm dan systemd-journal untuk membaca log sistem tanpa harus mengetik sudo terus.

Password-nya cuma untuk sudo. Login akan pakai kunci saja beberapa menit lagi.

Cek dulu sudo benar-benar jalan untuk pengguna baru sebelum mengandalkannya:

su - admin -c 'sudo -v'
Output
[sudo] password for admin:

Kalau ia minta password dan menerimanya, aman. sudo: command not found berarti package-nya belum ada, jadi pasang dulu selagi root masih bisa dipakai.

Memberi Kunci ke Pengguna

Kunci publik harus masuk ke ~/.ssh/authorized_keys milik pengguna baru. Dari laptop:

ssh-copy-id admin@IP_VPS
Output
/usr/bin/ssh-copy-id: INFO: 1 key(s) remain to be installed -- if you are prompted now it is to install the new keys
admin@IP_VPS's password:

Number of key(s) added: 1

Ia login pakai password untuk terakhir kalinya, dan mengatur izin berkasnya dengan benar.

Kalau image dari provider sudah mematikan login password, cara itu tidak jalan. Kalau begitu, salin kunci milik root di server:

install -d -m 700 -o admin -g admin /home/admin/.ssh
install -m 600 -o admin -g admin /root/.ssh/authorized_keys /home/admin/.ssh/authorized_keys

install membuat berkasnya dengan pemilik dan izin yang benar sekaligus. cp biasa akan meninggalkannya dimiliki root, dan SSH diam-diam mengabaikan berkas kunci yang bukan milik penggunanya.

Coba dari laptop:

ssh admin@IP_VPS
Output
Linux my-vps 6.12.48+deb13-amd64 #1 SMP Debian 6.12.48-1 x86_64
admin@my-vps:~$

Tidak ada prompt password, dan prompt-nya diakhiri $, bukan #. Itu pengguna biasa yang login dengan kunci.

Mengunci SSH

Sekarang hardening-nya. Config utama Debian ada di /etc/ssh/sshd_config, tapi saya tidak mengeditnya. Di bagian atas berkas itu ada baris ini:

Include /etc/ssh/sshd_config.d/*.conf

sshd memakai nilai pertama yang ia temukan untuk setiap pengaturan, dan berkas yang di-include dibaca duluan, urut abjad. Banyak image VPS membawa 50-cloud-init.conf di sana yang isinya PasswordAuthentication yes. Perubahan apa pun yang kita buat di bawah berkas utama kalah dengannya.

Jadi pengaturan saya taruh di berkas yang urutannya paling awal:

sudo nano /etc/ssh/sshd_config.d/00-hardening.conf
Port 22022
PermitRootLogin no
PasswordAuthentication no
KbdInteractiveAuthentication no
PermitEmptyPasswords no
PubkeyAuthentication yes
AuthenticationMethods publickey
AllowUsers admin

MaxAuthTries 3
MaxSessions 4
LoginGraceTime 20
ClientAliveInterval 300
ClientAliveCountMax 2

PermitUserEnvironment no
X11Forwarding no
AllowAgentForwarding no
AllowTcpForwarding local
AllowStreamLocalForwarding no
GatewayPorts no
PermitTunnel no
UseDNS no
LogLevel VERBOSE

HostKey /etc/ssh/ssh_host_ed25519_key
HostKey /etc/ssh/ssh_host_rsa_key
KexAlgorithms mlkem768x25519-sha256,sntrup761x25519-sha512,curve25519-sha256
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com

Banyak ya, jadi singkatnya:

  • Port 22022. Bukan keamanan sungguhan, tapi bot menggempur port 22 seharian dan ini mengurangi bisingnya di log.
  • Tanpa root, tanpa password, hanya kunci, dan hanya admin. Ini baris-baris yang paling penting. Dengan AllowUsers, kalaupun suatu hari ada akun lain di server, akun itu tetap tidak bisa login lewat SSH.
  • MaxAuthTries, LoginGraceTime. Cepat menyerah pada koneksi yang tidak ke mana-mana.
  • Forwarding. Semua saya matikan kecuali AllowTcpForwarding local, supaya saya masih bisa ssh -L untuk menjangkau database di server dari laptop.
  • LogLevel VERBOSE mencatat fingerprint kunci yang dipakai di setiap login, jadi saya bisa lihat kunci mana yang masuk.
  • Kripto. Hanya key exchange, cipher, dan MAC yang modern. Key exchange pertama, mlkem768x25519-sha256, tahan komputer kuantum dan baru ada di OpenSSH 10, yang dibawa Debian 13. Host key ECDSA saya buang, Ed25519 dan RSA tetap untuk klien yang lebih tua.

Kalau versi OpenSSH-nya beda, cek dulu apa yang didukung sebelum menyalin baris-baris terakhir itu:

ssh -Q kex

Sebelum me-restart apa pun, tes berkasnya. Ada salah ketik di sini dan SSH tidak akan kembali:

sudo sshd -t

Tidak ada output berarti aman. Lalu tanya sshd apa yang benar-benar akan ia pakai, setelah semua berkas include:

sudo sshd -T | grep -Ei '^(port|permitrootlogin|passwordauthentication|allowusers) '
Output
port 22022
permitrootlogin no
passwordauthentication no
allowusers admin

Itu config yang efektif, dan cuma itu yang saya percaya. Kalau passwordauthentication di sini bilang yes, ada berkas lain yang menang.

Port Harus Diganti di Dua Tempat

Yang ini sempat menjebak saya. Debian 13 bisa menjalankan SSH lewat socket systemd, dan kalau begitu, yang membuka port adalah systemd, bukan sshd. Baris Port di atas diabaikan. Server saya diatur seperti itu:

systemctl is-enabled ssh.socket
Output
enabled

Kalau hasilnya juga enabled, beri tahu socket-nya soal port baru:

sudo mkdir -p /etc/systemd/system/ssh.socket.d
sudo nano /etc/systemd/system/ssh.socket.d/port.conf
[Socket]
ListenStream=
ListenStream=22022

ListenStream= yang kosong itu menghapus port default 22. Tanpanya, ia mendengarkan di dua port.

sudo systemctl daemon-reload
sudo systemctl restart ssh.socket

Kalau hasilnya disabled, sshd membuka port sendiri dan cukup di-restart:

sudo systemctl restart ssh

Yang mana pun, cek apa yang sedang mendengarkan:

sudo ss -tlnp | grep 22022
Output
LISTEN 0  4096   0.0.0.0:22022  0.0.0.0:*  users:(("systemd",pid=1,fd=52))
LISTEN 0  4096      [::]:22022     [::]:*  users:(("systemd",pid=1,fd=53))

Tes Sebelum Menutup Apa Pun

Jangan tutup sesi yang sedang dipakai. Buka terminal kedua di laptop dan sambungkan ke port baru:

ssh -p 22022 admin@IP_VPS

Kalau jalan, selesai. Kalau tidak, sesi pertama masih terbuka dan bisa dipakai untuk membetulkannya. Kalau sesi itu ditutup dan config barunya rusak, satu-satunya jalan masuk tinggal konsol web dari provider.

Dan untuk memastikan root benar-benar terkunci:

ssh -p 22022 root@IP_VPS
Output
root@IP_VPS: Permission denied (publickey).

Membuat Koneksi Lebih Pendek

Mengetik ssh -p 22022 admin@IP_VPS setiap kali cepat bikin bosan. SSH punya berkas config di sisi laptop untuk ini:

nano ~/.ssh/config
Host vps
	HostName IP_VPS
	User admin
	Port 22022
	IdentityFile ~/.ssh/id_ed25519
	IdentitiesOnly yes
chmod 600 ~/.ssh/config
ssh vps

IdentitiesOnly yes bikin SSH hanya menawarkan satu kunci itu. Tanpanya, ia mencoba semua kunci di agent, dan dengan MaxAuthTries 3 di server, kita bisa ditolak sebelum sampai ke kunci yang benar.

Enaknya, semua yang lain yang pakai SSH juga membaca berkas ini. scp file vps:~/ dan rsync ./dist/ vps:~/site/ langsung jalan, begitu juga Remote-SSH di VS Code.

Bagian Selanjutnya

Sejauh ini: sistem sudah di-update, ada satu pengguna dengan kunci dan sudo, root tidak bisa login, dan SSH hanya menerima kunci pengguna itu di port yang tidak standar.

Itu baru urusan login. Port lain masih terbuka, tidak ada yang mengawasi kegagalan berulang, dan update cuma terjadi kalau saya ingat. Bagian 2 membereskan itu: firewall, fail2ban, update otomatis, dan sedikit tuning.