Aggira il blocco ILV con DNSTap e BGP

Aggira il blocco ILV con DNSTap e BGP

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:

Accesso ai siti bloccati

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?

Aggira il blocco ILV con DNSTap e BGP

È 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:
Aggira il blocco ILV con DNSTap e BGP

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

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",
]

uccello.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

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:

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

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

Download ed elaborazione delle liste

Script per il download e l'elaborazione di un elenco di indirizzi IP
Scarica l'elenco, riassume il prefisso pfx. In non_aggiungere и non_riassumere puoi dire agli IP e alle reti di saltare o non riassumere. Mi serviva. la sottorete del mio VPS era nella blocklist 🙂

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.

Aggiunte, modifiche, pull request - benvenuti!

Fonte: habr.com

Aggiungi un commento