Bypass ILV mengunci dengan DNSTap dan BGP


Subjeknya cukup keluar dari gambar, saya tahu. Misalnya, ada artikel yang bagus, tetapi hanya bagian IP dari daftar blokir yang dipertimbangkan di sana. Kami juga akan menambahkan domain.


Karena fakta bahwa pengadilan dan ILV memblokir semuanya dengan benar dan kiri, dan penyedia berusaha keras untuk tidak jatuh di bawah denda yang dikeluarkan oleh "Revizorro" - kerugian terkait dari kunci cukup besar. Ya, dan di antara situs yang "diblokir" secara legal ada banyak yang bermanfaat (halo, rutracker)


Saya tinggal di luar yurisdiksi ILV, tetapi orang tua, kerabat, dan teman saya tetap di rumah. Jadi diputuskan untuk membuat cara untuk memotong kunci yang mudah bagi mereka yang jauh dari IT, lebih disukai tanpa partisipasi mereka.


Dalam artikel ini, saya tidak akan menjelaskan hal-hal dasar jaringan dalam langkah-langkah, tetapi menjelaskan prinsip-prinsip umum tentang bagaimana skema ini dapat diimplementasikan. Jadi pengetahuan tentang bagaimana jaringan bekerja secara umum dan di Linux pada khususnya adalah harus dimiliki.


Jenis kunci


Pertama, mari menyegarkan apa yang diblokir.


Ada beberapa jenis kunci dalam paged XML dari ILV:


  • IP
  • Domain
  • URL

Untuk kesederhanaan, kami akan menguranginya menjadi dua: IP dan domain, dan kami hanya akan menarik domain keluar dari pemblokiran URL (lebih tepatnya, kami telah melakukan ini).


Orang-orang baik dari Roskomsvoboda telah menerapkan API luar biasa yang melaluinya kita dapat memperoleh apa yang kita butuhkan:



Akses situs yang diblokir


Untuk melakukan ini, kita memerlukan beberapa VPS asing kecil, lebih disukai dengan lalu lintas tanpa batas - ada banyak 3-5 dolar. Anda perlu membawanya di dekat luar negeri agar ping tidak terlalu besar, tetapi sekali lagi memperhitungkan bahwa Internet dan geografi tidak selalu bersamaan. Dan karena tidak ada SLA untuk 5 dolar, lebih baik mengambil 2+ buah dari penyedia yang berbeda untuk toleransi kesalahan.


Selanjutnya, kita perlu mengkonfigurasi tunnel terenkripsi dari router klien ke VPS. Saya menggunakan Wireguard sebagai yang tercepat dan termudah untuk dikonfigurasikan sejak itu Saya juga memiliki router klien yang berbasis Linux ( APU2 atau sesuatu di OpenWRT). Dalam beberapa Mikrotik / Cisco, Anda dapat menggunakan protokol yang tersedia pada mereka seperti OpenVPN dan GRE-over-IPSEC.


Identifikasi dan pengalihan lalu lintas kepentingan


Anda tentu saja dapat sepenuhnya membungkus semua lalu lintas Internet melalui luar negeri. Tetapi, kemungkinan besar, kecepatan bekerja dengan konten lokal akan sangat menderita karenanya. Plus, persyaratan bandwidth pada VPS akan jauh lebih tinggi.


Karena itu, kita perlu mengalokasikan lalu lintas ke situs yang diblokir dan mengirimnya ke terowongan secara selektif. Bahkan jika beberapa bagian dari lalu lintas "ekstra" tiba di sana, itu masih jauh lebih baik daripada mengendarai semuanya melalui terowongan.


Untuk mengelola lalu lintas, kami akan menggunakan protokol BGP dan mengumumkan rute ke jaringan yang diperlukan dari VPS kami ke klien. Sebagai daemon BGP, mari kita ambil BIRD, sebagai salah satu yang paling fungsional dan nyaman.


IP


Dengan pemblokiran IP, semuanya jelas: kami baru saja mengumumkan semua IP yang diblokir dengan VPS. Masalahnya adalah bahwa ada sekitar 600 ribu subnet dalam daftar yang diberikan oleh API, dan sebagian besar adalah / 32 host. Sejumlah rute seperti itu bisa membingungkan bagi router klien yang lemah.


Oleh karena itu, diputuskan untuk meringkas ke jaringan / 24 ketika memproses daftar jika ada 2 atau lebih host di dalamnya. Dengan demikian, jumlah rute dikurangi menjadi ~ 100 ribu. Script untuk ini akan menjadi yang berikutnya.


Domain


