Pintasan penyekatan ILV dengan DNSTap dan BGP

Pintasan penyekatan ILV dengan DNSTap dan BGP

Topiknya agak usang, saya tahu. Sebagai contoh, terdapat cemerlang artikel, tetapi hanya bahagian IP senarai sekat yang dipertimbangkan di sana. Kami juga akan menambah domain.

Disebabkan fakta bahawa mahkamah dan RKN menyekat semua kiri dan kanan, dan penyedia berusaha keras untuk tidak dikenakan denda yang dikeluarkan oleh Revizorro, kerugian yang berkaitan daripada menyekat adalah agak besar. Dan di antara tapak yang disekat "secara sah" terdapat banyak yang berguna (hello, rutracker)

Saya tinggal di luar bidang kuasa RKN, tetapi ibu bapa, saudara mara dan rakan-rakan saya kekal di tanah air saya. Oleh itu, telah diputuskan untuk menghasilkan cara mudah bagi orang yang jauh dari IT untuk memintas penyekatan, sebaik-baiknya tanpa penyertaan mereka sama sekali.

Dalam nota ini, saya tidak akan menerangkan perkara rangkaian asas langkah demi langkah, tetapi akan menerangkan prinsip umum bagaimana skim ini boleh dilaksanakan. Oleh itu, pengetahuan tentang cara rangkaian berfungsi secara umum dan di Linux khususnya adalah mesti ada.

Jenis-jenis kunci

Mula-mula, mari kita segarkan semula ingatan kita tentang perkara yang disekat.

Terdapat beberapa jenis kunci dalam XML yang dimuat turun daripada RKN:

  • IP
  • Π”ΠΎΠΌΠ΅Π½
  • URL

Untuk memudahkan, kami akan mengurangkannya kepada dua: IP dan domain, dan daripada menyekat melalui URL, kami hanya akan menarik keluar domain (atau lebih tepat lagi, ini telah dilakukan untuk kami).

Orang baik dari Roskomsvoboda dilaksanakan yang indah API, di mana kita boleh mendapatkan apa yang kita perlukan:

Akses ke tapak yang disekat

Untuk melakukan ini, kami memerlukan beberapa VPS asing yang kecil, sebaik-baiknya dengan trafik tanpa had - terdapat banyak daripada mereka untuk 3-5 dolar. Anda perlu membelinya di luar negara berhampiran supaya ping tidak terlalu tinggi, tetapi sekali lagi perlu diingat bahawa Internet dan geografi tidak selalunya bertepatan. Dan oleh kerana tiada SLA untuk 5 dolar, adalah lebih baik untuk mengambil 2+ keping daripada pembekal yang berbeza untuk toleransi kesalahan.

Seterusnya, kita perlu menyediakan terowong yang disulitkan dari penghala pelanggan ke VPS. Saya menggunakan Wireguard sebagai yang terpantas dan paling mudah untuk dikonfigurasikan kerana... Penghala pelanggan saya juga berasaskan Linux (APU2 atau sesuatu di OpenWRT). Dalam kes sesetengah Mikrotik/Cisco, anda boleh menggunakan protokol yang tersedia padanya seperti OpenVPN dan GRE-over-IPSEC.

Pengenalpastian dan pengalihan lalu lintas yang diminati

Anda boleh, sudah tentu, menghentikan semua trafik Internet daripada pergi ke luar negara. Tetapi, kemungkinan besar, kelajuan bekerja dengan kandungan tempatan akan mengalami banyak masalah daripada ini. Selain itu, keperluan lebar jalur pada VPS akan lebih tinggi.

Oleh itu, kami perlu mengasingkan trafik ke tapak yang disekat dan secara terpilih menghalakannya ke dalam terowong. Walaupun beberapa trafik "tambahan" tiba di sana, ia masih jauh lebih baik daripada memandu segala-galanya melalui terowong.

Untuk menguruskan trafik, kami akan menggunakan protokol BGP dan mengiklankan laluan ke rangkaian yang diperlukan daripada VPS kami kepada pelanggan. Mari kita ambil BIRD sebagai daemon BGP, kerana ia adalah salah satu yang paling berfungsi dan mudah.

IP

Semuanya jelas dengan penyekatan IP: kami hanya mengumumkan semua IP yang disekat daripada VPS. Masalahnya ialah terdapat kira-kira 600 ribu subnet dalam senarai yang disediakan oleh API, dan sebahagian besar daripadanya ialah /32 hos. Bilangan laluan ini boleh mengelirukan penghala pelanggan yang lemah.

Oleh itu, apabila memproses senarai, ia telah memutuskan untuk menjumlahkan kepada rangkaian /24 jika terdapat 2 atau lebih hos di dalamnya. Oleh itu, bilangan laluan dikurangkan kepada ~100 ribu. Skrip untuk ini akan menyusul.

