Preterpasu ILV-blokadon per DNSTap kaj BGP

Preterpasu ILV-blokadon per DNSTap kaj BGP

La temo estas sufiĉe batita, mi scias. Ekzemple, estas bonega artikolo, sed nur la IP-parto de la bloklisto estas konsiderata tie. Ni ankaŭ aldonos domajnojn.

Pro la fakto, ke la tribunaloj kaj la RKN blokas ĉion dekstre kaj maldekstre, kaj la provizantoj forte klopodas ne fali sub la monpunoj eldonitaj de Revizorro, la rilataj perdoj de blokado estas sufiĉe grandaj. Kaj inter la "leĝe" blokitaj retejoj estas multaj utilaj (saluton, rutracker)

Mi loĝas ekster la jurisdikcio de la RKN, sed miaj gepatroj, parencoj kaj amikoj restis hejme. Do estis decidite elpensi facilan manieron por homoj malproksimaj de IT por preteriri blokadon, prefere sen ilia partopreno entute.

En ĉi tiu noto, mi ne priskribos la bazajn retajn aferojn en paŝoj, sed mi priskribos la ĝeneralajn principojn pri kiel ĉi tiu skemo povas esti efektivigita. Do scio pri kiel la reto funkcias ĝenerale kaj en Linukso precipe estas nepra.

Tipoj de seruroj

Unue, ni refreŝigu nian memoron pri tio, kio estas blokita.

Estas pluraj specoj de seruroj en la malŝarĝita XML de la RKN:

  • IP
  • Demando
  • URL

Por simpleco, ni reduktos ilin al du: IP kaj domajno, kaj ni simple eltiros la domajnon de blokado per URL (pli precize, ili jam faris tion por ni).

bonaj homoj de Roskomsvoboda realigis mirindan API, per kiu ni povas akiri tion, kion ni bezonas:

Aliro al blokitaj retejoj

Por fari tion, ni bezonas iun malgrandan eksterlandan VPS, prefere kun senlima trafiko - estas multaj el ĉi tiuj por 3-5 dolaroj. Vi devas preni ĝin en la proksima eksterlando, por ke la ping ne estu tre granda, sed denove konsideru, ke interreto kaj geografio ne ĉiam koincidas. Kaj ĉar ne ekzistas SLA por 5 dolaroj, estas pli bone preni 2+ pecojn de malsamaj provizantoj por erartoleremo.

Poste, ni devas agordi ĉifritan tunelon de la klienta enkursigilo al la VPS. Mi uzas Wireguard kiel la plej rapidan kaj facile instaleblan. Mi ankaŭ havas klientajn enkursigilojn bazitajn sur Linukso (APU2 aŭ io en OpenWRT). En la kazo de iuj Mikrotik / Cisco, vi povas uzi la disponeblajn protokolojn kiel OpenVPN kaj GRE-over-IPSEC.

Identigo kaj alidirekto de interesotrafiko

Vi povas, kompreneble, malŝalti la tutan interretan trafikon tra fremdaj landoj. Sed, plej verŝajne, la rapideco labori kun loka enhavo multe suferos pro tio. Krome, la bendolarĝaj postuloj ĉe VPS estos multe pli altaj.

Tial ni devos iel asigni trafikon al blokitaj ejoj kaj selekteme direkti ĝin al la tunelo. Eĉ se iu el la "kroma" trafiko alvenas tie, ĝi estas ankoraŭ multe pli bona ol veturi ĉion tra la tunelo.

Por administri trafikon, ni uzos la BGP-protokolon kaj anoncos itinerojn al la necesaj retoj de nia VPS al klientoj. Ni prenu BIRD kiel unu el la plej funkciaj kaj oportunaj BGP-demonoj.

IP

Kun blokado per IP, ĉio estas klara: ni simple anoncas ĉiujn blokitajn IP-ojn kun VPS. La problemo estas, ke estas ĉirkaŭ 600 mil subretoj en la listo, kiun la API redonas, kaj la granda plimulto el ili estas /32 gastigantoj. Ĉi tiu nombro da itineroj povas konfuzi malfortajn klientajn enkursigilojn.

Tial, kiam oni prilaboras la liston, oni decidis resumi ĝis la reto / 24 se ĝi havas 2 aŭ pli da gastigantoj. Tiel, la nombro da itineroj estis reduktita al ~100 mil. La skripto por ĉi tio sekvos.

Domajnoj

Ĝi estas pli komplika kaj estas pluraj manieroj. Ekzemple, vi povas instali travideblan Squid sur ĉiu klienta enkursigilo kaj fari HTTP-interkapton tie kaj rigardi la TLS-manpremon por akiri la petitan URL en la unua kazo kaj la domajnon de SNI en la dua.

