Topiknya cukup dipukuli, saya tahu. Misalnya, ada yang hebat artikel, tetapi hanya bagian IP dari daftar blokir yang dipertimbangkan di sana. Kami juga akan menambahkan domain.
Karena fakta bahwa pengadilan dan RKN memblokir semua kanan dan kiri, dan penyedia berusaha keras untuk tidak jatuh di bawah denda yang dikeluarkan oleh Revizorro, kerugian terkait dari pemblokiran cukup besar. Dan di antara situs yang diblokir "secara sah" ada banyak situs yang berguna (halo, rutracker)
Saya tinggal di luar yurisdiksi RKN, tetapi orang tua, kerabat, dan teman saya tetap tinggal di rumah. Jadi diputuskan untuk menemukan cara mudah bagi orang yang jauh dari IT untuk melewati pemblokiran, lebih disukai tanpa partisipasi mereka sama sekali.
Dalam catatan ini, saya tidak akan menjelaskan hal-hal jaringan dasar secara bertahap, tetapi akan menjelaskan prinsip umum bagaimana skema ini dapat diimplementasikan. Jadi pengetahuan tentang bagaimana jaringan bekerja secara umum dan di Linux pada khususnya harus dimiliki.
Jenis kunci
Pertama, mari segarkan ingatan kita tentang apa yang diblokir.
Ada beberapa jenis kunci dalam XML yang diturunkan dari RKN:
IP
ΠΠΎΠΌΠ΅Π½
URL
Untuk kesederhanaan, kami akan menguranginya menjadi dua: IP dan domain, dan kami hanya akan menarik domain dari pemblokiran dengan URL (lebih tepatnya, mereka telah melakukannya untuk kami).
orang baik dari Roskomsvoboda menyadari indah API, melalui mana kita bisa mendapatkan apa yang kita butuhkan:
Untuk melakukan ini, kami memerlukan beberapa VPS asing kecil, sebaiknya dengan lalu lintas tidak terbatas - ada banyak di antaranya seharga 3-5 dolar. Anda perlu mengambilnya di luar negeri agar ping tidak terlalu besar, tetapi sekali lagi, pertimbangkan bahwa Internet dan geografi tidak selalu bersamaan. Dan karena tidak ada SLA untuk 5 dolar, lebih baik mengambil 2+ bagian dari penyedia yang berbeda untuk toleransi kesalahan.
Selanjutnya, kita perlu menyiapkan terowongan terenkripsi dari router klien ke VPS. Saya menggunakan Wireguard sebagai yang tercepat dan termudah untuk disiapkan. Saya juga memiliki router klien berbasis Linux (APU2 atau sesuatu di OpenWRT). Dalam kasus beberapa Mikrotik / Cisco, Anda dapat menggunakan protokol yang tersedia di dalamnya seperti OpenVPN dan GRE-over-IPSEC.
Identifikasi dan pengalihan lalu lintas kepentingan
Anda tentu saja dapat mematikan semua lalu lintas Internet melalui negara asing. Namun, kemungkinan besar, kecepatan bekerja dengan konten lokal akan sangat terpengaruh karenanya. Plus, persyaratan bandwidth pada VPS akan jauh lebih tinggi.
Oleh karena itu, kita perlu mengalokasikan lalu lintas ke situs yang diblokir dan secara selektif mengarahkannya ke terowongan. Bahkan jika beberapa lalu lintas "ekstra" sampai di sana, itu masih jauh lebih baik daripada mengemudikan semuanya melalui terowongan.
Untuk mengelola lalu lintas, kami akan menggunakan protokol BGP dan mengumumkan rute ke jaringan yang diperlukan dari VPS kami ke klien. Mari ambil BIRD sebagai salah satu daemon BGP yang paling fungsional dan nyaman.
IP
Dengan pemblokiran berdasarkan IP, semuanya menjadi jelas: kami cukup mengumumkan semua IP yang diblokir dengan VPS. Masalahnya adalah ada sekitar 600 ribu subnet dalam daftar yang dikembalikan oleh API, dan sebagian besar dari mereka adalah /32 host. Jumlah rute ini dapat membingungkan router klien yang lemah.
Oleh karena itu, saat memproses daftar, diputuskan untuk meringkas hingga jaringan / 24 jika memiliki 2 host atau lebih. Dengan demikian, jumlah rute dikurangi menjadi ~100 ribu. Script untuk ini akan mengikuti.
domain
Ini lebih rumit dan ada beberapa cara. Misalnya, Anda dapat menginstal Squid transparan pada setiap router klien dan melakukan intersepsi HTTP di sana dan mengintip ke dalam jabat tangan TLS untuk mendapatkan URL yang diminta pada kasus pertama dan domain dari SNI pada kasus kedua.
Namun karena semua jenis TLS1.3 + eSNI bermodel baru, analisis HTTPS menjadi semakin tidak nyata setiap hari. Ya, dan infrastruktur di sisi klien menjadi lebih rumit - Anda harus menggunakan setidaknya OpenWRT.
Oleh karena itu, saya memutuskan untuk mengambil jalur mencegat respons terhadap permintaan DNS. Di sini, juga, DNS-over-TLS / HTTPS apa pun mulai melayang di atas kepala Anda, tetapi kami dapat (untuk saat ini) mengontrol bagian ini pada klien - nonaktifkan atau gunakan server Anda sendiri untuk DoT / DoH.
Bagaimana cara mencegat DNS?
Di sini juga, mungkin ada beberapa pendekatan.
Intersepsi lalu lintas DNS melalui PCAP atau NFLOG
Kedua metode intersepsi ini diimplementasikan dalam utilitas sidmat. Tapi sudah lama tidak didukung dan fungsinya sangat primitif, jadi Anda masih perlu menulis harness untuk itu.
Analisis log server DNS
Sayangnya, rekursor yang saya kenal tidak dapat mencatat respons, tetapi hanya permintaan. Pada prinsipnya, ini logis, karena, tidak seperti permintaan, jawaban memiliki struktur yang rumit dan sulit untuk menuliskannya dalam bentuk teks.
Ketuk DNS
Untungnya, banyak dari mereka sudah mendukung DNSTap untuk tujuan ini.
Apa itu DNSTap?
Ini adalah protokol klien-server berdasarkan Protocol Buffer dan Frame Streams untuk mentransfer dari server DNS ke pengumpul permintaan dan respons DNS terstruktur. Pada dasarnya, server DNS mentransmisikan metadata kueri dan respons (jenis pesan, IP klien/server, dll.) ditambah pesan DNS lengkap dalam bentuk (biner) yang digunakannya melalui jaringan.
Penting untuk dipahami bahwa dalam paradigma DNSTap, server DNS bertindak sebagai klien dan kolektor bertindak sebagai server. Artinya, server DNS terhubung ke kolektor, dan bukan sebaliknya.
Hari ini DNSTap didukung di semua server DNS populer. Tapi, misalnya, BIND di banyak distribusi (seperti Ubuntu LTS) sering dibangun karena alasan tertentu tanpa dukungannya. Jadi jangan repot-repot memasang kembali, tetapi gunakan rekursor yang lebih ringan dan lebih cepat - Tidak terikat.
Bagaimana cara menangkap DNSTap?
Ada beberapanomor Utilitas CLI untuk bekerja dengan aliran peristiwa DNSTap, tetapi tidak cocok untuk menyelesaikan masalah kita. Oleh karena itu, saya memutuskan untuk menciptakan sepeda saya sendiri yang akan melakukan semua yang diperlukan: dnstap-bgp
Algoritma kerja:
Saat diluncurkan, ini memuat daftar domain dari file teks, membalikkannya (habr.com -> com.habr), mengecualikan garis putus-putus, duplikat, dan subdomain (yaitu jika daftar berisi habr.com dan www.habr.com, itu akan dimuat hanya yang pertama) dan membangun pohon awalan untuk pencarian cepat melalui daftar ini
Bertindak sebagai server DNSTap, menunggu koneksi dari server DNS. Pada prinsipnya, ini mendukung soket UNIX dan TCP, tetapi server DNS yang saya tahu hanya dapat menggunakan soket UNIX
Paket DNSTap yang masuk pertama-tama dideserialisasi menjadi struktur Protobuf, dan kemudian pesan DNS biner itu sendiri, yang terletak di salah satu bidang Protobuf, diurai ke tingkat catatan DNS RR
Itu diperiksa apakah host yang diminta (atau domain induknya) ada dalam daftar yang dimuat, jika tidak, responsnya diabaikan
Hanya RR A/AAAA/CNAME yang dipilih dari respons dan alamat IPv4/IPv6 yang sesuai akan diekstrak darinya
Alamat IP di-cache dengan TTL yang dapat dikonfigurasi dan diiklankan ke semua peer BGP yang dikonfigurasi
Saat menerima respons yang menunjuk ke IP yang sudah di-cache, TTL-nya diperbarui
Setelah TTL kedaluwarsa, entri dihapus dari cache dan dari pengumuman BGP
Fungsionalitas tambahan:
Membaca ulang daftar domain oleh SIGHUP
Menjaga cache tetap sinkron dengan instance lain dnstap-bgp melalui HTTP/JSON
Gandakan cache pada disk (dalam database BoltDB) untuk mengembalikan isinya setelah restart
Dukungan untuk beralih ke ruang nama jaringan yang berbeda (mengapa ini diperlukan akan dijelaskan di bawah)
dukungan IPv6
Keterbatasan:
Domain IDN belum didukung
Beberapa pengaturan BGP
saya kumpulkan RPM dan DEB paket untuk kemudahan instalasi. Harus bekerja pada semua OS yang relatif baru dengan systemd. mereka tidak memiliki ketergantungan.
Skema itu
Jadi, mari kita mulai merakit semua komponen menjadi satu. Akibatnya, kita harus mendapatkan sesuatu seperti topologi jaringan ini:
Logika kerja, menurut saya, jelas dari diagram:
Klien telah mengonfigurasi server kami sebagai DNS, dan kueri DNS juga harus melalui VPN. Ini diperlukan agar penyedia tidak dapat menggunakan intersepsi DNS untuk memblokir.
Saat membuka situs, klien mengirimkan kueri DNS seperti "berapa IP dari xxx.org"
Tidak terikat menyelesaikan xxx.org (atau mengambilnya dari cache) dan mengirimkan respons ke klien "xxx.org memiliki IP ini dan itu", menggandakannya secara paralel melalui DNSTap
dnstap-bgp mengumumkan alamat ini di BURUNG melalui BGP jika domain ada di daftar blokir
BURUNG mengiklankan rute ke IP ini dengan next-hop self router klien
Paket selanjutnya dari klien ke IP ini melewati terowongan
Di server, untuk rute ke situs yang diblokir, saya menggunakan tabel terpisah di dalam BIRD dan tidak bersinggungan dengan OS dengan cara apa pun.
Skema ini memiliki kekurangan: paket SYN pertama dari klien, kemungkinan besar, akan memiliki waktu untuk keluar melalui penyedia domestik. rute tidak segera diumumkan. Dan di sini opsi dimungkinkan tergantung pada bagaimana penyedia melakukan pemblokiran. Jika dia hanya menghentikan lalu lintas, maka tidak ada masalah. Dan jika dia mengarahkannya ke beberapa DPI, maka (secara teoritis) efek khusus dimungkinkan.
Mungkin juga klien tidak menghargai keajaiban DNS TTL, yang dapat menyebabkan klien menggunakan beberapa entri basi dari cache busuknya alih-alih meminta Unbound.
Dalam praktiknya, baik yang pertama maupun yang kedua tidak menyebabkan masalah bagi saya, tetapi jarak tempuh Anda mungkin berbeda.
Penyetelan Server
Untuk kemudahan bergulir, saya menulis peran untuk Ansible. Itu dapat mengkonfigurasi server dan klien berdasarkan Linux (dirancang untuk distribusi berbasis deb). Semua pengaturan cukup jelas dan diatur inventori.yml. Peran ini dipotong dari buku pedoman besar saya, sehingga mungkin mengandung kesalahan - tarik permintaan selamat datang π
Mari kita lihat komponen utamanya.
bgp
Menjalankan dua daemon BGP pada host yang sama memiliki masalah mendasar: BIRD tidak ingin menyiapkan peering BGP dengan localhost (atau antarmuka lokal apa pun). Dari kata sama sekali. Googling dan membaca milis tidak membantu, mereka mengklaim bahwa ini memang disengaja. Mungkin ada beberapa cara, tapi saya tidak menemukannya.
Anda dapat mencoba daemon BGP lain, tetapi saya suka BIRD dan digunakan di mana-mana oleh saya, saya tidak ingin menghasilkan entitas.
Oleh karena itu, saya menyembunyikan dnstap-bgp di dalam ruang nama jaringan, yang terhubung ke root melalui antarmuka veth: ini seperti pipa, yang ujungnya menonjol di ruang nama yang berbeda. Pada masing-masing tujuan ini, kami menggantungkan alamat IP p2p pribadi yang tidak melampaui host, sehingga bisa berupa apa saja. Ini adalah mekanisme yang sama yang digunakan untuk mengakses proses di dalamnya dicintai oleh semua Docker dan wadah lainnya.
Untuk ini ditulis naskah dan fungsi yang sudah dijelaskan di atas untuk menyeret rambut Anda sendiri ke namespace lain telah ditambahkan ke dnstap-bgp. Karena itu, ini harus dijalankan sebagai root atau dikeluarkan 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
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 melarangnya terhubung ke semua jenis soket DNSTap. Anda dapat menghapus profil ini, atau menonaktifkannya:
Ini mungkin harus ditambahkan ke buku pedoman. Sangat ideal, tentu saja, untuk memperbaiki profil dan memberikan hak yang diperlukan, tetapi saya terlalu malas.
tidak terikat.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
Ini mengunduh daftar, merangkum awalan pfx. Di jangan_tambahkan ΠΈ jangan_rangkum Anda dapat memberi tahu IP dan jaringan untuk melewati atau tidak meringkas. Saya membutuhkannya. subnet VPS saya ada di daftar blokir π
Lucunya, API RosKomSvoboda memblokir permintaan dengan agen pengguna Python default. Sepertinya script-kiddy mengerti. Oleh karena itu, kami mengubahnya menjadi Ognelis.
Sejauh ini, ini hanya berfungsi dengan IPv4. pangsa IPv6 kecil, tetapi akan mudah diperbaiki. Kecuali Anda harus menggunakan bird6 juga.
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
Saya menjalankannya di mahkota sekali sehari, mungkin perlu menariknya setiap 4 jam. ini menurut saya adalah masa pembaharuan yang disyaratkan RKN dari provider. Plus, mereka memiliki beberapa pemblokiran super mendesak lainnya, yang mungkin tiba lebih cepat.
Melakukan hal berikut:
Jalankan skrip pertama dan perbarui daftar rute (rkn_routes.list) untuk BURUNG
Muat ulang BURUNG
Memperbarui dan membersihkan daftar domain untuk dnstap-bgp
Muat ulang 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
Mereka ditulis tanpa banyak berpikir, jadi jika Anda melihat sesuatu yang dapat diperbaiki - lakukanlah.
Pengaturan klien
Di sini saya akan memberikan contoh untuk router Linux, tetapi dalam kasus Mikrotik / Cisco seharusnya lebih mudah.
Pertama, kami menyiapkan BURUNG:
burung.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 tabel perutean kernel nomor 222.
Setelah itu, cukup meminta kernel untuk melihat pelat ini sebelum melihat yang 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
Itu saja, tetap mengkonfigurasi DHCP pada router untuk mendistribusikan alamat IP terowongan server sebagai DNS, dan skema sudah siap.
Kekurangan:
Dengan algoritme saat ini untuk menghasilkan dan memproses daftar domain, antara lain mencakup youtube.com dan CDN-nya.
Dan ini mengarah pada fakta bahwa semua video akan melalui VPN, yang dapat menyumbat seluruh saluran. Mungkin ada baiknya menyusun daftar pengecualian domain populer yang memblokir RKN untuk saat ini, keberaniannya tipis. Dan lewati mereka saat menguraikan.
Kesimpulan
Metode yang dijelaskan memungkinkan Anda melewati hampir semua pemblokiran yang saat ini diterapkan oleh penyedia.
Pada prinsipnya, dnstap-bgp dapat digunakan untuk tujuan lain di mana beberapa tingkat kontrol lalu lintas diperlukan berdasarkan nama domain. Perlu diingat bahwa di zaman kita, ribuan situs dapat bergantung pada alamat IP yang sama (di belakang beberapa Cloudflare, misalnya), jadi metode ini memiliki akurasi yang agak rendah.
Tapi untuk kebutuhan melewati kunci, ini sudah cukup.