Apa Itu Protokol Traversal Using Relays around NAT (TURN)?
Traversal Using Relays around NAT (TURN) adalah sebuah protokol relay yang memungkinkan client berkomunikasi dengan peer yang tidak memiliki IP publik secara langsung (misalnya berada dibalik NAT). Protokol ini didefinisikan di RFC 8656. Komponen TURN terdiri atas TURN client dan TURN server. Komunikasi antara TURN client dengan peer selalu melalui TURN server yang berperan sebagai perantara. Oleh sebab itu, TURN server dan peer harus bisa saling berkomunikasi yang biasanya dilakukan dengan meletakkan TURN server pada jaringan publik.
Untuk struktur packet, TURN sendiri merupakan ekstensi dari STUN. Struktur packet TURN tetap mengikuti struktur packet STUN seperti yang saya tulis pada artikel sebelumnya. Yang berbeda adalah TURN menambahkan beberapa operasi baru pada STUN seperti Allocate
, Refresh
, Send
, Data
, CreatePermission
dan ChannelBind
. Selain itu, TURN juga menambahkan atribut baru di STUN seperti XOR-PEER-ADDRESS
, REQUESTED-TRANSPORT
, DATA
, dan sebagainya.
Sebagai latihan, saya akan membuat aplikasi yang mengerjakan perintah terminal Linux secara jarak jauh dimana terdapat dimana perintah yang sama yang diberikan pengguna akan dikerjakan oleh dua server Linux secara bersamaan. Komponen aplikasi latihan ini terdiri atas:
- Dua server Linux yang tidak dapat dihubungi dari Internet secara langsung karena berada di-balik NAT. Mereka akan menerima perintah dan mengembalikan hasil eksekusi perintah tersebut.
- Sebuah server Linux yang memberikan perintah jarak jauh. Server ini berada di jaringan publik yang sama dengan TURN server.
- Sebuah TURN server siap pakai. Saya memilih untuk menggunakan coturn pada latihan ini.
Arsitektur aplikasi latihan tersebut terlihat seperti pada gambar berikut ini:
Pada gambar di atas, saya menggunakan dua router berbeda untuk mensimulasikan Internet. Perangkat server1
dan server2
dapat menghubungi TURN_server
dan remote
, namun tidak berlaku sebaliknya. Perangkat remote
tidak bisa menghubungi langsung
server1
dan server2
yang berada di balik NAT. Ini adalah metode perlindungan yang umum dipakai untuk melindungi perangkat
dari serangan publik. Namun, berkat protokol TURN dan bantuan TURN_server
, perangkat remote
bisa melewati keterbatasan tersebut.
TURN Server
Untuk melakukan instalasi coturn, saya akan memberikan perintah:
Setelah instalasi selesai, saya akan melakukan perubahan di /etc/turnserver.conf
. Saya akan menambahkan static credential
sehingga tidak semua orang bisa menggunakan TURN server ini:
Authentication melalui long-term credential seperti yang tulis pada artikel sebelumnya adalah persyaratan yang wajib untuk menggunakan protokol TURN. Agar perubahannya efektif, saya akan menjalankan ulang coturn dengan perintah:
TURN Client
Seperti pada kode program di artikel-artikel sebelumnya, untuk mulai memakai protokol TURN, saya bisa menyiapkan struktur data yang dibutuhkan dengan menggunakan kode program seperti:
Salah satu hal penting bagi TURN client adalah mempertahankan 5-Tuple yang sama sehingga tetap bisa menerima packet dari TURN server
setelah UDP hole punching di router. Oleh sebab itu, saya akan menggunakan sebuah UDPConn
yang sama di seluruh komunikasi jaringan. Selain itu,
karena UDP yang bersifat stateless, saya menggunakan fitur channel di Go sebagai sebuah buffer FIFO untuk setiap packet STUN yang masuk. Saya
tidak bisa mengandalkan packet akan selalu dikirim dalam urutan yang sama persis sehingga saya perlu mencari packet yang
diharapkan berdasarkan transaction id dengan kode program seperti berikut ini:
Pada kode program di atas, saya menambahkan sebuah channel baru dengan nama timeoutChannel
sehingga function di atas
hanya akan menunggu hingga maksimal 5 detik. Hal ini karena pada protokol UDP, ada kemungkinan packet tidak akan pernah
sampai, sehingga saya tidak perlu terus menunggu. Pada kode program untuk production, saya perlu menambahkan bagian
yang mengulangi pengiriman pesan bila hal ini terjadi.
Tentu saja kode program di atas tidak akan bekerja karena belum ada kode program yang mengirim data ke channel
turnServerConnection.receivedMessageQueue
. Untuk itu, saya bisa membaca packet UDP dari UDPConn
dengan kode program
seperti berikut ini:
Kode program di atas memiliki sebuah for loop tak terhingga yang akan terus menerus membaca packet yang masuk
dan mengirimkannya ke channel turnServer.receivedMessageQueue
bila seandainya packet tersebut adalah packet STUN
yang valid.
Untuk mengirim packet STUN dan menunggu packet respon-nya (berdasarkan transaction id), saya dapat membuat kode program seperti berikut ini:
Sekarang, setelah kode program untuk fasilitas STUN selesai dibuat, saya siap untuk menggunakan protokol TURN. Langkah pertama
untuk memakai TURN adalah mengirim pesan Allocate
(0x003
) ke TURN server. Pesan STUN ini wajib memiliki
atribut REQUESTED-TRANSPORT
(0x0019
) yang berisi kode protokol yang hendak dipakai. Saya akan menggunakan nilai 17
untuk mewakili protokol UDP, seperti yang terlihat pada kode program berikut ini:
Bila TURN server berhasil melakukan alokasi alamat transportasi bagi TURN client ini, program akan mendapatkan respon
sukses. Packet STUN untuk respon sukses harus memiliki atribut XOR-RELAYED-ADDRESS
, LIFETIME
dan XOR-MAPPED-ADDRESS
.
Saya bisa membuat sebuah struktur baru untuk menampung hasil kembalian tersebut, seperti yang terlihat pada contoh
kode program berikut ini:
Nilai dari allocation.RelayedAddress
adalah alamat untuk relayed transport address. Pada arsitektur latihan ini, saya akan
mendapatkan nilai seperti 10.20.30.40:52726
dimana nilai port-nya akan acak tergantung pada apa
yang diberikan oleh NAT server. Bila peer ingin menghubungi client ini, ia hanya perlu mengirim pesan ke relayed transport address
tersebut (perhatikan bahwa IP-nya adalah IP NAT server).
Nilai allocation.ClientAddress
adalah apa yang yang disebut sebagai client reflexive transport address. Ini adalah IP
yang berhubungan dengan TURN client bila dilihat dari sisi TURN server. Walaupun TURN client memilik IP lokal 192.168.1.100
,
nilai allocation.ClientAddress
akan terlihat seperti 10.20.30.1:xxxx
karena yang dilihat oleh TURN server
dan yang berhubungan langsung dengan TURN server adalah IP router internet
di 10.20.30.1
.
Relayed transport address yang diberikan oleh TURN server tidak bersifat permanen. Ia hanya berlaku sesuai dengan nilai TTL
yang tertera di atribut LIFETIME
. Bahkan bila relayed transport address tidak kadaluarsa, saya tetap perlu mengirim
packet secara berkala agar NAT binding yang sudah ada tidak dihapus oleh router. Agar bisa tetap menggunakan relayed transport address
yang sama, saya perlu mengirim operasi Refresh
(0x004
) ke TURN server sebelum nilai TTL dicapai. Untuk itu, saya bisa menggunakan
kode program seperti berikut ini:
Satu-satunya atribut yang perlu saya berikan untuk operasi Refresh
adalah REQUESTED-ADDRESS-FAMILY
(0x0017
). Nilainya
hanya bisa berupa 0x01
untuk alokasi IPv4 dan 0x02
untuk alokasi IPv6. Bila proses refresh sukses, saya akan mendapatkan
packet STUN kembalian yang didalamnya berisi atribut LIFETIME
yang baru. Untuk mengerjakan Refresh()
secara periodik,
misalnya saat 3/4 dari TTL sudah dicapai, saya dapat menggunakan kode program seperti berikut ini:
Langkah berikutnya yang perlu saya lakukan adalah mengirim operasi CreatePermission
(0x008
) untuk mengizinkan perangkat
remote
dengan IP 10.20.30.50
mengirim pesan melalui TURN server. Operasi ini hanya membutuhkan atribut XOR-PEER-ADDRESS
(0x0012
) yang berisi IP peer yang diizinkan. Saya bisa membuat packet STUN untuk operasi ini dengan kode program
seperti berikut ini:
Berdasarkan RFC 8656, permission akan kadaluarsa setelah 5 menit. Saya perlu kembali mengirim operasi CreatePermission
sebelum
batas waktu 5 menit ini tercapai. Untuk itu, saya bisa menggunakan time.Ticker
seperti pada kode program seperti berikut ini:
Sebagai bagian yang paling terakhir, saya kini siap untuk menerima packet TURN yang berisi Data Indication
(0x0017
). Ini
adalah packet yang akan diterima oleh TURN client bila peer mengirim pesan ke relayed transport address. Pesan ini terdiri
atas 2 atribut: XOR-PEER-ADDRESS
yang berisi peer reflexive transport address dan DATA
yang berisi data yang dikirim oleh
peer. Saya bisa menggunakan nilai atribut XOR-PEER-ADDRESS
untuk mengirim respon ke peer yang bersangkutan. Sebagai contoh,
saya dapat membuat kode program seperti berikut ini:
Pada kode program di atas, saya menjalankan for loop tanpa henti yang akan menunggu datangnya packet Data Indication
. Bila menemukannya,
ia akan melewatkan data yang diterima ke function handler
. Hasil kembalian dari function handler
kemudian dipakai untuk
membuat packet Send Indication
(0x0016
). Sama seperti Data Indication
, pesan Send Indication
hanya mengandung atribut XOR-PEER-ADDRESS
dan DATA
(tanpa long term authentication). Send Indication
digunakan agar TURN server dapat mengirim nilai yang tertera di atribut
DATA
ke peer di alamat yang tertera di XOR-PEER-ADDRESS
. Untuk membuat packet STUN-nya, saya bisa menggunakan kode program
seperti berikut ini:
Untuk implementasi function handler
, saya bisa membuat sebuah function yang menerima perintah shell, mengerjakannya
melalui Bash dan mengembalikan output dari perintah tersebut seperti yang terlihat pada contoh kode program berikut ini:
Struktur kode program utama saya akan terlihat seperti berikut ini:
Bila saya menjalankan program ini, saya akan memperoleh hasil seperti berikut ini:
Sampai disini, program sudah siap untuk menerima pesan dari peer yang berupa server remote
.
Remote Peer
Pada arsitektur latihan ini, server remote
dengan IP 10.20.30.50
berada di jaringan publik yang sama yang terhubung
ke TURN server di 10.20.30.40
. Oleh sebab itu, saya tidak perlu membuat alokasi relayed transport address di TURN
server. Saya dapat langsung menghubungi TURN server untuk mengirim pesan ke TURN client, misalnya dengan menggunakan nc
seperti yang terlihat pada perintah berikut ini:
Terlihat bahwa walaupun saya menghubungi alamat 10.20.30.40
, sebenarnya saya berkomunikasi dengan TURN client di
alamat 192.168.1.100
yang sebelumnya tidak dapat saya hubungi dari publik. Selain itu, bila dilihat dari sisi
TURN client di 192.168.1.100
, seluruh komunikasi hanya terjadi ke TURN server di 10.20.30.40
tanpa melibatkan
peer remote
sama sekali di 10.20.30.50
, seperti yang diperlihatkan oleh hasil capture di Wireshark pada
gambar berikut ini: