今天在用apt安装一个软件的时候,出现了奇怪的错误:
Get:1 http://202.204.48.68/files/50420000001D8F93/cdn.packages.deepin.com/deepin xenial/main amd64 libgnomecanvas2-0 amd64 2.30.3-2 [82.7 kB] Err:1 http://202.204.48.68/files/50420000001D8F93/cdn.packages.deepin.com/deepin xenial/main amd64 libgnomecanvas2-0 amd64 2.30.3-2 Writing more data than expected (82993 > 82720) E: Failed to fetch http://202.204.48.68/files/50420000001D8F93/cdn.packages.deepin.com/deepin/pool/main/libg/libgnomecanvas/libgnomecanvas2-0_2.30.3-2_amd64.deb Writing more data than expected (82993 > 82720) E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?
后来在网上查找资料,联系之前校园网的通知,想到了校园网采用了文件缓存。。。
中国的网络是奇葩的,原因之一是有奇葩的屌丝运营商。
许多小运营商(二级运营商、三级运营商、N级运营商)为了节省成本,会使用缓存系统。 这个缓存系统可以认为是一种CDN,如果做得好的话,不仅会节省成本,也会提高用户体验, 例如许多小区里大家看优酷视频从来不会缓冲,下载速度都有3MB/s以上的速度。 然而,这些缓存系统并不是CDN,而是一种非常没有节操的黑盒子。
- 你不知道什么请求会被缓存,没法控制。
- 你不知道会被缓存多久。
- 这种缓存不遵守任何行业内的规则(例如不遵守Cache-Control头)。
- 发现缓存了错误的内容,你没有地方投诉。
其劫持的手段也是非常的无节操的,主要受益于郭嘉的某墙开发的成果。其劫持原理大致如下:
- 客户端C向服务器S发出一个HTTP请求;
- 运营商网关将该请求分光(复制)送到缓存服务器;
- 缓存服务器如果发现命中缓存,则伪装成S返回一个302响应,该响应通常比S的正确响应早到,因此C接受 了该响应,而忽略了S的响应,从而跳转到缓存服务器取数据;
是的,这正是伟大的墙发送RST的方法。
After this operation, 508 kB of additional disk space will be used. Do you want to continue? [Y/n] y Get:1 http://202.204.48.68/files/50420000001D8F93/cdn.packages.deepin.com/deepin xenial/main amd64 libgnomecanvas2-0 amd64 2.30.3-2 [82.7 kB] Err:1 http://202.204.48.68/files/50420000001D8F93/cdn.packages.deepin.com/deepin xenial/main amd64 libgnomecanvas2-0 amd64 2.30.3-2 Writing more data than expected (82993 > 82720) E: Failed to fetch http://202.204.48.68/files/50420000001D8F93/cdn.packages.deepin.com/deepin/pool/main/libg/libgnomecanvas/libgnomecanvas2-0_2.30.3-2_amd64.deb Writing more data than expected (82993 > 82720) E: Unable to fetch some archives, maybe run apt-get update or try with --fix-missing?
显然我用apt更新的时候,某一个依赖文件被劫持到了202.204.48.68….
解决办法:
通常缓存服务器的IP是有限的,因此我们可以这样绕过运营商的劫持,在或自己机器上添加一条iptables规则:
sudo iptables -A INPUT -p tcp --sport 80 -m string --string "Location: http://202.204.48.68" --algo bm -j DROP
如果是网关,则将INPUT替换成OUTPUT
该规则的意义是,如果某个HTTP响应包(这里并没有真正判断是否HTTP,仅分析来自80端口的包)中含有 Location: http://202.204.48.68 字样,就直接丢弃。这样后续S真实的响应包就能被客户端接收,从而保证正确的通信。
这里我们要感谢该运营商没有像某墙那样的没节操,某墙在向C发送RST的同时,也向S发送了RST,而该缓存服务器并没有 向S发送RST,彻底破坏tcp通信。