Omgå ILV-blokering med DNSTap og BGP

Omgå ILV-blokering med DNSTap og BGP

Emnet er ret banket, jeg ved det. For eksempel er der en stor artiklen, men kun IP-delen af ​​blokeringslisten tages i betragtning der. Vi tilføjer også domæner.

På grund af det faktum, at domstolene og RKN blokerer alt til højre og venstre, og udbyderne prøver hårdt på ikke at falde ind under bøderne udstedt af Revizorro, er de tilhørende tab ved blokering ret store. Og blandt de "lovligt" blokerede websteder er der mange nyttige (hej, rutracker)

Jeg bor uden for RKN's jurisdiktion, men mine forældre, slægtninge og venner blev hjemme. Så det blev besluttet at finde på en nem måde for folk langt fra IT at omgå blokering, helst uden deres deltagelse overhovedet.

I dette notat vil jeg ikke beskrive de grundlæggende netværksting i trin, men vil beskrive de generelle principper for, hvordan denne ordning kan implementeres. Så viden om hvordan netværket fungerer generelt og i Linux i særdeleshed er et must have.

Typer af låse

Lad os først genopfriske vores hukommelse om, hvad der bliver blokeret.

Der er flere typer låse i den aflæstede XML fra RKN:

  • IP
  • Domænenavn
  • URL

For nemheds skyld reducerer vi dem til to: IP og domæne, og vi vil simpelthen trække domænet ud af blokering via URL (mere præcist, de har allerede gjort dette for os).

gode mennesker fra Roskomsvoboda indså en vidunderlig API, hvorigennem vi kan få det, vi har brug for:

Adgang til blokerede websteder

For at gøre dette skal vi bruge nogle små udenlandske VPS, gerne med ubegrænset trafik - der er mange af disse for 3-5 kroner. Du skal tage det i det nære udland, så ping ikke er særlig stort, men igen, tag i betragtning, at internettet og geografi ikke altid er sammenfaldende. Og da der ikke er nogen SLA for 5 bukke, er det bedre at tage 2+ stykker fra forskellige udbydere for fejltolerance.

Dernæst skal vi konfigurere en krypteret tunnel fra klientrouteren til VPS'en. Jeg bruger Wireguard som den hurtigste og nemmeste at sætte op. Jeg har også klientroutere baseret på Linux (APU2 eller noget i OpenWRT). I tilfælde af nogle Mikrotik / Cisco kan du bruge de tilgængelige protokoller på dem som OpenVPN og GRE-over-IPSEC.

Identifikation og omdirigering af interessetrafik

Du kan selvfølgelig deaktivere al internettrafik gennem udlandet. Men højst sandsynligt vil hastigheden af ​​at arbejde med lokalt indhold lide meget under dette. Plus, båndbreddekravene på VPS vil være meget højere.

Derfor bliver vi nødt til på en eller anden måde at allokere trafik til blokerede websteder og selektivt dirigere den til tunnelen. Selvom noget af den "ekstra" trafik kommer dertil, er det stadig meget bedre end at køre alt gennem tunnelen.

For at styre trafikken vil vi bruge BGP-protokollen og annoncere ruter til de nødvendige netværk fra vores VPS til klienter. Lad os tage BIRD som en af ​​de mest funktionelle og bekvemme BGP-dæmoner.

IP

Med blokering af IP er alt klart: Vi annoncerer simpelthen alle blokerede IP'er med VPS. Problemet er, at der er omkring 600 tusinde undernet på listen, som API'en returnerer, og langt de fleste af dem er /32-værter. Dette antal ruter kan forvirre svage klientroutere.

Derfor blev det ved behandlingen af ​​listen besluttet at opsummere op til netværket / 24, hvis det har 2 eller flere værter. Således blev antallet af ruter reduceret til ~100 tusinde. Manuskriptet til dette følger.

domæner