Sed pro ĉiaj novŝanĝitaj TLS1.3 + eSNI, HTTPS-analizo fariĝas ĉiutage malpli kaj malpli reala. Jes, kaj la infrastrukturo ĉe la klienta flanko fariĝas pli komplika - vi devos uzi almenaŭ OpenWRT.

Tial mi decidis preni la vojon interkapti respondojn al DNS-demandoj. Ĉi tie ankaŭ ajna DNS-over-TLS / HTTPS komencas ŝvebi super via kapo, sed ni povas (nuntempe) kontroli ĉi tiun parton sur la kliento - aŭ malŝalti ĝin aŭ uzi vian propran servilon por DoT / DoH.

Kiel kapti DNS?

Ankaŭ ĉi tie povas esti pluraj aliroj.

  • Interkapto de DNS-trafiko per PCAP aŭ NFLOG
    Ambaŭ ĉi tiuj metodoj de interkapto estas efektivigitaj en la servaĵo sidmat. Sed ĝi ne estas subtenata dum longa tempo kaj la funkcieco estas tre primitiva, do vi ankoraŭ bezonas skribi jungilaron por ĝi.
  • Analizo de DNS-servilaj protokoloj
    Bedaŭrinde, la rekursiloj konataj de mi ne kapablas registri respondojn, sed nur petojn. Principe tio estas logika, ĉar, male al petoj, respondoj havas kompleksan strukturon kaj estas malfacile skribi ilin en tekstformo.
  • DNSTap
    Feliĉe, multaj el ili jam subtenas DNSTap tiucele.

Kio estas DNSTap?

Preterpasu ILV-blokadon per DNSTap kaj BGP

Ĝi estas klient-servila protokolo bazita sur Protocol Buffers kaj Frame Streams por translokado de DNS-servilo al kolektanto de strukturitaj DNS-demandoj kaj respondoj. Esence, la DNS-servilo transdonas metadatumojn pri demandaj kaj respondaj (speco de mesaĝo, kliento/servilo IP, ktp.) plus kompletajn DNS-mesaĝojn en la (binara) formo en kiu ĝi funkcias kun ili tra la reto.

Gravas kompreni, ke en la paradigmo DNSTap, la DNS-servilo agas kiel kliento kaj la kolektanto agas kiel servilo. Tio estas, la DNS-servilo konektas al la kolektanto, kaj ne inverse.

Hodiaŭ DNSTap estas subtenata en ĉiuj popularaj DNS-serviloj. Sed, ekzemple, BIND en multaj distribuoj (kiel Ubuntu LTS) ofte estas konstruita ial sen ĝia subteno. Do ni ne ĝenu vin pri remuntado, sed prenu pli malpezan kaj rapidan rimedon - Unbound.

Kiel kapti DNSTap?

Ekzistas iuj nombro CLI-utiloj por labori kun fluo de DNSTap-eventoj, sed ili ne taŭgas por solvi nian problemon. Tial mi decidis inventi mian propran biciklon, kiu faros ĉion necesan: dnstap-bgp

Laboralgoritmo:

  • Kiam ĝi estas lanĉita, ĝi ŝarĝas liston de domajnoj de tekstdosiero, inversigas ilin (habr.com -> com.habr), ekskludas rompitajn liniojn, duplikatojn kaj subdomajnojn (t.e. se la listo enhavas habr.com kaj www.habr.com, ĝi estos ŝargita nur la unua) kaj konstruas prefiksan arbon por rapida serĉado tra ĉi tiu listo
  • Agante kiel DNSTap-servilo, ĝi atendas konekton de DNS-servilo. Principe ĝi subtenas kaj UNIX- kaj TCP-ingojn, sed la DNS-serviloj, kiujn mi konas, povas uzi nur UNIX-ingojn.
  • Envenantaj DNSTap-pakaĵoj unue estas deserialigitaj en Protobuf-strukturon, kaj tiam la binara DNS-mesaĝo mem, situanta en unu el la Protobuf-kampoj, estas analizita al la nivelo de DNS RR-rekordoj.
  • Estas kontrolite ĉu la petita gastiganto (aŭ ĝia gepatra domajno) estas en la ŝarĝita listo, se ne, la respondo estas ignorita
  • Nur A/AAAA/CNAME RR-oj estas elektitaj el la respondo kaj la respondaj IPv4/IPv6-adresoj estas ĉerpitaj de ili
  • IP-adresoj estas konservitaj kun agordebla TTL kaj reklamitaj al ĉiuj agorditaj BGP-kunuloj
  • Kiam vi ricevas respondon montrantan al jam konservita IP, ĝia TTL estas ĝisdatigita
  • Post kiam la TTL eksvalidiĝas, la eniro estas forigita de la kaŝmemoro kaj de BGP-anoncoj

