Evita el bloqueig ILV amb DNSTap i BGP

Evita el bloqueig ILV amb DNSTap i BGP

El tema està bastant trencat, ho sé. Per exemple, hi ha un gran article, però només s'hi considera la part IP de la llista de bloqueig. També afegirem dominis.

A causa del fet que els jutjats i el RKN ho bloquegen tot a dreta i esquerra, i els proveïdors s'esforcen per no caure en les multes emeses per Revizorro, les pèrdues associades pel bloqueig són força grans. I entre els llocs bloquejats "legalment" n'hi ha molts d'utils (hola, rutracker)

Visc fora de la jurisdicció de la RKN, però els meus pares, familiars i amics es van quedar a casa. Així que es va decidir trobar una manera fàcil per a persones allunyades de les TI d'evitar el bloqueig, preferiblement sense la seva participació.

En aquesta nota, no descriuré les coses bàsiques de la xarxa per passos, però descriuré els principis generals de com es pot implementar aquest esquema. Per tant, el coneixement de com funciona la xarxa en general i a Linux en particular és imprescindible.

Tipus de panys

Primer, refresquem la nostra memòria del que s'està bloquejant.

Hi ha diversos tipus de bloqueig a l'XML descarregat de l'RKN:

  • IP
  • Nom del domini
  • URL

Per senzillesa, els reduirem a dos: IP i domini, i simplement retirarem el domini del bloqueig per URL (més precisament, ja ho han fet per nosaltres).

bona gent de Roskomsvoboda es va adonar d'una meravella API, a través del qual podem obtenir el que necessitem:

Accés a llocs bloquejats

Per fer-ho, necessitem uns VPS estrangers petits, preferiblement amb trànsit il·limitat; n'hi ha molts per 3-5 dòlars. Cal portar-lo a l'estranger perquè el ping no sigui molt gran, però de nou, tingueu en compte que Internet i la geografia no sempre coincideixen. I com que no hi ha SLA per 5 dòlars, és millor agafar més de 2 peces de diferents proveïdors per tenir tolerància a errors.

A continuació, hem de configurar un túnel xifrat des de l'encaminador del client fins al VPS. Utilitzo Wireguard com el més ràpid i fàcil de configurar. També tinc encaminadors de client basats en Linux (APU2 o alguna cosa a OpenWRT). En el cas d'alguns Mikrotik / Cisco, podeu utilitzar els protocols disponibles en ells com OpenVPN i GRE-over-IPSEC.

Identificació i redirecció del trànsit d'interès

Per descomptat, podeu desactivar tot el trànsit d'Internet a través de països estrangers. Però, molt probablement, la velocitat de treball amb contingut local en patirà molt. A més, els requisits d'amplada de banda en VPS seran molt més alts.

Per tant, haurem d'assignar d'alguna manera el trànsit als llocs bloquejats i dirigir-lo selectivament al túnel. Fins i tot si hi arriba part del trànsit "extra", encara és molt millor que conduir tot pel túnel.

Per gestionar el trànsit, utilitzarem el protocol BGP i anunciarem rutes a les xarxes necessàries des del nostre VPS als clients. Prenem BIRD com un dels dimonis BGP més funcionals i convenients.

IP

Amb el bloqueig per IP, tot està clar: simplement anunciem totes les IP bloquejades amb VPS. El problema és que hi ha unes 600 mil subxarxes a la llista que retorna l'API, i la gran majoria d'elles són amfitrions /32. Aquest nombre de rutes pot confondre els encaminadors clients febles.

Per tant, en processar la llista, es va decidir resumir fins a la xarxa / 24 si té 2 o més amfitrions. Així, el nombre de rutes es va reduir a ~ 100 mil. Seguirà el guió per a això.

dominis

És més complicat i hi ha diverses maneres. Per exemple, podeu instal·lar un Squid transparent a cada encaminador client i fer-hi una intercepció HTTP i mirar l'enllaç TLS per obtenir l'URL sol·licitat en el primer cas i el domini de SNI en el segon.