Det er mere kompliceret, og der er flere måder. For eksempel kan du installere en gennemsigtig blæksprutte på hver klientrouter og udføre HTTP-aflytning der og kigge ind i TLS-håndtrykket for at få den anmodede URL i det første tilfælde og domænet fra SNI i det andet.

Men på grund af alle mulige nymodens TLS1.3 + eSNI bliver HTTPS-analyse mindre og mindre reel hver dag. Ja, og infrastrukturen på klientsiden bliver mere kompliceret - du skal som minimum bruge OpenWRT.

Derfor besluttede jeg at tage vejen til at opsnappe svar på DNS-anmodninger. Også her begynder enhver DNS-over-TLS / HTTPS at svæve over dit hoved, men vi kan (indtil videre) styre denne del på klienten - enten deaktivere den eller bruge din egen server til DoT / DoH.

Hvordan opsnapper man DNS?

Også her kan der være flere tilgange.

  • Aflytning af DNS-trafik via PCAP eller NFLOG
    Begge disse metoder til aflytning er implementeret i hjælpeprogrammet sidmat. Men det har ikke været understøttet i lang tid, og funktionaliteten er meget primitiv, så du skal stadig skrive en sele til den.
  • Analyse af DNS-serverlogfiler
    Desværre er de gengangere, jeg kender, ikke i stand til at logge svar, men kun anmodninger. I princippet er dette logisk, da svar i modsætning til anmodninger har en kompleks struktur, og det er vanskeligt at skrive dem i tekstform.
  • DNSTap
    Heldigvis understøtter mange af dem allerede DNSTap til dette formål.

Hvad er DNSTap?

Omgå ILV-blokering med DNSTap og BGP

Det er en klient-server protokol baseret på protokolbuffere og rammestrømme til overførsel fra en DNS-server til en samler af strukturerede DNS-forespørgsler og -svar. I det væsentlige transmitterer DNS-serveren forespørgsels- og svarmetadata (meddelelsestype, klient/server-IP osv.) plus komplette DNS-meddelelser i den (binære) form, som den arbejder med dem over netværket.

Det er vigtigt at forstå, at i DNSTap-paradigmet fungerer DNS-serveren som en klient, og indsamleren fungerer som en server. Det vil sige, at DNS-serveren forbinder til samleren, og ikke omvendt.

I dag er DNSTap understøttet i alle populære DNS-servere. Men for eksempel er BIND i mange distributioner (som Ubuntu LTS) ofte bygget af en eller anden grund uden dets support. Så lad os ikke bøvle med genmontering, men tage en lettere og hurtigere recursor - Ubundet.

Hvordan fanger man DNSTap?

Der er nogle nummer CLI-værktøjer til at arbejde med en strøm af DNSTap-begivenheder, men de er ikke egnede til at løse vores problem. Derfor besluttede jeg at opfinde min egen cykel, der vil gøre alt, hvad der er nødvendigt: dnstap-bgp

Arbejdsalgoritme:

  • Når den startes, indlæser den en liste over domæner fra en tekstfil, inverterer dem (habr.com -> com.habr), ekskluderer stiplede linjer, dubletter og underdomæner (dvs. hvis listen indeholder habr.com og www.habr.com, det vil kun blive indlæst den første) og bygger et præfikstræ til hurtig søgning gennem denne liste
  • Den fungerer som en DNSTap-server og venter på en forbindelse fra en DNS-server. I princippet understøtter den både UNIX- og TCP-sockets, men de DNS-servere jeg kender, kan kun bruge UNIX-sockets
  • Indgående DNSTap-pakker deserialiseres først til en Protobuf-struktur, og derefter parses selve den binære DNS-meddelelse, der er placeret i et af Protobuf-felterne, til niveauet for DNS RR-poster
  • Det kontrolleres, om den anmodede vært (eller dens overordnede domæne) er på den indlæste liste, hvis ikke, ignoreres svaret
  • Kun A/AAAA/CNAME RR'er vælges fra svaret, og de tilsvarende IPv4/IPv6-adresser udtrækkes fra dem
  • IP-adresser cachelagres med konfigurerbar TTL og annonceres for alle konfigurerede BGP-peers
  • Når du modtager et svar, der peger på en allerede cachelagret IP, opdateres dens TTL
  • Når TTL udløber, fjernes posten fra cachen og fra BGP-meddelelser