Ini lebih rumit dan ada beberapa cara. Misalnya, Anda dapat meletakkan Squid transparan pada setiap router klien dan melakukan intersepsi HTTP dan mengintip dalam jabat tangan TLS untuk mendapatkan URL yang diminta dalam kasus pertama dan domain dari SNI di yang kedua.


Tetapi karena semua TLS1.3 + eSNI bermodel baru, analisis HTTPS menjadi semakin tidak nyata setiap hari. Dan infrastruktur di sisi klien menjadi lebih rumit - Anda harus menggunakan setidaknya OpenWRT.


Oleh karena itu, saya memutuskan untuk mengambil jalur mencegat tanggapan terhadap permintaan DNS. Di sini, juga, DNS-over-TLS / HTTPS mulai melonjak di atas kepala kami, tetapi kami dapat (untuk saat ini) mengontrol bagian ini pada klien - baik menonaktifkannya atau menggunakan server kami sendiri untuk DoT / DoH.


Bagaimana cara mencegat DNS?


Mungkin juga ada beberapa pendekatan.


  • Intersepsi lalu lintas DNS melalui PCAP atau NFLOG
    Kedua metode intersepsi ini diimplementasikan dalam utilitas sidmat . Tapi itu belum didukung untuk waktu yang lama dan fungsinya sangat primitif, jadi Anda harus tetap menulis yang mengikat.
  • Analisis log server DNS
    Sayangnya, rekursi yang saya tahu tidak tahu cara mencatat jawaban, tetapi hanya permintaan. Pada prinsipnya, ini logis, karena tidak seperti permintaan, jawaban memiliki struktur yang kompleks dan sulit untuk menuliskannya dalam bentuk teks.
  • DNSTap
    Untungnya, banyak dari mereka sudah mendukung DNSTap untuk keperluan ini.

Apa itu DNSTap?



Ini adalah protokol client-server berdasarkan Protokol Buffer dan Frame Stream untuk mentransfer dari server DNS ke kolektor pertanyaan dan respons DNS terstruktur. Bahkan, server DNS mentransmisikan metadata dari permintaan dan tanggapan (jenis pesan, IP klien / server, dan sebagainya) ditambah pesan DNS lengkap dalam bentuk (biner) di mana ia bekerja bersama mereka melalui jaringan.


Penting untuk dipahami bahwa dalam paradigma DNSTap, server DNS bertindak sebagai klien, dan kolektor sebagai server. Artinya, server DNS terhubung ke kolektor, dan bukan sebaliknya.


Hari ini DNSTap didukung di semua server DNS populer. Tetapi, misalnya, BIND dalam banyak distribusi (seperti Ubuntu LTS) sering dibangun untuk beberapa alasan tanpa dukungannya. Jadi kita tidak akan repot membangun kembali, tetapi mengambil recursor yang lebih ringan dan cepat - Tidak terikat.


Bagaimana cara menangkap DNSTap?


Ada sejumlah utilitas CLI untuk bekerja dengan aliran acara DNSTap, tetapi mereka tidak berfungsi dengan baik untuk tugas kita. Jadi saya memutuskan untuk menciptakan sepeda sendiri yang akan melakukan apa pun yang diperlukan: dnstap-bgp


Algoritma Operasi:


  • Saat diluncurkan, ia memuat daftar domain dari file teks, membalikkannya (habr.com -> com.habr), tidak termasuk garis terputus, duplikat, dan subdomain (mis. Jika habr.com dan www.habr.com ada dalam daftar - itu akan dimuat hanya yang pertama) dan buat pohon awalan untuk pencarian cepat pada daftar ini
  • Bertindak sebagai server DNSTap, menunggu koneksi dari server DNS. Pada prinsipnya, ia mendukung soket UNIX dan TCP, tetapi server DNS yang saya tahu hanya dapat menggunakan soket UNIX
  • Paket DNSTap yang masuk dideserialisasi terlebih dahulu ke dalam struktur Protobuf, dan kemudian pesan DNS biner yang terletak di salah satu bidang Protobuf diurai ke tingkat catatan DNS RR
  • Memeriksa apakah host yang diminta (atau domain induknya) ada dalam daftar yang dimuat, jika tidak, responsnya diabaikan
  • Hanya A / AAAA / CNAME RR yang dipilih dari respons dan alamat IPv4 / IPv6 yang sesuai ditarik darinya
  • Alamat IP di-cache dengan TTL khusus dan diiklankan ke semua rekan BGP yang dikonfigurasi
  • Setelah menerima tanggapan yang menunjukkan IP yang sudah di-cache - TTL-nya diperbarui
  • Setelah TTL berakhir, catatan dihapus dari cache dan dari pengumuman BGP

