『转载』使用Qv2ray+cgproxy配置透明代理(仅限Linux)

从Qv2ray电报群里转来的,透明代理老有人问,感觉这个方案比tproxy等方案好,转出来。版权归Qv2ray及cgproxy所有

用的是这个项目:https://github.com/springzfx/cgproxy

透明代理设置

  1. 在“首选项-入站设置”的下方启用透明代理选项。

    • 监听ipv4地址可填127.0.0.1或0.0.0.0,建议前者。若需双栈代理,则在监听ipv6地址填上::1(如果监听ipv4填了0.0.0.0则可不填)。
    • 在“网络模式”中勾选需要透明代理的协议。模式选择“tproxy”。
    • 如果希望在透明代理环境里让v2ray的内置dns接管本地dns,则勾选“dns拦截”。注意,在透明代理环境下,如果系统dns或v2ray的内置dns配置不当,可能导致系统无法解析域名从而无法正常上网。详见后文说明。

    如果是复杂配置,则需要手动添加相应的dokodemo-door入站。由于目前版本复杂配置并没有提供tproxy选项,因此tproxy模式需要通过编辑json来实现。

  2. 安装cgproxy软件

    • cgproxy软件已在archlinux, fedora 32, ubuntu 18.04, ubuntu 20.04, deepin 15.11, deepin v20 beta发行版中测试过。
    • Archlinux用户可直接在AUR上安装,deb或rpm系发行版用户可从github上下载安装包。非以上发行版用户,可自行从github上获取代码自行编译。
  3. 配置cgproxy,编辑/etc/cgproxy/config.json

    • cgroup_proxy中括号里加上”/“(包含引号),port改为Qv2ray首选项里的透明代理的端口。
    • cgproxy默认配置是代理所有tcp和udp,ipv4和ipv6的流量,如果不希望代理其中的某种(些)流量,则将对应的enable_xxx改为false。注意这里的配置要和Qv2ray选项里的配置一致(如,Qv2ray选项里没有勾选udp,则这里务必把enable_udp改为false)。
    • 如果希望当本机作为网关设备时为连接到本机网关的其他设备(如连接到本机开设的wifi热点的设备)也提供透明代理,则把enable_gateway改为true。
  4. (重要)透明代理的基本原理是拦截系统发出的所有流量,并将这些流量转到代理工具里,从而实现让系统所有流量都走代理的目的。此时,为了避免流量出现死循环(即代理工具发出的流量又转回到代理工具里),需要将代理工具排除在透明代理环境外面。有两种方式可以实现这一点:

    • 通过execsnoop监控代理工具的启动,并自动将其移至透明代理环境外面:

      • cgproxy软件自带execsnoop支持,以上cgproxy测试过的发行版均可支持。
      • 编辑/etc/cgproxy/config.json,在program_noproxy中括号里加上”v2ray”,”qv2ray”(包含引号和逗号),以使qv2rayv2ray发出的流量不经过透明代理。如果你的v2rayqv2ray不在PATH里,则需要填写它们的绝对路径。
    • 在每次连接代理节点时,让qv2ray自己把自己移到透明代理环境外面:

      • 安装Qvplugin-Command插件,在插件设置里的“pre-connection”栏里加上一句

        1
        sh -c "cgnoproxy --pid $(pgrep -x qv2ray)"

        即可。

  5. (重要)如果启用了udp的透明代理(dns也是udp),则给v2ray二进制文件加上相应的特权:

    1
    sudo setcap "cap_net_admin,cap_net_bind_service=ep" /path/of/your/v2ray

    否则udp的透明代理可能会出问题。

  6. 启动透明代理服务:systemctl start cgproxy.servicesystemctl enable --now cgproxy.service

以上步骤完成后,透明代理应该能正常使用了。

dns配置说明

如果勾选了“dns拦截”,且启用了dns和udp的透明代理,则v2ray会拦截对系统dns的请求,并将其转发到v2ray的内置dns里,即让v2ray内置dns接管系统dns。但v2ray内置dns是会遵循路由规则的。

如果没勾选“dns拦截”,则v2ray虽然不会让内置dns接管系统dns,但如果启用了dns和udp的透明代理,则系统dns也会走透明代理进v2ray,并遵循v2ray的路由规则。

因此,在启用了dns和udp的透明代理时,若系统dns或v2ray的内置dns配置不当,可能导致dns请求发不出去,从而影响正常上网。

