
我知道,这个主题很漂亮。 例如,有一篇很棒的文章 ,但其中仅考虑了阻止列表的IP部分。 我们还将添加域。
由于法院和ILV封锁了左右两侧的所有事实,提供者正在竭力不落入“ Revizorro”发出的罚款之列-锁造成的相关损失相当大。 是的,在“合法”阻止的站点中,有很多有用的站点(您好,rutracker)
我住在ILV管辖范围之外,但我的父母,亲戚和朋友留在家里。 因此,决定想出一种绕开锁的方法,这种方法对于远离IT部门的人员来说很容易,最好是无需他们的参与。
在本文中,我不会逐步描述基本的网络事物,而是描述如何实现此方案的一般原理。 因此,必须具备有关网络一般(特别是Linux)工作方式的知识。
锁的类型
首先,让我们刷新被阻止的内容。
ILV的分页XML中有几种类型的锁:
为简单起见,我们将它们减少为两个:IP和域,并且我们将域简单地从URL阻止中拉出(更准确地说,我们已经为我们完成了此操作)。
来自Roskomsvoboda的好人已经实现了出色的API,通过它我们可以获取所需的信息:
访问被阻止的网站
为此,我们需要一些小型的外国VPS,最好是无限制的流量-有很多这样的3-5美元。 您需要在国外使用它,以便ping不会很大,但再次考虑到Internet和地理位置并不总是一致。 而且,由于没有5美元的SLA,因此最好从不同的提供商处购买2件以上,以获得容错能力。
接下来,我们需要配置从客户端路由器到VPS的加密隧道。 我将Wireguard用作最快,最容易配置的工具,因为 我也有基于Linux的客户端路由器( APU2或OpenWRT上的某些产品)。 对于某些Mikrotik / Cisco,您可以使用在它们上可用的协议,例如OpenVPN和GRE-over-IPSEC。
识别和重定向感兴趣的流量
当然,您可以完全打包出国的所有Internet流量。 但是,最有可能的是,处理本地内容的速度将因此而受到极大的影响。 另外,VPS上的带宽要求会更高。
因此,我们将需要以某种方式将流量分配给被阻止的站点,然后有选择地将其发送到隧道。 即使“额外”流量的一部分到达了那里,它仍然比通过隧道运输所有东西要好得多。
为了管理流量,我们将使用BGP协议并宣布从VPS到客户端的必要网络的路由。 作为BGP守护程序,让我们将BIRD视为最实用,最方便的工具之一。
知识产权
使用IP阻止,一切都变得很清楚:我们只需使用VPS宣布所有被阻止的IP。 问题在于,API提供的列表中大约有60万个子网,其中绝大多数是/ 32主机。 如此多的路由可能会使弱小的客户端路由器感到困惑。
因此,在处理列表时,如果其中有2个或更多主机,则决定汇总到网络/ 24。 因此,路线数量减少到约10万。 接下来的脚本。
域
它比较复杂,有几种方法。 例如,您可以在每个客户端路由器上放置透明的Squid,并在TLS握手中进行HTTP侦听和窥视,以便在第一种情况下获取请求的URL,在第二种情况下从SNI获取域。
但是由于所有新的TLS1.3 + eSNI,HTTPS分析每天都变得越来越不真实。 而且客户端的基础架构变得越来越复杂-您将至少必须使用OpenWRT。
因此,我决定采用拦截DNS查询响应的方法。 在这里,任何基于TLS / HTTPS的DNS都开始突飞猛进,但是我们可以(现在)在客户端上控制此部分-禁用它或将我们自己的服务器用于DoT / DoH。
如何拦截DNS?
可能还有几种方法。
- 通过PCAP或NFLOG拦截DNS流量
这两种拦截方法均在sidmat实用程序中实现。 但是很长一段时间以来一直不支持它,并且该功能非常原始,因此无论如何您都需要为其编写一个绑定。 - DNS服务器日志分析
不幸的是,我知道的递归不知道如何记录答案,而只是请求。 原则上,这是合乎逻辑的,因为与请求不同,答案具有复杂的结构,并且很难以文本形式编写。 - 域名解析
幸运的是,出于这些目的,其中许多已经支持DNSTap。
什么是DNSTap?