Però a causa de tota mena de nous TLS1.3 + eSNI, l'anàlisi HTTPS és cada dia menys real. Sí, i la infraestructura del costat del client és cada cop més complicada: haureu d'utilitzar almenys OpenWRT.

Per tant, vaig decidir prendre el camí d'interceptar respostes a les sol·licituds de DNS. Aquí també, qualsevol DNS-over-TLS / HTTPS comença a passar per sobre del vostre cap, però podem (de moment) controlar aquesta part al client: desactiveu-la o utilitzeu el vostre propi servidor per a DoT / DoH.

Com interceptar el DNS?

Aquí també hi pot haver diversos enfocaments.

  • Intercepció del trànsit DNS via PCAP o NFLOG
    Ambdós mètodes d'intercepció s'implementen a la utilitat sidmat. Però fa molt de temps que no s'admet i la funcionalitat és molt primitiva, de manera que encara cal escriure-hi un arnès.
  • Anàlisi dels registres del servidor DNS
    Malauradament, els recursos coneguts per mi no poden registrar respostes, sinó només sol·licituds. En principi, això és lògic, ja que, a diferència de les peticions, les respostes tenen una estructura complexa i és difícil escriure-les en forma de text.
  • DNSTap
    Afortunadament, molts d'ells ja admeten DNSTap per a aquest propòsit.

Què és DNSTap?

Evita el bloqueig ILV amb DNSTap i BGP

És un protocol client-servidor basat en protocols buffers i Frame Streams per transferir des d'un servidor DNS a un col·lector de consultes i respostes DNS estructurades. Bàsicament, el servidor DNS transmet metadades de consulta i resposta (tipus de missatge, IP client/servidor, etc.) més missatges DNS complets en la forma (binari) en què treballa amb ells a través de la xarxa.

És important entendre que en el paradigma DNSTap, el servidor DNS actua com a client i el col·lector actua com a servidor. És a dir, el servidor DNS es connecta al col·lector, i no viceversa.

Avui DNSTap és compatible amb tots els servidors DNS populars. Però, per exemple, BIND en moltes distribucions (com Ubuntu LTS) sovint es construeix per algun motiu sense el seu suport. Així que no ens molestem amb el muntatge, sinó que prenguem un recurs més lleuger i ràpid - Unbound.

Com agafar DNSTap?

Hi alguns nombre Utilitats CLI per treballar amb un flux d'esdeveniments DNSTap, però no són adequades per resoldre el nostre problema. Per això, vaig decidir inventar la meva pròpia bicicleta que farà tot el que sigui necessari: dnstap-bgp

Algorisme de treball:

  • Quan s'inicia, carrega una llista de dominis des d'un fitxer de text, els inverteix (habr.com -> com.habr), exclou línies trencades, duplicats i subdominis (és a dir, si la llista conté habr.com i www.habr.com, només es carregarà el primer) i crea un arbre de prefixos per a una cerca ràpida a través d'aquesta llista
  • Actuant com a servidor DNSTap, espera una connexió des d'un servidor DNS. En principi, admet tant sòcols UNIX com TCP, però els servidors DNS que conec només poden utilitzar sòcols UNIX
  • Els paquets DNSTap entrants es deserialitzen primer en una estructura Protobuf, i després el propi missatge DNS binari, situat en un dels camps Protobuf, s'analitza al nivell de registres DNS RR.
  • Es comprova si l'amfitrió sol·licitat (o el seu domini principal) es troba a la llista carregada, si no, la resposta s'ignora
  • Només es seleccionen els RR A/AAAA/CNAME de la resposta i s'extreuen les adreces IPv4/IPv6 corresponents.
  • Les adreces IP s'emmagatzemen a la memòria cau amb TTL configurable i s'anuncien a tots els parells BGP configurats
  • Quan rebeu una resposta que apunta a una IP ja guardada a la memòria cau, el seu TTL s'actualitza
  • Un cop caduca el TTL, l'entrada s'elimina de la memòria cau i dels anuncis de BGP

