Apeiti ILV blokavimą naudojant DNSTap ir BGP

Apeiti ILV blokavimą naudojant DNSTap ir BGP

Tema gana sumušta, žinau. Pavyzdžiui, yra puikus straipsnis, tačiau ten atsižvelgiama tik į blokavimo sąrašo IP dalį. Taip pat pridėsime domenus.

Dėl to, kad teismai ir RKN blokuoja viską į dešinę ir į kairę, o tiekėjai labai stengiasi nepakliūti į Revizorro skirtas baudas, su tuo susiję blokavimo nuostoliai yra gana dideli. Ir tarp „teisėtai“ užblokuotų svetainių yra daug naudingų (labas, rutracker)

Gyvenu už RKN jurisdikcijos ribų, bet mano tėvai, giminės ir draugai liko namuose. Taigi buvo nuspręsta sugalvoti paprastą būdą, kaip žmonėms, nutolusiems nuo IT, apeiti blokavimą, pageidautina visai nedalyvaujant.

Šioje pastaboje aš neaprašysiu pagrindinių tinklo dalykų etapais, o aprašysiu bendruosius principus, kaip ši schema gali būti įgyvendinta. Taigi žinios apie tinklo veikimą apskritai ir ypač „Linux“ yra privalomos.

Spynų tipai

Pirmiausia atnaujinkime atmintį apie tai, kas užblokuota.

Iš RKN neįkeltame XML yra kelių tipų užraktai:

  • IP
  • Домен
  • URL adresas

Paprastumo dėlei juos sumažinsime iki dviejų: IP ir domeno, o domeną tiesiog ištrauksime iš blokavimo pagal URL (tiksliau, jie tai jau padarė už mus).

geri žmonės iš Roskomsvoboda suprato nuostabų API, per kurią galime gauti tai, ko mums reikia:

Prieiga prie užblokuotų svetainių

Norėdami tai padaryti, mums reikia nedidelio užsienio VPS, pageidautina su neribotu srautu - tokių yra daug už 3-5 dolerius. Jį reikia pasiimti į artimiausią užsienį, kad ping nebūtų labai didelis, bet vėlgi, atsižvelkite į tai, kad internetas ir geografija ne visada sutampa. Ir kadangi nėra SLA už 5 dolerius, geriau paimti 2 ir daugiau vienetų iš skirtingų tiekėjų, kad būtų išvengta gedimų.

Tada turime nustatyti šifruotą tunelį nuo kliento maršrutizatoriaus iki VPS. Naudoju Wireguard kaip greičiausią ir lengviausiai nustatomą. Taip pat turiu klientų maršrutizatorius, paremtus Linux (APU2 arba kažkas OpenWRT). Kai kurių „Mikrotik“ / „Cisco“ atveju galite naudoti juose esančius protokolus, tokius kaip „OpenVPN“ ir „GRE-over-IPSEC“.

Dominančio srauto identifikavimas ir nukreipimas

Žinoma, galite išjungti visą interneto srautą per užsienio šalis. Tačiau greičiausiai nuo to labai nukentės darbo su vietiniu turiniu greitis. Be to, pralaidumo reikalavimai VPS bus daug didesni.

Todėl turėsime kažkaip paskirstyti srautą blokuotoms svetainėms ir pasirinktinai nukreipti jį į tunelį. Net jei ten patenka dalis „papildomo“ srauto, tai vis tiek daug geriau, nei viską važiuoti tuneliu.

Norėdami valdyti srautą, naudosime BGP protokolą ir skelbsime maršrutus į reikiamus tinklus iš mūsų VPS klientams. Paimkime BIRD kaip vieną funkcionaliausių ir patogiausių BGP demonų.

IP

Blokuojant IP, viskas aišku: apie visus užblokuotus IP tiesiog pranešame VPS. Problema ta, kad sąraše, kurį grąžina API, yra apie 600 tūkstančių potinklių, ir didžioji dauguma jų yra /32 prieglobos. Toks maršrutų skaičius gali supainioti silpnus klientų maršrutizatorius.

Todėl apdorojant sąrašą buvo nuspręsta apibendrinti iki tinklo / 24, jei jame yra 2 ar daugiau pagrindinių kompiuterių. Taip maršrutų skaičius sumažėjo iki ~100 tūkst. Scenarijus bus toliau.

Domenai

Tai sudėtingiau ir yra keletas būdų. Pavyzdžiui, galite įdiegti skaidrų „Squid“ kiekviename kliento maršrutizatoriuje ir ten atlikti HTTP perėmimą bei žvilgtelėti į TLS rankos paspaudimą, kad gautumėte prašomą URL pirmuoju atveju ir domeną iš SNI antruoju.

