Contourner le blocage ILV avec DNSTap et BGP

Contourner le blocage ILV avec DNSTap et BGP

Le sujet est assez tordu, je sais. Par exemple, il y a un grand article, mais seule la partie IP de la liste de blocage y est prise en compte. Nous ajouterons également des domaines.

Étant donné que les tribunaux et le RKN bloquent tout à droite et à gauche, et que les fournisseurs s'efforcent de ne pas tomber sous le coup des amendes infligées par Revizorro, les pertes associées au blocage sont assez importantes. Et parmi les sites "légalement" bloqués, il y en a beaucoup d'utiles (bonjour, rutracker)

Je vis en dehors de la juridiction du RKN, mais mes parents, ma famille et mes amis sont restés à la maison. Il a donc été décidé de trouver un moyen simple pour les personnes éloignées de l'informatique de contourner le blocage, de préférence sans leur participation du tout.

Dans cette note, je ne décrirai pas les éléments de base du réseau par étapes, mais je décrirai les principes généraux de la manière dont ce schéma peut être mis en œuvre. La connaissance du fonctionnement du réseau en général et de Linux en particulier est donc indispensable.

Types de serrures

Tout d'abord, rafraîchissons notre mémoire de ce qui est bloqué.

Il existe plusieurs types de verrous dans le XML déchargé du RKN :

  • IP
  • Nom de domaine
  • URL

