Zaobiđite ILV blokiranje pomoću DNSTap i BGP

Zaobiđite ILV blokiranje pomoću DNSTap i BGP

Znam da je tema poprilicno preterana. Na primjer, postoji odličan članak, ali se tu razmatra samo IP dio liste blokova. Dodaćemo i domene.

Zbog činjenice da sudovi i RKN blokiraju sve desno i lijevo, a provajderi se trude da ne potpadnu pod kazne koje izdaje Revizorro, povezani gubici blokiranjem su prilično veliki. A među "zakonito" blokiranim stranicama ima mnogo korisnih (zdravo, rutracker)

Živim van nadležnosti RKN, ali su moji roditelji, rođaci i prijatelji ostali kod kuće. Stoga je odlučeno da se smisli jednostavan način za ljude koji su daleko od IT-a da zaobiđu blokiranje, po mogućnosti bez njihovog učešća.

U ovoj napomeni neću opisivati ​​osnovne mrežne stvari u koracima, ali ću opisati opšte principe kako se ova šema može implementirati. Dakle, znanje o tome kako mreža funkcionira općenito, a posebno u Linuxu, mora imati.

Vrste brava

Prvo, osvježimo sjećanje na ono što je blokirano.

Postoji nekoliko tipova zaključavanja u neučitanom XML-u iz RKN-a:

  • IP
  • Ime domena
  • URL

Radi jednostavnosti, smanjit ćemo ih na dva: IP i domenu, a domenu ćemo jednostavno povući iz blokiranja po URL-u (tačnije, to su već uradili za nas).

dobri ljudi iz Roskomsvoboda realizovao divno API, preko koje možemo dobiti ono što nam je potrebno:

Pristup blokiranim stranicama

Za to nam je potreban neki mali strani VPS, po mogućnosti sa neograničenim prometom - ima ih mnogo za 3-5 dolara. Morate ga uzeti u blisko inostranstvo tako da ping ne bude jako velik, ali opet, uzmite u obzir da se internet i geografija ne poklapaju uvijek. A pošto nema SLA za 5 dolara, bolje je uzeti 2+ komada od različitih provajdera radi tolerancije grešaka.

Zatim moramo postaviti šifrirani tunel od klijentskog rutera do VPS-a. Koristim Wireguard kao najbrži i najjednostavniji za postavljanje. Također imam klijentske rutere bazirane na Linuxu (APU2 ili nešto u OpenWRT). U slučaju nekih Mikrotik / Cisco, možete koristiti protokole dostupne na njima kao što su OpenVPN i GRE-over-IPSEC.

Identifikacija i preusmjeravanje prometa od interesa

Možete, naravno, isključiti sav internet promet preko stranih zemalja. Ali, najvjerovatnije, brzina rada s lokalnim sadržajem će uvelike patiti od toga. Osim toga, zahtjevi za propusnim opsegom na VPS-u će biti mnogo veći.

Stoga ćemo morati nekako dodijeliti promet blokiranim stranicama i selektivno ga usmjeriti na tunel. Čak i ako nešto od "dodatnog" saobraćaja stigne tamo, to je ipak mnogo bolje nego voziti sve kroz tunel.

Za upravljanje prometom koristit ćemo BGP protokol i najaviti rute do potrebnih mreža od našeg VPS-a do klijenata. Uzmimo BIRD kao jednog od najfunkcionalnijih i najprikladnijih BGP demona.

IP

Kod blokiranja po IP-u sve je jasno: jednostavno objavljujemo sve blokirane IP-ove s VPS-om. Problem je što postoji oko 600 hiljada podmreža na listi koju API vraća, a velika većina njih su /32 hosta. Ovaj broj ruta može zbuniti slabe klijentske rutere.

Stoga je prilikom obrade liste odlučeno da se sumira do mreže /24 ako ima 2 ili više hostova. Tako je broj ruta smanjen na ~100 hiljada. Scenario za ovo će uslijediti.

Domains

Komplikovanije je i postoji nekoliko načina. Na primjer, možete instalirati transparentni Squid na svaki klijentski ruter i tamo obaviti HTTP presretanje i zaviriti u TLS rukovanje kako biste dobili traženi URL u prvom slučaju i domenu od SNI u drugom.