domain

Ia lebih rumit dan terdapat beberapa cara. Sebagai contoh, anda boleh memasang Squid lutsinar pada setiap penghala pelanggan dan memintas HTTP di sana dan mengintip jabat tangan TLS untuk mendapatkan URL yang diminta dalam kes pertama dan domain daripada SNI dalam kes kedua.

Tetapi disebabkan semua model TLS1.3+eSNI yang baru, analisis HTTPS menjadi semakin kurang realistik setiap hari. Dan infrastruktur di bahagian pelanggan menjadi lebih rumit - anda perlu menggunakan sekurang-kurangnya OpenWRT.

Oleh itu, saya memutuskan untuk mengambil jalan memintas respons kepada pertanyaan DNS. Di sini juga, semua jenis DNS-over-TLS/HTTPS mula berlegar di atas kepala kami, tetapi kami boleh (buat masa ini) mengawal bahagian ini pada klien - sama ada melumpuhkannya atau menggunakan pelayan kami sendiri untuk DoT/DoH.

Bagaimana untuk merampas DNS?

Mungkin juga terdapat beberapa pendekatan di sini.

  • Memintas trafik DNS melalui PCAP atau NFLOG
    Kedua-dua kaedah pemintasan ini dilaksanakan dalam utiliti sidmat. Tetapi ia tidak disokong untuk masa yang lama dan fungsinya sangat primitif, jadi anda masih perlu menulis pengikat untuknya.
  • Analisis log pelayan DNS
    Malangnya, rekursor yang saya kenali tidak tahu cara log respons, tetapi hanya meminta. Pada dasarnya, ini adalah logik, kerana, tidak seperti permintaan, respons mempunyai struktur yang kompleks dan sukar untuk menulisnya dalam bentuk teks.
  • DNSTap
    Nasib baik, ramai daripada mereka sudah menyokong DNSTap untuk tujuan ini.

Apakah DNSTap?

Pintasan penyekatan ILV dengan DNSTap dan BGP

Ini ialah protokol pelayan pelanggan berdasarkan Penampan Protokol dan Strim Bingkai untuk memindahkan pertanyaan dan respons DNS berstruktur daripada pelayan DNS kepada pengumpul. Pada asasnya, pelayan DNS menghantar metadata permintaan dan tindak balas (jenis mesej, IP klien/pelayan, dll.) serta mesej DNS penuh dalam bentuk (perduaan) di mana ia berfungsi dengan mereka melalui rangkaian.

Adalah penting untuk memahami bahawa dalam paradigma DNSTap, pelayan DNS bertindak sebagai pelanggan, dan pengumpul bertindak sebagai pelayan. Iaitu, pelayan DNS menyambung kepada pengumpul, dan bukan sebaliknya.

Hari ini, DNSTap disokong dalam semua pelayan DNS yang popular. Tetapi, sebagai contoh, BIND dalam banyak pengedaran (seperti Ubuntu LTS) sering disusun atas sebab tertentu tanpa sokongannya. Oleh itu, jangan risau dengan membina semula, tetapi ambil pengulangan yang lebih ringan dan lebih pantas - Tidak terikat.

Bagaimana untuk menangkap DNSTap?

Terdapat beberapa nombor Terdapat utiliti CLI untuk bekerja dengan aliran peristiwa DNSTap, tetapi ia tidak sesuai untuk menyelesaikan masalah kami. Jadi saya memutuskan untuk mencipta basikal saya sendiri, yang akan melakukan semua yang diperlukan: dnstap-bgp

Algoritma kerja:

  • Apabila dilancarkan, ia memuatkan senarai domain daripada fail teks, membalikkannya (habr.com -> com.habr), mengecualikan baris putus, pendua dan subdomain (iaitu jika senarai mengandungi habr.com dan www.habr.com, ia akan dimuatkan hanya yang pertama) dan membina pepohon awalan untuk carian pantas melalui senarai ini
  • Bertindak sebagai pelayan DNSTap, ia menunggu sambungan daripada pelayan DNS. Pada dasarnya, ia menyokong kedua-dua soket UNIX dan TCP, tetapi pelayan DNS yang saya tahu hanya menyokong soket UNIX
  • Paket DNSTap yang masuk pertama kali dinyahsiri ke dalam struktur Protobuf, dan kemudian mesej DNS binari itu sendiri, yang terletak dalam salah satu medan Protobuf, dihuraikan ke tahap rekod DNS RR
  • Ia disemak sama ada hos yang diminta (atau domain induknya) berada dalam senarai dimuatkan; jika tidak, respons diabaikan
  • Hanya RR A/AAAA/CNAME dipilih daripada respons dan alamat IPv4/IPv6 yang sepadan diekstrak daripadanya
  • Alamat IP dicache dengan TTL boleh dikonfigurasikan dan diiklankan kepada semua rakan BGP yang dikonfigurasikan
  • Apabila menerima respons yang menunjuk ke IP yang telah dicache, TTLnya dikemas kini
  • Selepas TTL tamat tempoh, entri dialih keluar daripada cache dan daripada pengumuman BGP

