Omzeil ILV-blokkering met DNSTap en BGP

Omzeil ILV-blokkering met DNSTap en BGP

Het onderwerp is behoorlijk in elkaar geslagen, ik weet het. Er is bijvoorbeeld een geweldige artikel, maar daar wordt alleen rekening gehouden met het IP-gedeelte van de blokkeerlijst. We zullen ook domeinen toevoegen.

Doordat de rechtbanken en de RKN alles rechts en links blokkeren en de providers hard hun best doen om niet onder de boetes van Revizorro te vallen, zijn de bijbehorende verliezen door blokkering behoorlijk groot. En onder de "rechtmatig" geblokkeerde sites zijn er veel nuttige (hallo, rutracker)

Ik woon buiten het rechtsgebied van de RKN, maar mijn ouders, familie en vrienden bleven thuis. Dus werd besloten om een ​​gemakkelijke manier te bedenken voor mensen die ver van IT staan ​​om de blokkering te omzeilen, bij voorkeur zonder hun deelname.

In deze notitie zal ik de basisnetwerkdingen niet in stappen beschrijven, maar zal ik de algemene principes beschrijven van hoe dit schema kan worden geïmplementeerd. Dus kennis van hoe het netwerk werkt in het algemeen en in Linux in het bijzonder is een must.

Soorten sloten

Laten we eerst ons geheugen opfrissen van wat er wordt geblokkeerd.

Er zijn verschillende soorten sloten in de ongeladen XML van de RKN:

  • IP
  • domein
  • URL

Voor de eenvoud zullen we ze terugbrengen tot twee: IP en domein, en we zullen het domein gewoon uit de blokkering halen door URL (meer precies, ze hebben dit al voor ons gedaan).

goede mensen van Roskomsvoboda realiseerde een prachtige API, waardoor we kunnen krijgen wat we nodig hebben:

Toegang tot geblokkeerde sites

Om dit te doen, hebben we een paar kleine buitenlandse VPS nodig, bij voorkeur met onbeperkt verkeer - er zijn er veel voor 3-5 dollar. Je moet het in het nabije buitenland meenemen, zodat de ping niet erg groot is, maar nogmaals, houd er rekening mee dat internet en geografie niet altijd samenvallen. En aangezien er geen SLA is voor 5 dollar, is het beter om 2+ stuks van verschillende providers te nemen voor fouttolerantie.

Vervolgens moeten we een versleutelde tunnel opzetten van de clientrouter naar de VPS. Ik gebruik Wireguard als de snelste en gemakkelijkst in te stellen. Ik heb ook client-routers op basis van Linux (APU2 of iets in OpenWRT). In het geval van sommige Mikrotik / Cisco kunt u de protocollen gebruiken die erop beschikbaar zijn, zoals OpenVPN en GRE-over-IPSEC.

Identificatie en omleiding van interessant verkeer

U kunt natuurlijk al het internetverkeer via het buitenland uitschakelen. Maar hoogstwaarschijnlijk zal de snelheid van het werken met lokale inhoud hier sterk onder lijden. Bovendien zullen de bandbreedtevereisten op VPS veel hoger zijn.

Daarom moeten we op de een of andere manier verkeer toewijzen aan geblokkeerde sites en het selectief naar de tunnel leiden. Zelfs als een deel van het "extra" verkeer daar komt, is het nog steeds veel beter dan alles door de tunnel te rijden.

Om het verkeer te beheren, gebruiken we het BGP-protocol en kondigen we routes naar de benodigde netwerken van onze VPS aan klanten aan. Laten we BIRD nemen als een van de meest functionele en handige BGP-daemons.

IP

Met blokkering via IP is alles duidelijk: we kondigen eenvoudig alle geblokkeerde IP's aan met VPS. Het probleem is dat er ongeveer 600 subnetten in de lijst staan ​​die de API retourneert, en de overgrote meerderheid daarvan zijn /32-hosts. Dit aantal routes kan zwakke clientrouters verwarren.

Daarom werd bij het verwerken van de lijst besloten om samen te vatten tot het netwerk / 24 als het 2 of meer hosts heeft. Zo werd het aantal routes teruggebracht tot ~ 100 duizend. Het script hiervoor volgt nog.

domeinen