Ali zbog svih vrsta novonastalih TLS1.3 + eSNI, HTTPS analiza svakim danom postaje sve manje stvarna. Da, i infrastruktura na strani klijenta postaje sve složenija - morat ćete koristiti barem OpenWRT.

Stoga sam odlučio krenuti putem presretanja odgovora na DNS upite. I ovdje svaki DNS-over-TLS / HTTPS počinje da lebdi iznad vaše glave, ali mi možemo (za sada) kontrolirati ovaj dio na klijentu - ili ga onemogućiti ili koristiti vlastiti server za DoT / DoH.

Kako presresti DNS?

I ovdje može postojati nekoliko pristupa.

  • Presretanje DNS saobraćaja putem PCAP-a ili NFLOG-a
    Obje ove metode presretanja implementirane su u uslužni program sidmat. Ali dugo nije podržan i funkcionalnost je vrlo primitivna, tako da još uvijek trebate napisati pojas za njega.
  • Analiza logova DNS servera
    Nažalost, meni poznati rekurzori ne mogu zapisivati ​​odgovore, već samo zahtjeve. U principu, to je logično, jer, za razliku od zahtjeva, odgovori imaju složenu strukturu i teško ih je napisati u tekstualnom obliku.
  • DNSTap
    Srećom, mnogi od njih već podržavaju DNSTap u tu svrhu.

Šta je DNSTap?

Zaobiđite ILV blokiranje pomoću DNSTap i BGP

To je klijent-server protokol baziran na baferima protokola i tokovima okvira za prijenos sa DNS servera na kolektor strukturiranih DNS upita i odgovora. U suštini, DNS server prenosi metapodatke upita i odgovora (tip poruke, klijent/server IP, itd.) plus kompletne DNS poruke u (binarnom) obliku u kojem radi sa njima preko mreže.

Važno je razumjeti da u DNSTap paradigmi, DNS server djeluje kao klijent, a kolektor djeluje kao server. Odnosno, DNS server se povezuje sa kolektorom, a ne obrnuto.

Danas je DNSTap podržan na svim popularnim DNS serverima. Ali, na primjer, BIND je u mnogim distribucijama (kao što je Ubuntu LTS) često izgrađen iz nekog razloga bez njegove podrške. Dakle, nemojmo se zamarati ponovnim sastavljanjem, već uzmimo lakši i brži rekursor - Unbound.

Kako uhvatiti DNSTap?

Postoje neki broj CLI uslužni programi za rad sa nizom DNSTap događaja, ali nisu prikladni za rješavanje našeg problema. Stoga sam odlučio da izmislim svoj bicikl koji će učiniti sve što je potrebno: dnstap-bgp

Algoritam rada:

  • Kada se pokrene, učitava listu domena iz tekstualne datoteke, invertuje ih (habr.com -> com.habr), isključuje isprekidane linije, duplikate i poddomene (tj. ako lista sadrži habr.com i www.habr.com, bit će učitan samo prvi) i gradi stablo prefiksa za brzo pretraživanje ove liste
  • Djelujući kao DNSTap server, čeka vezu sa DNS servera. U principu, podržava i UNIX i TCP utičnice, ali DNS serveri koje poznajem mogu koristiti samo UNIX utičnice
  • Dolazni DNSTap paketi se prvo deserializiraju u Protobuf strukturu, a zatim se sama binarna DNS poruka, koja se nalazi u jednom od Protobuf polja, analizira na nivo DNS RR zapisa.
  • Provjerava se da li je traženi host (ili njegova nadređena domena) na učitanoj listi, ako nije, odgovor se zanemaruje
  • Od odgovora se biraju samo A/AAAA/CNAME RR-ovi i iz njih se izdvajaju odgovarajuće IPv4/IPv6 adrese
  • IP adrese se keširaju s podesivim TTL-om i oglašavaju svim konfiguriranim BGP kolegama
  • Kada primite odgovor koji ukazuje na već keširanu IP adresu, njegov TTL se ažurira
  • Nakon što TTL istekne, unos se uklanja iz keš memorije i iz BGP najava

