Omseil ILV-blokkering met DNSTap en BGP

Omseil ILV-blokkering met DNSTap en BGP

Die onderwerp is redelik deurmekaar, ek weet. Byvoorbeeld, daar is 'n groot статья, maar slegs die IP-deel van die bloklys word daar oorweeg. Ons sal ook domeine byvoeg.

As gevolg van die feit dat die howe en die RKN alles regs en links blokkeer, en die verskaffers hard probeer om nie onder die boetes te val wat deur Revizorro uitgereik is nie, is die gepaardgaande verliese deur blokkering redelik groot. En onder die "wettiglik" geblokkeerde werwe is daar baie nuttiges (hallo, rutracker)

Ek woon buite die jurisdiksie van die RKN, maar my ouers, familie en vriende het by die huis gebly. Daar is dus besluit om met 'n maklike manier vorendag te kom vir mense ver van IT om blokkering te omseil, verkieslik sonder hul deelname enigsins.

In hierdie nota sal ek nie die basiese netwerkdinge in stappe beskryf nie, maar ek sal die algemene beginsels beskryf van hoe hierdie skema geïmplementeer kan word. So kennis van hoe die netwerk werk in die algemeen en in Linux in die besonder is 'n moet hê.

Soorte slotte

Kom ons verfris eers ons geheue van wat geblokkeer word.

Daar is verskeie tipes slotte in die afgelaaide XML van die RKN:

  • IP
  • Домен
  • URL

Vir eenvoud, sal ons hulle verminder tot twee: IP en domein, en ons sal eenvoudig die domein uit die blokkering deur URL onttrek (meer presies, hulle het dit reeds vir ons gedoen).

goeie mense van Roskomsvoboda 'n wonderlike besef API, waardeur ons kan kry wat ons nodig het:

Toegang tot geblokkeerde werwe

Om dit te doen, het ons 'n paar klein buitelandse VPS nodig, verkieslik met onbeperkte verkeer - daar is baie van hierdie vir 3-5 dollar. Jy moet dit in die nabye buiteland neem sodat die ping nie baie groot is nie, maar neem weer in ag dat die internet en geografie nie altyd saamval nie. En aangesien daar geen SLA vir 5 dollar is nie, is dit beter om 2+ stukke van verskillende verskaffers te neem vir fouttoleransie.

Vervolgens moet ons 'n geënkripteerde tonnel vanaf die kliëntrouter na die VPS opstel. Ek gebruik Wireguard as die vinnigste en maklikste om op te stel. Ek het ook kliënt routers gebaseer op Linux (APU2 of iets in OpenWRT). In die geval van sommige Mikrotik / Cisco, kan u die protokolle wat op hulle beskikbaar is, soos OpenVPN en GRE-over-IPSEC, gebruik.

Identifisering en herleiding van verkeer van belang

U kan natuurlik alle internetverkeer deur die buiteland afskakel. Maar waarskynlik sal die spoed van werk met plaaslike inhoud baie hierdeur ly. Boonop sal die bandwydtevereistes op VPS baie hoër wees.

Daarom sal ons op een of ander manier verkeer na geblokkeerde werwe moet toewys en dit selektief na die tonnel moet lei. Al kom van die “ekstra” verkeer daar, is dit steeds baie beter as om alles deur die tonnel te ry.

Om verkeer te bestuur, sal ons die BGP-protokol gebruik en roetes na die nodige netwerke vanaf ons VPS aan kliënte aankondig. Kom ons neem BIRD as een van die mees funksionele en gerieflikste BGP-demone.

IP

Met blokkering deur IP is alles duidelik: ons kondig eenvoudig alle geblokkeerde IP's met VPS aan. Die probleem is dat daar ongeveer 600 duisend subnette in die lys is wat die API terugstuur, en die oorgrote meerderheid van hulle is /32 gashere. Hierdie aantal roetes kan swak kliëntrouters verwar.

Daarom is daar tydens die verwerking van die lys besluit om op te som tot by die netwerk / 24 as dit 2 of meer gashere het. Dus is die aantal roetes verminder tot ~100 duisend. Die draaiboek hiervoor sal volg.

Domain

Dit is meer ingewikkeld en daar is verskeie maniere. U kan byvoorbeeld 'n deursigtige Squid op elke kliëntroeteerder installeer en HTTP-onderskepping daar doen en in die TLS-handdruk loer om die gevraagde URL in die eerste geval te verkry en die domein van SNI in die tweede geval.

