Obejít blokování ILV pomocí DNSTap a BGP

Obejít blokování ILV pomocí DNSTap a BGP

Téma je dost otřepané, já vím. Existuje například skvělá článek, ale tam se bere v úvahu pouze IP část seznamu. Přidáme i domény.

Vzhledem k tomu, že soudy a RKN blokují vše napravo i nalevo a poskytovatelé se usilovně snaží nepodléhat pokutám udělovaným společností Revizorro, jsou související ztráty z blokování poměrně velké. A mezi „zákonně“ zablokovanými weby je mnoho užitečných (ahoj, rutracker)

Žiji mimo jurisdikci RKN, ale moji rodiče, příbuzní a přátelé zůstali doma. Bylo tedy rozhodnuto vymyslet snadný způsob, jak obejít blokování pro lidi daleko od IT, nejlépe bez jejich účasti.

V této poznámce nebudu popisovat základní síťové věci po krocích, ale popíšu obecné principy, jak lze toto schéma implementovat. Takže znalost toho, jak síť funguje obecně a v Linuxu konkrétně, je nutností.

Typy zámků

Nejprve si osvěžme paměť toho, co je blokováno.

V uvolněném XML z RKN existuje několik typů zámků:

  • IP
  • Domén
  • URL

Pro jednoduchost je zredukujeme na dvě: IP a doménu a doménu jednoduše vytáhneme z blokování podle URL (přesněji to už udělali za nás).

dobří lidé z Roskomsvoboda uvědomil si úžasné API, jehož prostřednictvím můžeme získat to, co potřebujeme:

Přístup na blokované stránky

K tomu potřebujeme nějaké malé zahraniční VPS, nejlépe s neomezeným provozem - těch je za 3-5 babek hodně. Je potřeba to vzít v blízkém zahraničí, aby ping nebyl moc velký, ale zase počítejte s tím, že internet a geografie se ne vždy shodují. A protože neexistuje žádná smlouva SLA za 5 babek, je lepší vzít 2+ kusy od různých poskytovatelů pro odolnost proti chybám.

Dále musíme nastavit šifrovaný tunel z klientského routeru do VPS. Wireguard používám jako nejrychlejší a nejjednodušší na nastavení. Mám také klientské směrovače založené na Linuxu (APU2 nebo něco v OpenWRT). V případě některých Mikrotik / Cisco můžete použít protokoly, které jsou na nich dostupné, jako je OpenVPN a GRE-over-IPSEC.

Identifikace a přesměrování zájmového provozu

Veškerý internetový provoz přes cizí země můžete samozřejmě vypnout. S největší pravděpodobností tím ale značně utrpí rychlost práce s lokálním obsahem. Navíc požadavky na šířku pásma na VPS budou mnohem vyšší.

Proto budeme muset nějakým způsobem alokovat provoz na blokované stránky a selektivně jej nasměrovat do tunelu. I když se tam nějaký ten provoz "navíc" dostane, pořád je to mnohem lepší, než vše prohánět tunelem.

Pro řízení provozu využijeme protokol BGP a budeme klientům oznamovat trasy do potřebných sítí z našich VPS. Vezměme BIRD jako jeden z nejfunkčnějších a nejpohodlnějších BGP démonů.

IP

S blokováním podle IP je vše jasné: všechny blokované IP jednoduše oznámíme pomocí VPS. Problém je, že v seznamu, který API vrací, je asi 600 tisíc podsítí a drtivá většina z nich je /32 hostitelů. Tento počet tras může zmást slabé klientské směrovače.

Proto bylo při zpracování seznamu rozhodnuto shrnout až do sítě / 24 pokud má 2 nebo více hostitelů. Počet tras se tak snížil na ~100 tisíc. Skript k tomu bude následovat.

domény

Je to složitější a existuje několik způsobů. Můžete například nainstalovat transparentní Squid na každý klientský router a provést tam zachycení HTTP a nahlédnout do TLS handshake, abyste získali požadovanou URL v prvním případě a doménu od SNI ve druhém.