Tačiau dėl visų naujų TLS1.3 + eSNI, HTTPS analizė kasdien tampa vis mažiau reali. Taip, ir infrastruktūra kliento pusėje darosi vis sudėtingesnė – teks naudoti bent jau OpenWRT.

Todėl nusprendžiau perimti atsakymus į DNS užklausas. Čia taip pat bet koks DNS-over-TLS / HTTPS pradeda sklandyti virš jūsų galvos, tačiau mes galime (kol kas) valdyti šią kliento dalį – arba ją išjungti, arba naudoti savo serverį DoT / DoH.

Kaip perimti DNS?

Čia taip pat gali būti keli požiūriai.

  • DNS srauto perėmimas per PCAP arba NFLOG
    Abu šie perėmimo būdai yra įdiegti programoje sidmat. Bet jis jau seniai nepalaikomas, o funkcionalumas labai primityvus, tad dar reikia rašyti pakinktus.
  • DNS serverio žurnalų analizė
    Deja, man žinomi rekursoriai negali registruoti atsakymų, o tik užklausas. Iš esmės tai yra logiška, nes, skirtingai nei užklausos, atsakymai turi sudėtingą struktūrą ir juos sunku parašyti teksto forma.
  • DNSTap
    Laimei, daugelis iš jų jau palaiko DNSTap šiuo tikslu.

Kas yra DNSTap?

Apeiti ILV blokavimą naudojant DNSTap ir BGP

Tai kliento ir serverio protokolas, pagrįstas protokolų buferiais ir kadrų srautais, skirtas perduoti iš DNS serverio į struktūrinių DNS užklausų ir atsakymų rinktuvą. Iš esmės DNS serveris tinkle perduoda užklausų ir atsakymų metaduomenis (pranešimo tipą, kliento / serverio IP ir kt.) ir visus DNS pranešimus (dvejetaine) forma, kuria jis dirba su jais.

Svarbu suprasti, kad DNSTap paradigmoje DNS serveris veikia kaip klientas, o kolektorius – kaip serveris. Tai reiškia, kad DNS serveris prisijungia prie kolektoriaus, o ne atvirkščiai.

Šiandien DNSTap palaiko visi populiarūs DNS serveriai. Bet, pavyzdžiui, BIND daugelyje platinimų (pvz., Ubuntu LTS) dažnai dėl kokių nors priežasčių kuriamas be jo palaikymo. Taigi nesivarginkime su perrinkimu, o imkime lengvesnį ir greitesnį rekursorių – Neribota.

Kaip sugauti DNSTap?

Yra kai kurie skaičius CLI paslaugų, skirtų darbui su DNSTap įvykių srautu, tačiau jos netinka mūsų problemai išspręsti. Todėl nusprendžiau išrasti savo dviratį, kuris padarys viską, ko reikia: dnstap-bgp

Darbo algoritmas:

  • Paleidus, jis įkelia domenų sąrašą iš tekstinio failo, juos apverčia (habr.com -> com.habr), neįtraukia trūkinių linijų, pasikartojančių domenų ir subdomenų (t. y. jei sąraše yra habr.com ir www.habr.com, bus įkeltas tik pirmasis) ir sukuria prefiksų medį, kad būtų galima greitai ieškoti šiame sąraše
  • Veikdamas kaip DNSTap serveris, jis laukia ryšio iš DNS serverio. Iš esmės jis palaiko ir UNIX, ir TCP lizdus, ​​bet mano žinomi DNS serveriai gali naudoti tik UNIX lizdus
  • Įeinantys DNSTap paketai pirmiausia deserializuojami į Protobuf struktūrą, o tada pats dvejetainis DNS pranešimas, esantis viename iš Protobuf laukų, išanalizuojamas iki DNS RR įrašų lygio.
  • Patikrinama, ar prašomas kompiuteris (arba jo pirminis domenas) yra įkeliamame sąraše, jei ne, atsakymas ignoruojamas
  • Iš atsakymo pasirenkami tik A/AAAA/CNAME RR ir iš jų išgaunami atitinkami IPv4/IPv6 adresai
  • IP adresai talpykloje saugomi naudojant konfigūruojamą TTL ir skelbiami visiems sukonfigūruotiems BGP partneriams
  • Gavus atsakymą, nukreipiantį į jau talpykloje saugomą IP, jo TTL atnaujinamas
  • Pasibaigus TTL galiojimo laikui, įrašas pašalinamas iš talpyklos ir iš BGP pranešimų

