SSH Tunneling untuk Local Development yang Lebih Aman

by Marcus Chen
SSH Tunneling untuk Local Development yang Lebih Aman

Masalahnya: Port Kamu Terbuka ke Mana-Mana

Kamu lagi develop webhook handler — katakanlah untuk Midtrans atau Stripe. Service mereka butuh endpoint yang bisa diakses dari internet untuk ngirim notifikasi pembayaran. Solusi paling gampang? Langsung buka port di VPS, atau pakai ngrok free tier yang limitnya bikin frustrasi.

Tapi ada masalah lain yang lebih sering terjadi: kamu kerja di belakang NAT (router kantor, kampus, atau ISP yang nggak kasih IP publik), jadi port forwarding biasa nggak mungkin. Atau kamu punya VPS tapi males setup reverse proxy cuma buat testing.

SSH tunneling adalah solusi yang udah ada sejak lama, built-in di hampir semua sistem, dan gratis. Kalau kamu udah punya VPS atau server remote dengan SSH access, kamu udah punya semua yang dibutuhkan.


Tiga Mode SSH Tunnel yang Perlu Kamu Tahu

Sebelum masuk ke kode, penting buat ngerti perbedaan tiga mode tunneling. Banyak dev bingung karena namanya mirip tapi arahnya beda.

1. Local Port Forwarding (-L)

Traffic dari port lokal kamu diteruskan ke port di remote server (atau host lain yang bisa dijangkau remote server).

Use case: Akses database production yang cuma bisa diakses dari dalam server, tanpa expose port-nya ke internet.

# Format: ssh -L [local_port]:[destination_host]:[destination_port] [user]@[ssh_server]
ssh -L 5432:localhost:5432 deploy@vps.kamu.com

Setelah command ini jalan, kamu bisa connect ke PostgreSQL production via localhost:5432 dari laptop kamu — padahal database itu ada di VPS dan port-nya nggak dibuka ke publik.

2. Remote Port Forwarding (-R)

Ini kebalikannya. Port di remote server diteruskan ke port di mesin lokal kamu.

Use case: Expose local development server kamu ke internet via VPS — ini yang paling relevan buat webhook testing.

# Format: ssh -R [remote_port]:[local_host]:[local_port] [user]@[ssh_server]
ssh -R 8080:localhost:3000 deploy@vps.kamu.com

Setelah ini, siapapun yang akses http://vps.kamu.com:8080 akan diteruskan ke localhost:3000 di laptop kamu. Midtrans bisa kirim webhook ke VPS kamu, dan request-nya nyampe ke local dev server.

3. Dynamic Port Forwarding (-D)

Bikin SOCKS proxy. Semua traffic dari aplikasi yang dikonfigurasi lewat proxy ini akan keluar dari remote server.

ssh -D 1080 deploy@vps.kamu.com

Kemudian set browser atau curl pakai SOCKS5 proxy di localhost:1080. Berguna kalau kamu mau test request yang seolah-olah berasal dari IP VPS kamu.


Setup Remote Port Forwarding untuk Webhook Testing

Ini skenario paling umum: kamu develop webhook handler di local, dan butuh URL publik buat dikasih ke payment gateway atau third-party service.

Langkah 1: Aktifkan GatewayPorts di VPS

By default, remote port forwarding di SSH hanya bisa diakses dari localhost VPS itu sendiri. Untuk expose ke internet, kamu perlu ubah satu config di VPS:

# Di VPS kamu
sudo nano /etc/ssh/sshd_config

Tambahkan atau ubah baris ini:

GatewayPorts yes

Kemudian restart SSH daemon:

sudo systemctl restart sshd

Gotcha: Kalau kamu lupa step ini, tunnel-nya tetap jalan tapi cuma bisa diakses dari dalam VPS (curl localhost:8080), bukan dari luar. Ini bikin bingung karena SSH-nya connect tanpa error.

Langkah 2: Buka Port di Firewall VPS

Kalau VPS kamu pakai UFW:

sudo ufw allow 8080/tcp

Kalau pakai iptables langsung:

sudo iptables -A INPUT -p tcp --dport 8080 -j ACCEPT

Kalau pakai cloud provider (DigitalOcean, AWS, GCP), jangan lupa juga buka port di security group / firewall panel mereka — ini sering kelewat.

Langkah 3: Jalankan Tunnel dari Laptop

Misalnya local dev server kamu jalan di port 3000:

ssh -R 8080:localhost:3000 deploy@vps.kamu.com -N -v

Flag yang dipakai:

  • -R 8080:localhost:3000 — remote forward port 8080 VPS ke localhost:3000 laptop
  • -N — jangan jalankan command apapun, cuma tunnel
  • -v — verbose, berguna buat debug

Sekarang kasih URL http://vps.kamu.com:8080/webhook ke Midtrans atau service apapun, dan request-nya akan nyampe ke local dev server kamu.

Test dulu sebelum kasih ke third-party:

# Dari terminal lain atau mesin lain
curl -X POST http://vps.kamu.com:8080/webhook \
  -H "Content-Type: application/json" \
  -d '{"order_id": "TEST-001", "status": "success"}'

Biar Tunnel Nggak Putus Sendiri

Problem klasik: SSH tunnel tiba-tiba mati setelah idle beberapa menit karena timeout dari router atau firewall di antara kamu dan server. Ini nyebelin banget pas lagi nunggu webhook masuk.

Ada dua cara handle ini.

Cara 1: SSH Config dengan KeepAlive

Edit ~/.ssh/config di laptop kamu:

Host vps-tunnel
    HostName vps.kamu.com
    User deploy
    ServerAliveInterval 30
    ServerAliveCountMax 3
    ExitOnForwardFailure yes
    RemoteForward 8080 localhost:3000

Sekarang kamu tinggal jalankan:

ssh -N vps-tunnel

ServerAliveInterval 30 bikin SSH kirim keepalive packet setiap 30 detik. ExitOnForwardFailure yes bikin SSH exit kalau port forwarding gagal di-setup — berguna buat auto-restart.

Cara 2: AutoSSH untuk Auto-Reconnect

Install autossh:

# Ubuntu/Debian
sudo apt install autossh

# macOS
brew install autossh

Jalankan tunnel dengan autossh:

autossh -M 0 -N \
  -o "ServerAliveInterval=30" \
  -o "ServerAliveCountMax=3" \
  -R 8080:localhost:3000 \
  deploy@vps.kamu.com

-M 0 artinya matikan monitoring port bawaan autossh dan pakai SSH keepalive aja. Kalau koneksi putus, autossh otomatis reconnect.


Local Port Forwarding: Akses Database Tanpa Expose Port

Skenario kedua yang sering gue pakai: akses PostgreSQL atau Redis di production server langsung dari local tanpa buka port ke publik.

Misalnya PostgreSQL di VPS jalan di port 5432 tapi cuma listen di 127.0.0.1 (best practice — jangan pernah expose database port ke 0.0.0.0):

ssh -L 15432:localhost:5432 deploy@vps.kamu.com -N

Gue pakai port 15432 di lokal biar nggak bentrok sama PostgreSQL lokal yang mungkin udah jalan di 5432.

Sekarang connect dari laptop:

psql -h localhost -p 15432 -U myuser -d mydb

Atau dari aplikasi, tinggal ganti connection string:

# Python dengan psycopg2
import psycopg2

conn = psycopg2.connect(
    host="localhost",
    port=15432,
    database="mydb",
    user="myuser",
    password="secret"
)

Semua traffic antara laptop dan VPS dienkripsi via SSH. Database port-nya sendiri nggak pernah dibuka ke internet.

Gotcha: Kalau kamu pakai pg_hba.conf dengan restriction berdasarkan IP, pastikan connection dari 127.0.0.1 diizinkan — karena dari sisi PostgreSQL, koneksinya datang dari localhost VPS.


Bikin Tunnel Jalan Otomatis Saat Boot (systemd)

Kalau kamu butuh tunnel yang selalu aktif — misalnya buat monitoring atau akses database — jadikan systemd service.

Buat file /etc/systemd/system/ssh-tunnel-db.service:

[Unit]
Description=SSH Tunnel ke Database Production
After=network.target

[Service]
User=kamu
ExecStart=/usr/bin/autossh -M 0 -N \
  -o "ServerAliveInterval=30" \
  -o "ServerAliveCountMax=3" \
  -o "StrictHostKeyChecking=no" \
  -L 15432:localhost:5432 \
  deploy@vps.kamu.com
Restart=always
RestartSec=10

[Install]
WantedBy=multi-user.target

Aktifkan:

sudo systemctl daemon-reload
sudo systemctl enable ssh-tunnel-db
sudo systemctl start ssh-tunnel-db

# Cek status
sudo systemctl status ssh-tunnel-db

Catatan: Pastikan SSH key sudah di-setup tanpa passphrase untuk user yang menjalankan service ini, atau pakai ssh-agent dengan proper setup. Kalau pakai passphrase, service nggak bisa auto-start.


Perbandingan: SSH Tunnel vs ngrok vs Cloudflare Tunnel

Biar kamu bisa pilih yang tepat:

SSH Tunnel ngrok Free Cloudflare Tunnel
Biaya Gratis (butuh VPS) Gratis (limited) Gratis
URL custom Ya (domain VPS) Random URL Ya
Bandwidth Unlimited Limited Unlimited
Latency Rendah Medium Medium
Setup Manual tapi simpel Sangat mudah Perlu domain CF
Privacy Full control Data lewat ngrok Data lewat CF

Kalau kamu udah punya VPS, SSH tunneling adalah pilihan paling masuk akal — nggak ada dependency eksternal, nggak ada data yang lewat pihak ketiga, dan nggak ada limit bandwidth.

ngrok masih worth it buat quick demo atau sharing ke klien yang butuh URL yang lebih "proper". Cloudflare Tunnel bagus kalau kamu udah all-in di ekosistem Cloudflare.


Yang Gue Lakuin Sekarang

Setup gue saat ini untuk SSH tunneling local development:

  1. SSH config di ~/.ssh/config dengan semua tunnel yang sering dipakai — database staging, Redis, dan satu remote forward untuk webhook testing.
  2. autossh untuk tunnel database yang harus selalu aktif, dijadikan systemd service.
  3. Remote forward on-demand via command line kalau lagi butuh expose local server untuk webhook atau demo ke klien.

Langkah lanjutan yang bisa kamu coba:

  • Kombinasikan dengan mosh kalau koneksi internet kamu sering putus-putus
  • Setup Nginx di VPS sebagai reverse proxy di depan tunnel port — biar bisa pakai HTTPS dan domain yang proper
  • Eksplorasi sshuttle kalau kamu butuh route seluruh traffic (bukan cuma satu port) lewat SSH — ini basically WordPress object cache berbasis SSH

SSH tunneling bukan teknologi baru, tapi justru itu kelebihannya — battle-tested, ada di mana-mana, dan nggak butuh install tool tambahan di server. Kadang solusi lama memang yang paling reliable.