Pliaj funkcioj:

  • Relegante la liston de domajnoj de SIGHUP
  • Tenante la kaŝmemoron sinkronigita kun aliaj okazoj dnstap-bgp per HTTP/JSON
  • Duobligu la kaŝmemoron sur disko (en la BoltDB-datumbazo) por restarigi ĝian enhavon post rekomenco
  • Subteno por ŝanĝi al malsama reta nomspaco (kial tio necesas estos priskribita sube)
  • IPv6-subteno

Limigoj:

  • IDN-domajnoj ankoraŭ ne estas subtenataj
  • Malmultaj BGP-agordoj

mi kolektis RPM kaj DEB pakaĵoj por facila instalado. Devus funkcii sur ĉiuj relative lastatempaj OS-oj kun systemd. ili ne havas dependecojn.

Skemo

Do, ni komencu kunmeti ĉiujn komponantojn. Kiel rezulto, ni devus ricevi ion kiel ĉi tiu reto-topologio:
Preterpasu ILV-blokadon per DNSTap kaj BGP

La logiko de laboro, mi pensas, estas klara de la diagramo:

  • La kliento havas nian servilon agordita kiel DNS, kaj DNS-demandoj ankaŭ devas transiri la VPN. Ĉi tio estas necesa por ke la provizanto ne povas uzi DNS-interkapton por bloki.
  • Malfermante la retejon, la kliento sendas DNS-demandon kiel "kio estas la IP-oj de xxx.org"
  • Unbound solvas xxx.org (aŭ prenas ĝin el la kaŝmemoro) kaj sendas respondon al la kliento "xxx.org havas tian kaj tian IP", duobligante ĝin paralele per DNSTap
  • dnstap-bgp anoncas ĉi tiujn adresojn en BIRDO per BGP se la domajno estas en la blokita listo
  • BIRDO reklamas vojon al ĉi tiuj IP-oj kun next-hop self klienta enkursigilo
  • Postaj pakoj de la kliento al ĉi tiuj IP-oj trairas la tunelon

Sur la servilo, por itineroj al blokitaj retejoj, mi uzas apartan tablon ene de BIRD kaj ĝi neniel intersekcas kun la OS.

Estas malavantaĝo en ĉi tiu skemo: la unua SYN-pako de la kliento, plej verŝajne, havos tempon eliri tra la hejma provizanto. la itinero ne estas tuj anoncita. Kaj ĉi tie opcioj eblas depende de kiel la provizanto faras la blokadon. Se li nur faligas trafikon, tiam ne estas problemo. Kaj se li alidirektas ĝin al iu DPI, tiam (teorie) specialaj efektoj eblas.

Eblas ankaŭ, ke klientoj ne respektas DNS-TTL-miraklojn, kiuj povas kaŭzi, ke la kliento uzu kelkajn malnoviĝintajn enskribojn el ĝia putra kaŝmemoro anstataŭ demandi Unbound.

En la praktiko, nek la unua nek la dua kaŭzis problemojn al mi, sed via kilometraĵo povas varii.

Agordo de Servilo

Por facileco ruliĝi, mi skribis rolo por Ansible. Ĝi povas agordi kaj servilojn kaj klientojn bazitajn sur Linukso (dezajnita por deb-bazitaj distribuoj). Ĉiuj agordoj estas sufiĉe evidentaj kaj estas fiksitaj inventaro.yml. Ĉi tiu rolo estas tranĉita el mia granda ludlibro, do ĝi povas enhavi erarojn - tiri petojn bonvenon 🙂

Ni iru tra la ĉefaj komponantoj.

BGP

Ruli du BGP-demonojn sur la sama gastiganto havas fundamentan problemon: BIRD ne volas agordi BGP-peering kun la lokagastiganto (aŭ ajna loka interfaco). De la vorto entute. Guglado kaj legado de dissendolistoj ne helpis, ili asertas, ke tio estas laŭdezajne. Eble ekzistas ia maniero, sed mi ne trovis ĝin.

Vi povas provi alian BGP-demonon, sed mi ŝatas BIRD kaj ĝi estas ĉie uzata de mi, mi ne volas produkti entojn.

Tial mi kaŝis dnstap-bgp ene de la retnomspaco, kiu estas konektita al la radiko per la veth-interfaco: ĝi estas kiel pipo, kies finoj elstaras en malsamaj nomspacoj. Sur ĉiu el ĉi tiuj finoj, ni pendigas privatajn p2p IP-adresojn, kiuj ne iras preter la gastiganto, do ili povas esti io ajn. Ĉi tiu estas la sama mekanismo uzata por aliri procezojn interne amata de ĉiuj Docker kaj aliaj ujoj.

