Ocoliți blocarea ILV cu DNSTap și BGP

Ocoliți blocarea ILV cu DNSTap și BGP

Subiectul este destul de aglomerat, știu. De exemplu, există o mare articol, dar numai partea IP a listei de blocare este considerată acolo. Vom adăuga și domenii.

Din cauza faptului că instanțele și RKN blochează totul în dreapta și în stânga, iar furnizorii se străduiesc din răsputeri să nu cadă sub amenzile emise de Revizorro, pierderile asociate din blocare sunt destul de mari. Și printre site-urile blocate „legal” există multe utile (bună ziua, rutracker)

Locuiesc în afara jurisdicției RKN, dar părinții, rudele și prietenii mei au rămas acasă. Așa că s-a decis să se vină cu o modalitate ușoară pentru oamenii departe de IT de a ocoli blocarea, de preferință fără participarea lor.

În această notă, nu voi descrie lucrurile de bază ale rețelei în pași, dar voi descrie principiile generale ale modului în care această schemă poate fi implementată. Deci, cunoașterea modului în care funcționează rețeaua în general și în Linux în special este o necesitate.

Tipuri de încuietori

Mai întâi, să ne reîmprospătăm memoria despre ceea ce este blocat.

Există mai multe tipuri de blocări în XML descărcat din RKN:

  • IP
  • Numele de domeniu
  • URL-ul

Pentru simplitate, le vom reduce la două: IP și domeniu și pur și simplu vom scoate domeniul de la blocarea prin URL (mai precis, au făcut deja acest lucru pentru noi).

oameni buni din Roskomsvoboda realizat un minunat API, prin care putem obține ceea ce ne trebuie:

Acces la site-uri blocate

Pentru a face acest lucru, avem nevoie de niște VPS străini mici, de preferință cu trafic nelimitat - există multe dintre acestea pentru 3-5 dolari. Trebuie să o luați în străinătate, astfel încât ping-ul să nu fie foarte mare, dar, din nou, țineți cont de faptul că internetul și geografia nu coincid întotdeauna. Și, deoarece nu există un SLA pentru 5 dolari, este mai bine să luați 2+ bucăți de la diferiți furnizori pentru toleranță la erori.

În continuare, trebuie să setăm un tunel criptat de la routerul client la VPS. Folosesc Wireguard ca fiind cel mai rapid și mai ușor de configurat. Am, de asemenea, routere client bazate pe Linux (APU2 sau ceva în OpenWRT). În cazul unor Mikrotik / Cisco, puteți utiliza protocoalele disponibile pe acestea, cum ar fi OpenVPN și GRE-over-IPSEC.

Identificarea și redirecționarea traficului de interes

Puteți, desigur, să opriți tot traficul de internet prin țări străine. Dar, cel mai probabil, viteza de lucru cu conținut local va avea de suferit foarte mult din cauza asta. În plus, cerințele de lățime de bandă pe VPS vor fi mult mai mari.

Prin urmare, va trebui să alocăm cumva traficul site-urilor blocate și să-l direcționăm selectiv către tunel. Chiar dacă o parte din traficul „în plus” ajunge acolo, este totuși mult mai bine decât să conduci totul prin tunel.

Pentru a gestiona traficul, vom folosi protocolul BGP și vom anunța rute către rețelele necesare de la VPS-ul nostru către clienți. Să luăm BIRD drept unul dintre cei mai funcționali și mai convenabil demoni BGP.

IP

Cu blocarea prin IP, totul este clar: pur și simplu anunțăm toate IP-urile blocate cu VPS. Problema este că există aproximativ 600 de mii de subrețele în lista pe care o returnează API-ul, iar marea majoritate a acestora sunt gazde /32. Acest număr de rute poate deruta routerele client slabe.

Prin urmare, la procesarea listei, s-a decis să rezumați până la rețea / 24 dacă are 2 sau mai multe gazde. Astfel, numărul de rute a fost redus la ~100 de mii. Va urma scenariul pentru aceasta.

domenii