Fungsionalitas tambahan:


  • Membaca ulang daftar domain oleh SIGHUP
  • Menyinkronkan cache dengan instance dnstap-bgp lainnya melalui HTTP / JSON
  • Duplikasi cache pada disk (dalam database BoltDB) untuk mengembalikan kontennya setelah restart
  • Dukungan untuk beralih ke namespace jaringan yang berbeda (mengapa ini akan dijelaskan di bawah)
  • Dukungan IPv6

Keterbatasan:


  • Domain IDN belum didukung
  • Beberapa Pengaturan BGP

Saya telah menyusun paket RPM dan DEB untuk kemudahan instalasi. Harus bekerja pada semua OS yang relatif baru dengan systemd, as mereka tidak memiliki ketergantungan.


Skema


Jadi, mari kita mulai merakit semua komponen bersama-sama. Akibatnya, kita harus mendapatkan sesuatu seperti topologi jaringan ini:


Logika kerja, saya pikir, jelas dari diagram:


  • Klien memiliki server kami yang dikonfigurasi sebagai DNS, dan permintaan DNS juga harus melalui VPN. Ini diperlukan agar penyedia tidak dapat menggunakan intersepsi DNS untuk memblokir.
  • Ketika situs dibuka, klien mengirim permintaan DNS dari formulir "apa yang dimiliki IP.org?"
  • Tidak terikat menyelesaikan xxx.org (atau mengambilnya dari cache) dan mengirimkan respons ke klien "xxx.org memiliki IP ini dan itu", menduplikasinya secara paralel melalui DNSTap
  • dnstap-bgp mengumumkan alamat-alamat ini dalam BIRD oleh BGP jika domainnya ada dalam daftar diblokir
  • BIRD mengumumkan rute ke IP-IP ini dengan router next-hop self
  • Paket selanjutnya dari klien ke IP ini melewati terowongan

Di server, untuk rute ke situs yang diblokir, saya memiliki tabel terpisah di dalam BIRD dan tidak bersinggungan dengan OS.


Ada kekurangan dalam skema ini: paket SYN pertama dari klien kemungkinan besar akan memiliki waktu untuk pergi melalui penyedia domestik karena rute tidak diumumkan secara instan. Dan di sini opsi dimungkinkan tergantung pada bagaimana penyedia membuat kunci. Jika dia hanya menjatuhkan lalu lintas, maka tidak ada masalah. Dan jika dia mengalihkannya ke beberapa DPI, maka (secara teoritis) efek khusus mungkin terjadi.


Mukjizat juga mungkin terjadi dengan klien yang tidak mengamati DNS TTL, yang dapat menyebabkan klien menggunakan beberapa entri usang dari cache busuk mereka alih-alih meminta Tidak terikat.


Dalam praktiknya, baik yang pertama maupun yang kedua tidak menimbulkan masalah bagi saya, tetapi jarak tempuh Anda mungkin bervariasi.


Pengaturan server


Untuk kemudahan bergulir, saya menulis peran untuk Ansible . Ia dapat mengonfigurasi server dan klien berbasis Linux (dirancang untuk distribusi berbasis deb). Semua pengaturan cukup jelas dan diatur di inventory.yml . Peran ini dipotong dari buku pedoman besar saya, jadi mungkin mengandung kesalahan - tarik permintaan, selamat datang :)


Mari kita lihat komponen utama.


BGP


Saat meluncurkan dua daemon BGP pada host yang sama, muncul masalah mendasar: BIRD tidak ingin meningkatkan peering BGP dengan localhost (atau dengan antarmuka lokal apa pun). Dari kata sama sekali. Googling dan membaca milis tidak membantu, mereka mengklaim itu adalah desain. Mungkin ada beberapa cara, tetapi saya tidak menemukannya.


Anda dapat mencoba daemon BGP lain, tapi saya suka BIRD dan digunakan di mana-mana dengan saya, saya tidak ingin menghasilkan entitas.


Oleh karena itu, saya menyembunyikan dnstap-bgp di dalam namespace jaringan, yang terhubung ke root melalui antarmuka veth: itu seperti pipa yang ujungnya menonjol di namespace yang berbeda. Pada masing-masing ujung ini kita menggantung alamat IP p2p pribadi yang tidak pergi ke luar host, sehingga mereka bisa berupa apa saja. Ini adalah mekanisme yang sama yang digunakan untuk mengakses proses di dalam Docker tercinta dan wadah lainnya.


Untuk ini, skrip ditulis dan dalam dnstap-bgp fungsionalitas yang dijelaskan di atas untuk menyeret diri oleh rambut ke namespace lain ditambahkan. Karena itu, ini harus dijalankan sebagai root atau dikembalikan ke biner CAP_SYS_ADMIN melalui perintah setcap.