由于qv2ray默认的路由规则是绕过国内ip,国外ip均走代理。在这个情形中,以下两个配置是典型的有问题的dns配置方式:

  • 配置了国外普通dns作为首选,但代理本身不支持udp(此时dns查询的udp流量出不去,dns无法查询)
  • 配置了使用域名的doh作为首选(此时doh的域名无法解析,从而doh也无法使用)

一般而言,如果并不在意将dns查询发给谁,那么,在绕过国内ip的情况下,只需要配置一个国内普通dns作为首选即可保证不会出问题。若代理本身不支持udp,又希望使用国外dns,则可以考虑使用使用ip的doh(如https://1.1.1.1/dns-query等)。

如果需要更复杂的dns配置,建议参考上游文档,并选择合适的不会影响正常上网的dns配置。

常见问题

  • 启用透明代理后无法访问任何外网,且v2ray的cpu占用率飙升

    可能是流量陷入死循环了,检查第4步有没有正确配置。如果配置没问题,执行systemctl status cgproxy.service看下有没有诸如info: process noproxy pid msg: xxx之类的输出。如果没有,则说明cgproxy软件或execsnoop没有正常工作。注意cgproxy软件需要cgroup v2。

    尝试退出qv2ray,随后在终端里执行cgnoproxy qv2ray看是否恢复正常,如恢复正常,说明cgproxy正常工作,只是execsnoop没有正常工作。由于execsnoop一定程度上依赖于内核,非上述cgproxy测试过的发行版用户,建议使用第4步中的第2种方法。另外,对kde用户,5.19+版的plasma会给从krunner里启动的程序额外设置cgroup,尽管cgproxy软件考虑到了这一点,但仍有极少数场合可能出现plasma设置的cgroup覆盖掉了cgproxy设置的cgroup的情况,此时通常重启一下qv2ray即可。

  • 启用透明代理后,无法访问(部分)域名

    可能是dns无法解析(部分)域名。一般这种故障只发生在启用了dns及udp透明代理的时候。

    终端里执行dig 无法访问的域名看下报什么错:

    • 若出现类似reply from unexpected source: 192.168.0.100#42050, expected 8.8.8.8#53的报错,则检查第5步的有没有正确配置。

    • 若出现类似connection timed out; no servers could be reache的报错,则说明dns查询的流量出不去,此时往往是系统dns或v2ray内置dns配置不当。请检查是否出现了前文提到的几种不当配置。如果没有勾选“dns拦截”,则此时v2ray虽然不会用内置dns接管系统dns,但它仍然会让系统dns走透明代理,从而遵循v2ray的路由规则,此时需要检查系统dns是否是前文提到的那几种不当配置。

  • 能不能分应用代理(如,下载BT时不能走代理)

    对于本机的程序,可以,可通过两种方式实现:

    • 通过cgnoproxy实现:如,在命令行中执行cgnoproxy qbittorrent,启动的qbittorrent程序就不会走透明代理。又如,在命令行中执行cgnoproxy --pid 12345,执行之后pid为12345的程序就不再走透明代理。这种方式可支持任何应用。
    • 通过/etc/cgproxy/config.json实现:在配置里的program_noproxy中括号里加上相应的应用即可。这种方式只支持可执行文件,不支持各种脚本。注意修改config.json之后,需要重启cgproxy服务才能生效,执行systemctl restart cgproxy.service即可。

    对于当本机作为网关设备时为连接到本机网关的其他设备,不行,那些设备的所有流量(到本机的流量除外)都必然会走代理。

  • 透明代理环境中响应速度变慢

    由于iptables是在域名解析成ip之后,才对相应的流量进行重定向。因此,在透明代理环境中,访问一个域名s可能会需要解析至少2次dns(系统解析一次,重定向到v2ray之后v2ray分流模块再解析一次)。因此,响应理论上是会变慢一点的,变慢的幅度取决于系统dns及v2ray的dns的响应速度。

自己写的更新和群友遇到的一些问题

排查问题

1
2
3
systemd-cgls /noproxy.slice # 检查一下被排除代理的应用
cgproxy curl -sSLv https://www.google.com/ # 开着代理连一下谷歌试试,检查一下你的透明代理是不是好的
cgnoproxy firefox # 临时关掉透明代理运行某个应用(比如Firefox),检查一下是不是透明代理造成的问题

Docker和透明代理冲突的问题

这也是一个频繁被问到的问题

你是装了某些可能会破坏 cgroup matching 的东西吗
比如 docker 之类的肮脏程序
docker 不仅会破坏cgroup matching
把网络搞炸
docker 还有 hairpin nat
巨坑

我自己试了一下是这样的

systemctl enable docker后没有问题

这个时候我没把当前用户添加到个人用户组,不能直接使用Docker,需要sudo docker ...
在我把Docker添加到当前用户组后出问题了,重启后qv2ray和clash等失效了,延迟测试全部显示0ms

报错

1
2
3
4
5
2020/11/19 20:34:03 192.168.1.105:58398 accepted tcp:211.72.35.152:80 [outBound_PROXY] 

2020/11/19 20:34:10 [Warning] [4118491953] v2ray.com/core/app/proxyman/outbound: failed to process outbound traffic > v2ray.com/core/proxy/vmess/outbound: failed to find an available destination > v2ray.com/core/common/retry: [v2ray.com/core/transport/internet/websocket: failed to dial WebSocket > v2ray.com/core/transport/internet/websocket: failed to dial to (ws://feec8af.rf.cloudflare.systems/s/feec8af.fm.apple.com:29306): > read tcp 192.168.1.105:58370->211.72.35.152:80: i/o timeout v2ray.com/core/transport/internet/websocket: failed to dial WebSocket > v2ray.com/core/transport/internet/websocket: failed to dial to (ws://feec8af.rf.cloudflare.systems/s/feec8af.fm.apple.com:29306): > dial tcp: operation was canceled] > v2ray.com/core/common/retry: all retry attempts failed
2020/11/19 20:34:10 [Warning] v2ray.com/core/transport/internet/tcp: failed to accepted raw connections > accept tcp 127.0.0.1:12345: accept4: too many open files
2020/11/19 20:34:10 192.168.1.105:58946 accepted tcp:211.72.35.152:80 [outBound_PROXY]

一个办法是用docker的时候加sudo以root用户运行。

另一个解决办法见:

https://github.com/springzfx/cgproxy/issues/

编辑/etc/default/grub
添加cgroup_no_v1=net_cls,net_prioGRUB_CMDLINE_LINUX_DEFAULT中,
然后更新grub,重启
示例:

1
2
GRUB_CMDLINE_LINUX_DEFAULT="text cgroup_no_v1=net_cls,net_prio"
sudo grub-mkconfig -o /boot/grub/grub.cfg

但是,加了这些参数经常也不太好用。

最简单的办法,扬了 docker,换 podman / podman-docker

1
sudo pacman -Syu podman-docker

或者安装podman,然后

1
alias docker=podman

然后你需要编辑/etc/subuid/etc/subgid加上podman:165536:4096,然后

1
usermod --add-subuids 165536-169631 --add-subgids 165536-169631 yourusername

不然会报错,没法pull images

1
ERRO[0000] cannot find UID/GID for user zjk: open /etc/subuid: no such file or directory - check rootless mode in man pages.

pacman更新报错

本来都是好的,突然有一次更新问题了。浏览器等访问都没有问题,怀疑是透明代理的问题。Qv2ray输出看不到异常。

1
2
3
4
5
:: Synchronizing package databases...
error: failed retrieving file 'core.db' from mirrors.uestc.cn : Failed to connect to mirrors.uestc.cn port 80: Connection timed out
error: failed to update core (download library error)
error: failed to synchronize all databases
error installing repo packages

使用cgnoproxy yay -Syu 看了一下,果然。
然后在cproxy中将pacman和yay加入noproxy组中。编辑/etc/cgproxy/config.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
{
"comment":"For usage, see https://github.com/springzfx/cgproxy",

"port": 12345,
"program_noproxy": ["v2ray", "qv2ray", "yay", "pacman"],
"program_proxy": [],
"cgroup_noproxy": ["/system.slice/v2ray.service"],
"cgroup_proxy": ["/"],
"enable_gateway": false,
"enable_dns": false,
"enable_udp": false,
"enable_tcp": true,
"enable_ipv4": true,
"enable_ipv6": true,
"table": 10007,
"fwmark": 39283
}

透明代理和其他的代理设置冲突

比如SwitchyOmega、Firefox的代理设置

开了透明代理,理论上不需要对应用单独指定代理了,直接把SwitchyOmega关掉或者规则选 Direct,Firefox的代理设置也填不使用代理

报错 too many open files

报这种错:

2021/03/11 22:32:32 [Warning] github.com/v2fly/v2ray-core/v4/transport/internet/tcp: failed to accepted raw connections > accept tcp 127.0.0.1:8889: accept4: too many open files

这个在开启UDP透明代理的时候常见, 可能是文件大小限制,也可能是你配置错误出现循环转发,比如这个issue

编辑/usr/lib/systemd/system/v2ray.service/etc/systemd/system/v2ray.service

加入

1
2
3
[Service]
LimitNPROC=500
LimitNOFILE=1000000

然后:

1
systemctl daemon-reload && systemctl restart v2ray

参考v2ray配置指南的透明代理部分

或者修改/etc/security/limits.conf在末尾添加

1
2
user   soft   nofile    40690
user hard nofile 40690

user改为你的用户名(echo $USER查看),或者想为所有用户设置就改为*(不推荐)

番外

其他平台和其他方案

目前Linux透明代理的绝大多数方案都是基于iptables(nftables)的
v2raya有自带的透明代理,看起来设置比这个还要简单一些。
如果是软路由的透明代理,那Openwrt有很多成熟的方案。如果是非桌面版,请考虑软路由的ssr plus、clash或者v2raya等透明代理方案(或者自己写iptables规则)

Win用户推荐使用Proxifier达到类似的效果

Qv2ray的gcc和rprx的纷争

gcc几个月前宣布隐退,当时很多人(包括我)都在期待gcc回来。不过在前一阵gcc和rprx等人的争吵中,gcc被鸭鸭移除了权限并踢了出去。
为什么我要说这个事情呢?cgproxy已经在一年多以前停止维护了。现在Qv2ray的开发在gcc走后也大幅放缓了,而透明代理功能Qv2ray维护者表示这是祖传代码不会继续更新,所以————————(当然现在的版本已经够用了,继续用下去没有问题)

ps:这件事上我是倾向于支持gcc的。之前我对鸭鸭和gcc印象比较好,很不怎么喜欢 rprx x 。
这件事我不支持鸭鸭的做法,gcc作为qv2ray的创始人和主要贡献者,虽然隐退了,应该有对这个项目处置的权利(虽然qv2ray是一个社区项目,然而90%的贡献都是gcc的)。我心目中比较好的处理方式是Qv2ray分家,fork并改名出一个Qxray来作为支持xray的版本(就像Project V、v2fly和Project X那样)。
Qv2ray是Linux下这方面为数不多很棒的GUI图形化桌面软件(其他的有Trojan-Qt5,大都先于Qv2ray凉了),唉,Linux桌面的图形化软件前路迢迢

关于Qv2ray停止维护的事

现在star最多的那个组织Qv2ray 停止维护了,意料之中吧。这件事我不想多评价了,我要评价肯定忍不住对rprx的脏话。下面简单说一下qv2ray的事。

如果你想用老版本的Qv2ray(Qv2ray 3 以前)

目前AUR里面的qv2ray 2.7.0 版本是无法正常使用插件的。如果想使用可以自行降级到2.6然后用插件。

更推荐的方式是换qv2ray-dev-git (这个也停止维护了,是Qv2ray/qv2ray的打包) 这个要新一点(众所周知dev版本比稳定版稳定)

上面两个版本足够正常使用,但是都停止更新了。

1
yay -S qv2ray-dev-git qv2ray-plugin-command-dev-git 

如果你想用gcc还在开发中的新版的Qv2ray 3.0,可以自行编译或下载release(完全移除了xray的支持,gcc原班人马开发维护,是的,你们永远喜欢的gcc又回来了)。这个版本变化比较大,迁移到了Qt6,重写了不少东西,插件也不通用,只是界面看起来一样而已。

下面是Gcc的新版本Qv2ray,Conflict With xray

Archlinux可以用qv2ray-static-bin-nightly这个包,想追最新的可以用qv2ray-git

1
yay -S qv2ray-static-bin-nightly

关于grpc

grpc经常进行不兼容的更新(对,小版本号也会有破坏性改动)

所以经常出现滚动更新grpc后,qv2ray就炸掉不能用了(这个时候建议降级grpc先应急)

或者你依赖grpc的其他包不多的话,可以考虑直接锁grpc的版本。sudo downgrade grpc

事实上不仅是qv2ray会随着grpc的更新出问题,其他的包也会,比如sysdig。经常出现grpc版本更新的但是API变了,其他依赖grpc的包没有更改API还是用老版本就出事了。

1
2
3
error: failed to prepare transaction (could not satisfy dependencies)
:: installing grpc (1.41.0-1) breaks dependency 'libgrpc++_unsecure.so=1.39-64' required by sysdig
error installing repo packages