Este mai complicat și există mai multe moduri. De exemplu, puteți instala un Squid transparent pe fiecare router client și puteți face interceptarea HTTP acolo și vă uitați în strângere de mână TLS pentru a obține URL-ul solicitat în primul caz și domeniul de la SNI în al doilea.

Dar, datorită a tot felul de noi TLS1.3 + eSNI, analiza HTTPS devine din ce în ce mai puțin realistă în fiecare zi. Da, iar infrastructura din partea clientului devine din ce în ce mai complicată - va trebui să utilizați cel puțin OpenWRT.

Prin urmare, am decis să iau calea interceptării răspunsurilor la solicitările DNS. Și aici, orice DNS-over-TLS / HTTPS începe să plutească deasupra capului tău, dar putem (deocamdată) controla această parte pe client - fie dezactivează-l, fie folosește propriul tău server pentru DoT / DoH.

Cum se interceptează DNS?

Și aici pot exista mai multe abordări.

  • Interceptarea traficului DNS prin PCAP sau NFLOG
    Ambele metode de interceptare sunt implementate în utilitate sidmat. Dar nu a fost susținut de mult timp și funcționalitatea este foarte primitivă, așa că trebuie să scrieți un ham pentru el.
  • Analiza jurnalelor serverului DNS
    Din păcate, recurgerii cunoscuți de mine nu sunt capabili să înregistreze răspunsuri, ci doar cereri. În principiu, acest lucru este logic, deoarece, spre deosebire de cereri, răspunsurile au o structură complexă și este dificil să le scrieți sub formă de text.
  • DNSTap
    Din fericire, multe dintre ele acceptă deja DNSTap în acest scop.

Ce este DNSTap?

Ocoliți blocarea ILV cu DNSTap și BGP

Este un protocol client-server bazat pe Protocol Buffers și Frame Streams pentru transferul de la un server DNS la un colector de interogări și răspunsuri DNS structurate. În esență, serverul DNS transmite metadate de interogare și răspuns (tip de mesaj, IP client/server etc.) plus mesaje DNS complete în forma (binară) în care lucrează cu ele prin rețea.

Este important de înțeles că în paradigma DNSTap, serverul DNS acționează ca un client, iar colectorul acționează ca un server. Adică serverul DNS se conectează la colector și nu invers.

Astăzi, DNSTap este acceptat de toate serverele DNS populare. Dar, de exemplu, BIND în multe distribuții (cum ar fi Ubuntu LTS) este adesea construit din anumite motive fără suportul său. Deci, să nu ne deranjam cu reasamblarea, ci să luăm un recursor mai ușor și mai rapid - Unbound.

Cum să prind DNSTap?

Există niste număr Utilitare CLI pentru lucrul cu un flux de evenimente DNSTap, dar nu sunt potrivite pentru rezolvarea problemei noastre. Prin urmare, am decis să-mi inventez propria bicicletă care va face tot ce este necesar: dnstap-bgp

Algoritm de lucru:

  • Când este lansat, încarcă o listă de domenii dintr-un fișier text, le inversează (habr.com -> com.habr), exclude liniile întrerupte, duplicatele și subdomeniile (adică dacă lista conține habr.com și www.habr.com, va fi încărcat doar primul) și construiește un arbore de prefix pentru căutarea rapidă prin această listă
  • Acționând ca un server DNSTap, așteaptă o conexiune de la un server DNS. În principiu, acceptă atât socket-uri UNIX, cât și TCP, dar serverele DNS pe care le cunosc pot folosi doar socket-uri UNIX
  • Pachetele DNSTap primite sunt mai întâi deserializate într-o structură Protobuf, iar apoi mesajul DNS binar însuși, situat într-unul dintre câmpurile Protobuf, este analizat la nivelul înregistrărilor DNS RR.
  • Se verifică dacă gazda solicitată (sau domeniul său părinte) se află în lista încărcată, dacă nu, răspunsul este ignorat
  • Doar A/AAAA/CNAME RR sunt selectate din răspuns și adresele IPv4/IPv6 corespunzătoare sunt extrase din acestea
  • Adresele IP sunt stocate în cache cu TTL configurabil și sunt anunțate tuturor colegilor BGP configurați
  • Când primiți un răspuns care indică un IP deja memorat în cache, TTL-ul acestuia este actualizat
  • După expirarea TTL, intrarea este eliminată din cache și din anunțurile BGP