Ale kvůli všem druhům nových TLS1.3 + eSNI je analýza HTTPS každým dnem méně a méně reálná. Ano, a infrastruktura na straně klienta se komplikuje – budete muset používat minimálně OpenWRT.

Proto jsem se rozhodl jít cestou zachycování odpovědí na DNS dotazy. I zde se vám nad hlavou začne vznášet jakýkoli DNS-over-TLS / HTTPS, ale tuto část můžeme (zatím) ovládat na klientovi – buď ji deaktivovat, nebo použít vlastní server pro DoT/DoH.

Jak zachytit DNS?

I zde může existovat několik přístupů.

  • Zachycování DNS provozu přes PCAP nebo NFLOG
    Oba tyto způsoby odposlechu jsou implementovány v obslužném programu sidmat. Ten už ale dávno není podporován a funkcionalita je velmi primitivní, takže pro něj musíte ještě napsat postroj.
  • Analýza protokolů DNS serveru
    Bohužel mi známé rekurzory nejsou schopny logovat odpovědi, ale pouze požadavky. V zásadě je to logické, protože na rozdíl od požadavků mají odpovědi složitou strukturu a je obtížné je napsat v textové podobě.
  • DNSTap
    Naštěstí mnoho z nich již pro tento účel podporuje DNSTap.

Co je DNSTap?

Obejít blokování ILV pomocí DNSTap a BGP

Jedná se o protokol klient-server založený na vyrovnávací paměti protokolů a tocích rámců pro přenos ze serveru DNS do kolektoru strukturovaných dotazů a odpovědí DNS. DNS server v podstatě přenáší metadata dotazů a odpovědí (typ zprávy, IP klient/server atd.) plus kompletní DNS zprávy v (binární) podobě, ve které s nimi pracuje po síti.

Je důležité pochopit, že v paradigmatu DNSTap server DNS funguje jako klient a kolektor funguje jako server. To znamená, že server DNS se připojuje ke kolektoru a ne naopak.

Dnes je DNSTap podporován na všech populárních DNS serverech. Ale například BIND v mnoha distribucích (jako Ubuntu LTS) je často z nějakého důvodu postaven bez jeho podpory. Netrapme se tedy opětovným složením, ale vezměme si lehčí a rychlejší rekurzor – Unbound.

Jak chytit DNSTap?

K dispozici je nějaký číslo CLI utility pro práci s proudem událostí DNSTap, ale pro řešení našeho problému nejsou vhodné. Proto jsem se rozhodl vymyslet vlastní kolo, které bude dělat vše, co je potřeba: dnstap-bgp

Algoritmus operace:

  • Po spuštění načte seznam domén z textového souboru, invertuje je (habr.com -> com.habr), vyloučí přerušované řádky, duplikáty a subdomény (tj. pokud seznam obsahuje habr.com a www.habr.com, načte se pouze první) a vytvoří strom předpon pro rychlé prohledávání tohoto seznamu
  • Funguje jako server DNSTap a čeká na připojení ze serveru DNS. V zásadě podporuje sokety UNIX i TCP, ale servery DNS, které znám, mohou používat pouze sokety UNIX
  • Příchozí pakety DNSTap jsou nejprve deserializovány do struktury Protobuf a poté je samotná binární zpráva DNS umístěná v jednom z polí Protobuf analyzována na úroveň záznamů DNS RR.
  • Kontroluje se, zda je požadovaný hostitel (nebo jeho nadřazená doména) v načteném seznamu, pokud ne, odpověď je ignorována
  • Z odpovědi jsou vybrány pouze RR A/AAAA/CNAME a jsou z nich extrahovány odpovídající adresy IPv4/IPv6
  • IP adresy jsou ukládány do mezipaměti s konfigurovatelným TTL a inzerovány všem nakonfigurovaným BGP peerům
  • Při přijetí odpovědi ukazující na již uloženou IP adresu se aktualizuje její TTL
  • Po vypršení platnosti TTL je záznam odstraněn z mezipaměti az oznámení BGP