Papildomos funkcijos:

  • Dar kartą perskaito SIGHUP domenų sąrašą
  • Talpyklos sinchronizavimas su kitais atvejais dnstap-bgp per HTTP/JSON
  • Dubliuokite talpyklą diske (BoltDB duomenų bazėje), kad atkurtumėte jos turinį po paleidimo iš naujo
  • Palaikymas perjungimui į kitą tinklo vardų sritį (kodėl to reikia, bus aprašyta toliau)
  • IPv6 palaikymas

Apribojimai:

  • IDN domenai dar nepalaikomi
  • Keletas BGP nustatymų

surinkau RPM ir DEB paketus lengvam montavimui. Turėtų veikti visose palyginti naujausiose OS su systemd. jie neturi jokių priklausomybių.

Schema

Taigi, pradėkime surinkti visus komponentus. Dėl to turėtume gauti kažką panašaus į šią tinklo topologiją:
Apeiti ILV blokavimą naudojant DNSTap ir BGP

Darbo logika, manau, aiškėja iš diagramos:

  • Kliento serveris sukonfigūruotas kaip DNS, o DNS užklausos taip pat turi eiti per VPN. Tai būtina, kad teikėjas negalėtų blokuoti DNS perėmimo.
  • Atidarydamas svetainę, klientas siunčia DNS užklausą, pvz., „What are the IPs of xxx.org“
  • Nesusijęs išsprendžia xxx.org (arba paima jį iš talpyklos) ir išsiunčia atsakymą klientui "xxx.org turi tokį ir tokį IP", lygiagrečiai dubliuodamas jį per DNSTap
  • dnstap-bgp skelbia šiuos adresus PAUKŠTIS per BGP, jei domenas yra blokuojamų sąraše
  • PAUKŠTIS reklamuoja maršrutą į šiuos IP su next-hop self kliento maršrutizatorius
  • Vėlesni paketai iš kliento į šiuos IP eina tuneliu

Serveryje maršrutams į užblokuotas svetaines naudoju atskirą lentelę BIRD viduje ir ji niekaip nesikerta su OS.

Ši schema turi trūkumą: pirmasis SYN paketas iš kliento greičiausiai turės laiko palikti per vietinį teikėją. maršrutas skelbiamas ne iš karto. Ir čia galimos parinktys, atsižvelgiant į tai, kaip teikėjas blokuoja. Jei jis tiesiog sumažina eismą, tada nėra jokių problemų. O jei jis nukreipia į kokį nors DPI, tai (teoriškai) galimi specialieji efektai.

Taip pat gali būti, kad klientai nepaiso DNS TTL stebuklų, dėl kurių klientas gali naudoti kai kuriuos pasenusius įrašus iš supuvusios talpyklos, užuot klausęs Neribota.

Praktiškai nei pirmas, nei antras man problemų nesukėlė, bet jūsų rida gali skirtis.

Serverio derinimas

Kad būtų lengviau riedėti, parašiau Ansible vaidmenį. Jis gali sukonfigūruoti tiek serverius, tiek klientus, pagrįstus „Linux“ (sukurta deb pagrįstiems platinimams). Visi nustatymai yra gana akivaizdūs ir nustatyti inventorius.yml. Šis vaidmuo ištrauktas iš mano didelio žaidimo knygos, todėl jame gali būti klaidų – traukimo užklausos sveiki atvykę 🙂

Peržiūrėkime pagrindinius komponentus.

bgp

Paleidus du BGP demonus tame pačiame pagrindiniame kompiuteryje, kyla esminė problema: BIRD nenori nustatyti BGP ryšio su vietiniu kompiuteriu (ar bet kokia vietine sąsaja). Iš viso nuo žodžio. Googlinimas ir adresatų sąrašų skaitymas nepadėjo, jie teigia, kad tai yra sukurta. Galbūt yra koks nors būdas, bet aš jo neradau.

Galite pabandyti kitą BGP demoną, bet man patinka BIRD ir aš jį naudoju visur, aš nenoriu kurti subjektų.

Todėl dnstap-bgp paslėpiau tinklo vardų erdvėje, kuri yra prijungta prie šaknies per veth sąsają: tai tarsi vamzdis, kurio galai išsikiša į skirtingas vardų sritis. Kiekviename iš šių galų pakabiname privačius p2p IP adresus, kurie neviršija pagrindinio kompiuterio, todėl jie gali būti bet kokie. Tai yra tas pats mechanizmas, naudojamas norint pasiekti procesus viduje visų mylimas Dokeris ir kiti konteineriai.