Fungsi tambahan:

  • Membaca semula senarai domain oleh SIGHUP
  • Segerakkan cache dengan kejadian lain dnstap-bgp melalui HTTP/JSON
  • Menduakan cache pada cakera (dalam pangkalan data BoltDB) untuk memulihkan kandungannya selepas dimulakan semula
  • Sokongan untuk beralih ke ruang nama rangkaian yang berbeza (mengapa ini diperlukan akan diterangkan di bawah)
  • sokongan IPv6

Had:

  • Domain IDN belum lagi disokong
  • Beberapa tetapan BGP

saya kumpul RPM dan DEB pakej untuk pemasangan mudah. Harus berfungsi pada semua OS yang agak baru dengan systemd, kerana... mereka tidak mempunyai tanggungan.

Skim ini

Jadi, mari kita mula memasang semua komponen bersama-sama. Akibatnya, kita harus mendapatkan sesuatu seperti topologi rangkaian ini:
Pintasan penyekatan ILV dengan DNSTap dan BGP

Logik kerja, saya fikir, jelas dari rajah:

  • Pelanggan telah mengkonfigurasi pelayan kami sebagai DNS, dan permintaan DNS juga mesti melalui VPN. Ini adalah perlu supaya pembekal tidak boleh menggunakan pemintasan DNS untuk menyekat.
  • Apabila pelanggan membuka tapak web, ia menghantar permintaan DNS seperti "apa IP yang xxx.org ada?"
  • Tidak terjual menyelesaikan xxx.org (atau mengambilnya dari cache) dan menghantar respons kepada klien "xxx.org mempunyai IP begini dan begitu", serentak menduplikasinya melalui DNSTap
  • dnstap-bgp mengiklankan alamat ini di BURUNG melalui BGP jika domain berada dalam senarai disekat
  • BURUNG mengiklankan laluan ke IP ini dengan next-hop self penghala pelanggan
  • Paket seterusnya daripada pelanggan kepada IP ini melalui terowong

Pada pelayan, saya menggunakan jadual berasingan di dalam BIRD untuk laluan ke tapak yang disekat dan ia tidak bersilang dengan OS dalam apa jua cara.

Terdapat kelemahan dalam skim ini: paket SYN pertama daripada pelanggan berkemungkinan besar mempunyai masa untuk melalui pembekal domestik kerana Laluan tidak diumumkan serta-merta. Dan di sini terdapat pilihan yang mungkin bergantung pada cara penyedia melakukan penyekatan. Jika dia hanya menurunkan trafik, maka tidak ada masalah. Dan jika ia mengubah hala ke beberapa DPI, maka (secara teorinya) kesan khas adalah mungkin.

Keajaiban juga boleh berlaku apabila pelanggan tidak menghormati TTL DNS, yang boleh menyebabkan pelanggan menggunakan beberapa rekod lapuk daripada cache busuknya dan bukannya meminta Unbound.

Dalam amalan, yang pertama mahupun yang kedua tidak menyebabkan masalah kepada saya, tetapi perbatuan anda mungkin berbeza-beza.

Penalaan Pelayan

Untuk memudahkan pelancaran, saya menulis peranan untuk Ansible. Ia boleh mengkonfigurasi kedua-dua pelayan dan pelanggan berdasarkan Linux (direka bentuk untuk pengedaran berasaskan deb). Semua tetapan agak jelas dan ditetapkan inventori.yml. Peranan ini dipotong daripada buku permainan besar saya, jadi ia mungkin mengandungi ralat - tarik permintaan selamat datang :)

Mari kita lihat komponen utama.

BGP

Apabila menjalankan dua daemon BGP pada hos yang sama, masalah asas timbul: BIRD tidak mahu meningkatkan peering BGP dengan localhost (atau dengan mana-mana antara muka tempatan). Daripada perkataan mutlak. Googling dan membaca senarai mel tidak membantu, mereka mendakwa bahawa ia adalah dengan reka bentuk. Mungkin ada beberapa cara, tetapi saya tidak menemuinya.

Anda boleh mencuba daemon BGP lain, tetapi saya suka BIRD dan saya menggunakannya di mana-mana, saya tidak mahu mencipta lebih banyak entiti.