Functionalitate suplimentara:

  • Recitirea listei de domenii de către SIGHUP
  • Menținerea cache-ului în sincronizare cu alte instanțe dnstap-bgp prin HTTP/JSON
  • Duplicați memoria cache de pe disc (în baza de date BoltDB) pentru a-i restabili conținutul după o repornire
  • Suport pentru comutarea la un alt spațiu de nume de rețea (de ce este necesar acest lucru va fi descris mai jos)
  • Suport IPv6

restricţii:

  • Domeniile IDN nu sunt acceptate încă
  • Puține setări BGP

am adunat RPM și DEB pachete pentru instalare ușoară. Ar trebui să funcționeze pe toate sistemele de operare relativ recente cu systemd. nu au nicio dependență.

Schema

Deci, să începem asamblarea tuturor componentelor împreună. Ca rezultat, ar trebui să obținem ceva ca această topologie de rețea:
Ocoliți blocarea ILV cu DNSTap și BGP

Logica muncii, cred, este clară din diagramă:

  • Clientul are serverul nostru configurat ca DNS, iar interogările DNS trebuie să treacă și prin VPN. Acest lucru este necesar pentru ca furnizorul să nu poată utiliza interceptarea DNS pentru a bloca.
  • La deschiderea site-ului, clientul trimite o interogare DNS de genul „care sunt IP-urile xxx.org”
  • unbound rezolvă xxx.org (sau îl ia din cache) și trimite un răspuns clientului „xxx.org are un astfel de IP”, duplicându-l în paralel prin DNSTap
  • dnstap-bgp anunta aceste adrese in BIRD prin BGP dacă domeniul se află pe lista blocate
  • BIRD face publicitate unei rute către aceste IP-uri cu next-hop self router client
  • Pachetele ulterioare de la client la aceste IP-uri trec prin tunel

Pe server, pentru rute către site-uri blocate, folosesc un tabel separat în interiorul BIRD și nu se intersectează în niciun fel cu OS.

Există un dezavantaj în această schemă: primul pachet SYN de la client, cel mai probabil, va avea timp să plece prin furnizorul local. traseul nu este anunțat imediat. Și aici opțiunile sunt posibile în funcție de modul în care furnizorul efectuează blocarea. Dacă doar renunță la trafic, atunci nu este nicio problemă. Și dacă îl redirecționează către niște DPI, atunci (teoretic) sunt posibile efecte speciale.

De asemenea, este posibil ca clienții să nu respecte miracolele DNS TTL, ceea ce poate determina clientul să folosească unele intrări învechite din memoria cache putredă în loc să ceară Unbound.

În practică, nici primul, nici al doilea nu mi-au cauzat probleme, dar kilometrajul tău poate varia.

Ajustarea serverului

Pentru ușurință de rulare, am scris rol pentru Ansible. Poate configura atât servere, cât și clienți bazate pe Linux (conceput pentru distribuții bazate pe deb). Toate setările sunt destul de evidente și sunt setate inventar.yml. Acest rol este tăiat din cartea mea mare, așa că poate conține erori - trageți cererile bun venit 🙂

Să trecem prin componentele principale.

BGP

Rularea a doi demoni BGP pe aceeași gazdă are o problemă fundamentală: BIRD nu dorește să configureze peeringul BGP cu localhost (sau orice interfață locală). De la cuvânt la toate. Googlarea și citirea listelor de corespondență nu au ajutat, ei susțin că acest lucru este prin proiect. Poate că există o cale, dar nu am găsit-o.

Puteți încerca un alt demon BGP, dar îmi place BIRD și este folosit peste tot de mine, nu vreau să produc entități.

Prin urmare, am ascuns dnstap-bgp în interiorul spațiului de nume de rețea, care este conectat la rădăcină prin interfața veth: este ca o țeavă, ale cărei capete ies în spații de nume diferite. La fiecare dintre aceste capete, agățăm adrese IP private p2p care nu depășesc gazda, astfel încât să poată fi orice. Acesta este același mecanism folosit pentru a accesa procesele din interior iubit de toti Docker și alte containere.