Por tio estis skribita skripto kaj la funkcio jam priskribita supre por treni vin per la haroj al alia nomspaco estis aldonita al dnstap-bgp. Pro ĉi tio, ĝi devas esti rulita kiel radiko aŭ eldonita al la CAP_SYS_ADMIN-duumaro per la setcap-komando.

Ekzempla skripto por krei nomspacon

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

birdo.konf

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

Defaŭlte, en Ubuntu, la Unbound binaro estas fiksita de la AppArmor-profilo, kiu malpermesas al ĝi konektiĝi al ĉiaj DNSTap-ingoj. Vi povas aŭ forigi ĉi tiun profilon, aŭ malŝalti ĝin:

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

Ĉi tio verŝajne estu aldonita al la ludlibro. Estas ideale, kompreneble, korekti la profilon kaj elsendi la necesajn rajtojn, sed mi estis tro maldiligenta.

malligita.konf

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

Elŝutado kaj prilaborado de listoj

Skripto por elŝuti kaj prilabori liston de IP-adresoj
Ĝi elŝutas la liston, resumas al la prefikso pfx. la ne_aldonu и ne_resumu vi povas diri al la IP-oj kaj retoj salti aŭ ne resumi. Mi bezonis ĝin. la subreto de mia VPS estis en la bloklisto 🙂

La amuza afero estas, ke la RosKomSvoboda API blokas petojn per la defaŭlta Python uzantagento. Ŝajnas, ke la script-kiddy ricevis ĝin. Tial ni ŝanĝas ĝin al Ognelis.

Ĝis nun ĝi funkcias nur kun IPv4. la parto de IPv6 estas malgranda, sed ĝi estos facile ripari. Krom se vi devas uzi ankaŭ 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)

Skripto por ĝisdatigi
Mi kuras ĝin sur la krono unufoje tage, eble indas tiri ĝin ĉiujn 4 horojn. ĉi tio, laŭ mi, estas la renovigperiodo, kiun RKN postulas de provizantoj. Krome, ili havas alian superurĝan blokadon, kiu povas alveni pli rapide.

Faras la jenon:

  • Rulas la unuan skripton kaj ĝisdatigas la liston de itineroj (rkn_routes.list) por BIRDO
  • Reŝargi BIRD
  • Ĝisdatigas kaj purigas la liston de domajnoj por dnstap-bgp
  • Reŝargi 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

Ili estis skribitaj sen multe da pripensado, do se vi vidas ion plibonigeblan - iru por ĝi.

Klienta agordo

Ĉi tie mi donos ekzemplojn por Linuksaj enkursigiloj, sed en la kazo de Mikrotik / Cisco devus esti eĉ pli facila.

Unue, ni starigis BIRD:

birdo.konf

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

Tiel, ni sinkronigos la itinerojn ricevitajn de BGP kun la kerna vojtabelo numero 222.

Post tio, sufiĉas peti la kernon rigardi ĉi tiun platon antaŭ ol rigardi la defaŭltan:

# 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

Ĉio, restas agordi DHCP sur la enkursigilo por distribui la tunelan IP-adreson de la servilo kiel DNS, kaj la skemo estas preta.

mankoj

Kun la nuna algoritmo por generi kaj prilabori la liston de domajnoj, ĝi inkluzivas, interalie, youtube.com kaj ĝiaj CDNoj.

Kaj ĉi tio kondukas al la fakto, ke ĉiuj filmetoj trairos la VPN, kiu povas ŝtopi la tutan kanalon. Eble indas kompili liston de popularaj domajnoj-ekskludoj, kiuj blokas la RKN por la momento, la intestoj estas maldikaj. Kaj preterlasu ilin dum analizado.

konkludo

La priskribita metodo permesas vin preteriri preskaŭ ajnan blokadon, kiun provizantoj nuntempe efektivigas.

Principe, dnstap-bgp povas esti uzata por iu ajn alia celo, kie necesas iu nivelo de trafika kontrolo surbaze de la domajna nomo. Nur memoru, ke en nia tempo, mil retejoj povas pendi sur la sama IP-adreso (malantaŭ iu Cloudflare, ekzemple), do ĉi tiu metodo havas sufiĉe malaltan precizecon.

Sed por la bezonoj de preterpasi serurojn, ĉi tio sufiĉas.

Aldonoj, redaktoj, tiraj petoj - bonvenon!

fonto: www.habr.com

Aldoni komenton