Maar as gevolg van allerhande nuwerwetse TLS1.3 + eSNI, word HTTPS-analise elke dag minder en minder werklik. Ja, en die infrastruktuur aan die kliëntkant raak meer ingewikkeld - jy sal ten minste OpenWRT moet gebruik.

Daarom het ek besluit om die pad te neem om antwoorde op DNS-versoeke te onderskep. Ook hier begin enige DNS-oor-TLS / HTTPS oor jou kop beweeg, maar ons kan (vir nou) hierdie deel op die kliënt beheer - óf deaktiveer dit óf gebruik jou eie bediener vir DoT / DoH.

Hoe om DNS te onderskep?

Ook hier kan daar verskeie benaderings wees.

  • Onderskepping van DNS-verkeer via PCAP of NFLOG
    Beide hierdie metodes van onderskepping word in die nut geïmplementeer sidmat. Maar dit word lanklaas ondersteun en die funksionaliteit is baie primitief, so jy moet nog 'n harnas daarvoor skryf.
  • Ontleding van DNS-bedienerlogboeke
    Ongelukkig kan die herhalers wat aan my bekend is nie antwoorde aanteken nie, maar slegs versoeke. In beginsel is dit logies, aangesien antwoorde, anders as versoeke, 'n komplekse struktuur het en dit moeilik is om dit in teksvorm te skryf.
  • DNSTap
    Gelukkig ondersteun baie van hulle reeds DNSTap vir hierdie doel.

Wat is DNSTap?

Omseil ILV-blokkering met DNSTap en BGP

Dit is 'n kliënt-bediener protokol gebaseer op protokolbuffers en raamstrome vir die oordrag van 'n DNS-bediener na 'n versamelaar van gestruktureerde DNS-navrae en -antwoorde. In wese stuur die DNS-bediener navraag- en reaksie-metadata (tipe boodskap, kliënt/bediener-IP, ens.) plus volledige DNS-boodskappe in die (binêre) vorm waarin dit met hulle werk oor die netwerk.

Dit is belangrik om te verstaan ​​dat in die DNSTap-paradigma die DNS-bediener as 'n kliënt optree en die versamelaar as 'n bediener. Dit wil sê, die DNS-bediener koppel aan die versamelaar, en nie andersom nie.

Vandag word DNSTap in alle gewilde DNS-bedieners ondersteun. Maar, byvoorbeeld, BIND in baie verspreidings (soos Ubuntu LTS) word dikwels om een ​​of ander rede sonder die ondersteuning daarvan gebou. Laat ons ons dus nie steur aan hersamestelling nie, maar 'n ligter en vinniger herhaler neem - Unbound.

Hoe om DNSTap te vang?

Daar is sommige aantal CLI-nutsprogramme om met 'n stroom DNSTap-gebeurtenisse te werk, maar dit is nie geskik om ons probleem op te los nie. Daarom het ek besluit om my eie fiets uit te vind wat alles sal doen wat nodig is: dnstap-bgp

Werk algoritme:

  • Wanneer dit bekendgestel word, laai dit 'n lys domeine vanaf 'n tekslêer, omkeer hulle (habr.com -> com.habr), sluit gebroke lyne, duplikate en subdomeine uit (d.w.s. as die lys habr.com en www.habr.com bevat, dit sal slegs die eerste een gelaai word) en bou 'n voorvoegselboom om vinnig deur hierdie lys te soek
  • Dien as 'n DNSTap-bediener en wag vir 'n verbinding vanaf 'n DNS-bediener. In beginsel ondersteun dit beide UNIX- en TCP-sokke, maar die DNS-bedieners wat ek ken, kan net UNIX-sokke gebruik
  • Inkomende DNSTap-pakkies word eers in 'n Protobuf-struktuur gedeserialiseer, en dan word die binêre DNS-boodskap self, geleë in een van die Protobuf-velde, na die vlak van DNS RR-rekords ontleed.
  • Daar word gekontroleer of die versoekte gasheer (of sy ouerdomein) in die gelaaide lys is, indien nie, word die antwoord geïgnoreer
  • Slegs A/AAAA/CNAME RR'e ​​word uit die antwoord gekies en die ooreenstemmende IPv4/IPv6-adresse word daaruit onttrek
  • IP-adresse word gekas met konfigureerbare TTL en geadverteer aan alle gekonfigureerde BGP-eweknieë
  • Wanneer 'n antwoord ontvang word wat na 'n reeds gekas IP wys, word sy TTL opgedateer
  • Nadat die TTL verstryk het, word die inskrywing uit die kas en van BGP-aankondigings verwyder