Het is ingewikkelder en er zijn verschillende manieren. U kunt bijvoorbeeld een transparante Squid op elke clientrouter installeren en daar HTTP-onderschepping uitvoeren en in de TLS-handshake kijken om in het eerste geval de gevraagde URL en in het tweede het domein van SNI te verkrijgen.

Maar door allerlei nieuwerwetse TLS1.3 + eSNI wordt HTTPS-analyse met de dag steeds minder reëel. Ja, en de infrastructuur aan de clientzijde wordt ingewikkelder - u zult op zijn minst OpenWRT moeten gebruiken.

Daarom besloot ik het pad te nemen van het onderscheppen van antwoorden op DNS-verzoeken. Ook hier begint elke DNS-over-TLS / HTTPS boven je hoofd te zweven, maar we kunnen (voorlopig) dit onderdeel op de client besturen - ofwel uitschakelen of je eigen server gebruiken voor DoT / DoH.

Hoe DNS onderscheppen?

Ook hier kunnen meerdere benaderingen zijn.

  • Onderschepping van DNS-verkeer via PCAP of NFLOG
    Beide onderscheppingsmethoden zijn geïmplementeerd in het hulpprogramma sidmat. Maar het wordt al lang niet meer ondersteund en de functionaliteit is erg primitief, dus je moet er nog een harnas voor schrijven.
  • Analyse van DNS-serverlogboeken
    Helaas kunnen de mij bekende recursors geen reacties loggen, maar alleen verzoeken. In principe is dit logisch, aangezien antwoorden, in tegenstelling tot verzoeken, een complexe structuur hebben en moeilijk in tekstvorm te schrijven zijn.
  • DNSTap
    Gelukkig ondersteunen velen van hen hiervoor al DNSTap.

Wat is DNSTap?

Omzeil ILV-blokkering met DNSTap en BGP

Het is een client-serverprotocol gebaseerd op protocolbuffers en framestreams voor overdracht van een DNS-server naar een verzamelaar van gestructureerde DNS-query's en -antwoorden. In wezen verzendt de DNS-server query- en antwoordmetadata (type bericht, client/server-IP, enz.) plus complete DNS-berichten in de (binaire) vorm waarin hij ermee werkt via het netwerk.

Het is belangrijk om te begrijpen dat in het DNSTap-paradigma de DNS-server als client fungeert en de collector als server. Dat wil zeggen, de DNS-server maakt verbinding met de collector en niet andersom.

Tegenwoordig wordt DNSTap ondersteund in alle populaire DNS-servers. Maar BIND wordt bijvoorbeeld in veel distributies (zoals Ubuntu LTS) vaak om de een of andere reden zonder ondersteuning gebouwd. Dus laten we ons niet druk maken over hermontage, maar een lichtere en snellere recursor nemen - Unbound.

Hoe DNSTap te vangen?

Er is sommige aantal CLI-hulpprogramma's voor het werken met een stroom DNSTap-gebeurtenissen, maar ze zijn niet geschikt om ons probleem op te lossen. Daarom besloot ik mijn eigen fiets uit te vinden die alles doet wat nodig is: dnstap-bgp

Werk algoritme:

  • Bij het opstarten laadt het een lijst met domeinen uit een tekstbestand, keert ze om (habr.com -> com.habr), sluit gebroken lijnen, duplicaten en subdomeinen uit (d.w.z. als de lijst habr.com en www.habr.com bevat, het zal alleen de eerste worden geladen) en bouwt een prefixboom op om snel door deze lijst te kunnen zoeken
  • Het fungeert als een DNSTap-server en wacht op een verbinding van een DNS-server. In principe ondersteunt het zowel UNIX- als TCP-sockets, maar de DNS-servers die ik ken kunnen alleen UNIX-sockets gebruiken
  • Inkomende DNSTap-pakketten worden eerst gedeserialiseerd naar een Protobuf-structuur en vervolgens wordt het binaire DNS-bericht zelf, dat zich in een van de Protobuf-velden bevindt, geparseerd naar het niveau van DNS RR-records
  • Er wordt gecontroleerd of de aangevraagde host (of het bovenliggende domein) in de geladen lijst staat, zo niet, dan wordt het antwoord genegeerd
  • Uit het antwoord worden alleen A/AAAA/CNAME RR's geselecteerd en de corresponderende IPv4/IPv6-adressen worden eruit gehaald
  • IP-adressen worden in de cache opgeslagen met configureerbare TTL en geadverteerd naar alle geconfigureerde BGP-peers
  • Wanneer een antwoord wordt ontvangen dat verwijst naar een IP die al in de cache is opgeslagen, wordt de TTL bijgewerkt
  • Nadat de TTL is verlopen, wordt het item verwijderd uit de cache en uit BGP-aankondigingen