Další funkce:

  • Opětovné čtení seznamu domén od SIGHUP
  • Udržování mezipaměti synchronizované s ostatními instancemi dnstap-bgp přes HTTP/JSON
  • Duplikujte mezipaměť na disku (v databázi BoltDB), abyste po restartu obnovili její obsah
  • Podpora přepínání na jiný síťový jmenný prostor (proč je to potřeba, bude popsáno níže)
  • podpora IPv6

Omezení:

  • Domény IDN zatím nejsou podporovány
  • Málo nastavení BGP

sbíral jsem RPM a DEB balíčky pro snadnou instalaci. Mělo by fungovat na všech relativně nedávných operačních systémech se systemd. nemají žádnou závislost.

systém

Začněme tedy sestavovat všechny komponenty dohromady. V důsledku toho bychom měli získat něco jako tato topologie sítě:
Obejít blokování ILV pomocí DNSTap a BGP

Logika práce, myslím, je jasná z diagramu:

  • Klient má náš server nakonfigurovaný jako DNS a dotazy DNS musí také procházet přes VPN. Je to nutné, aby poskytovatel nemohl použít k blokování odposlech DNS.
  • Při otevření webu klient odešle DNS dotaz jako „jaké jsou IP adresy xxx.org“
  • nevázaný přeloží xxx.org (nebo jej vezme z mezipaměti) a odešle klientovi odpověď „xxx.org má takovou a takovou IP“, přičemž ji paralelně duplikuje přes DNSTap
  • dnstap-bgp oznamuje tyto adresy v BIRD přes BGP, pokud je doména na seznamu blokovaných
  • BIRD inzeruje cestu k těmto IP adresám s next-hop self klientský router
  • Následující pakety od klienta k těmto IP adresám procházejí tunelem

Na serveru pro cesty na blokované stránky používám samostatnou tabulku uvnitř BIRDu a nijak se nekříží s OS.

Toto schéma má nevýhodu: první paket SYN od klienta bude mít s největší pravděpodobností čas odejít prostřednictvím domácího poskytovatele. trasa není oznámena okamžitě. A zde jsou možné možnosti v závislosti na tom, jak poskytovatel blokování provádí. Pokud jen sníží provoz, pak není problém. A pokud to přesměruje na nějaké DPI, tak jsou možné (teoreticky) speciální efekty.

Je také možné, že klienti nerespektují DNS TTL zázraky, což může způsobit, že klient použije některé zastaralé položky ze své shnilé mezipaměti místo toho, aby se zeptal na Unbound.

V praxi mi problémy nedělalo ani první, ani druhé, ale váš počet najetých kilometrů se může lišit.

Ladění serveru

Pro usnadnění rolování jsem napsal role pro Ansible. Může konfigurovat servery i klienty založené na Linuxu (navrženo pro distribuce založené na deb). Všechna nastavení jsou zcela jasná a jsou nastavena inventář.yml. Tato role je vystřižena z mé velké příručky, takže může obsahovat chyby - vyžádání vítejte 🙂

Pojďme si projít hlavní komponenty.

bgp

Spuštění dvou BGP démonů na stejném hostiteli má zásadní problém: BIRD nechce nastavit BGP peering s localhostem (nebo jakýmkoli lokálním rozhraním). Od slova vůbec. Googlování a čtení mailinglistů nepomohlo, tvrdí, že je to záměrné. Možná existuje nějaký způsob, ale nenašel jsem ho.

Můžete zkusit jiného démona BGP, ale mám rád BIRD a používám ho všude, nechci produkovat entity.

Proto jsem skryl dnstap-bgp do síťového jmenného prostoru, který je připojen ke kořenu přes rozhraní veth: je to jako roura, jejíž konce trčí v různých jmenných prostorech. Na každém z těchto konců zavěšujeme soukromé p2p IP adresy, které nepřesahují hostitele, takže to může být cokoliv. Jedná se o stejný mechanismus používaný pro přístup k procesům uvnitř všemi milován Docker a další kontejnery.

