Klien VPN terhubung, peramban bekerja, dan satu perintah yang benar-benar Anda pedulikan tetap keluar lewat koneksi rumah Anda. Atau yang sebaliknya terjadi: terowongan mengambil rute bawaan dan server berkas lokal menghilang dari desktop. Saya menemui ini pada agen build yang harus menjangkau cermin artefak internal sementara sisa host tetap di LAN kantor, dan klien hanya menawarkan dua pilihan: semua lalu lintas atau tidak sama sekali. Permintaannya di kedua arah sama, program ini dan bukan seluruh mesin, dan sebagian besar klien tidak punya kotak centang untuk itu, karena tabel rute tidak pernah tahu apa itu program.

Mengapa tabel rute tidak bisa melihat program Anda

Ketika sebuah proses mengirim paket, tumpukan mencari alamat tujuan di tabel rute. Baik nama berkas yang dapat dieksekusi maupun porta tidak masuk ke pencarian itu. Nama domain gagal karena alasan yang sama: terowongan membawa lalu lintas ke sebuah alamat, dan saat sebuah nama selesai diselesaikan menjadi alamat, perutean adalah satu-satunya tuas yang masih dimiliki kernel. Itu sebabnya «VPN hanya untuk situs ini» akhirnya berarti proksi, dan «VPN hanya untuk program ini» berarti salah satu dari sekumpulan kecil mekanisme.

MechanismWhat selectsCoversOn tunnel drop
Socket bindsource addressa client you controlconnections fail
UID rule (Linux)owning userevery process of one accountconnections fail
Firewall profile (Windows)adapter profileone programfails closed
Network namespacenamespace membershipeverything started insideconnections fail
SOCKS hopapp proxy settingone app or one browser profileconnections fail

Tidak satu pun dari ini adalah rute per proses. Masing-masing memindahkan keputusan ke lapisan yang bisa melihat program, pengguna, atau konfigurasi aplikasi itu sendiri.

Satu tujuan tetap sebuah rute, bukan aturan program. Di macOS resep biasanya adalah menghapus centang «Kirim semua lalu lintas lewat VPN» dan menambahkan rute host: sudo route add -host internal-tuner.example -interface ppp0, yang dihapus lagi dengan sudo route delete -host internal-tuner.example -interface ppp0. Baca dulu nama antarmuka dari ifconfig, karena ppp0 hanya contoh. Kartu liar tidak berfungsi dalam rute host, jadi layanan yang berbicara dengan beberapa nama perlu satu entri per nama, dan aturan itu mencakup setiap proses di mesin, bukan yang Anda pikirkan.

Ikat soket ke alamat terowongan

Ketika Anda mengendalikan klien, pengikatan adalah pemilih termurah. Soket yang terikat ke alamat lokal terowongan keluar lewat antarmuka itu selama ada rute ke tujuan di sana, dan semua hal lain di mesin mempertahankan jalur normalnya. Kehalusannya adalah pengikatan memilih alamat sumber sementara tabel rute masih memilih jalan keluar. Pengikatan saja tidak memindahkan lalu lintas yang tidak punya rute ke terowongan, dan itulah kesalahan di balik sebagian besar laporan «saya ikat dan masih keluar lewat sisi yang salah».

import socket
s = socket.socket()
s.bind(("10.8.0.2", 0))
s.connect(("build.internal.example", 443))

Alat tingkat lebih tinggi menawarkan gagasan yang sama: curl --interface 10.8.0.2, CURLOPT_INTERFACE di PHP, dan argumen source_address di klien HTTP Python. Alamatnya harus milik terowongan, dan salah ketik di sini gagal sebagai kesalahan koneksi biasa, bukan keluhan perutean. Mengikat ke nama perangkat alih-alih alamat adalah keistimewaan Linux dan tidak terbawa ke Windows atau macOS, itulah sebabnya skrip portabel mengirimkan alamatnya.

Linux: cocokkan pemiliknya, bukan nama program

Kernel dapat mencocokkan pemilik soket, jadi akun khusus menjadi pemilihnya. Taruh terowongan di tabel rute sendiri dan kirim hanya lalu lintas akun itu ke sana:

ip rule add uidrange 1001-1001 lookup 100
ip route add default dev tun0 table 100

Ini hanya bekerja jika terowongan bukan rute bawaan. Jalankan klien dengan --route-nopull, atau saring pengalihan yang didorong dengan --pull-filter ignore "redirect-gateway" pada OpenVPN 2.4 dan yang lebih baru, lalu tambahkan entri tabelnya sendiri. Kalau tidak, kedua bawaan ada dan yang prioritasnya lebih rendah menang. Versi yang disengaja dari itu adalah mengarahkan rute bawaan ke gerbang terowongan, atau melakukannya dari skrip pemutusan: ini menjauhkan lalu lintas dari internet saat terowongan jatuh, tetapi itu sakelar mati, bukan pemilih, dan tetap ada sampai seseorang menghapusnya.

