El tema está bastante trillado, lo sé. Por ejemplo, hay un gran artículo, pero allí solo se considera la parte IP de la lista de bloqueo. También añadiremos dominios.
Debido al hecho de que los tribunales y la RKN bloquean todo a diestra y siniestra, y los proveedores se esfuerzan por no caer en las multas emitidas por Revizorro, las pérdidas asociadas al bloqueo son bastante grandes. Y entre los sitios bloqueados "legalmente" hay muchos útiles (hola, rutracker)
Vivo fuera de la jurisdicción de la RKN, pero mis padres, familiares y amigos se quedaron en casa. Por lo tanto, se decidió idear una manera fácil para que las personas que no están en TI eviten el bloqueo, preferiblemente sin su participación.
En esta nota, no describiré las cosas básicas de la red en pasos, pero describiré los principios generales de cómo se puede implementar este esquema. Por lo tanto, el conocimiento de cómo funciona la red en general y en Linux en particular es imprescindible.
tipos de cerraduras
Primero, refresquemos nuestra memoria de lo que está siendo bloqueado.
Hay varios tipos de bloqueos en el XML descargado del RKN:
IP
Nombre de dominio
Enlance
Para simplificar, los reduciremos a dos: IP y dominio, y simplemente sacaremos el dominio del bloqueo por URL (más precisamente, ya lo han hecho por nosotros).
buena gente de Roskomsvoboda se dio cuenta de una maravillosa API, a través del cual podemos obtener lo que necesitamos:
Para hacer esto, necesitamos algunos VPS extranjeros pequeños, preferiblemente con tráfico ilimitado; hay muchos de estos por 3-5 dólares. Debes llevarlo en el extranjero cercano para que el ping no sea muy grande, pero nuevamente, ten en cuenta que Internet y la geografía no siempre coinciden. Y dado que no hay SLA por 5 dólares, es mejor tomar más de 2 piezas de diferentes proveedores para la tolerancia a fallas.
A continuación, debemos configurar un túnel encriptado desde el enrutador del cliente hasta el VPS. Uso Wireguard como el más rápido y fácil de configurar. También tengo enrutadores de clientes basados en Linux (APU2 o algo en OpenWRT). En el caso de algunos Mikrotik/Cisco, puede usar los protocolos disponibles en ellos como OpenVPN y GRE-over-IPSEC.
Identificación y redirección de tráfico de interés
Por supuesto, puede desactivar todo el tráfico de Internet a través de países extranjeros. Pero, muy probablemente, la velocidad de trabajar con contenido local sufrirá mucho por esto. Además, los requisitos de ancho de banda en VPS serán mucho más altos.
Por lo tanto, tendremos que asignar de alguna manera el tráfico a los sitios bloqueados y dirigirlo selectivamente al túnel. Incluso si parte del tráfico "extra" llega allí, sigue siendo mucho mejor que conducir todo a través del túnel.
Para gestionar el tráfico, utilizaremos el protocolo BGP y anunciaremos las rutas a las redes necesarias desde nuestro VPS a los clientes. Tomemos a BIRD como uno de los demonios BGP más funcionales y convenientes.
IP
Con el bloqueo por IP, todo está claro: simplemente anunciamos todas las IP bloqueadas con VPS. El problema es que hay unas 600 mil subredes en la lista que devuelve la API, y la gran mayoría son hosts /32. Esta cantidad de rutas puede confundir a los enrutadores de clientes débiles.
Por lo tanto, al procesar la lista, se decidió resumir hasta la red /24 si tiene 2 o más hosts. Así, el número de rutas se redujo a ~100 mil. El guión para esto seguirá.
dominios
Es más complicado y hay varias formas. Por ejemplo, puede instalar un Squid transparente en cada enrutador de cliente y hacer una intercepción HTTP allí y mirar el protocolo de enlace TLS para obtener la URL solicitada en el primer caso y el dominio de SNI en el segundo.
Pero debido a todo tipo de TLS1.3 + eSNI novedosos, el análisis de HTTPS se está volviendo cada vez menos real cada día. Sí, y la infraestructura del lado del cliente se está volviendo más complicada: deberá usar al menos OpenWRT.
Por lo tanto, decidí tomar el camino de interceptar respuestas a consultas de DNS. Aquí, también, cualquier DNS sobre TLS/HTTPS comienza a flotar sobre su cabeza, pero podemos (por ahora) controlar esta parte en el cliente, ya sea desactivándola o usando su propio servidor para DoT/DoH.
¿Cómo interceptar DNS?
Aquí, también, puede haber varios enfoques.
Intercepción de tráfico DNS a través de PCAP o NFLOG
Ambos métodos de interceptación se implementan en la utilidad Sidmat. Pero no ha sido compatible durante mucho tiempo y la funcionalidad es muy primitiva, por lo que aún necesita escribir un arnés para ello.
Análisis de registros del servidor DNS
Desafortunadamente, los recursors que conozco no pueden registrar respuestas, sino solo solicitudes. En principio, esto es lógico, ya que, a diferencia de las solicitudes, las respuestas tienen una estructura compleja y es difícil escribirlas en forma de texto.
DNSTap
Afortunadamente, muchos de ellos ya admiten DNSTap para este propósito.
¿Qué es DNSTap?
Es un protocolo cliente-servidor basado en Protocol Buffers y Frame Streams para transferir desde un servidor DNS a un colector de consultas y respuestas DNS estructuradas. Esencialmente, el servidor DNS transmite metadatos de consultas y respuestas (tipo de mensaje, IP de cliente/servidor, etc.) además de mensajes DNS completos en el formato (binario) en el que trabaja con ellos a través de la red.
Es importante comprender que en el paradigma DNSTap, el servidor DNS actúa como cliente y el recopilador actúa como servidor. Es decir, el servidor DNS se conecta al recopilador y no al revés.
Hoy, DNSTap es compatible con todos los servidores DNS populares. Pero, por ejemplo, BIND en muchas distribuciones (como Ubuntu LTS) a menudo se construye por alguna razón sin su soporte. Así que no nos molestemos en volver a ensamblar, pero tomemos un recursor más ligero y rápido: Unbound.
¿Cómo capturar DNSTap?
Hay algunosnúmero Las utilidades CLI para trabajar con una secuencia de eventos DNSTap, pero no son adecuadas para resolver nuestro problema. Por lo tanto, decidí inventar mi propia bicicleta que hará todo lo necesario: dnstap-bgp
Algoritmo de trabajo:
Cuando se inicia, carga una lista de dominios desde un archivo de texto, los invierte (habr.com -> com.habr), excluye líneas discontinuas, duplicados y subdominios (es decir, si la lista contiene habr.com y www.habr.com, se cargará solo el primero) y construye un árbol de prefijos para una búsqueda rápida a través de esta lista
Actuando como un servidor DNSTap, espera una conexión desde un servidor DNS. En principio, admite sockets UNIX y TCP, pero los servidores DNS que conozco solo pueden usar sockets UNIX
Los paquetes DNSTap entrantes primero se deserializan en una estructura Protobuf, y luego el mensaje DNS binario en sí, ubicado en uno de los campos Protobuf, se analiza al nivel de los registros DNS RR.
Se comprueba si el host solicitado (o su dominio principal) está en la lista cargada, de lo contrario, se ignora la respuesta
Solo se seleccionan los RR A/AAAA/CNAME de la respuesta y se extraen de ellos las direcciones IPv4/IPv6 correspondientes
Las direcciones IP se almacenan en caché con TTL configurable y se anuncian a todos los pares BGP configurados
Al recibir una respuesta que apunta a una IP ya almacenada en caché, su TTL se actualiza
Una vez que caduca el TTL, la entrada se elimina de la memoria caché y de los anuncios de BGP.
Funcionalidad adicional:
Releyendo la lista de dominios por SIGHUP
Mantener el caché sincronizado con otras instancias dnstap-bgp a través de HTTP/JSON
Duplique el caché en el disco (en la base de datos de BoltDB) para restaurar su contenido después de reiniciar
Soporte para cambiar a un espacio de nombres de red diferente (a continuación se describe por qué es necesario)
Compatibilidad con IPv6
Limitaciones:
Los dominios IDN aún no son compatibles
Pocas configuraciones de BGP
yo coleccióno RPM y DEB paquetes para una fácil instalación. Debería funcionar en todos los sistemas operativos relativamente recientes con systemd. no tienen ninguna dependencia.
esquema
Entonces, comencemos a ensamblar todos los componentes juntos. Como resultado, deberíamos obtener algo como esta topología de red:
La lógica del trabajo, creo, queda clara en el diagrama:
El cliente tiene nuestro servidor configurado como DNS, y las consultas de DNS también deben pasar por la VPN. Esto es necesario para que el proveedor no pueda usar la interceptación de DNS para bloquear.
Al abrir el sitio, el cliente envía una consulta de DNS como "¿cuáles son las IP de xxx.org?"
Sin consolidar resuelve xxx.org (o lo toma de la caché) y envía una respuesta al cliente "xxx.org tiene tal o cual IP", duplicándolo en paralelo a través de DNSTap
dnstap-bgp anuncia estas direcciones en PÁJARO a través de BGP si el dominio está en la lista de bloqueados
PÁJARO anuncia una ruta a estas direcciones IP con next-hop self enrutador cliente
Los paquetes posteriores del cliente a estas direcciones IP pasan por el túnel
En el servidor, para las rutas a los sitios bloqueados, uso una tabla separada dentro de BIRD y no se cruza con el sistema operativo de ninguna manera.
Este esquema tiene un inconveniente: lo más probable es que el primer paquete SYN del cliente tenga tiempo de salir a través del proveedor nacional. la ruta no se anuncia inmediatamente. Y aquí las opciones son posibles dependiendo de cómo el proveedor realiza el bloqueo. Si solo deja caer el tráfico, entonces no hay problema. Y si lo redirige a algún DPI, entonces (teóricamente) los efectos especiales son posibles.
También es posible que los clientes no respeten los milagros de TTL de DNS, lo que puede hacer que el cliente use algunas entradas obsoletas de su caché podrida en lugar de solicitar Unbound.
En la práctica, ni el primero ni el segundo me causaron problemas, pero su kilometraje puede variar.
Ajuste del servidor
Para facilitar el enrollado, escribí papel para Ansible. Puede configurar tanto servidores como clientes basados en Linux (diseñado para distribuciones basadas en deb). Todos los ajustes son bastante obvios y se establecen en inventario.yml. Este rol se eliminó de mi gran libro de jugadas, por lo que puede contener errores: solicitudes de extracción bienvenido 🙂
Repasemos los componentes principales.
BGP
Ejecutar dos demonios BGP en el mismo host tiene un problema fundamental: BIRD no quiere configurar el emparejamiento BGP con el host local (o cualquier interfaz local). De la palabra en absoluto. Buscar en Google y leer listas de correo no ayudó, afirman que esto es así por diseño. Quizás haya alguna forma, pero no la encontré.
Puede probar con otro demonio BGP, pero me gusta BIRD y lo uso en todas partes, no quiero producir entidades.
Por lo tanto, escondí dnstap-bgp dentro del espacio de nombres de la red, que está conectado a la raíz a través de la interfaz veth: es como una tubería, cuyos extremos sobresalen en diferentes espacios de nombres. En cada uno de estos extremos, colgamos direcciones IP p2p privadas que no van más allá del host, por lo que pueden ser cualquier cosa. Este es el mismo mecanismo utilizado para acceder a los procesos dentro amado por todos Docker y otros contenedores.
Para esto fue escrito guión y se agregó a dnstap-bgp la funcionalidad ya descrita anteriormente para arrastrarse del cabello a otro espacio de nombres. Debido a esto, debe ejecutarse como raíz o enviarse al binario CAP_SYS_ADMIN a través del comando setcap.
Script de ejemplo para crear un espacio de nombres
#!/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
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.lista
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
De forma predeterminada, en Ubuntu, el binario Unbound está sujeto al perfil AppArmor, que le prohíbe conectarse a todo tipo de sockets DNSTap. Puede eliminar este perfil o desactivarlo:
Esto probablemente debería agregarse al libro de jugadas. Es ideal, por supuesto, corregir el perfil y emitir los derechos necesarios, pero me dio mucha flojera.
desatado.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
Descarga y procesamiento de listas
Script para descargar y procesar una lista de direcciones IP
Descarga la lista, suma el prefijo pfj. En no_añadir и no_resumir puede decirle a las IP y redes que se salten o que no se resuman. Lo necesitaba. la subred de mi VPS estaba en la lista de bloqueo 🙂
Lo curioso es que la API de RosKomSvoboda bloquea las solicitudes con el agente de usuario predeterminado de Python. Parece que el script-kiddy lo entendió. Por lo tanto, lo cambiamos a Ognelis.
Hasta ahora, solo funciona con IPv4. la proporción de IPv6 es pequeña, pero será fácil de arreglar. A menos que tengas que usar bird6 también.
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)
Guión para actualizar
Lo ejecuto en la corona una vez al día, tal vez valga la pena tirar de él cada 4 horas. este, en mi opinión, es el período de renovación que la RKN exige a los proveedores. Además, tienen algún otro bloqueo súper urgente, que puede llegar más rápido.
Hace lo siguiente:
Ejecuta el primer script y actualiza la lista de rutas (rkn_routes.list) para PÁJARO
Recargar PÁJARO
Actualiza y limpia la lista de dominios para dnstap-bgp
Recargar 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
Fueron escritos sin pensarlo mucho, así que si ves algo que se puede mejorar, hazlo.
Configuración del cliente
Aquí daré ejemplos para enrutadores Linux, pero en el caso de Mikrotik/Cisco debería ser aún más fácil.
Primero, configuramos BIRD:
pájaro.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;
}
Así, sincronizaremos las rutas recibidas de BGP con la tabla de enrutamiento del kernel número 222.
Después de eso, basta con pedirle al kernel que mire esta placa antes 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
Todo, queda configurar DHCP en el enrutador para distribuir la dirección IP del túnel del servidor como DNS, y el esquema está listo.
Limitaciones
Con el algoritmo actual para generar y procesar la lista de dominios, incluye, entre otras cosas, youtube.com y sus CDN.
Y esto lleva al hecho de que todos los videos pasarán por la VPN, lo que puede obstruir todo el canal. Tal vez valga la pena compilar una lista de exclusiones de dominios populares que bloquean el RKN por el momento, las agallas son delgadas. Y sáltelos al analizar.
Conclusión
El método descrito le permite eludir casi cualquier bloqueo que los proveedores implementen actualmente.
En principio, los dnstap-bgp se puede utilizar para cualquier otro propósito en el que se necesite cierto nivel de control de tráfico en función del nombre de dominio. Solo tenga en cuenta que en nuestro tiempo, miles de sitios pueden colgarse de la misma dirección IP (detrás de algunos Cloudflare, por ejemplo), por lo que este método tiene una precisión bastante baja.
Pero para las necesidades de eludir bloqueos, esto es suficiente.
Adiciones, ediciones, solicitudes de incorporación de cambios: ¡bienvenidos!