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:
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?
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ě:
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
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:
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.