Pour simplifier, nous les réduirons à deux : IP et domaine, et nous retirerons simplement le domaine du blocage par URL (plus précisément, ils l'ont déjà fait pour nous).

bonnes personnes de Roskomsvoboda réalisé un merveilleux API, grâce auquel nous pouvons obtenir ce dont nous avons besoin :

Accès aux sites bloqués

Pour ce faire, nous avons besoin de petits VPS étrangers, de préférence avec un trafic illimité - il y en a beaucoup pour 3 à 5 dollars. Vous devez le prendre dans l'étranger proche pour que le ping ne soit pas très important, mais encore une fois, tenez compte du fait qu'Internet et la géographie ne coïncident pas toujours. Et comme il n'y a pas de SLA pour 5 dollars, il est préférable de prendre plus de 2 pièces de différents fournisseurs pour la tolérance aux pannes.

Ensuite, nous devons configurer un tunnel crypté du routeur client au VPS. J'utilise Wireguard comme le plus rapide et le plus facile à configurer. J'ai aussi des routeurs clients basés sur Linux (APU2 ou quelque chose dans OpenWRT). Dans le cas de certains Mikrotik / Cisco, vous pouvez utiliser les protocoles disponibles sur eux comme OpenVPN et GRE-over-IPSEC.

Identification et redirection du trafic d'intérêt

Vous pouvez, bien sûr, désactiver tout trafic Internet passant par des pays étrangers. Mais, très probablement, la vitesse de travail avec le contenu local en souffrira grandement. De plus, les besoins en bande passante sur VPS seront beaucoup plus élevés.

Par conséquent, nous devrons d'une manière ou d'une autre allouer le trafic aux sites bloqués et le diriger de manière sélective vers le tunnel. Même si une partie du trafic "supplémentaire" y arrive, c'est toujours bien mieux que de tout conduire à travers le tunnel.

Pour gérer le trafic, nous utiliserons le protocole BGP et annoncerons les routes vers les réseaux nécessaires de notre VPS aux clients. Considérons BIRD comme l'un des démons BGP les plus fonctionnels et les plus pratiques.

IP

Avec le blocage par IP, tout est clair : on annonce simplement toutes les IP bloquées avec VPS. Le problème est qu'il y a environ 600 32 sous-réseaux dans la liste renvoyée par l'API, et la grande majorité d'entre eux sont des hôtes /XNUMX. Ce nombre de routes peut perturber les routeurs clients faibles.

Par conséquent, lors du traitement de la liste, il a été décidé de résumer jusqu'au réseau / 24 s'il comporte 2 hôtes ou plus. Ainsi, le nombre d'itinéraires a été réduit à environ 100 XNUMX. Le script pour cela suivra.

domaines

C'est plus compliqué et il y a plusieurs façons. Par exemple, vous pouvez installer un Squid transparent sur chaque routeur client et y effectuer une interception HTTP et jeter un coup d'œil dans la poignée de main TLS afin d'obtenir l'URL demandée dans le premier cas et le domaine de SNI dans le second.

Mais en raison de toutes sortes de TLS1.3 + eSNI dernier cri, l'analyse HTTPS devient de moins en moins réelle chaque jour. Oui, et l'infrastructure côté client devient de plus en plus compliquée - vous devrez utiliser au moins OpenWRT.

Par conséquent, j'ai décidé de prendre la voie de l'interception des réponses aux requêtes DNS. Ici aussi, tout DNS sur TLS / HTTPS commence à planer au-dessus de votre tête, mais nous pouvons (pour l'instant) contrôler cette partie sur le client - soit la désactiver, soit utiliser votre propre serveur pour DoT / DoH.

Comment intercepter le DNS ?

Là aussi, il peut y avoir plusieurs approches.

  • Interception du trafic DNS via PCAP ou NFLOG
    Ces deux méthodes d'interception sont implémentées dans l'utilitaire sidmat. Mais il n'a pas été pris en charge depuis longtemps et la fonctionnalité est très primitive, vous devez donc toujours écrire un harnais pour cela.
  • Analyse des logs du serveur DNS
    Malheureusement, les récurseurs que je connais ne sont pas capables d'enregistrer les réponses, mais uniquement les requêtes. En principe, cela est logique, car contrairement aux demandes, les réponses ont une structure complexe et il est difficile de les rédiger sous forme de texte.
  • DNSTap
    Heureusement, beaucoup d'entre eux prennent déjà en charge DNSTap à cette fin.

Qu'est-ce que DNSTap ?

Contourner le blocage ILV avec DNSTap et BGP

Il s'agit d'un protocole client-serveur basé sur Protocol Buffers et Frame Streams pour le transfert d'un serveur DNS vers un collecteur de requêtes et de réponses DNS structurées. Essentiellement, le serveur DNS transmet les métadonnées de requête et de réponse (type de message, IP client/serveur, etc.) ainsi que les messages DNS complets sous la forme (binaire) dans laquelle il les utilise sur le réseau.

Il est important de comprendre que dans le paradigme DNSTap, le serveur DNS agit en tant que client et le collecteur agit en tant que serveur. Autrement dit, le serveur DNS se connecte au collecteur, et non l'inverse.

Aujourd'hui, DNSTap est pris en charge par tous les serveurs DNS populaires. Mais, par exemple, BIND dans de nombreuses distributions (comme Ubuntu LTS) est souvent construit pour une raison quelconque sans son support. Ne nous embêtons donc pas avec le réassemblage, mais prenons un récurseur plus léger et plus rapide - Unbound.

Comment attraper DNSTap ?

Il est certains nombre Utilitaires CLI pour travailler avec un flux d'événements DNSTap, mais ils ne conviennent pas pour résoudre notre problème. Par conséquent, j'ai décidé d'inventer mon propre vélo qui fera tout ce qui est nécessaire : dnstap-bgp

Algorithme de travail :

  • Lorsqu'il est lancé, il charge une liste de domaines à partir d'un fichier texte, les inverse (habr.com -> com.habr), exclut les lignes brisées, les doublons et les sous-domaines (c'est-à-dire si la liste contient habr.com et www.habr.com, il ne sera chargé que le premier) et construit une arborescence de préfixes pour une recherche rapide dans cette liste
  • Agissant comme un serveur DNSTap, il attend une connexion d'un serveur DNS. En principe, il prend en charge les sockets UNIX et TCP, mais les serveurs DNS que je connais ne peuvent utiliser que les sockets UNIX
  • Les paquets DNSTap entrants sont d'abord désérialisés dans une structure Protobuf, puis le message DNS binaire lui-même, situé dans l'un des champs Protobuf, est analysé au niveau des enregistrements DNS RR
  • Il est vérifié si l'hôte demandé (ou son domaine parent) est dans la liste chargée, sinon, la réponse est ignorée
  • Seuls les RR A/AAAA/CNAME sont sélectionnés à partir de la réponse et les adresses IPv4/IPv6 correspondantes en sont extraites
  • Les adresses IP sont mises en cache avec TTL configurable et annoncées à tous les pairs BGP configurés
  • Lors de la réception d'une réponse pointant vers une adresse IP déjà mise en cache, son TTL est mis à jour
  • Après l'expiration de la durée de vie, l'entrée est supprimée du cache et des annonces BGP

Fonctionnalité supplémentaire :

  • Relire la liste des domaines par SIGHUP
  • Synchronisation du cache avec les autres instances dnstap-bgp par HTTP/JSON
  • Dupliquer le cache sur disque (dans la base de données BoltDB) pour restaurer son contenu après un redémarrage
  • Prise en charge du basculement vers un espace de noms de réseau différent (la raison pour laquelle cela est nécessaire sera décrite ci-dessous)
  • Prise en charge IPv6

Restrictions:

  • Les domaines IDN ne sont pas encore pris en charge
  • Peu de paramètres BGP

j'ai récupéré RPM et DEB forfaits pour une installation facile. Devrait fonctionner sur tous les systèmes d'exploitation relativement récents avec systemd. ils n'ont aucune dépendance.

Conduire

Alors, commençons à assembler tous les composants ensemble. En conséquence, nous devrions obtenir quelque chose comme cette topologie de réseau :
Contourner le blocage ILV avec DNSTap et BGP

La logique du travail, je pense, ressort clairement du schéma:

  • Le client a notre serveur configuré en tant que DNS, et les requêtes DNS doivent également passer par le VPN. Cela est nécessaire pour que le fournisseur ne puisse pas utiliser l'interception DNS pour bloquer.
  • Lors de l'ouverture du site, le client envoie une requête DNS du type « quelles sont les IP de xxx.org »
  • Non lié résout xxx.org (ou le prend dans le cache) et envoie une réponse au client « xxx.org a telle ou telle IP », en la dupliquant en parallèle via DNSTap
  • dnstap-bgp annonce ces adresses dans OISEAU via BGP si le domaine est sur la liste bloquée
  • OISEAU annonce une route vers ces IP avec next-hop self routeur client
  • Les paquets suivants du client vers ces IP passent par le tunnel

Sur le serveur, pour les routes vers les sites bloqués, j'utilise une table séparée à l'intérieur de BIRD et elle ne se croise en aucune façon avec le système d'exploitation.

Ce schéma présente un inconvénient: le premier paquet SYN du client aura très probablement le temps de passer par le fournisseur national. l'itinéraire n'est pas annoncé immédiatement. Et ici, des options sont possibles en fonction de la manière dont le fournisseur effectue le blocage. S'il supprime simplement le trafic, il n'y a pas de problème. Et s'il le redirige vers certains DPI, alors (théoriquement) des effets spéciaux sont possibles.

Il est également possible que les clients ne respectent pas les miracles DNS TTL, ce qui peut amener le client à utiliser certaines entrées obsolètes de son cache pourri au lieu de demander à Unbound.

En pratique, ni le premier ni le second ne m'ont causé de problèmes, mais votre kilométrage peut varier.

Réglage du serveur

Pour faciliter le roulement, j'ai écrit rôle pour Ansible. Il peut configurer à la fois les serveurs et les clients basés sur Linux (conçus pour les distributions basées sur deb). Tous les paramètres sont assez évidents et sont définis dans inventaire.yml. Ce rôle est coupé de mon grand playbook, il peut donc contenir des erreurs - demandes de tirage bienvenue 🙂

Passons en revue les principaux composants.

BGP

L'exécution de deux démons BGP sur le même hôte pose un problème fondamental : BIRD ne souhaite pas configurer le peering BGP avec l'hôte local (ou toute autre interface locale). Du mot du tout. Googler et lire les listes de diffusion n'a pas aidé, ils prétendent que c'est voulu. Il y a peut-être un moyen, mais je ne l'ai pas trouvé.

Vous pouvez essayer un autre démon BGP, mais j'aime BIRD et il est utilisé partout par moi, je ne veux pas produire d'entités.

Par conséquent, j'ai caché dnstap-bgp dans l'espace de noms du réseau, qui est connecté à la racine via l'interface veth : c'est comme un tuyau dont les extrémités dépassent dans différents espaces de noms. À chacune de ces extrémités, nous accrochons des adresses IP p2p privées qui ne vont pas au-delà de l'hôte, elles peuvent donc être n'importe quoi. C'est le même mécanisme utilisé pour accéder aux processus à l'intérieur aimé de tous Docker et autres conteneurs.

Car ceci a été écrit scénario et la fonctionnalité déjà décrite ci-dessus pour se traîner par les cheveux vers un autre espace de noms a été ajoutée à dnstap-bgp. Pour cette raison, il doit être exécuté en tant que root ou envoyé au binaire CAP_SYS_ADMIN via la commande setcap.

Exemple de script pour créer un espace 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",
]

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

Par défaut, dans Ubuntu, le binaire Unbound est bloqué par le profil AppArmor, qui lui interdit de se connecter à toutes sortes de sockets DNSTap. Vous pouvez soit supprimer ce profil, soit le désactiver :

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

Cela devrait probablement être ajouté au playbook. Il est idéal, bien sûr, de corriger le profil et de délivrer les droits nécessaires, mais j'étais trop paresseux.

non lié.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

Téléchargement et traitement des listes

Script de téléchargement et de traitement d'une liste d'adresses IP
Il télécharge la liste, résume jusqu'au préfixe pfx. la ne pas ajouter и ne pas_résumer vous pouvez dire aux adresses IP et aux réseaux de sauter ou de ne pas résumer. J'en avais besoin. le sous-réseau de mon VPS était dans la liste de blocage 🙂

Le plus drôle est que l'API RosKomSvoboda bloque les requêtes avec l'agent utilisateur Python par défaut. On dirait que le script-kiddy l'a compris. Par conséquent, nous le changeons en Ognelis.

Jusqu'à présent, cela ne fonctionne qu'avec IPv4. la part d'IPv6 est faible, mais elle sera facile à corriger. Sauf si vous devez également utiliser 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 à mettre à jour
Je le passe sur la couronne une fois par jour, ça vaut peut-être la peine de le tirer toutes les 4 heures. c'est, à mon avis, la période de renouvellement que le RKN exige des fournisseurs. De plus, ils ont d'autres blocages super urgents, qui peuvent arriver plus rapidement.

Fait ce qui suit :

  • Exécute le premier script et met à jour la liste des routes (rkn_routes.list) pour OISEAU
  • Recharger BIRD
  • Met à jour et nettoie la liste des domaines pour dnstap-bgp
  • Recharger 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

Ils ont été écrits sans trop de réflexion, donc si vous voyez quelque chose qui peut être amélioré, allez-y.

Configuration client

Ici, je vais donner des exemples pour les routeurs Linux, mais dans le cas de Mikrotik / Cisco, cela devrait être encore plus simple.

Tout d'abord, nous avons configuré BIRD :

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

Ainsi, nous allons synchroniser les routes reçues de BGP avec la table de routage du noyau numéro 222.

Après cela, il suffit de demander au noyau de regarder cette plaque avant de regarder celle par défaut :

# 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

Tout, il reste à configurer DHCP sur le routeur pour distribuer l'adresse IP du tunnel du serveur en tant que DNS, et le schéma est prêt.

Limites

Avec l'algorithme actuel de génération et de traitement de la liste des domaines, il comprend, entre autres, youtube.com et ses CDN.

Et cela conduit au fait que toutes les vidéos passeront par le VPN, ce qui peut obstruer l'ensemble du canal. Peut-être vaut-il la peine de compiler une liste des exclusions de domaines populaires qui bloquent le RKN pour le moment, les tripes sont minces. Et ignorez-les lors de l'analyse.

Conclusion

La méthode décrite vous permet de contourner presque tous les blocages que les fournisseurs implémentent actuellement.

En principe, dnstap-bgp peut être utilisé à toute autre fin où un certain niveau de contrôle du trafic est nécessaire en fonction du nom de domaine. Gardez simplement à l'esprit qu'à notre époque, un millier de sites peuvent se bloquer sur la même adresse IP (derrière certains Cloudflare, par exemple), donc cette méthode a une précision plutôt faible.

Mais pour les besoins de contournement des verrous, cela suffit amplement.

Ajouts, modifications, demandes d'extraction - bienvenue !

Source: habr.com

Ajouter un commentaire