Yderligere funktionalitet:

  • Genlæser listen over domæner af SIGHUP
  • Holder cachen synkroniseret med andre forekomster dnstap-bgp via HTTP/JSON
  • Dupliker cachen på disken (i BoltDB-databasen) for at gendanne dens indhold efter en genstart
  • Understøttelse af skift til et andet netværksnavneområde (hvorfor dette er nødvendigt vil blive beskrevet nedenfor)
  • IPv6-understøttelse

begrænsninger:

  • IDN-domæner er ikke understøttet endnu
  • Få BGP-indstillinger

jeg samlede RPM og DEB pakker til nem installation. Bør fungere på alle relativt nyere OS'er med systemd. de har ingen afhængigheder.

Ordningen

Så lad os begynde at samle alle komponenterne sammen. Som et resultat burde vi få noget som denne netværkstopologi:
Omgå ILV-blokering med DNSTap og BGP

Arbejdets logik, tror jeg, fremgår tydeligt af diagrammet:

  • Klienten har vores server konfigureret som DNS, og DNS-forespørgsler skal også gå over VPN. Dette er nødvendigt for at udbyderen ikke kan bruge DNS-aflytning til at blokere.
  • Når du åbner webstedet, sender klienten en DNS-forespørgsel som "hvad er IP'erne på xxx.org"
  • Ubundet løser xxx.org (eller tager det fra cachen) og sender et svar til klienten "xxx.org har sådan og sådan IP", duplikerer den parallelt via DNSTap
  • dnstap-bgp annoncerer disse adresser i FUGL via BGP, hvis domænet er på blokeringslisten
  • FUGL annoncerer en rute til disse IP'er med next-hop self klient router
  • Efterfølgende pakker fra klienten til disse IP'er går gennem tunnelen

På serveren, til ruter til blokerede websteder, bruger jeg en separat tabel inde i BIRD, og ​​den krydser ikke OS på nogen måde.

Denne ordning har en ulempe: den første SYN-pakke fra klienten vil sandsynligvis have tid til at forlade den indenlandske udbyder. ruten annonceres ikke med det samme. Og her er muligheder mulige afhængigt af hvordan udbyderen blokerer. Hvis han bare dropper trafikken, så er der ikke noget problem. Og hvis han omdirigerer det til en eller anden DPI, så er (teoretisk) specialeffekter mulige.

Det er også muligt, at klienter ikke respekterer DNS TTL-mirakler, hvilket kan få klienten til at bruge nogle forældede poster fra sin rådne cache i stedet for at spørge Unbound.

I praksis gav hverken den første eller den anden problemer for mig, men din kilometertal kan variere.

Server Tuning

For at lette at rulle, skrev jeg rolle for Ansible. Det kan konfigurere både servere og klienter baseret på Linux (designet til deb-baserede distributioner). Alle indstillinger er ret indlysende og er indstillet inventory.yml. Denne rolle er klippet fra min store spillebog, så den kan indeholde fejl - Træk anmodninger velkommen 🙂

Lad os gennemgå hovedkomponenterne.

bgp

At køre to BGP-dæmoner på den samme vært har et grundlæggende problem: BIRD ønsker ikke at opsætte BGP-peering med den lokale vært (eller nogen lokal grænseflade). Fra ordet overhovedet. Det hjalp ikke at google og læse mailing-lister, de hævder, at dette er ved design. Måske er der en måde, men jeg fandt den ikke.

Du kan prøve en anden BGP-dæmon, men jeg kan godt lide BIRD, og ​​den bruges overalt af mig, jeg ønsker ikke at producere enheder.