Dodatne funkcionalnosti:

  • Ponovno čitanje liste domena od strane SIGHUP-a
  • Održavanje predmemorije u sinhronizaciji s drugim instancama dnstap-bgp putem HTTP/JSON-a
  • Duplirajte keš memoriju na disku (u BoltDB bazi podataka) da biste vratili njen sadržaj nakon ponovnog pokretanja
  • Podrška za prebacivanje na drugi mrežni prostor imena (zašto je to potrebno bit će opisano u nastavku)
  • IPv6 podrška

Ograničenja:

  • IDN domene još nisu podržane
  • Nekoliko BGP postavki

Sakupio sam RPM i DEB paketi za jednostavnu instalaciju. Trebao bi raditi na svim relativno novijim OS-ovima sa systemd-om. nemaju nikakve zavisnosti.

Shema

Dakle, počnimo sa sastavljanjem svih komponenti zajedno. Kao rezultat, trebali bismo dobiti nešto poput ove mrežne topologije:
Zaobiđite ILV blokiranje pomoću DNSTap i BGP

Logika rada, mislim, jasna je iz dijagrama:

  • Klijent ima naš server konfiguriran kao DNS, a DNS upiti također moraju ići preko VPN-a. Ovo je neophodno kako provajder ne može koristiti DNS presretanje za blokiranje.
  • Prilikom otvaranja stranice, klijent šalje DNS upit poput "koji su IP-ovi xxx.org"
  • nevezan rješava xxx.org (ili ga uzima iz keša) i šalje odgovor klijentu "xxx.org ima takav i takav IP", duplirajući ga paralelno preko DNSTap
  • dnstap-bgp objavljuje ove adrese u PTICA preko BGP-a ako je domen na listi blokiranih
  • PTICA oglašava rutu do ovih IP adresa sa next-hop self klijentski ruter
  • Naredni paketi od klijenta do ovih IP adresa prolaze kroz tunel

Na serveru za rute do blokiranih sajtova koristim zasebnu tabelu unutar BIRD-a i ni na koji način se ne ukršta sa OS-om.

Ova shema ima nedostatak: prvi SYN paket od klijenta će najvjerovatnije imati vremena da ode preko domaćeg provajdera. ruta se ne objavljuje odmah. I ovdje su moguće opcije ovisno o tome kako provajder radi blokiranje. Ako samo obustavi saobraćaj, onda nema problema. A ako ga preusmjeri na neki DPI, onda su (teoretski) mogući specijalni efekti.

Takođe je moguće da klijenti ne poštuju DNS TTL čuda, što može dovesti do toga da klijent koristi neke zastarele unose iz svog pokvarenog keša umesto da traži Unbound.

U praksi mi ni prvi ni drugi nisu pravili probleme, ali vaša kilometraža može varirati.

Podešavanje servera

Radi lakšeg kotrljanja, napisao sam uloga za Ansible. Može da konfiguriše i servere i klijente zasnovane na Linuxu (dizajniran za distribucije zasnovane na deb-u). Sva podešavanja su prilično očigledna i postavljena su inventar.yml. Ova uloga je izrezana iz moje velike knjige igranja, tako da može sadržavati greške - povuci zahtjeve dobrodošli 🙂

Idemo kroz glavne komponente.

Bgp

Pokretanje dva BGP demona na istom hostu ima fundamentalni problem: BIRD ne želi da podesi BGP peering sa lokalnim hostom (ili bilo kojim lokalnim interfejsom). Od riječi uopće. Guglanje i čitanje mailing-lista nije pomoglo, tvrde da je to osmišljeno. Možda postoji neki način, ali ga nisam našao.

Možete isprobati drugi BGP demon, ali ja volim BIRD i koristim ga svuda, ne želim da proizvodim entitete.