Contoh skrip untuk membuat namespace
 #!/bin/bash NS="dtap" IP="/sbin/ip" IPNS="$IP netns exec $NS $IP" IF_R="veth-$NS-r" IF_NS="veth-$NS-ns" IP_R="192.168.149.1" IP_NS="192.168.149.2" /bin/systemctl stop dnstap-bgp || true $IP netns del $NS > /dev/null 2>&1 $IP netns add $NS $IP link add $IF_R type veth peer name $IF_NS $IP link set $IF_NS netns $NS $IP addr add $IP_R remote $IP_NS dev $IF_R $IP link set $IF_R up $IPNS addr add $IP_NS remote $IP_R dev $IF_NS $IPNS link set $IF_NS up /bin/systemctl start dnstap-bgp 

dnstap-bgp.conf
 namespace = "dtap" domains = "/var/cache/rkn_domains.txt" ttl = "168h" [dnstap] listen = "/tmp/dnstap.sock" perm = "0666" [bgp] as = 65000 routerid = "192.168.149.2" peers = [ "192.168.149.1", ] 

bird.conf
 router id 192.168.1.1; table rkn; # Clients protocol bgp bgp_client1 { table rkn; local as 65000; neighbor 192.168.1.2 as 65000; direct; bfd on; next hop self; graceful restart; graceful restart time 60; export all; import none; } # DNSTap-BGP protocol bgp bgp_dnstap { table rkn; local as 65000; neighbor 192.168.149.2 as 65000; direct; passive on; rr client; import all; export none; } # Static routes list protocol static static_rkn { table rkn; include "rkn_routes.list"; import all; export none; } 

rkn_routes.list
 route 3.226.79.85/32 via "ens3"; route 18.236.189.0/24 via "ens3"; route 3.224.21.0/24 via "ens3"; ... 

DNS


Secara default, di Ubuntu, biner Unbound dijepit oleh profil AppArmor, yang mencegahnya terhubung ke soket DNSTap di sana. Anda dapat menghapus profil ini atau menonaktifkannya:


 # cd /etc/apparmor.d/disable && ln -s ../usr.sbin.unbound . # apparmor_parser -R /etc/apparmor.d/usr.sbin.unbound 

Ini mungkin harus ditambahkan ke buku pedoman. Sangat ideal, tentu saja, untuk memperbaiki profil dan mengeluarkan hak yang diperlukan, tetapi saya terlalu malas.


unbound.conf
 server: chroot: "" port: 53 interface: 0.0.0.0 root-hints: "/var/lib/unbound/named.root" auto-trust-anchor-file: "/var/lib/unbound/root.key" access-control: 192.168.0.0/16 allow remote-control: control-enable: yes control-use-cert: no dnstap: dnstap-enable: yes dnstap-socket-path: "/tmp/dnstap.sock" dnstap-send-identity: no dnstap-send-version: no dnstap-log-client-response-messages: yes 

Mengunduh dan memproses daftar


Skrip untuk mengunduh dan memproses daftar alamat IP
Ia mengunduh daftar, merangkum sebelum awalan pfx . Dalam dont_add dan dont_summarize, Anda dapat memberitahu IP dan jaringan untuk melewati atau tidak merangkum. Saya membutuhkannya karena subnet VPS saya ada di daftar blokir :)


Yang lucu adalah bahwa RosKomSvoboda API memblokir permintaan dengan agen pengguna Python default. Sepertinya naskah kiddy mengerti. Karena itu, kami mengubahnya ke Firelis.


Sejauh ini, ini hanya berfungsi dengan IPv4 karena IPv6 kecil, tetapi akan mudah diperbaiki. Kecuali jika perlu menggunakan juga bird6.


rkn.py
 #!/usr/bin/python3 import json, urllib.request, ipaddress as ipa url = 'https://api.reserve-rbl.ru/api/v2/ips/json' pfx = '24' dont_summarize = { # ipa.IPv4Network('1.1.1.0/24'), } dont_add = { # ipa.IPv4Address('1.1.1.1'), } req = urllib.request.Request( url, data=None, headers={ 'User-Agent': 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_9_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.47 Safari/537.36' } ) f = urllib.request.urlopen(req) ips = json.loads(f.read().decode('utf-8')) prefix32 = ipa.IPv4Address('255.255.255.255') r = {} for i in ips: ip = ipa.ip_network(i) if not isinstance(ip, ipa.IPv4Network): continue addr = ip.network_address if addr in dont_add: continue m = ip.netmask if m != prefix32: r[m] = [addr, 1] continue sn = ipa.IPv4Network(str(addr) + '/' + pfx, strict=False) if sn in dont_summarize: tgt = addr else: tgt = sn if not sn in r: r[tgt] = [addr, 1] else: r[tgt][1] += 1 o = [] for n, v in r.items(): if v[1] == 1: o.append(str(v[0]) + '/32') else: o.append(n) for k in o: print(k) 

