L'argomento è piuttosto battuto, lo so. Ad esempio, c'è un grande articolo, ma qui viene considerata solo la parte IP della blocklist. Aggiungeremo anche domini.
A causa del fatto che i tribunali e l'RKN bloccano tutto a destra ea sinistra e che i fornitori si stanno sforzando di non cadere nelle multe emesse da Revizorro, le perdite associate al blocco sono piuttosto elevate. E tra i siti "legittimamente" bloccati ce ne sono molti utili (ciao, rutracker)
Vivo fuori dalla giurisdizione dell'RKN, ma i miei genitori, parenti e amici sono rimasti a casa. Quindi è stato deciso di trovare un modo semplice per le persone lontane dall'IT per aggirare il blocco, preferibilmente senza la loro partecipazione.
In questa nota, non descriverò le cose di base della rete in passaggi, ma descriverò i principi generali di come questo schema può essere implementato. Quindi la conoscenza di come funziona la rete in generale e in Linux in particolare è un must.
Tipi di serrature
Per prima cosa, rinfreschiamo la memoria di ciò che viene bloccato.
Esistono diversi tipi di blocchi nell'XML scaricato dall'RKN:
IP
Nome di dominio
URL
Per semplicità, li ridurremo a due: IP e dominio, e semplicemente elimineremo il dominio dal blocco tramite URL (più precisamente, lo hanno già fatto per noi).
brava gente da Roskomsvoboda realizzato un meraviglioso API, attraverso il quale possiamo ottenere ciò di cui abbiamo bisogno:
Per fare questo, abbiamo bisogno di alcuni piccoli VPS stranieri, preferibilmente con traffico illimitato - ce ne sono molti per 3-5 dollari. Devi prenderlo nel vicino estero in modo che il ping non sia molto grande, ma ancora una volta, tieni presente che Internet e la geografia non sempre coincidono. E poiché non esiste un contratto di servizio per 5 dollari, è meglio prendere più di 2 pezzi da fornitori diversi per la tolleranza ai guasti.
Successivamente, dobbiamo impostare un tunnel crittografato dal router client al VPS. Uso Wireguard come il più veloce e facile da configurare. Ho anche router client basati su Linux (APU2 o qualcosa in OpenWRT). Nel caso di alcuni Mikrotik/Cisco, puoi utilizzare i protocolli disponibili su di essi come OpenVPN e GRE-over-IPSEC.
Identificazione e reindirizzamento del traffico di interesse
Ovviamente puoi disattivare tutto il traffico Internet attraverso paesi stranieri. Ma, molto probabilmente, la velocità di lavoro con i contenuti locali ne risentirà notevolmente. Inoltre, i requisiti di larghezza di banda su VPS saranno molto più elevati.
Pertanto, dovremo in qualche modo allocare il traffico ai siti bloccati e indirizzarlo selettivamente al tunnel. Anche se parte del traffico "extra" arriva lì, è comunque molto meglio che guidare tutto attraverso il tunnel.
Per gestire il traffico, utilizzeremo il protocollo BGP e annunceremo i percorsi alle reti necessarie dal nostro VPS ai clienti. Prendiamo BIRD come uno dei demoni BGP più funzionali e convenienti.
IP
Con il blocco per IP, tutto è chiaro: annunciamo semplicemente tutti gli IP bloccati con VPS. Il problema è che ci sono circa 600mila sottoreti nell'elenco restituito dall'API e la stragrande maggioranza di esse sono host /32. Questo numero di percorsi può confondere i router client deboli.
Pertanto, durante l'elaborazione dell'elenco, si è deciso di riassumere fino alla rete / 24 se ha 2 o più host. Pertanto, il numero di rotte è stato ridotto a ~ 100 mila. Lo script per questo seguirà.
domini
È più complicato e ci sono diversi modi. Ad esempio, è possibile installare uno Squid trasparente su ciascun router client ed eseguire lì l'intercettazione HTTP e sbirciare nell'handshake TLS per ottenere l'URL richiesto nel primo caso e il dominio da SNI nel secondo.
Ma a causa di tutti i tipi di nuovi TLS1.3 + eSNI, l'analisi HTTPS sta diventando sempre meno realistica ogni giorno. Sì, e l'infrastruttura lato client sta diventando più complicata: dovrai utilizzare almeno OpenWRT.
Pertanto, ho deciso di intraprendere la strada dell'intercettazione delle risposte alle richieste DNS. Anche qui, qualsiasi DNS-over-TLS / HTTPS inizia a librarsi sopra la tua testa, ma possiamo (per ora) controllare questa parte sul client: disabilitarla o utilizzare il tuo server per DoT / DoH.
Come intercettare i DNS?
Anche qui possono esserci diversi approcci.
Intercettazione del traffico DNS tramite PCAP o NFLOG
Entrambi questi metodi di intercettazione sono implementati nell'utilità sidmat. Ma non è supportato da molto tempo e la funzionalità è molto primitiva, quindi è ancora necessario scrivere un'imbracatura per questo.
Analisi dei log del server DNS
Sfortunatamente, i ricorrenti a me noti non sono in grado di registrare le risposte, ma solo le richieste. In linea di principio, questo è logico, poiché, a differenza delle richieste, le risposte hanno una struttura complessa ed è difficile scriverle in forma testuale.
DNSTap
Fortunatamente, molti di loro supportano già DNSTap per questo scopo.
Che cos'è DNSTap?
È un protocollo client-server basato su Protocol Buffers e Frame Stream per il trasferimento da un server DNS a un raccoglitore di query e risposte DNS strutturate. In sostanza, il server DNS trasmette i metadati di query e risposta (tipo di messaggio, IP client/server, ecc.) oltre a messaggi DNS completi nella forma (binaria) in cui lavora con loro sulla rete.
È importante capire che nel paradigma DNSTap, il server DNS funge da client e il raccoglitore funge da server. Cioè, il server DNS si connette al raccoglitore e non viceversa.
Oggi DNSTap è supportato in tutti i server DNS più diffusi. Ma, ad esempio, BIND in molte distribuzioni (come Ubuntu LTS) è spesso costruito per qualche motivo senza il suo supporto. Quindi non preoccupiamoci del riassemblaggio, ma prendiamo un ricorrente più leggero e veloce: Unbound.
Come catturare DNSTap?
C'è un po 'numero Utilità CLI per lavorare con un flusso di eventi DNSTap, ma non sono adatte a risolvere il nostro problema. Pertanto, ho deciso di inventare la mia bicicletta che farà tutto il necessario: dnstap-bgp
Algoritmo di lavoro:
Quando viene lanciato, carica un elenco di domini da un file di testo, li inverte (habr.com -> com.habr), esclude linee spezzate, duplicati e sottodomini (ovvero se l'elenco contiene habr.com e www.habr.com, verrà caricato solo il primo) e costruisce un albero dei prefissi per la ricerca veloce in questo elenco
Agendo come un server DNSTap, attende una connessione da un server DNS. In linea di principio, supporta sia socket UNIX che TCP, ma i server DNS che conosco possono utilizzare solo socket UNIX
I pacchetti DNSTap in arrivo vengono prima deserializzati in una struttura Protobuf, quindi il messaggio DNS binario stesso, situato in uno dei campi Protobuf, viene analizzato a livello di record DNS RR
Viene verificato se l'host richiesto (o il suo dominio padre) è nell'elenco caricato, in caso contrario, la risposta viene ignorata
Dalla risposta vengono selezionati solo gli RR A/AAAA/CNAME e da essi vengono estratti gli indirizzi IPv4/IPv6 corrispondenti
Gli indirizzi IP vengono memorizzati nella cache con TTL configurabile e annunciati a tutti i peer BGP configurati
Quando si riceve una risposta che punta a un IP già memorizzato nella cache, il suo TTL viene aggiornato
Dopo la scadenza del TTL, la voce viene rimossa dalla cache e dagli annunci BGP
Funzionalità aggiuntive:
Rileggendo la lista dei domini di SIGHUP
Mantenere la cache sincronizzata con altre istanze dnstap-bgp tramite HTTP/JSON
Duplica la cache su disco (nel database BoltDB) per ripristinarne il contenuto dopo un riavvio
Supporto per il passaggio a uno spazio dei nomi di rete diverso (il motivo per cui è necessario verrà descritto di seguito)
Supporto IPv6
Restrizioni:
I domini IDN non sono ancora supportati
Poche impostazioni BGP
ho raccolto RPM e DEB pacchetti per una facile installazione. Dovrebbe funzionare su tutti i sistemi operativi relativamente recenti con systemd. non hanno dipendenze.
Guida
Quindi, iniziamo ad assemblare tutti i componenti insieme. Di conseguenza, dovremmo ottenere qualcosa di simile a questa topologia di rete:
La logica del lavoro, credo, è chiara dal diagramma:
Il client ha il nostro server configurato come DNS e anche le query DNS devono passare attraverso la VPN. Ciò è necessario affinché il provider non possa utilizzare l'intercettazione DNS per bloccare.
All'apertura del sito, il client invia una query DNS del tipo "quali sono gli IP di xxx.org"
Unbound risolve xxx.org (o lo prende dalla cache) e invia una risposta al client "xxx.org ha tale e tale IP", duplicandolo in parallelo tramite DNSTap
dnstap-bgp annuncia questi indirizzi in BIRD tramite BGP se il dominio è nell'elenco bloccato
BIRD pubblicizza un percorso verso questi IP con next-hop self router cliente
I successivi pacchetti dal client a questi IP passano attraverso il tunnel
Sul server, per le rotte verso i siti bloccati, utilizzo una tabella separata all'interno di BIRD e non si interseca in alcun modo con il sistema operativo.
Questo schema ha uno svantaggio: il primo pacchetto SYN dal client, molto probabilmente, avrà il tempo di partire attraverso il provider nazionale. il percorso non viene annunciato immediatamente. E qui le opzioni sono possibili a seconda di come il provider esegue il blocco. Se interrompe solo il traffico, non ci sono problemi. E se lo reindirizza a qualche DPI, allora (teoricamente) sono possibili effetti speciali.
È anche possibile che i client non rispettino i miracoli DNS TTL, il che può indurre il client a utilizzare alcune voci obsolete dalla sua cache marcia invece di chiedere Unbound.
In pratica, né il primo né il secondo mi hanno creato problemi, ma il tuo chilometraggio può variare.
Ottimizzazione del server
Per facilità di rotolamento, ho scritto ruolo per Ansible. Può configurare sia server che client basati su Linux (progettato per distribuzioni basate su deb). Tutte le impostazioni sono abbastanza ovvie e sono impostate inventario.yml. Questo ruolo è stato tagliato dal mio grande playbook, quindi potrebbe contenere errori... richieste di pull benvenuto 🙂
Esaminiamo i componenti principali.
BGP
L'esecuzione di due demoni BGP sullo stesso host presenta un problema fondamentale: BIRD non desidera configurare il peering BGP con l'host locale (o qualsiasi interfaccia locale). Dalla parola affatto. Cercare su Google e leggere le mailing list non ha aiutato, affermano che questo è di progettazione. Forse c'è un modo, ma non l'ho trovato.
Puoi provare un altro demone BGP, ma mi piace BIRD ed è usato ovunque da me, non voglio produrre entità.
Pertanto, ho nascosto dnstap-bgp all'interno dello spazio dei nomi di rete, che è connesso alla radice tramite l'interfaccia veth: è come una pipa, le cui estremità sporgono in diversi spazi dei nomi. A ciascuna di queste estremità, appendiamo indirizzi IP p2p privati che non vanno oltre l'host, quindi possono essere qualsiasi cosa. Questo è lo stesso meccanismo utilizzato per accedere ai processi all'interno amato da tutti Docker e altri contenitori.
Per questo è stato scritto copione e la funzionalità già descritta sopra per trascinarsi per i capelli in un altro spazio dei nomi è stata aggiunta a dnstap-bgp. Per questo motivo, deve essere eseguito come root o inviato al file binario CAP_SYS_ADMIN tramite il comando setcap.
Script di esempio per la creazione di uno spazio dei nomi
#!/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
Per impostazione predefinita, in Ubuntu, il binario Unbound è bloccato dal profilo AppArmor, che gli impedisce di connettersi a tutti i tipi di socket DNSTap. Puoi eliminare questo profilo o disabilitarlo:
Questo dovrebbe probabilmente essere aggiunto al playbook. L'ideale, ovviamente, è correggere il profilo e rilasciare i diritti necessari, ma ero troppo pigro.
non associato.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
La cosa divertente è che l'API RosKomSvoboda blocca le richieste con l'agente utente Python predefinito. Sembra che lo sceneggiatore l'abbia capito. Pertanto, lo cambiamo in Ognelis.
Finora funziona solo con IPv4. la quota di IPv6 è piccola, ma sarà facile risolverla. A meno che tu non debba usare anche 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 da aggiornare
Lo eseguo sulla corona una volta al giorno, forse vale la pena tirarlo ogni 4 ore. questo, a mio avviso, è il periodo di rinnovo che l'RKN richiede ai fornitori. Inoltre, hanno altri blocchi super urgenti, che potrebbero arrivare più velocemente.
Fa quanto segue:
Esegue il primo script e aggiorna l'elenco dei percorsi (rkn_routes.list) per UCCELLO
Ricarica UCCELLO
Aggiorna e ripulisce l'elenco dei domini per dnstap-bgp
Ricarica 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
Sono stati scritti senza pensarci troppo, quindi se vedi qualcosa che può essere migliorato, fallo.
Configurazione del cliente
Qui fornirò esempi per router Linux, ma nel caso di Mikrotik / Cisco dovrebbe essere ancora più semplice.
Innanzitutto, impostiamo BIRD:
uccello.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;
}
Pertanto, sincronizzeremo le route ricevute da BGP con la tabella di routing del kernel numero 222.
Dopodiché, è sufficiente chiedere al kernel di guardare questa piastra prima di guardare quella di default:
# 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
Tutto, resta da configurare DHCP sul router per distribuire l'indirizzo IP del tunnel del server come DNS e lo schema è pronto.
Limitazioni
Con l'attuale algoritmo per la generazione e l'elaborazione dell'elenco dei domini, include, tra le altre cose, youtube.com e i suoi CDN.
E questo porta al fatto che tutti i video passeranno attraverso la VPN, che può intasare l'intero canale. Forse vale la pena compilare un elenco di esclusioni di domini popolari che bloccano l'RKN per il momento, le viscere sono sottili. E saltali durante l'analisi.
conclusione
Il metodo descritto consente di aggirare quasi tutti i blocchi attualmente implementati dai provider.
In linea di principio, dnstap-bgp può essere utilizzato per qualsiasi altro scopo in cui è necessario un certo livello di controllo del traffico in base al nome di dominio. Tieni presente che ai nostri tempi mille siti possono bloccarsi sullo stesso indirizzo IP (dietro alcuni Cloudflare, ad esempio), quindi questo metodo ha una precisione piuttosto bassa.
Ma per le esigenze di aggirare i blocchi, questo è abbastanza.