Oleh itu, saya menyembunyikan dnstap-bgp di dalam ruang nama rangkaian, yang disambungkan ke akar melalui antara muka veth: ia seperti paip, yang hujungnya melekat dalam ruang nama yang berbeza. Pada setiap hujung ini kami melampirkan alamat IP p2p peribadi yang tidak melampaui hos, jadi ia boleh menjadi apa sahaja. Ini adalah mekanisme yang sama yang digunakan untuk mengakses proses secara dalaman kegemaran semua orang Docker dan bekas lain.

Inilah sebabnya mengapa ia ditulis skrip dan fungsi yang telah diterangkan di atas untuk menyeret diri anda dengan rambut ke ruang nama lain telah ditambahkan pada dnstap-bgp. Oleh sebab itu, ia mesti dijalankan sebagai akar atau diberikan kepada binari CAP_SYS_ADMIN melalui perintah setcap.

Contoh skrip untuk mencipta ruang nama

#!/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 lalai, dalam Ubuntu, binari Tidak Terikat diapit dengan profil AppArmor, yang melarangnya daripada menyambung ke mana-mana soket DNSTap. Anda boleh sama ada memadam profil ini atau melumpuhkannya:

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

Ini mungkin perlu ditambah pada buku permainan. Ia akan menjadi ideal, sudah tentu, untuk membetulkan profil dan mengeluarkan 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

Memuat turun dan memproses senarai

Skrip untuk memuat turun dan memproses senarai alamat IP
Ia memuat turun senarai, meringkaskan kepada awalan pfx. Π’ jangan_tambah ΠΈ jangan_ringkaskan anda boleh memberitahu IP dan rangkaian yang perlu dilangkau atau tidak diringkaskan. Saya perlukan ini kerana... subnet VPS saya berada dalam senarai sekatan :)

Perkara yang lucu ialah API RosKomSvoboda menyekat permintaan dengan ejen pengguna Python lalai. Nampaknya mereka mendapat skrip kiddy. Oleh itu, kami menukarnya kepada Ognelis.

Buat masa ini ia hanya berfungsi dengan IPv4 kerana... Bahagian IPv6 adalah kecil, tetapi ia akan mudah untuk diperbaiki. Melainkan anda juga perlu menggunakan burung6.

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 kemas kini
Saya menjalankannya sekali sehari melalui mahkota, mungkin ia berbaloi untuk menjalankannya sekali setiap 4 jam kerana... Ini, pada pendapat saya, adalah tempoh pembaharuan yang diperlukan oleh RKN daripada pembekal. Selain itu, mereka mempunyai beberapa sekatan sangat mendesak lain yang mungkin tiba lebih cepat.

Adakah perkara berikut:

  • Menjalankan skrip pertama dan mengemas kini senarai laluan (rkn_routes.list) untuk BURUNG
  • Muat semula BIRD
  • Mengemas kini dan membersihkan senarai domain untuk dnstap-bgp
  • Muat semula 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 berfikir, jadi jika anda melihat sesuatu yang boleh diperbaiki, lakukannya.

Persediaan pelanggan

Di sini saya akan memberikan contoh untuk penghala Linux, tetapi dalam kes Mikrotik/Cisco ia sepatutnya lebih mudah.

Mula-mula, sediakan BIRD:

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 cara ini kita akan menyegerakkan laluan yang diterima daripada BGP dengan jadual penghalaan kernel nombor 222.

Selepas ini, sudah cukup untuk meminta kernel melihat jadual ini sebelum melihat yang lalai:

# 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 sahaja, yang tinggal hanyalah mengkonfigurasi DHCP pada penghala untuk mengedarkan alamat IP terowong pelayan sebagai DNS dan skema sudah sedia.

Kecacatan

Dengan algoritma semasa untuk menjana dan memproses senarai domain, ia termasuk, antara lain, youtube.com dan CDNnya.

Dan ini membawa kepada fakta bahawa semua video akan dihantar melalui VPN, yang boleh menyumbat keseluruhan saluran. Mungkin berbaloi untuk menyusun senarai domain pengecualian popular yang RKN masih terlalu lemah untuk disekat. Dan langkau mereka apabila menghuraikan.

Kesimpulan

Kaedah yang diterangkan membolehkan anda memintas hampir semua sekatan yang sedang dilaksanakan oleh penyedia.

Pada asasnya dnstap-bgp boleh digunakan untuk sebarang tujuan lain di mana tahap pengurusan trafik tertentu berdasarkan nama domain diperlukan. Anda hanya perlu mengambil kira bahawa pada masa kini seribu tapak boleh digantung pada alamat IP yang sama (di belakang beberapa Cloudflare, sebagai contoh), jadi kaedah ini mempunyai ketepatan yang agak rendah.

Tetapi untuk keperluan memintas menyekat, ini sudah cukup.

Penambahan, suntingan, permintaan tarik adalah dialu-alukan!

Sumber: www.habr.com

Tambah komen