Funcionalitat addicional:

  • Rellegint la llista de dominis per SIGHUP
  • Mantenir la memòria cau sincronitzada amb altres instàncies dnstap-bgp mitjançant HTTP/JSON
  • Dupliqueu la memòria cau al disc (a la base de dades BoltDB) per restaurar-ne el contingut després d'un reinici
  • Suport per canviar a un espai de noms de xarxa diferent (a continuació es descriu per què és necessari)
  • Suport IPv6

Limitacions:

  • Els dominis IDN encara no són compatibles
  • Poques configuracions de BGP

vaig recollir RPM i DEB paquets per a una fàcil instal·lació. Hauria de funcionar en tots els sistemes operatius relativament recents amb systemd. no tenen cap dependència.

L'esquema

Per tant, comencem a muntar tots els components junts. Com a resultat, hauríem d'obtenir alguna cosa com aquesta topologia de xarxa:
Evita el bloqueig ILV amb DNSTap i BGP

La lògica del treball, crec, és clara del diagrama:

  • El client té el nostre servidor configurat com a DNS i les consultes de DNS també han de passar per la VPN. Això és necessari perquè el proveïdor no pugui utilitzar la intercepció de DNS per bloquejar.
  • En obrir el lloc, el client envia una consulta DNS com "quines són les IP de xxx.org"
  • sense consolidar resol xxx.org (o el treu de la memòria cau) i envia una resposta al client "xxx.org té tal i tal IP", duplicant-lo en paral·lel mitjançant DNSTap
  • dnstap-bgp anuncia aquestes adreces a NAT mitjançant BGP si el domini es troba a la llista bloquejada
  • NAT anuncia una ruta a aquestes IP amb next-hop self encaminador client
  • Els paquets posteriors del client a aquestes IP passen pel túnel

Al servidor, per a rutes a llocs bloquejats, faig servir una taula separada dins de BIRD i no es creua amb el sistema operatiu de cap manera.

Aquest esquema té un inconvenient: el primer paquet SYN del client, molt probablement, tindrà temps per sortir a través del proveïdor nacional. la ruta no s'anuncia immediatament. I aquí les opcions són possibles en funció de com el proveïdor fa el bloqueig. Si només baixa el trànsit, no hi ha cap problema. I si el redirigeix ​​a algun DPI, llavors (teòricament) els efectes especials són possibles.

També és possible que els clients no respectin els miracles DNS TTL, cosa que pot fer que el client utilitzi algunes entrades obsoletes de la seva memòria cau podrida en lloc de demanar Unbound.

A la pràctica, ni el primer ni el segon em van causar problemes, però el vostre quilometratge pot variar.

Ajust del servidor

Per facilitar el rodatge, vaig escriure paper per a Ansible. Pot configurar servidors i clients basats en Linux (dissenyat per a distribucions basades en deb). Tots els paràmetres són força evidents i estan configurats inventari.yml. Aquesta funció està tallada del meu llibre de jocs gran, de manera que pot contenir errors: demanar peticions benvingut 🙂

Repassem els components principals.

BGP

L'execució de dos dimonis BGP al mateix host té un problema fonamental: BIRD no vol configurar el peering BGP amb el localhost (o cap interfície local). De la paraula en absolut. Buscar a Google i llegir llistes de correu no va ajudar, afirmen que això és per disseny. Potser hi ha alguna manera, però no la vaig trobar.

Podeu provar un altre dimoni BGP, però m'agrada BIRD i jo l'utilitzo a tot arreu, no vull produir entitats.

Per tant, he amagat dnstap-bgp dins de l'espai de noms de la xarxa, que està connectat a l'arrel a través de la interfície veth: és com una canonada, els extrems de la qual sobresurten en diferents espais de noms. En cadascun d'aquests extrems, pengem adreces IP privades p2p que no van més enllà de l'amfitrió, de manera que poden ser qualsevol cosa. Aquest és el mateix mecanisme utilitzat per accedir als processos a l'interior estimat per tots Docker i altres contenidors.