Pentru asta a fost scris scenariu iar funcționalitatea deja descrisă mai sus pentru a te trage de păr în alt spațiu de nume a fost adăugată la dnstap-bgp. Din acest motiv, trebuie să fie rulat ca root sau emis către binarul CAP_SYS_ADMIN prin comanda setcap.

Exemplu de script pentru crearea spațiului de nume

#!/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",
]

pasăre.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

În mod implicit, în Ubuntu, binarul Unbound este blocat de profilul AppArmor, care îi interzice conectarea la tot felul de prize DNSTap. Puteți fie să ștergeți acest profil, fie să îl dezactivați:

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

Acest lucru ar trebui probabil adăugat la playbook. Ideal este, desigur, să corectez profilul și să eliberez drepturile necesare, dar am fost prea leneș.

nelegat.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

Descărcarea și procesarea listelor

Script pentru descărcarea și procesarea unei liste de adrese IP
Descarcă lista, însumează prefixul pfx. În nu_adaugă и nu_rezuma puteți spune IP-urilor și rețelelor să omite sau să nu rezuma. aveam nevoie. subrețeaua VPS-ului meu era în blocklist 🙂

Lucrul amuzant este că API-ul RosKomSvoboda blochează cererile cu agentul de utilizator Python implicit. Se pare că scenarul a înțeles. Prin urmare, îl schimbăm în Ognelis.

Până acum, funcționează doar cu IPv4. ponderea IPv6 este mică, dar va fi ușor de remediat. Dacă nu trebuie să utilizați ș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)

Script de actualizat
Îl rulez pe coroană o dată pe zi, poate că merită să-l trag la fiecare 4 ore. aceasta, în opinia mea, este perioada de reînnoire pe care RKN o cere furnizorilor. În plus, au și alte blocări super-urgente, care pot ajunge mai repede.

Face următoarele:

  • Rulează primul script și actualizează lista de rute (rkn_routes.list) pentru PASĂRĂ
  • Reîncărcați BIRD
  • Actualizează și curăță lista de domenii pentru dnstap-bgp
  • Reîncărcați 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

Au fost scrise fără să se gândească prea mult, așa că dacă vezi ceva ce poate fi îmbunătățit, mergi.

Configurarea clientului

Aici voi da exemple pentru routere Linux, dar în cazul Mikrotik / Cisco ar trebui să fie și mai ușor.

Mai întâi, am configurat BIRD:

pasăre.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;
}

Astfel, vom sincroniza rutele primite de la BGP cu tabela de rutare a nucleului numărul 222.

După aceea, este suficient să ceri nucleului să se uite la această placă înainte de a se uita la cea implicită:

# 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

Totul, rămâne să configurați DHCP pe router pentru a distribui adresa IP a tunelului serverului ca DNS, iar schema este gata.

Limitări

Cu algoritmul actual pentru generarea și procesarea listei de domenii, acesta include, printre altele, youtube.com și CDN-urile sale.

Și acest lucru duce la faptul că toate videoclipurile vor trece prin VPN, ceea ce poate înfunda întregul canal. Poate că merită să alcătuiești o listă de domenii populare-excluderi care blochează RKN pentru moment, curajul este subțire. Și săriți peste ele când le analizați.

Concluzie

Metoda descrisă vă permite să ocoliți aproape orice blocare pe care furnizorii o implementează în prezent.

În principiu, dnstap-bgp poate fi folosit în orice alt scop în care este necesar un anumit nivel de control al traficului pe baza numelui de domeniu. Ține cont de faptul că în vremea noastră, o mie de site-uri se pot suspenda pe aceeași adresă IP (în spatele unor Cloudflare, de exemplu), așa că această metodă are o precizie destul de scăzută.

Dar pentru nevoile de ocolire a încuietorilor, acest lucru este suficient.

Adăugări, modificări, solicitări de extragere - bine ați venit!

Sursa: www.habr.com

Adauga un comentariu