Derfor gemte jeg dnstap-bgp inde i netværkets navneområde, som er forbundet til roden gennem veth-grænsefladen: det er som et rør, hvis ender stikker ud i forskellige navnerum. I hver af disse ender hænger vi private p2p IP-adresser, der ikke går ud over værten, så de kan være hvad som helst. Dette er den samme mekanisme, der bruges til at få adgang til processer indeni elsket af alle Docker og andre containere.

Til dette blev det skrevet manuskript og den funktionalitet, der allerede er beskrevet ovenfor til at trække dig selv i håret til et andet navneområde, blev tilføjet til dnstap-bgp. På grund af dette skal den køres som root eller udstedes til CAP_SYS_ADMIN binær via setcap-kommandoen.

Eksempel script til oprettelse af navneområde

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

Som standard, i Ubuntu, er Unbound binæren fastspændt af AppArmor-profilen, som forbyder den at oprette forbindelse til alle mulige DNSTap-sockets. Du kan enten slette denne profil eller deaktivere den:

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

Dette skal nok føjes til spillebogen. Det er selvfølgelig ideelt at rette profilen og udstede de nødvendige rettigheder, men jeg var for doven.

ubundet.konf

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

Download og behandling af lister

Script til at downloade og behandle en liste over IP-adresser
Det downloader listen, opsummerer til præfikset pfx. I dont_add и dont_summarize du kan bede IP'erne og netværkene om at springe over eller ikke opsummere. Jeg havde brug for det. undernettet af min VPS var på blokeringslisten 🙂

Det sjove er, at RosKomSvoboda API blokerer forespørgsler med standard Python-brugeragenten. Det ser ud til, at script-kiddy har forstået det. Derfor ændrer vi det til Ognelis.

Indtil videre virker det kun med IPv4. andelen af ​​IPv6 er lille, men det vil være nemt at rette. Medmindre du også skal bruge 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 til opdatering
Jeg kører den på kronen en gang om dagen, måske er det værd at trække den hver 4. time. dette er efter min mening den fornyelsesperiode, som RKN kræver af udbyderne. Plus, de har en anden super-hastende blokering, som kan ankomme hurtigere.

Gør følgende:

  • Kører det første script og opdaterer listen over ruter (rkn_routes.list) for BIRD
  • Genindlæs BIRD
  • Opdaterer og rydder op i listen over domæner for dnstap-bgp
  • Genindlæs 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

De er skrevet uden særlig omtanke, så hvis du ser noget, der kan forbedres - så gå efter det.

Klient opsætning

Her vil jeg give eksempler på Linux-routere, men i tilfældet med Mikrotik / Cisco skulle det være endnu nemmere.

Først sætter vi BIRD op:

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

Således vil vi synkronisere ruterne modtaget fra BGP med kernel routing tabel nummer 222.

Derefter er det nok at bede kernen om at se på denne plade, før du ser på standarden:

# 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

Alt, det er tilbage at konfigurere DHCP på routeren til at distribuere serverens tunnel IP-adresse som DNS, og ordningen er klar.

Begrænsninger

Med den nuværende algoritme til generering og behandling af domænelisten omfatter den bl.a. youtube.com og dets CDN'er.

Og dette fører til, at alle videoer vil gå gennem VPN, som kan tilstoppe hele kanalen. Måske er det værd at kompilere en liste over populære domæner-ekskluderinger, der blokerer RKN for øjeblikket, indvoldene er tynde. Og spring dem over, når du analyserer.

Konklusion

Den beskrevne metode giver dig mulighed for at omgå næsten enhver blokering, som udbydere i øjeblikket implementerer.

I princippet dnstap-bgp kan bruges til ethvert andet formål, hvor en vis grad af trafikkontrol er nødvendig baseret på domænenavnet. Bare husk på, at i vores tid kan tusind steder hænge på den samme IP-adresse (bag nogle Cloudflare, for eksempel), så denne metode har en ret lav nøjagtighed.

Men til behovene for at omgå låse er dette ganske nok.

Tilføjelser, redigeringer, pull-anmodninger - velkommen!

Kilde: www.habr.com

Tilføj en kommentar