Cara Login SSH: Dasar Server Linux dan SSH Key untuk Pemula
Cara login SSH ke server Linux dengan key Ed25519 tanpa password, membuka port 22, 80, dan 443 saja, serta menghindari jebakan SSH di balik Cloudflare.
Oleh Matthews WongDitinjau pada
Poin utama
SSH adalah protokol terenkripsi untuk login ke server Linux dan menjalankan perintah di sana. Dengan autentikasi key, laptop Anda menyimpan private key dan server hanya menyimpan public key pasangannya, jadi login dengan password bisa dimatikan. Firewall lalu menyisakan tiga pintu saja untuk server web: 22, 80, dan 443.
Setelah pelajaran ini, Anda bisa
- Membuat pasangan key Ed25519 dan login ke server dengannya, dengan login password dimatikan.
- Menjelaskan authorized_keys, known_hosts, dan izin file yang diwajibkan SSH.
- Menentukan port mana yang dibuka server web, dan kenapa SSH tidak boleh lewat proxy Cloudflare.
Ikuti diagramnya langkah demi langkah
Tekan Putar, atau melangkah dengan panah. Setiap langkah juga ditulis di bawah diagram.
Cara kerja login SSH dengan key, dan pintu mana yang tetap terbuka
ssh-keygen membuat pasangan key, public key masuk ke authorized_keys, fingerprint server disimpan di known_hosts, private key membuktikan siapa Anda, ufw hanya membuka 22, 80, dan 443, dan SSH tidak boleh lewat proxy Cloudflare.
Tombol: ← → langkah · Home / End · K putar atau jeda
Laptop Anda dan sebuah VPS Linux. SSH dipakai untuk login lewat port 22; nginx melayani web di port 80 dan 443. Key-nya belum dibuat.
ssh-keygen membuat dua file: private key yang tidak pernah meninggalkan laptop Anda, dan public key yang boleh dibagikan ke siapa saja.
ssh-keygen -t ed25519Tebak langkah berikutnya Dari dua file baru itu, mana yang dipasang di server? (Private key, id_ed25519 · Public key, id_ed25519.pub)Taruh public key di authorized_keys di server. Hanya pemiliknya yang boleh menulis ke file itu; kalau tidak, sshd menolak memakainya.
ssh-copy-id username@target_machineSaat pertama kali terhubung, server menunjukkan fingerprint host key-nya. Laptop Anda mencatatnya di known_hosts dan memberi peringatan keras kalau fingerprint itu berubah.
ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pubLaptop Anda membuktikan bahwa ia memegang private key dengan membuat tanda tangan untuk sesi ini; sshd mencocokkannya dengan authorized_keys. Dengan password dimatikan, menebak password tidak ada gunanya.
PasswordAuthentication no PermitRootLogin noKebijakan default ufw menolak semua trafik masuk. Izinkan 22 untuk SSH, 80 dan 443 untuk web, lalu aktifkan. Port aplikasi tetap di 127.0.0.1.
sudo ufw allow 22 sudo ufw allow 80/tcp sudo ufw allow 443/tcp sudo ufw enableTebak langkah berikutnya CI menjalankan ssh ke hostname situs yang di-proxy Cloudflare. Apa yang terjadi? (Koneksinya tidak pernah sampai ke server · Cloudflare meneruskannya ke port 22)Cloudflare mem-proxy port web seperti 80 dan 443, bukan SSH. SSH dari CI ke hostname yang di-proxy tidak pernah sampai ke server, jadi arahkan ke nama DNS-only.
Bagaimana cara kerja autentikasi SSH key?
ssh-keygen -t ed25519 membuat dua file: private key, ~/.ssh/id_ed25519, dan public key, ~/.ssh/id_ed25519.pub. Private key tidak pernah meninggalkan laptop Anda, dan manualnya menyebut file itu tidak boleh bisa dibaca siapa pun selain Anda. Public key sama sekali tidak perlu dirahasiakan. Anda menambahkannya ke ~/.ssh/authorized_keys di server, yaitu file yang mendaftar public key yang boleh login sebagai user tersebut.
Baca selengkapnya (2)Tutup
Saat Anda terhubung, klien menandatangani data yang unik untuk sesi itu dengan private key, lalu server memeriksa dua hal: public key-nya ada di authorized_keys, dan tanda tangannya benar. Kepemilikan private key itulah buktinya, jadi key itu sendiri tidak pernah dikirim lewat jaringan. Begitu login password dimatikan, penyerang yang mencoba menebak password tidak akan berhasil, karena server tidak lagi menerima password sama sekali.
Server juga membuktikan identitasnya. Pada koneksi pertama, ssh menampilkan fingerprint host key server dan, setelah Anda menerimanya, menyimpannya di ~/.ssh/known_hosts. Kalau key itu suatu saat berubah, ssh menampilkan peringatan keras dan menolak melanjutkan, karena host key yang berubah bisa berarti ada pihak lain di antara Anda dan server.
Kenapa SSH peduli soal izin file?
Mode file di Linux punya bit read (4), write (2), dan execute (1) untuk tiga pihak: pemilik, grup, dan semua orang lain. chmod 600 berarti baca dan tulis hanya untuk pemilik. chmod 700 pada folder berarti hanya pemilik yang bisa membukanya. Mode yang disarankan adalah 700 untuk ~/.ssh dan 600 untuk authorized_keys serta private key Anda.
Baca selengkapnya (1)Tutup
OpenSSH tidak sekadar percaya, tetapi memaksakannya. Dengan StrictModes aktif, yang merupakan default, sshd menolak file authorized_keys kalau file itu, folder ~/.ssh, atau folder home bisa ditulisi user lain, dan klien ssh mengabaikan private key yang bisa diakses orang lain. Jadi chmod 777 untuk membereskan login yang gagal justru memperburuk keadaan. Solusinya kebalikannya: kurangi izinnya.
Port mana yang harus terbuka di server web?
Port adalah pintu bernomor di sebuah mesin. Server web butuh tiga pintu terbuka ke internet: 22 untuk SSH, 80 untuk HTTP, dan 443 untuk HTTPS. Selebihnya tetap tertutup. Di Ubuntu, alatnya adalah ufw, tool konfigurasi firewall default, yang terpasang dalam keadaan nonaktif dengan kebijakan default menolak trafik masuk. Izinkan port 22 sebelum mengaktifkannya: mengaktifkan ufw bisa memutus koneksi yang sedang berjalan, termasuk sesi SSH yang sedang Anda pakai.
Baca selengkapnya (1)Tutup
Docker menambah satu jebakan. Trafik ke port yang dipublikasikan container sudah dialihkan sebelum aturan ufw diterapkan, jadi container yang dipublikasikan di semua interface tetap bisa diakses walaupun ufw bilang port itu tertutup. Dokumentasi Docker sendiri menyebut keduanya tidak kompatibel. Solusinya, publikasikan port aplikasi hanya di 127.0.0.1, seperti yang dilakukan file compose situs ini: hanya nginx, di mesin yang sama, yang bisa menjangkau aplikasi.
Kenapa SSH timeout di balik proxy Cloudflare?
Saat record DNS di-proxy, Cloudflare menjawab lookup dengan alamat anycast miliknya sendiri dan berdiri di antara pengunjung dan server Anda. Cloudflare mem-proxy daftar port web yang tetap, seperti 80 dan 443. Port 22 tidak ada di daftar itu, dan dokumentasi Cloudflare sendiri memakai server SSH di port 22 sebagai contoh trafik yang butuh record DNS-only.
Baca selengkapnya (1)Tutup
Jadi SSH ke hostname yang di-proxy tidak pernah sampai ke server Anda, dan deploy CI yang memakainya akan menggantung sampai timeout. Domain situs ini di-proxy, karena itu deploy-nya membaca host SSH dari secret yang bisa diarahkan ke nama DNS-only atau alamat server. Ada harga yang dibayar: record DNS-only memperlihatkan alamat server Anda kepada siapa pun yang mencarinya.
Konfigurasi nyata di balik pelajaran ini
Kutipan dari repositori yang membangun dan men-deploy situs ini, apa adanya.
- name: Deploy over SSH
uses: appleboy/ssh-action@v1.0.3
with:
host: ${{ secrets.VPS_SSH_HOST }}
username: ${{ secrets.VPS_SSH_USER }}
key: ${{ secrets.VPS_SSH_KEY }}
port: ${{ vars.VPS_SSH_PORT || 22 }}
script: |
set -euo pipefail
cd "${{ vars.DEPLOY_PATH }}"Langkah SSH di job deploy. Host, user, dan private key diambil dari secret terenkripsi, dan port dari variable yang default-nya 22, jadi tidak ada alamat atau nama user yang tertulis di repository. set -euo pipefail menghentikan script di server pada perintah pertama yang gagal, jadi cd yang gagal tidak akan pernah membuat perintah berikutnya berjalan di folder yang salah.
# Write runtime env from GitHub Secrets/Variables. Quoted heredoc so
# shell does not expand anything in the (already-rendered) values.
cat > .env <<'ENVEOF'
GROQ_API_KEY=${{ secrets.GROQ_API_KEY }}
RESEND_API_KEY=${{ secrets.RESEND_API_KEY }}
CONTACT_FROM_EMAIL=${{ vars.CONTACT_FROM_EMAIL }}
CONTACT_TO_EMAIL=${{ vars.CONTACT_TO_EMAIL }}
IMAGE=${{ needs.build-and-push.outputs.image }}
APP_PORT=${{ vars.APP_PORT || 3002 }}
ENVEOF
chmod 600 .envHal pertama yang dilakukan sesi SSH. Penanda heredoc yang diberi tanda kutip, <<'ENVEOF', mencegah shell di server mengekspansi apa pun di dalam nilai yang sudah diisi GitHub. chmod 600 lalu membuat file itu hanya bisa dibaca dan ditulis pemiliknya, mode yang sama dengan yang diminta SSH untuk authorized_keys.
RUN addgroup --system --gid 1001 nodejs && adduser --system --uid 1001 nextjs
COPY --from=builder /app/public ./public
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
USER nextjsPrinsip hak akses minimum yang sama, di dalam container. Stage terakhir membuat system user dengan uid 1001, memberinya kepemilikan file aplikasi, lalu berpindah ke user itu dengan USER sebelum aplikasi berjalan, jadi proses Node tidak pernah berjalan sebagai root.
Kesalahan yang benar-benar membuang waktu
Terkunci di luar server sendiri
Dua cara klasik: salah ketik di sshd_config lalu restart, dan mengaktifkan ufw sebelum mengizinkan port 22. Simpan salinan konfigurasi aslinya, jalankan sudo sshd -t untuk memeriksa file sebelum sudo systemctl restart ssh.service, dan biarkan satu sesi tetap terbuka selagi Anda menguji perubahan dari sesi kedua.
Mengandalkan ufw untuk menyembunyikan port Docker
Container yang dijalankan dengan -p 3000:3000 tetap bisa diakses dari internet walaupun ufw aktif, karena Docker mengalihkan trafik port yang dipublikasikan sebelum aturan ufw diterapkan. Publikasikan dengan -p 127.0.0.1:3000:3000 dan biarkan reverse proxy di mesin yang sama meneruskannya.
chmod 777 untuk membereskan login
SSH menolak key dan folder yang bisa ditulis atau dibaca user lain, jadi membuka izin lebar-lebar mengubah satu login yang gagal menjadi gagal terus. Pasang 700 di ~/.ssh dan 600 di authorized_keys serta private key.
SSH ke hostname yang di-proxy
Ubah sebuah hostname menjadi proxied di Cloudflare, maka setiap deploy yang SSH ke sana akan menggantung sampai timeout, sementara situsnya tetap berjalan normal. Berikan CI nama DNS-only atau alamat server.
Yang akan saya ubah di sini: jaga deploy key seperti akses root
Key CI login sebagai user yang bisa menjalankan Docker, dan dokumentasi Docker menyebut grup docker memberi hak setara root. Perlakukan key itu sebagai kredensial root dan rotasi secara berkala. Dua action SSH di sini di-pin ke tag, bukan ke commit SHA seperti saran panduan keamanan GitHub, dan keduanya tidak mengisi input fingerprint yang mengunci host key server.
| Proxied | DNS-only | |
|---|---|---|
| Hasil lookup DNS | Alamat anycast Cloudflare | Alamat asli server Anda |
| Trafik web di 80 dan 443 | Lewat Cloudflare, yang bisa men-cache dan melindunginya | Langsung ke server Anda |
| SSH di port 22 | Tidak diteruskan ke server Anda | Langsung sampai ke sshd |
| Dipakai untuk | Situs web | SSH, deploy CI, dan layanan non-HTTP lainnya |
Lakukan di komputer Anda sendiri
Anda butuh Docker dan klien ssh (macOS, Linux, atau WSL). Lab ini menjalankan server SSH sekali pakai di dalam container di mesin Anda sendiri, yang hanya dipublikasikan di localhost, jadi tidak ada server sungguhan yang tersentuh.
Di folder kosong, buat Dockerfile ini. Isinya meng-install OpenSSH, menambah user biasa, memasang key lab, dan mematikan login password serta login root.
FROM ubuntu:24.04 RUN apt-get update \ && apt-get install -y --no-install-recommends openssh-server \ && rm -rf /var/lib/apt/lists/* \ && mkdir -p /run/sshd \ && useradd --create-home --shell /bin/bash deployer COPY --chown=deployer:deployer --chmod=600 id_lab.pub /home/deployer/.ssh/authorized_keys RUN chmod 700 /home/deployer/.ssh \ && printf 'PasswordAuthentication no\nPermitRootLogin no\n' > /etc/ssh/sshd_config.d/99-lab.conf EXPOSE 22 CMD ["/usr/sbin/sshd", "-D", "-e"]Buat key lab, build image-nya, lalu jalankan di 127.0.0.1:2222. docker port memastikan port itu hanya dipublikasikan di localhost.
ssh-keygen -t ed25519 -f ./id_lab -N '' -C 'devops-lab' # lab only; use a passphrase for real keys docker build -t ssh-lab . docker run -d --name ssh-lab -p 127.0.0.1:2222:22 ssh-lab docker port ssh-labLogin dengan key dan ketik yes saat diminta konfirmasi fingerprint. Lalu tampilkan fingerprint host key milik server dan pastikan sama dengan yang Anda terima. Daftar file-nya menampilkan drwx------ dan -rw-------, yaitu mode 700 dan 600.
ssh -i ./id_lab -p 2222 deployer@127.0.0.1 'whoami; ls -ld ~/.ssh ~/.ssh/authorized_keys' docker exec ssh-lab ssh-keygen -l -f /etc/ssh/ssh_host_ed25519_key.pubCoba pintu yang seharusnya tertutup. Tanpa key, dan sebagai root, keduanya berakhir dengan Permission denied (publickey). sshd -T menampilkan pengaturan yang benar-benar dipakai sshd.
ssh -p 2222 -o PubkeyAuthentication=no deployer@127.0.0.1 # Permission denied (publickey) ssh -i ./id_lab -p 2222 root@127.0.0.1 # refused too docker exec ssh-lab sshd -T | grep -E '^(passwordauthentication|permitrootlogin) 'Build ulang server dari nol supaya mendapat host key baru, lalu sambungkan lagi. ssh berhenti dengan WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED, yaitu pemeriksaan known_hosts dari diagram tadi.
docker rm -f ssh-lab docker build --no-cache -t ssh-lab . docker run -d --name ssh-lab -p 127.0.0.1:2222:22 ssh-lab ssh -i ./id_lab -p 2222 deployer@127.0.0.1Bersihkan container dan entri lab di known_hosts Anda.
docker rm -f ssh-lab ssh-keygen -R '[127.0.0.1]:2222'
Tiga pertanyaan sebelum lanjut
Pilih jawaban untuk melihat kenapa benar atau salah. Tidak ada yang disimpan atau dinilai, jadi tebak saja dengan bebas.
Jawab dalam hati, lalu cek
1.Bagian mana dari pasangan key yang dipasang di server?Lihat jawabannya
Jawaban:Public key, di ~/.ssh/authorized_keys. Server hanya perlu memeriksa tanda tangan. Manual ssh-keygen menyebut file private key tidak boleh bisa dibaca siapa pun selain Anda, dan public key tidak perlu dirahasiakan.
2.Default OpenSSH untuk PermitRootLogin adalah prohibit-password. Kenapa tetap diubah menjadi no?Lihat jawabannya
Jawaban:prohibit-password hanya mematikan login password dan keyboard-interactive untuk root, jadi root masih bisa login dengan key. no memaksa semua orang masuk sebagai user biasa, jadi satu key yang dicuri tidak langsung berarti akses root.
3.Setelah hostname server diubah menjadi proxied di Cloudflare, deploy CI timeout di port 22. Kenapa?Lihat jawabannya
Jawaban:Cloudflare mem-proxy daftar port HTTP dan HTTPS yang tetap, dan 22 tidak termasuk. Dokumentasinya menyarankan record DNS-only untuk server SSH di port 22, jadi berikan CI nama DNS-only atau alamat server.
4.ufw aktif dan hanya mengizinkan 22, 80, dan 443, tetapi container yang dijalankan dengan -p 3000:3000 tetap menjawab dari internet. Kenapa?Lihat jawabannya
Jawaban:Docker mengalihkan trafik untuk port yang dipublikasikan sebelum aturan ufw diterapkan, jadi ufw tidak pernah melihatnya. Publikasikan di 127.0.0.1 dan pasang reverse proxy di depannya.
Bahasan mendalam terkait di situs ini
Sumber
Sumber primer yang diperiksa pada tanggal riset. Ikuti tautannya untuk versi terbaru.
- Halaman manual ssh-keygen(1)
- Halaman manual ssh(1): known_hosts dan authorized_keys
- sshd_config(5): PasswordAuthentication, PermitRootLogin, StrictModes
- Halaman manual sshd(8): izin file dan -T
- RFC 4252: protokol autentikasi SSH (metode public key)
- Dokumentasi Ubuntu Server: OpenSSH server
- Dokumentasi Ubuntu Server: firewall (ufw)
- Halaman manual ufw(8)
- Dokumentasi Docker: packet filtering dan firewall
- Dokumentasi Docker: langkah setelah instalasi di Linux (grup docker)
- Dokumentasi Cloudflare: port jaringan
- Dokumentasi Cloudflare: status proxy (proxied vs DNS-only)
- README appleboy/ssh-action (input fingerprint)
- Dokumentasi GitHub: referensi penggunaan aman (pin action)