Gambaran Umum
SSH tunneling adalah teknik memanfaatkan protokol SSH (Secure Shell) untuk membuat jalur koneksi terenkripsi antara dua mesin, sehingga traffic jaringan yang seharusnya dikirim secara terbuka dapat "diselubungi" dalam saluran SSH yang aman. Secara sederhana, SSH tunneling membungkus (encapsulate) data dari protokol lain ke dalam sesi SSH, lalu meneruskannya ke tujuan seolah-olah berasal dari mesin remote. Hasilnya, siapa pun yang mencoba menyadap koneksi hanya akan melihat traffic SSH terenkripsi, tanpa bisa membaca payload di dalamnya.
Teknik ini penting karena di lingkungan jaringan yang tidak terpercaya—misalnya Wi-Fi publik, jaringan kantor yang ketat, atau infrastruktur cloud dengan private subnet—data sensitif seperti kredensial database, API key, atau cookie HTTP bisa dengan mudah dimata-matai (sniffed) jika dikirim tanpa enkripsi. SSH tunneling menjawab masalah itu tanpa memerlukan setup VPN penuh; cukup satu mesin yang bisa di-SSH, dan seluruh port yang dibutuhkan bisa diakses secara aman. Selain aspek keamanan, tunneling juga dipakai untuk melewati firewall yang membatasi akses keluar—karena kebanyakan firewall tidak memblokir port 22 (SSH), traffic yang dibungkus SSH lolos tanpa hambatan.
Untuk memahami analoginya, bayangkan Anda ingin mengirim paket barang ke ruangan yang dijaga satpam, tetapi Anda tidak punya akses langsung. Namun, Anda punya teman—sebut saja Andi—yang boleh masuk ke ruangan itu. Maka Anda menyuruh Andi keluar, menyerahkan paket kepadanya, lalu Andi membawa paket itu masuk dan menaruhnya di meja tujuan. Dalam konteks jaringan: Andi adalah SSH server, paket adalah request dari aplikasi lokal Anda, dan ruangan jaga satpam adalah service yang berada di balik firewall. Contoh konkret: Anda punya MySQL server di IP 10.0.0.5:3306 dalam private VPC, tidak bisa diakses dari laptop Anda di rumah. Tetapi ada bastion host di 203.0.113.10 yang bisa di-SSH. Dengan SSH tunneling, Anda mengalihkan port 3306 lokal ke 10.0.0.5:3306 via bastion, lalu cukup mysql -h 127.0.0.1 -P 3306 dari laptop—seolah-olah database itu berjalan di mesin lokal.
Mekanisme di Baliknya
Konsep inti SSH tunneling adalah port forwarding: mengaitkan (bind) sebuah port di sisi lokal (atau remote) ke port tujuan di sisi lawan, lalu semua data yang masuk ke port itu diteruskan melalui terowongan SSH. Saat Anda menjalankan perintah tunneling, SSH client membuka koneksi TCP ke SSH server seperti biasa, kemudian di dalam sesi itu ia membuat logical channel tambahan sesuai jenis tunnel yang diminta. Setiap byte yang ditulis ke port lokal dienkripsi oleh SSH client, dikirim melalui sesi SSH, didekripsi oleh SSH server, lalu diteruskan ke host:port tujuan. Balikannya, respons dari tujuan mengalir lewat jalur yang sama dalam arah sebaliknya.
Ada tiga komponen utama yang harus dipahami. Pertama, SSH client—mesin Anda—yang bertindak sebagai pengguna tunnel. Client bertanggung jawab membuka koneksi ke server, melakukan handshake kryptografi, lalu membuat channel forwarding. Kedua, SSH server—mesin remote—yang menerima koneksi, mengautentikasi client, kemudian bertindak sebagai proxy: menerima data dari channel, membuka koneksi TCP ke tujuan akhir, dan meneruskan data. Ketiga, port binding—yakni aturan mapping antara port lokal, port remote, dan port tujuan. Mapping ini menentukan arah dan jenis tunnel.
SSH tunneling hadir dalam tiga varian yang masing-masing memiliki arah dan kasus penggunaan berbeda. Local port forwarding (-L) mengikat port di mesin client dan meneruskan ke host:port yang bisa dijangkau dari mesin server. Ini yang paling sering dipakai untuk mengakses service internal. Remote port forwarding (-R) kebalikannya: mengikat port di mesin server dan meneruskan ke host:port yang bisa dijangkau dari mesin client. Ini dipakai saat Anda ingin membiarkan pihak luar mengakses service di mesin Anda. Dynamic port forwarding (-D) mengikat sebuah port di mesin client sebagai SOCKS proxy—sehingga aplikasi apa pun yang mendukung SOCKS bisa merute traffic-nya lewat tunnel tanpa perlu membuat satu forwarding per tujuan.
Praktik di Dunia Nyata
Skenario 1: Mengakses Database Internal via Local Forwarding
Anda memiliki PostgreSQL di 10.0.1.50:5432 dalam private subnet. Hanya bastion host bastion.example.com yang bisa di-SSH dari internet. Anda ingin menjalankan psql dari laptop tanpa mengekspos port database.
# Buka tunnel di background
ssh -f -N -L 15432:10.0.1.50:5432 admin@bastion.example.com
# Konek ke database seolah-olah lokal
psql -h 127.0.0.1 -p 15432 -U dbadmin -d production
Output yang diharapkan saat tunnel berjalan:
production=> \conninfo
You are connected to database "production" as user "dbadmin" on host "127.0.0.1" at port "15432".
Flag -f membuat SSH berjalan di background setelah autentikasi berhasil, dan -N menyatakan tidak ada perintah remote yang dijalankan—hanya tunnel saja.
Skenario 2: Mengakses Web UI Internal (Jenkins, Grafana, dll)
Tim DevOps Anda menjalankan Grafana di 10.0.2.20:3000, hanya bisa diakses dari dalam VPC. Dengan satu baris perintah, Anda bisa membuka dashboard di browser lokal.
ssh -L 3000:10.0.2.20:3000 dev@bastion.example.com
Setelah perintah di atas dijalankan, buka browser dan akses http://localhost:3000. Semua request HTTP dikirim melalui SSH ke bastion, lalu bastion meneruskannya ke Grafana. Anda tidak perlu VPN, tidak perlu membuka port 3000 ke internet, dan semua traffic terenkripsi end-to-end antara laptop dan bastion.
Skenario 3: Remote Forwarding untuk Mengekspos Service Lokal
Anda sedang mengembangkan webhook handler yang berjalan di localhost:8080 di laptop Anda, tetapi payment gateway perlu mengirim callback ke URL publik. Anda punya VPS di 203.0.113.50.
ssh -R 9080:localhost:8080 user@203.0.113.50
Sekarang, ketika payment gateway mengirim POST ke http://203.0.113.50:9080/webhook, request itu diterima oleh SSH server di VPS, dikirimkan melalui tunnel ke laptop Anda, dan sampai di aplikasi di port 8080. Catatan penting: pada OpenSSH versi lama, GatewayPorts di sshd_config harus di-set ke yes atau clientspecified agar port 9080 di-bind ke 0.0.0.0 (bukan hanya 127.0.0.1 di VPS), sehingga bisa diakses dari luar.
Skenario 4: Dynamic Forwarding sebagai SOCKS Proxy
Anda sedang bekerja dari kafe dan ingin semua traffic browsing—tidak hanya satu port—melewati server kantor. Dynamic forwarding membuat SOCKS5 proxy yang bisa dipakai browser atau aplikasi lain.
ssh -D 1080 -f -N user@office.example.com
Kemudian konfigurasi browser Anda (atau sistem) untuk menggunakan SOCKS5 proxy di 127.0.0.1:1080. Setelah itu, setiap koneksi HTTP, HTTPS, DNS query (jika mendukung remote DNS), dan bahkan koneksi ke IP internal kantor akan dirutekan melalui SSH tunnel. Ini bukan pengganti VPN penuh—hanya traffic yang melewati SOCKS proxy yang akan di-tunnel—tetapi untuk kebanyakan kebutuhan browsing dan debugging, ini sudah memadai.
Skenario 5: Chained Tunnel untuk Menjangkau Host Lebih Dalam
Kadang tujuan tidak berada satu hop dari bastion, tetapi dua hop atau lebih. Misalnya Anda perlu mengakses Redis di 192.168.5.10:6379 yang hanya bisa dijangkau dari app-server.internal, bukan dari bastion langsung. Anda bisa membuat dua tunnel bertingkat.
# Tunnel pertama: lokal 16379 -> app-server:6379 via bastion
ssh -L 16379:192.168.5.10:6379 admin@bastion.example.com
# Atau jika bastion tidak bisa menjangkau 192.168.5.10 langsung,
# gunakan ProxyJump
ssh -J admin@bastion.example.com -L 16379:192.168.5.10:6379 dev@app-server.internal
Dengan ProxyJump (-J), SSH client otomatis membuat tunnel pertama ke bastion, lalu dari bastion membuka koneksi kedua ke app-server.internal, dan meneruskan port dari sana. Ini jauh lebih bersih daripada menjalankan dua perintah SSH secara manual.
Masalah Umum dan Cara Mengatasinya
Masalah Umum dan Solusinya
Tunnel berjalan tetapi koneksi ditolak. Ini biasanya berarti SSH server berhasil menerima koneksi, tetapi host tujuan menolak. Periksa apakah service tujuan benar-benar berjalan dan bisa dijangkau dari SSH server—bukan dari laptop Anda. Login ke bastion, lalu curl -v http://10.0.1.50:5432 atau nc -zv 10.0.1.50 5432. Jika gagal dari bastion, tunnel tidak akan membantu karena SSH server hanya bertindak sebagai proxy; ia tidak bisa menjangkau tujuan yang tidak bisa dijangkau olehnya sendiri.
Port lokal sudah dipakai. Pesan bind: Address already in use muncul ketika port yang Anda tentikan di -L sudah terikat oleh proses lain. Solusinya: pilih port lain yang kosong (misalnya 15432 alih-alih 5432), atau bunuh proses yang memakai port tersebut dengan lsof -i :5432 atau fuser -k 5432/tcp.
Tunnel putus diam-diam. Koneksi SSH yang idle lama sering diputus oleh NAT timeout atau firewall stateful. Solusi terbaik adalah mengaktifkan keepalive di level SSH:
ssh -o ServerAliveInterval=60 -o ServerAliveCountMax=3 -L 5432:10.0.1.50:5432 admin@bastion.example.com
ServerAliveInterval=60 mengirim heartbeat setiap 60 detik, dan ServerAliveCountMax=3 memutus koneksi setelah 3 kali heartbeat gagal. Jika tunnel tetap sering putus, pertimbangkan autossh yang otomatis me-reconnect:
autossh -M 0 -o "ServerAliveInterval 60" -o "ServerAliveCountMax 3" \
-N -L 5432:10.0.1.50:5432 admin@bastion.example.com
Remote forwarding tidak bisa diakses dari luar. Secara default, port yang di-bind oleh -R hanya mendengarkan di 127.0.0.1 di mesin remote, sehingga tidak bisa diakses dari mesin lain. Untuk mengubahnya, set GatewayPorts yes di /etc/ssh/sshd_config pada SSH server, lalu restart sshd. Atau gunakan GatewayPorts clientspecified dan tentukan bind address secara eksplis