Per això es va escriure guió i la funcionalitat ja descrita anteriorment per arrossegar-se pel cabell a un altre espai de noms es va afegir a dnstap-bgp. Per això, s'ha d'executar com a root o emetre's al binari CAP_SYS_ADMIN mitjançant l'ordre setcap.

Exemple d'script per crear un espai de noms

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

ocell.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 defecte, a Ubuntu, el binari Unbound està subjectat pel perfil AppArmor, que li prohibeix connectar-se a tot tipus de sòcols DNSTap. Pots suprimir aquest perfil o desactivar-lo:

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

Això probablement s'hauria d'afegir al llibre de jugades. És ideal, és clar, corregir el perfil i emetre els drets necessaris, però em feia massa mandra.

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

Descàrrega i processament de llistes

Script per descarregar i processar una llista d'adreces IP
Descarrega la llista, suma el prefix pfx. En no_afegiu и no_resumir podeu dir que les IP i les xarxes s'ometin o no resumeixin. Ho necessitava. la subxarxa del meu VPS estava a la llista de bloqueig 🙂

El més curiós és que l'API RosKomSvoboda bloqueja les sol·licituds amb l'agent d'usuari Python predeterminat. Sembla que el guió-infant ho va entendre. Per tant, ho canviem per Ognelis.

Fins ara, només funciona amb IPv4. la quota d'IPv6 és petita, però serà fàcil de solucionar. A menys que també hàgiu d'utilitzar 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 per actualitzar
El faig a la corona un cop al dia, potser val la pena tirar-lo cada 4 hores. aquest, al meu entendre, és el període de renovació que la RKN requereix als proveïdors. A més, tenen un altre bloqueig súper urgent, que pot arribar més ràpid.

Fa el següent:

  • Executa el primer script i actualitza la llista de rutes (rkn_routes.list) per Ocell
  • Torna a carregar BIRD
  • Actualitza i neteja la llista de dominis per a dnstap-bgp
  • Torneu a carregar 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

Es van escriure sense pensar gaire, així que si veieu alguna cosa que es pot millorar, feu-ho.

Configuració del client

Aquí donaré exemples d'encaminadors Linux, però en el cas de Mikrotik / Cisco hauria de ser encara més fàcil.

Primer, vam configurar BIRD:

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

Així, sincronitzarem les rutes rebudes de BGP amb la taula d'encaminament del nucli número 222.

Després d'això, n'hi ha prou amb demanar al nucli que miri aquesta placa abans de mirar la predeterminada:

# 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

Tot, queda configurar DHCP al router per distribuir l'adreça IP del túnel del servidor com a DNS i l'esquema està llest.

Limitacions

Amb l'algoritme actual de generació i processament de la llista de dominis, inclou, entre altres coses, youtube.com i els seus CDN.

I això porta al fet que tots els vídeos passaran per la VPN, que pot obstruir tot el canal. Potser val la pena compilar una llista de dominis populars: exclusions que bloquegen el RKN de moment, les entranyes són primes. I saltar-los en analitzar.

Conclusió

El mètode descrit us permet evitar gairebé qualsevol bloqueig que implementen actualment els proveïdors.

Bàsicament, dnstap-bgp es pot utilitzar per a qualsevol altre propòsit on es necessiti algun nivell de control del trànsit basat en el nom de domini. Només cal tenir en compte que en els nostres dies, mil llocs poden penjar-se a la mateixa adreça IP (darrere d'alguns Cloudflare, per exemple), de manera que aquest mètode té una precisió força baixa.

Però per a les necessitats de saltar els panys, amb això n'hi ha prou.

Addicions, modificacions, sol·licituds d'extracció: benvingut!

Font: www.habr.com

Afegeix comentari