这是基于协议缓冲区和帧流的客户端-服务器协议,用于从DNS服务器传输到结构化DNS查询和响应的收集器。 本质上,DNS服务器传输请求和响应的元数据(消息类型,客户端/服务器IP等)以及完整的DNS消息(二进制形式),使其通过网络与它们一起工作。
重要的是要理解,在DNSTap范例中,DNS服务器充当客户端,而收集器充当服务器。 即,DNS服务器连接到收集器,反之亦然。
今天,所有流行的DNS服务器都支持DNSTap。 但是,例如,由于某些原因,许多发行版(例如Ubuntu LTS)中的BIND经常由于没有支持而被构建。 因此,我们不会费心重建,而是采用更轻,更快的递归-Unbound。
如何捕获DNSTap?
有许多 CLI实用程序可用于处理DNSTap事件流,但它们不能很好地完成我们的任务。 因此,我决定发明自己的自行车来完成所有工作: dnstap-bgp
操作算法:
- 启动后,它将从文本文件中加载域列表,将其反转(habr.com-> com.habr),排除虚线,重复项和子域(即,如果habr.com和www.habr.com在列表中-将会加载仅第一个),并在此列表上构建前缀树以便快速搜索
- 充当DNSTap服务器,它等待来自DNS服务器的连接。 原则上,它支持UNIX和TCP套接字,但是我知道的DNS服务器只能使用UNIX套接字。
- 首先将传入的DNSTap数据包反序列化为Protobuf结构,然后将位于Protobuf字段之一中的二进制DNS消息本身解析为DNS RR记录的级别
- 检查请求的主机(或其父域)是否在加载的列表中;如果不是,则忽略响应
- 从响应中仅选择A / AAAA / CNAME RR,并从中拉出相应的IPv4 / IPv6地址
- IP地址使用自定义TTL缓存,并发布给所有已配置的BGP对等体
- 收到表示已缓存IP的响应后-更新其TTL
- TTL过期后,将从缓存和BGP公告中删除该记录
附加功能:
- 通过SIGHUP重新读取域列表
- 通过HTTP / JSON将缓存与其他dnstap-bgp实例同步
- 复制磁盘上的缓存(在BoltDB数据库中)以在重新启动后恢复其内容
- 支持切换到其他网络名称空间(为什么将在下面进行描述)
- IPv6支持
局限性:
我已经编译了RPM和DEB软件包以便于安装。 应该可以在所有使用systemd的相对较新的操作系统上使用,例如 他们没有依赖性。
方案
因此,让我们开始将所有组件组装在一起。 结果,我们应该得到如下网络拓扑:

我认为,从图中可以清楚地看出工作的逻辑:
- 客户端将我们的服务器配置为DNS,并且DNS查询也必须通过VPN。 这是必需的,以便提供程序不能使用DNS拦截进行阻止。
- 打开站点后,客户端将以“ xxx.org具有哪些IP?”的形式发送DNS查询。
- Unbound解析xxx.org(或从缓存中获取它)并将响应发送到客户端“ xxx.org具有此类IP”,并通过DNSTap并行复制它
- 如果域在阻止列表中,则dnstap-bgp通过BGP在BIRD中宣布这些地址
- BIRD通过
next-hop self
宣布到这些IP的路由next-hop self
客户端路由器 - 从客户端到这些IP的后续数据包通过隧道
在服务器上,对于通往被阻止站点的路由,我在BIRD内有一个单独的表,它与操作系统不相交。
此方案有一个缺点:来自客户端的第一个SYN数据包很可能有时间通过国内提供商离开 该路线不会立即宣布。 根据提供者如何进行锁定,这里的选项是可能的。 如果他只是丢掉流量,那就没有问题。 而且,如果他将其重定向到某些DPI,则(理论上)可能会产生特殊效果。
如果客户端不遵守DNS TTL,也可能会产生奇迹,这可能导致客户端使用其腐烂缓存中一些过时的条目,而不是询问“未绑定”。
实际上,第一个和第二个都不会引起我问题,但是您的行驶里程可能会有所不同。
服务器设置
为了便于滚动,我为Ansible编写了一个角色 。 它可以配置服务器和基于Linux的客户端(专为基于deb的发行版而设计)。 所有设置都非常明显,并在stock.yml中设置。 这个角色是从我的大剧本中删除的,因此它可能包含错误-欢迎拉取请求 :)
让我们看一下主要组件。
BGP协议
在同一主机上启动两个BGP守护程序时,出现一个基本问题:BIRD不想与本地主机(或任何本地接口)进行BGP对等。 从所有的词。 谷歌搜索和阅读邮件列表没有帮助,他们声称这是设计使然。 也许有某种方法,但是我没有找到。
您可以尝试其他BGP守护程序,但是我喜欢BIRD,并且它随处可见,我不想生成实体。
因此,我将dnstap-bgp隐藏在网络名称空间中,该名称空间通过veth接口连接到根:这就像一条管道,其末端伸到不同的名称空间中。 在这些目的的每一个上,我们都挂起了不在主机外部的专用p2p IP地址,因此它们可以是任意的。 这是用于访问心爱的 Docker和其他容器中的进程的相同机制。
为此,编写了一个脚本 ,并在dnstap-bgp中添加了上面描述的将头发拖到另一个命名空间中的功能。 因此,它必须以root身份运行或通过setcap命令返回到CAP_SYS_ADMIN二进制文件。
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", ]
bird.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"; ...
域名解析
默认情况下,在Ubuntu中,UnArbined二进制文件由AppArmor配置文件钳位,这可防止它连接到那里的任何DNSTap套接字。 您可以删除此配置文件或将其禁用:
# cd /etc/apparmor.d/disable && ln -s ../usr.sbin.unbound . # apparmor_parser -R /etc/apparmor.d/usr.sbin.unbound
这可能应该添加到剧本中。 当然,更正个人资料并颁发必要的权利是理想的选择,但是我实在太懒了。
unbound.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
下载和列表处理
用于下载和处理IP地址列表的脚本
它下载列表,在pfx前缀之前汇总。 在dont_add和dont_summarize中,您可以告诉IP和网络跳过或不进行总结。 我需要它,因为 我的VPS子网位于阻止列表中:)
有趣的是,RosKomSvoboda API会使用默认的Python用户代理阻止请求。 看起来像一个脚本小子得到了它。 因此,我们将其更改为Firelis。
到目前为止,它仅适用于IPv4,因为 IPv6很小,但是很容易修复。 除非有必要也使用bird6。
脚本更新
每天从我的皇冠上开始一次,也许值得每4小时拉一次,因为 我认为,这是ILV要求供应商提供的更新期限。 另外,还有一些其他的紧急锁可以使它们更快地飞行。
请执行以下操作:
- 运行第一个脚本并更新BIRD的路由列表(
rkn_routes.list
) - 重新加载BIRD
- 更新并清理dnstap-bgp的域列表
- 重新定位dnstap-bgp
他们写的时候没多想,所以如果您看到可以改进的地方,那就去做吧。
客户端设置
在这里,我将给出Linux路由器的示例,但是对于Mikrotik / Cisco,这应该更加简单。
首先,配置BIRD:
bird.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; }
因此,我们会将从BGP收到的路由与内核路由表编号222同步。
之后,在查看默认值之前,让内核先查看一下此板就足够了:
# 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
剩下的就是在路由器上配置DHCP以将服务器的隧道IP地址分配为DNS,并且该方案已准备就绪。
缺点
使用当前用于创建和处理域列表的算法, youtube.com
及其CDN属于其中。
这导致所有视频都将通过VPN,从而阻塞整个频道。 也许值得列出暂时被ILV阻止的流行例外域列表。 并在解析时跳过它们。
结论
所描述的方法使您可以绕过提供程序当前实现的几乎所有锁。
原则上, dnstap-bgp可用于任何其他需要基于域名的流量控制级别的目的。 只需记住,如今一千个站点可以挂在同一个IP地址上(例如,对于某些Cloudflare),因此此方法的准确性较低。
但是对于绕过锁的需求,这已经足够了。
欢迎添加,编辑,拉取任务!