K tomu to bylo napsáno skript a do dnstap-bgp byla přidána již výše popsaná funkce pro přetažení za vlasy do jiného jmenného prostoru. Z tohoto důvodu musí být spuštěn jako uživatel root nebo odeslán do binárního souboru CAP_SYS_ADMIN pomocí příkazu setcap.

Příklad skriptu pro vytvoření jmenného prostoru

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

pták.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

Ve výchozím nastavení je v Ubuntu unbound binární soubor upnut profilem AppArmor, který mu zakazuje připojení k nejrůznějším soketům DNSTap. Tento profil můžete buď smazat, nebo jej zakázat:

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

To by se asi mělo přidat do playbooku. Ideální je samozřejmě opravit profil a vydat potřebná práva, ale byl jsem líný.

nevázaný.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

Stahování a zpracování seznamů

Skript pro stahování a zpracování seznamu IP adres
Stáhne seznam, sečte se do předpony PFX. V dont_add и dont_sumarizovat IP adresám a sítím můžete říci, aby přeskakovaly nebo neshrnovaly. Potřeboval jsem to. podsíť mého VPS byla v blocklistu 🙂

Legrační je, že rozhraní API RosKomSvoboda blokuje požadavky pomocí výchozího uživatelského agenta Pythonu. Vypadá to, že to script-kiddy pochopil. Proto jej změníme na Ognelis.

Zatím to funguje jen s IPv4. podíl IPv6 je malý, ale bude snadné to opravit. Ledaže bys musel používat 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)

Skript k aktualizaci
Spouštím to na korunu jednou denně, možná to stojí za to tahat každé 4 hodiny. to je podle mě doba obnovy, kterou RKN od poskytovatelů vyžaduje. Navíc mají nějaké další superurgentní blokování, které může přijít rychleji.

Provádí následující:

  • Spustí první skript a aktualizuje seznam tras (rkn_routes.list) pro BIRD
  • Znovu načíst BIRD
  • Aktualizuje a vyčistí seznam domén pro dnstap-bgp
  • Znovu načtěte 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

Byly napsány bez dlouhého přemýšlení, takže pokud vidíte něco, co by se dalo zlepšit – jděte do toho.

Nastavení klienta

Zde uvedu příklady pro linuxové routery, ale v případě Mikrotik / Cisco by to mělo být ještě jednodušší.

Nejprve nastavíme BIRD:

pták.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;
}

Budeme tedy synchronizovat cesty přijaté z BGP se směrovací tabulkou jádra číslo 222.

Poté stačí požádat jádro, aby se podívalo na tento štítek, než se podívá na výchozí:

# 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

Vše, zbývá nakonfigurovat DHCP na routeru pro distribuci IP adresy tunelu serveru jako DNS a schéma je připraveno.

Omezení

Se současným algoritmem pro generování a zpracování seznamu domén zahrnuje mj. youtube.com a jeho CDN.

A to vede k tomu, že všechna videa projdou VPN, což může ucpat celý kanál. Možná stojí za to sestavit seznam oblíbených domén-výjimek, které prozatím blokují RKN, vnitřnosti jsou tenké. A přeskočte je při analýze.

Závěr

Popsaná metoda umožňuje obejít téměř jakékoli blokování, které poskytovatelé aktuálně implementují.

V zásadě, dnstap-bgp lze použít pro jakýkoli jiný účel, kde je potřeba určitá úroveň řízení provozu na základě názvu domény. Jen mějte na paměti, že v naší době může na stejné IP adrese viset tisíc stránek (například za nějakým Cloudflare), takže tato metoda má poměrně nízkou přesnost.

Ale pro potřeby obcházení zámků to docela stačí.

Dodatky, úpravy, žádosti o stažení – vítejte!

Zdroj: www.habr.com

Přidat komentář