Extra functionaliteit:

  • Herlezen van de lijst met domeinen door SIGHUP
  • De cache synchroon houden met andere instanties dnstap-bgp via HTTP/JSON
  • Dupliceer de cache op schijf (in de BoltDB-database) om de inhoud ervan te herstellen na een herstart
  • Ondersteuning voor het overschakelen naar een andere netwerknaamruimte (waarom dit nodig is, wordt hieronder beschreven)
  • IPv6-ondersteuning

beperkingen:

  • IDN-domeinen worden nog niet ondersteund
  • Weinig BGP-instellingen

ik verzamelde RPM en DEB pakketten voor eenvoudige installatie. Zou moeten werken op alle relatief recente besturingssystemen met systemd. ze hebben geen afhankelijkheden.

Het schema

Dus laten we beginnen met het samenvoegen van alle componenten. Als gevolg hiervan zouden we zoiets als deze netwerktopologie moeten krijgen:
Omzeil ILV-blokkering met DNSTap en BGP

De logica van het werk, denk ik, is duidelijk uit het diagram:

  • De klant heeft onze server geconfigureerd als DNS en DNS-query's moeten ook via de VPN gaan. Dit is nodig zodat de provider geen DNS-onderschepping kan gebruiken om te blokkeren.
  • Bij het openen van de site stuurt de client een DNS-query zoals "wat zijn de IP's van xxx.org"
  • Ongebonden lost xxx.org op (of haalt het uit de cache) en stuurt een antwoord naar de client "xxx.org heeft dit en dat IP", waarbij het parallel wordt gedupliceerd via DNSTap
  • dnstap-bgp kondigt deze adressen aan in VOGEL via BGP als het domein op de geblokkeerde lijst staat
  • VOGEL adverteert een route naar deze IP's met next-hop self client-router
  • Daaropvolgende pakketten van de client naar deze IP's gaan door de tunnel

Op de server, voor routes naar geblokkeerde sites, gebruik ik een aparte tabel binnen BIRD en deze kruist op geen enkele manier met het besturingssysteem.

Dit schema heeft een nadeel: het eerste SYN-pakket van de klant zal hoogstwaarschijnlijk tijd hebben om via de binnenlandse provider te vertrekken. de route wordt niet meteen aangekondigd. En hier zijn opties mogelijk, afhankelijk van hoe de provider de blokkering uitvoert. Als hij gewoon het verkeer laat vallen, is er geen probleem. En als hij het omleidt naar een of andere DPI, dan zijn (theoretisch) speciale effecten mogelijk.

Het is ook mogelijk dat clients de DNS TTL-mirakels niet respecteren, waardoor de client verouderde ingangen uit zijn verrotte cache kan gebruiken in plaats van Unbound te vragen.

In de praktijk leverden noch de eerste noch de tweede problemen voor mij op, maar uw kilometerstand kan variëren.

Serverafstemming

Om het rollen te vergemakkelijken, schreef ik rol voor Ansible. Het kan zowel servers als clients configureren op basis van Linux (ontworpen voor op deb gebaseerde distributies). Alle instellingen zijn vrij duidelijk en zijn ingesteld inventaris.yml. Deze rol is uit mijn grote draaiboek geknipt, dus het kan fouten bevatten - trek verzoeken welkom :)

Laten we de belangrijkste componenten doornemen.

BGP

Het draaien van twee BGP-daemons op dezelfde host heeft een fundamenteel probleem: BIRD wil geen BGP-peering opzetten met de localhost (of een andere lokale interface). Helemaal van het woord. Googelen en mailinglijsten lezen hielp niet, ze beweren dat dit zo is ontworpen. Misschien is er een manier, maar ik heb het niet gevonden.

Je kunt een andere BGP-daemon proberen, maar ik hou van BIRD en het wordt overal door mij gebruikt, ik wil geen entiteiten produceren.

Daarom verborg ik dnstap-bgp in de naamruimte van het netwerk, die via de veth-interface met de root is verbonden: het is als een pijp waarvan de uiteinden in verschillende naamruimten uitsteken. Aan elk van deze uiteinden hangen we privé p2p IP-adressen die niet verder gaan dan de host, dus ze kunnen van alles zijn. Dit is hetzelfde mechanisme dat wordt gebruikt om toegang te krijgen tot processen binnenin geliefd bij iedereen Docker en andere containers.