Bykomende funksionaliteit:

  • Herlees die lys van domeine deur SIGHUP
  • Hou die kas gesinchroniseer met ander gevalle dnstap-bgp via HTTP/JSON
  • Dupliseer die kas op skyf (in die BoltDB-databasis) om die inhoud daarvan te herstel na 'n herbegin
  • Ondersteuning vir oorskakeling na 'n ander netwerknaamruimte (waarom dit nodig is, sal hieronder beskryf word)
  • IPv6 ondersteuning

beperkings:

  • IDN-domeine word nog nie ondersteun nie
  • Min BGP instellings

Ek het versamel RPM en DEB pakkette vir maklike installasie. Behoort op alle relatief onlangse bedryfstelsels met systemd te werk. hulle het geen afhanklikhede nie.

Die skema

Dus, laat ons begin om al die komponente saam te stel. As gevolg hiervan behoort ons iets soos hierdie netwerktopologie te kry:
Omseil ILV-blokkering met DNSTap en BGP

Die logika van werk, dink ek, is duidelik uit die diagram:

  • Die kliënt het ons bediener opgestel as DNS, en DNS-navrae moet ook oor die VPN gaan. Dit is nodig sodat die verskaffer nie DNS-onderskepping kan gebruik om te blokkeer nie.
  • Wanneer die webwerf oopgemaak word, stuur die kliënt 'n DNS-navraag soos "wat is die IP's van xxx.org"
  • ongebonde los xxx.org op (of neem dit uit die kas) en stuur 'n antwoord aan die kliënt "xxx.org het so en so IP", en dupliseer dit parallel via DNSTap
  • dnstap-bgp kondig hierdie adresse aan in BIRD via BGP as die domein op die geblokkeerde lys is
  • BIRD adverteer 'n roete na hierdie IP's met next-hop self kliënt router
  • Daaropvolgende pakkies van die kliënt na hierdie IP's gaan deur die tonnel

Op die bediener, vir roetes na geblokkeerde werwe, gebruik ek 'n aparte tabel binne BIRD en dit sny op geen manier met die bedryfstelsel nie.

Hierdie skema het 'n nadeel: die eerste SYN-pakkie van die kliënt sal waarskynlik tyd hê om deur die binnelandse verskaffer te vertrek. die roete word nie dadelik aangekondig nie. En hier is opsies moontlik, afhangende van hoe die verskaffer die blokkering doen. As hy net verkeer laat val, dan is daar geen probleem nie. En as hy dit na een of ander DPI herlei, dan is (teoreties) spesiale effekte moontlik.

Dit is ook moontlik dat kliënte nie DNS TTL-wonderwerke respekteer nie, wat kan veroorsaak dat die kliënt sommige verouderde inskrywings uit sy vrot kas gebruik in plaas daarvan om Unbound te vra.

In die praktyk het nie die eerste nóg die tweede vir my probleme veroorsaak nie, maar jou kilometers kan verskil.

Bedienerinstelling

Vir gemak van rol, het ek geskryf rol vir Ansible. Dit kan beide bedieners en kliënte konfigureer gebaseer op Linux (ontwerp vir deb-gebaseerde verspreidings). Alle instellings is redelik voor die hand liggend en is ingestel voorraad.yml. Hierdie rol is uit my groot speelboek gesny, so dit kan foute bevat - trek versoeke welkom 🙂

Kom ons gaan deur die hoofkomponente.

BGP

Om twee BGP-demone op dieselfde gasheer te laat loop het 'n fundamentele probleem: BIRD wil nie BGP-peering met die plaaslike gasheer (of enige plaaslike koppelvlak) opstel nie. Van die woord enigsins. Om te google en poslyste te lees het nie gehelp nie, hulle beweer dat dit deur ontwerp is. Miskien is daar 'n manier, maar ek het dit nie gevind nie.

Jy kan 'n ander BGP-demoon probeer, maar ek hou van BIRD en dit word oral deur my gebruik, ek wil nie entiteite produseer nie.