Versi yang lebih kuat adalah ruang nama jaringan. Buat itu, jalankan klien di dalamnya, dan jalankan aplikasi dengan ip netns exec. Tidak ada yang di luar ruang nama melihat antarmuka terowongan, jadi aplikasi tidak bisa jatuh kembali ke rute host secara tidak sengaja. Biayanya nyata: ruang nama perlu pasangan veth untuk mencapai jaringan fisik dan berkas penyelesai sendiri, karena ruang nama pemasangan tidak berbagi pengaturan penyelesaian host.

Dua mode kegagalan menjelaskan sebagian besar kebocoran. Penyelesaian nama masih terjadi di penyelesai host, jadi kuerinya keluar di luar terowongan meskipun koneksi TCP masuk ke dalam. Dan IPv6 perlu ip -6 rule sendiri, karena aturan v4 tidak mencakupnya.

Windows dan macOS: batasi firewall atau serahkan proksi

Windows juga tidak punya pemilih rute per aplikasi, tetapi aturan firewallnya bisa dibatasi ke profil, dan adaptor VPN bisa diklasifikasikan sebagai «publik» sementara LAN rumah tetap «pribadi». Buat aturan blokir keluar untuk program itu di profil «pribadi» dan «domain», biarkan «publik» terbuka, dan aplikasi hanya bisa mencapai jaringan lewat terowongan. Tetapkan dulu kategori jaringan koneksi, lalu pastikan aturannya cocok di panel pemantau firewall. Itu blokir, bukan rute: gagal tertutup saat terowongan jatuh, dan tidak bisa menghentikan aplikasi yang mengabaikan firewall.

macOS mengikuti logika yang sama dengan alat lain. Pendengar SOCKS yang dicapai lewat SSH adalah pilihan paling tidak invasif, karena ssh -D 1080 user@host membuat satu porta lokal dan hanya aplikasi yang dikonfigurasi untuk memakainya menempuh jalur itu. Aturan pf dapat memblokir program menurut pengguna atau grup, tetapi masih belum bisa memilih terowongan hanya untuk program itu. Tunnelblick tidak bisa menyaring menurut domain sama sekali, karena rute bekerja dengan alamat; pengguna OpenConnect beralih ke vpn-slice ketika server mendorong daftar prefiks yang terlalu lebar.

Dua jalan pintas menutupi apa yang terlewat oleh alat platform. Ekstensi peramban yang mengganti proksi per domain adalah jawaban praktis untuk «hanya situs ini», karena bertindak di lapisan aplikasi tempat nama masih ada; itu mencakup satu peramban dan tidak lebih. Cara kasarnya adalah menjalankan klien vendor di dalam mesin virtual sehingga tamu memiliki perubahan rute sementara host mempertahankan jalurnya, dengan biaya dua tumpukan jaringan untuk dipelihara.

Lompatan SOCKS untuk alat tanpa opsi pengikatan

Sebagian besar aplikasi bisa diarahkan ke proksi HTTP atau SOCKS bahkan ketika mereka tidak tahu apa-apa tentang antarmuka. Klien SSH menyediakan satu tanpa perubahan di sisi server:

ssh -N -D 127.0.0.1:9999 [email protected]

Apa pun yang dikonfigurasi untuk memakai porta itu keluar di bastion sementara sisa desktop mempertahankan koneksi normalnya. Instans 3proxy lokal yang terikat ke alamat terowongan melakukan hal yang sama untuk satu porta tanpa SSH, dan stunnel mencakup satu porta terbungkus TLS. Batasnya praktis: proksi hanya membawa protokol yang tahu cara memakainya, UDP perlu asosiasi UDP SOCKS5 yang tidak diaktifkan pengaturan «SOCKS5» banyak klien, dan DNS harus diselesaikan oleh proksi (socks5h di curl), kalau tidak namanya bocor ke penyelesai lokal.

Pertahankan pendengar di loopback kecuali mesin lain membutuhkannya.

Verifikasi jalur dengan tangkapan, bukan pesan status

Jendela status aplikasi menjelaskan pengaturan, bukan paketnya. Tangkap di kedua antarmuka dan bandingkan:

tcpdump -i any -n port 443
ss -tnp | grep 443

Jika koneksi muncul di antarmuka fisik tanpa entri yang cocok di terowongan, pemilihnya tidak berlaku, apa pun yang dilaporkan klien. Periksa IPv6 dan penyelesai secara terpisah, dan periksa apakah ada jalur kedua seperti proses pembantu atau proksi sistem warisan.

Lalu pilih batas yang sesuai dengan kebutuhan: proksi cukup untuk satu situs, aturan UID cukup untuk satu akun, profil firewall cukup ketika koneksi harus gagal tertutup, dan ruang nama adalah satu-satunya pilihan yang menyembunyikan terowongan dari segala hal di luarnya.

Artikel terkait