Hiervoor is het geschreven script en de functionaliteit die hierboven al is beschreven om jezelf aan het haar naar een andere naamruimte te slepen, is toegevoegd aan dnstap-bgp. Daarom moet het worden uitgevoerd als root of worden uitgegeven aan het binaire bestand CAP_SYS_ADMIN via de opdracht setcap.

Voorbeeldscript voor het maken van een naamruimte

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

vogel.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.lijst

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

In Ubuntu wordt de Unbound binary standaard geklemd door het AppArmor-profiel, waardoor het geen verbinding kan maken met allerlei soorten DNSTap-sockets. U kunt dit profiel verwijderen of uitschakelen:

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

Dit zou waarschijnlijk aan het draaiboek moeten worden toegevoegd. Het is natuurlijk ideaal om het profiel te corrigeren en de nodige rechten te verlenen, maar ik was te lui.

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

Lijsten downloaden en verwerken

Script voor het downloaden en verwerken van een lijst met IP-adressen
Het downloadt de lijst, somt op tot het voorvoegsel PFX. In niet_toevoegen и niet samenvatten u kunt de IP's en netwerken vertellen om over te slaan of niet samen te vatten. Ik had het nodig. het subnet van mijn VPS stond in de blokkeerlijst 🙂

Het grappige is dat de RosKomSvoboda API verzoeken blokkeert met de standaard Python user agent. Het lijkt erop dat de scriptkiddy het heeft begrepen. Daarom veranderen we het in Ognelis.

Tot nu toe werkt het alleen met IPv4. het aandeel van IPv6 is klein, maar het zal gemakkelijk te repareren zijn. Tenzij je ook bird6 moet gebruiken.

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 om bij te werken
Ik laat het één keer per dag op de kruin lopen, misschien is het de moeite waard om het elke 4 uur te trekken. dit is mijns inziens de verlengingstermijn die de RKN van aanbieders verlangt. Bovendien hebben ze een andere superdringende blokkering, die mogelijk sneller aankomt.

Doet het volgende:

  • Voert het eerste script uit en werkt de lijst met routes bij (rkn_routes.list) voor VOGEL
  • Herlaad VOGEL
  • Werkt de lijst met domeinen voor dnstap-bgp bij en ruimt deze op
  • Herlaad 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

Ze zijn zonder veel nadenken geschreven, dus als je iets ziet dat verbeterd kan worden, ga ervoor.

Cliënt instellen

Hier zal ik voorbeelden geven voor Linux-routers, maar in het geval van Mikrotik/Cisco zou het nog makkelijker moeten zijn.

Eerst hebben we BIRD opgezet:

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

We zullen dus de routes ontvangen van BGP synchroniseren met de kernel-routeringstabel nummer 222.

Daarna is het voldoende om de kernel te vragen naar deze plaat te kijken voordat hij naar de standaardplaat kijkt:

# 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

Alles, het blijft om DHCP op de router te configureren om het tunnel-IP-adres van de server als DNS te verdelen, en het schema is klaar.

Beperkingen

Met het huidige algoritme voor het genereren en verwerken van de lijst met domeinen bevat deze onder andere youtube.com en zijn CDN's.

En dit leidt ertoe dat alle video's via de VPN gaan, wat het hele kanaal kan verstoppen. Misschien is het de moeite waard om een ​​lijst samen te stellen met populaire domeinuitsluitingen die de RKN voorlopig blokkeren, het lef is dun. En sla ze over bij het ontleden.

Conclusie

Met de beschreven methode kunt u bijna elke blokkering omzeilen die providers momenteel implementeren.

In beginsel dnstap-bgp kan worden gebruikt voor elk ander doel waar een bepaald niveau van verkeerscontrole nodig is op basis van de domeinnaam. Houd er rekening mee dat er in onze tijd duizend sites op hetzelfde IP-adres kunnen hangen (bijvoorbeeld achter sommige Cloudflare), dus deze methode heeft een vrij lage nauwkeurigheid.

Maar voor de behoeften van het omzeilen van sluizen is dit voldoende.

Toevoegingen, bewerkingen, pull-aanvragen - welkom!

Bron: www.habr.com

Voeg een reactie