Stoga sam sakrio dnstap-bgp unutar mrežnog imenskog prostora, koji je povezan sa root-om preko veth interfejsa: to je kao cev čiji krajevi vire u različitim imenskim prostorima. Na svakom od ovih krajeva kačimo privatne p2p IP adrese koje ne idu dalje od hosta, tako da mogu biti bilo šta. Ovo je isti mehanizam koji se koristi za pristup unutrašnjim procesima voljen od svih Docker i drugi kontejneri.

Za ovo je i napisano skripta a već gore opisana funkcionalnost za prevlačenje za kosu u drugi imenski prostor je dodana u dnstap-bgp. Zbog toga se mora pokrenuti kao root ili izdati binarnom sistemu CAP_SYS_ADMIN putem naredbe setcap.

Primjer skripte za kreiranje prostora imena

#!/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

Podrazumevano, u Ubuntu-u, Unbound binarni fajl je stegnut AppArmor profilom, koji mu zabranjuje povezivanje sa svim vrstama DNSTap utičnica. Možete ili izbrisati ovaj profil ili ga onemogućiti:

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

Ovo bi vjerovatno trebalo dodati u knjigu. Idealno je, naravno, ispraviti profil i izdati potrebna prava, ali sam bio previše lijen.

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

Preuzimanje i obrada lista

Skripta za preuzimanje i obradu liste IP adresa
Preuzima listu, sumira do prefiksa pfx. The dont_add и dont_summarize možete reći IP-ovima i mrežama da preskoče ili ne sumiraju. Trebao mi je. podmreža mog VPS-a je bila na listi blokiranih 🙂

Smiješno je to što RosKomSvoboda API blokira zahtjeve sa zadanim Python korisničkim agentom. Izgleda da je scenarista shvatio. Stoga ga mijenjamo u Ognelis.

Za sada radi samo sa IPv4. udio IPv6 je mali, ali će se to lako popraviti. Osim ako ne morate koristiti i 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)

Skripta za ažuriranje
Nanosim ga na krunu jednom dnevno, možda se isplati povlačiti svaka 4 sata. ovo je, po mom mišljenju, period obnove koji RKN zahtijeva od provajdera. Osim toga, imaju još neke super-hitne blokade, koje mogu stići brže.

Radi sljedeće:

  • Pokreće prvu skriptu i ažurira listu ruta (rkn_routes.list) za BIRD
  • Reload BIRD
  • Ažurira i čisti listu domena za dnstap-bgp
  • Ponovo učitaj 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

Napisane su bez puno razmišljanja, pa ako vidite nešto što se može poboljšati - samo napred.

Podešavanje klijenta

Ovdje ću dati primjere za Linux rutere, ali u slučaju Mikrotik / Cisco to bi trebalo biti još lakše.

Prvo smo postavili 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;
}

Tako ćemo sinhronizovati rute primljene od BGP-a sa tabelom rutiranja kernela broj 222.

Nakon toga, dovoljno je zamoliti kernel da pogleda ovu ploču prije nego što pogleda zadanu:

# 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

Sve, ostaje da konfigurišete DHCP na ruteru da distribuira IP adresu tunela servera kao DNS i shema je spremna.

mane

Sa aktuelnim algoritmom za generisanje i obradu liste domena, on između ostalog uključuje i youtube.com i njegove CDN-ove.

A to dovodi do činjenice da će svi videozapisi proći kroz VPN, što može začepiti cijeli kanal. Možda je vrijedno sastaviti listu popularnih domena-isključenja koje blokiraju RKN za sada, crijeva su tanka. I preskočite ih prilikom raščlanjivanja.

zaključak

Opisani način vam omogućava da zaobiđete gotovo svako blokiranje koje provajderi trenutno implementiraju.

U principu, dnstap-bgp može se koristiti za bilo koju drugu svrhu gdje je potreban određeni nivo kontrole prometa na osnovu naziva domene. Samo imajte na umu da u naše vrijeme hiljadu lokacija može visiti na istoj IP adresi (iza nekog Cloudflarea, na primjer), tako da ova metoda ima prilično nisku preciznost.

Ali za potrebe zaobilaženja brava, ovo je sasvim dovoljno.

Dodaci, izmjene, zahtjevi za povlačenjem - dobrodošli!

izvor: www.habr.com

Dodajte komentar