Daarom het ek dnstap-bgp in die netwerknaamruimte versteek, wat deur die veth-koppelvlak aan die wortel gekoppel is: dit is soos 'n pyp waarvan die punte in verskillende naamruimtes uitsteek. Aan elk van hierdie punte hang ons private p2p IP-adresse op wat nie verder gaan as die gasheer nie, sodat hulle enigiets kan wees. Dit is dieselfde meganisme wat gebruik word om toegang tot prosesse binne te kry geliefd deur almal Docker en ander houers.

Hiervoor is dit geskryf skrif en die funksionaliteit wat reeds hierbo beskryf is om jouself aan die hare na 'n ander naamruimte te sleep, is by dnstap-bgp gevoeg. As gevolg hiervan moet dit as wortel uitgevoer word of aan die CAP_SYS_ADMIN-binêre uitgereik word via die setcap-opdrag.

Voorbeeldskrif vir die skep van naamruimte

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

voël.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_roetes.lys

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

By verstek, in Ubuntu, word die Unbound-binêr vasgeklem deur die AppArmor-profiel, wat dit verbied om aan allerhande DNSTap-sokke te koppel. Jy kan óf hierdie profiel uitvee óf dit deaktiveer:

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

Dit moet waarskynlik by die speelboek gevoeg word. Dit is natuurlik ideaal om die profiel reg te stel en die nodige regte uit te reik, maar ek was te lui.

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

Laai af en verwerk lyste

Skrip vir die aflaai en verwerking van 'n lys van IP-adresse
Dit laai die lys af, som tot die voorvoegsel op pfx. In moenie_byvoeg nie и moenie_opsom nie jy kan die IP's en netwerke vertel om oor te slaan of nie op te som nie. Ek het dit nodig gehad. die subnet van my VPS was in die bloklys 🙂

Die snaakse ding is dat die RosKomSvoboda API versoeke blokkeer met die standaard Python-gebruikersagent. Lyk of die script-kiddy dit gekry het. Daarom verander ons dit na Ognelis.

Tot dusver werk dit net met IPv4. die aandeel van IPv6 is klein, maar dit sal maklik wees om reg te stel. Tensy jy bird6 ook moet gebruik.

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 om op te dateer
Ek hardloop dit een keer per dag op die kroon, miskien is dit die moeite werd om dit elke 4 uur te trek. dit is na my mening die hernuwingstydperk wat die RKN van verskaffers vereis. Boonop het hulle 'n ander superdringende blokkering, wat vinniger kan opdaag.

Doen die volgende:

  • Begin die eerste skrif en werk die lys roetes op (rkn_routes.list) vir BIRD
  • Herlaai BIRD
  • Dateer en maak die lys domeine vir dnstap-bgp skoon
  • Herlaai 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

Hulle is geskryf sonder veel nadenke, so as jy iets sien wat verbeter kan word - gaan daarvoor.

Kliënt opstelling

Hier sal ek voorbeelde gee vir Linux routers, maar in die geval van Mikrotik / Cisco behoort dit selfs makliker te wees.

Eerstens stel ons BIRD op:

voël.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;
}

Ons sal dus die roetes wat vanaf BGP ontvang is sinchroniseer met die kernroeteringtabel nommer 222.

Daarna is dit genoeg om die kern te vra om na hierdie plaat te kyk voordat jy na die verstek een kyk:

# 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

Alles, dit bly om DHCP op die router te konfigureer om die bediener se tonnel IP-adres as DNS te versprei, en die skema is gereed.

Beperkings

Met die huidige algoritme vir die generering en verwerking van die lys van domeine, sluit dit o.a. youtube.com en sy CDN's.

En dit lei tot die feit dat alle video's deur die VPN gaan, wat die hele kanaal kan verstop. Miskien is dit die moeite werd om 'n lys saam te stel van gewilde domeine-uitsluitings wat die RKN vir eers blokkeer, die ingewande is dun. En slaan hulle oor wanneer jy ontleed.

Gevolgtrekking

Met die beskryfde metode kan u byna enige blokkering wat verskaffers tans implementeer, omseil.

In beginsel, dnstap-bgp kan gebruik word vir enige ander doel waar 'n mate van verkeersbeheer nodig is gebaseer op die domeinnaam. Hou net in gedagte dat in ons tyd 'n duisend werwe aan dieselfde IP-adres kan hang (agter sommige Cloudflare, byvoorbeeld), so hierdie metode het 'n taamlike lae akkuraatheid.

Maar vir die behoeftes om slotte te omseil, is dit heeltemal genoeg.

Byvoegings, wysigings, trekversoeke - welkom!

Bron: will.com

Voeg 'n opmerking