Skrip untuk diperbarui
Itu dimulai pada mahkota saya sekali sehari, mungkin perlu menariknya setiap 4 jam, karena ini, menurut saya, adalah periode pembaruan yang ILV butuhkan dari penyedia. Selain itu, ada beberapa kunci lain yang sangat mendesak agar mereka dapat terbang lebih cepat.


Apakah yang berikut ini:


  • Jalankan skrip pertama dan rkn_routes.list daftar rute ( rkn_routes.list ) untuk BIRD
  • Reload BIRD
  • Memperbarui dan membersihkan daftar domain untuk dnstap-bgp
  • Pindahkan dnstap-bgp

rkn_update.sh
 #!/bin/bash ROUTES="/etc/bird/rkn_routes.list" DOMAINS="/var/cache/rkn_domains.txt" # Get & summarize routes /opt/rkn.py | sed 's/\(.*\)/route \1 via "ens3";/' > $ROUTES.new if [ $? -ne 0 ]; then rm -f $ROUTES.new echo "Unable to download RKN routes" exit 1 fi if [ -e $ROUTES ]; then mv $ROUTES $ROUTES.old fi mv $ROUTES.new $ROUTES /bin/systemctl try-reload-or-restart bird # Get domains curl -s https://api.reserve-rbl.ru/api/v2/domains/json -o - | jq -r '.[]' | sed 's/^\*\.//' | sort | uniq > $DOMAINS.new if [ $? -ne 0 ]; then rm -f $DOMAINS.new echo "Unable to download RKN domains" exit 1 fi if [ -e $DOMAINS ]; then mv $DOMAINS $DOMAINS.old fi mv $DOMAINS.new $DOMAINS /bin/systemctl try-reload-or-restart dnstap-bgp 

Itu ditulis tanpa banyak pemikiran, jadi jika Anda melihat apa yang dapat diperbaiki, lakukanlah.


Pengaturan klien


Di sini saya akan memberikan contoh untuk router Linux, tetapi dalam kasus Mikrotik / Cisco ini harus lebih sederhana.


Pertama, konfigurasikan BIRD:


bird.conf
 router id 192.168.1.2; table rkn; protocol device { scan time 10; }; # Servers protocol bgp bgp_server1 { table rkn; local as 65000; neighbor 192.168.1.1 as 65000; direct; bfd on; next hop self; graceful restart; graceful restart time 60; rr client; export none; import all; } protocol kernel { table rkn; kernel table 222; scan time 10; export all; import none; } 

Dengan demikian, kami akan menyinkronkan rute yang diterima dari BGP dengan nomor tabel rute kernel 222.


Setelah itu, cukup meminta kernel untuk melihat plat ini sebelum melihat default:


 # ip rule add from all pref 256 lookup 222 # ip rule 0: from all lookup local 256: from all lookup 222 32766: from all lookup main 32767: from all lookup default 

Yang tersisa adalah mengkonfigurasi DHCP pada router untuk mendistribusikan alamat IP tunnel server sebagai DNS dan skema siap.


Kekurangan


Dengan algoritma saat ini untuk membuat dan memproses daftar domain, youtube.com dan CDN-nya termasuk di dalamnya.


Dan ini mengarah pada fakta bahwa semua video akan melalui VPN, yang dapat menyumbat seluruh saluran. Mungkin ada baiknya menyusun daftar domain pengecualian populer yang diblokir di ILV untuk saat ini. Dan lewati saat parsing.


Kesimpulan


Metode yang dijelaskan ini memungkinkan Anda untuk mem-bypass hampir semua kunci yang saat ini diterapkan oleh penyedia.


Pada prinsipnya, dnstap-bgp dapat digunakan untuk tujuan lain di mana diperlukan tingkat kontrol lalu lintas tertentu berdasarkan nama domain. Perlu diingat bahwa saat ini seribu situs dapat bertahan di alamat IP yang sama (untuk beberapa Cloudflare, misalnya), sehingga metode ini memiliki akurasi yang agak rendah.


Tetapi untuk kebutuhan melewati kunci, ini sudah cukup.


Tambahan, suntingan, pull-quest dipersilahkan!

Source: https://habr.com/ru/post/id467547/


All Articles