Abaikan pemblokiran ILV dengan DNSTap dan BGP

Abaikan pemblokiran ILV dengan DNSTap dan BGP

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:

Akses ke situs yang diblokir

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?

Abaikan pemblokiran ILV dengan DNSTap dan BGP

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 beberapa nomor 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:
Abaikan pemblokiran ILV dengan DNSTap dan BGP

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

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",
]

burung.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 melarangnya terhubung ke semua jenis soket DNSTap. 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 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.

Penambahan, pengeditan, permintaan tarik - selamat datang!

Sumber: www.habr.com

Tambah komentar