Už tai buvo parašyta scenarijus o dnstap-bgp buvo pridėta aukščiau aprašyta funkcija, skirta tempti save už plaukų į kitą vardų sritį. Dėl šios priežasties jis turi būti paleistas kaip root arba išduotas dvejetainiui CAP_SYS_ADMIN naudojant komandą setcap.

Vardų erdvės kūrimo scenarijaus pavyzdys

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

paukštis.konf

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

Pagal numatytuosius nustatymus „Ubuntu“ neapribotą dvejetainį elementą fiksuoja „AppArmor“ profilis, kuris neleidžia jam prisijungti prie visų rūšių DNSTap lizdų. Galite ištrinti šį profilį arba jį išjungti:

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

Tai tikriausiai turėtų būti įtraukta į žaidimų knygą. Idealu, žinoma, pataisyti profilį ir išduoti reikiamas teises, bet buvau per daug tingus.

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

Sąrašų atsisiuntimas ir apdorojimas

Scenarijus, skirtas atsisiųsti ir apdoroti IP adresų sąrašą
Jis atsisiunčia sąrašą, susumuoja iki priešdėlio pfx. Į nepridėti и neapibendrinti galite nurodyti IP ir tinklus praleisti arba neapibendrinti. Man to reikėjo. mano VPS potinklis buvo blokavimo sąraše 🙂

Juokingiausia, kad RosKomSvoboda API blokuoja užklausas su numatytuoju Python vartotojo agentu. Panašu, kad scenarijus-kiddy tai suprato. Todėl keičiame į Ognelį.

Kol kas jis veikia tik su IPv4. IPv6 dalis nedidelė, bet ją bus nesunku pataisyti. Nebent teks naudoti ir 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)

Atnaujintinas scenarijus
Paleidžiu ant karūnos kartą per dieną, gal verta traukti kas 4 val. tai, mano nuomone, yra atnaujinimo laikotarpis, kurio RKN reikalauja iš tiekėjų. Be to, jie turi kitą ypač skubų blokavimą, kuris gali pasirodyti greičiau.

Atlieka šiuos veiksmus:

  • Paleidžia pirmąjį scenarijų ir atnaujina maršrutų sąrašą (rkn_routes.list) BIRD
  • Iš naujo įkelti BIRD
  • Atnaujina ir išvalo dnstap-bgp domenų sąrašą
  • Iš naujo įkelti dnsap-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

Jie buvo parašyti daug negalvojant, tad jei matote ką nors, ką galima patobulinti – pirmyn.

Kliento sąranka

Čia pateiksiu Linux maršrutizatorių pavyzdžius, bet Mikrotik / Cisco atveju tai turėtų būti dar lengviau.

Pirma, mes nustatėme BIRD:

paukštis.konf

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;
}

Taigi, mes sinchronizuosime maršrutus, gautus iš BGP, su branduolio maršruto lentelės numeriu 222.

Po to pakanka paprašyti branduolio pažvelgti į šią plokštelę prieš žiūrint į numatytąją:

# 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

Viskas, belieka sukonfigūruoti DHCP maršrutizatoriuje, kad serverio tunelio IP adresas būtų paskirstytas kaip DNS, ir schema paruošta.

Trūkumai

Naudojant dabartinį domenų sąrašo generavimo ir apdorojimo algoritmą, jis, be kita ko, apima youtube.com ir jo CDN.

Ir tai lemia tai, kad visi vaizdo įrašai eis per VPN, o tai gali užkimšti visą kanalą. Galbūt verta sudaryti sąrašą populiarių domenų - išimčių, kurios blokuoja RKN kol kas, žarnos yra plonos. Ir praleiskite juos analizuodami.

išvada

Aprašytas metodas leidžia apeiti beveik bet kokį blokavimą, kurį šiuo metu įgyvendina teikėjai.

Iš esmės, dnstap-bgp gali būti naudojamas bet kokiam kitam tikslui, kai reikia tam tikro lygio srauto valdymo pagal domeno pavadinimą. Tik atminkite, kad mūsų laikais tūkstantis svetainių gali kabėti tame pačiame IP adresu (pavyzdžiui, už kokio nors „Cloudflare“), todėl šio metodo tikslumas yra gana mažas.

Tačiau norint apeiti spynas, to visiškai pakanka.

Papildymai, taisymai, ištraukimo užklausos – sveiki!

Šaltinis: www.habr.com

Добавить комментарий