V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iceorange
V2EX  ›  程序员

cproxy: 使用简单的 Linux per app 透明代理

  •  
  •   iceorange · 2021-02-24 22:29:45 +08:00 · 14949 次点击
    这是一个创建于 1150 天前的主题,其中的信息可能已经有所发展或是发生改变。

    刚开源了我的新项目 cproxy: https://github.com/NOBLES5E/cproxy

    解决的问题和 proxychains 一样,就是对单独进程走代理,而不需要进程本身支持代理。

    使用上和 proxychains 一样简单,但是可以代理 TCP, UDP 并且对静态链接的程序(比如像 docker 这样的 Go 程序) 和已经在运行的程序也可以进行代理。

    之前也用过 graftcp 等等软件,大部分情况下挺好用。不过 graftcp 每次要起两个程序,而且最近有代理 DNS 请求的需求了,只好自己动手了。

    既然开源出来了,还是希望更多人能知道,对大家产生帮助,也收集一下建议。欢迎讨论~

    29 条回复    2021-02-25 21:27:36 +08:00
    tia
        1
    tia  
       2021-02-24 22:37:01 +08:00   ❤️ 1
    支持一下
    cathedrel
        2
    cathedrel  
       2021-02-24 22:57:46 +08:00
    大佬,能不能写个“Linux all app 透明代理”? 所有流量,tcp,udp,icmp,全部自动走?功能和配置都简单一点,类似 proxifier 设置好代理地址就能代理整个系统的?在 github 上找到两个,但是都有点复杂,在 alpine linux 上都跑不起来......您要是做了这个,功德无量啊~~~
    holyzhou
        3
    holyzhou  
       2021-02-24 22:57:52 +08:00
    好像有点意思 star 一个
    generic
        4
    generic  
       2021-02-25 00:09:08 +08:00 via Android
    @cathedrel 全部流量那你 vpn 呀。
    Kobayashi
        5
    Kobayashi  
       2021-02-25 00:42:13 +08:00 via Android
    @cathedrel 试试 clash,不确定 UDP 、ICMP 是否支持。
    Jirajine
        6
    Jirajine  
       2021-02-25 01:09:23 +08:00 via Android
    还以为是又发明了什么新的魔法,一看是操作防火墙的。
    而且是通过调用 iptables 命令操作,十分不合适。
    没有原子性、性能也不好,并发启动很可能出问题。
    正确的做法应该是通过 libnftnl 这样的库。
    而且你这样没必要用 rust 啊,直接用 shell 脚本更合适。
    iceorange
        7
    iceorange  
    OP
       2021-02-25 01:36:32 +08:00
    @Jirajine 谢谢,

    1. 修改 iptables 不觉得哪里 low 了,请问是解决不了什么问题?
    2. 关于是调用库还是调用 iptables 命令这个问题我一开始思考过。在这种场景下,原子性没有影响,因为子进程是所有 iptables rule apply 完再 spawn 的,子进程退出后才会进行 iptables 的清空操作。说到性能问题就莫名其妙了,调用几行 iptables 命令的开销在这个使用场景中值得考虑?并发启动没看出问题,请教一下。
    3. 不用 libnftnl 可能的好处:不需要整个程序 uid 0,同时支持 nftables 和 iptables 。
    4. 能用 rust 更好地组织代码干嘛非得写个 shell ?

    最后,希望能以一个正常讨论的态度对话,谢谢。
    Jirajine
        8
    Jirajine  
       2021-02-25 02:10:51 +08:00 via Android
    @iceorange 想象一下用户用你的工具并发启动多个进程,多个进程同时操作 iptables 会怎样。
    nftables 一大亮点就是提供了多个程序并发操作防火墙的原子性。
    因为你的核心逻辑基本是 shell 命令实现的,每次都需要需要启动多个外部进程,开销不小。
    既然用 shell 命令实现,为啥不用 shell 脚本呢。
    iceorange
        9
    iceorange  
    OP
       2021-02-25 02:17:57 +08:00
    @Jirajine
    1. 多个进程同时操作 iptables 没有问题啊,子进程只有在该子进程需要的 rule apply 后才会 spawn 。不同进程使用的 rule 是不同的。
    2. 为什么非要纠结 iptables 调用的开销呢,一般所代理的程序运行开销远高于 iptables 调用,所以不能 justify 使用 libnftables 更好,详见上述使用 libnftables 带来的问题
    3. 使用了外部程序就得用 shell 这个逻辑实在是不通。写 shell 光那一堆错误检查得写很多行。parse 命令得写很多行。安装 signal handler 得写很多行。懂得都懂。
    iceorange
        10
    iceorange  
    OP
       2021-02-25 02:24:58 +08:00
    @Jirajine
    其实是有一个 fundamental 的认知问题,工具的重点是能够解决新的痛点问题,而不是用了什么魔法。
    v2webdev
        11
    v2webdev  
       2021-02-25 03:13:02 +08:00
    这个项目在 Hacker News 上火了呢。
    cathedrel
        12
    cathedrel  
       2021-02-25 04:46:45 +08:00
    @Kobayashi 我说的在 alpine linux 上跑不起来的两个之一就是 clash,alpine 上面装了 tun,装了 glibc,也试过 chroot 的 arch 里跑,全部失败
    gleport
        13
    gleport  
       2021-02-25 09:06:22 +08:00
    回应一下楼主说的 graftcp 每次要起两个程序的问题:graftcp-local 是设计为守护进程方式使用的,"sudo systemctl --now enable graftcp-local.service" 后使用方式和 proxychains 基本一致。
    iceorange
        14
    iceorange  
    OP
       2021-02-25 09:26:26 +08:00
    @gleport 是的,只需要代理 tcp 的话 graftcp 已经很好用了
    我平时用 graftcp 的时候主要有两个需求解决不了,一个是代理 DNS 。另一个是像是已经运行起来的 docker,经常需要走代理 docker pull 些内网私有 image,用 graftcp 的话我得每次先修改 docker.service 让他套上 graftcp 然后重启 service,用完再改回去,没法直接让现有 docker 进程代理
    gfreezy
        15
    gfreezy  
       2021-02-25 09:28:30 +08:00 via iPhone   ❤️ 1
    @cathedrel 可以看下我写的这个 https://github.com/gfreezy/seeker
    yanqiyu
        16
    yanqiyu  
       2021-02-25 09:29:28 +08:00   ❤️ 1
    让我想起这个项目 https://github.com/springzfx/cgproxy,不过 cgproxy 是一个 daemon,监听 cgroup 然后对于设置的 slice 等应用代理
    muxueqz
        17
    muxueqz  
       2021-02-25 09:53:30 +08:00   ❤️ 1
    @iceorange 嗯,这个思路挺有趣的,不过用 shell 实现也有个好处:依赖更少,不需要编译。目前的实现已经很接近 shell 了

    哈,不过当然是想用什么实现就用什么实现了
    iceorange
        18
    iceorange  
    OP
       2021-02-25 10:19:28 +08:00
    @yanqiyu
    诶 这个使用和系统要求不同,但是在实现原理上和 cproxy 非常接近。我一会把它补充到 readme 里。
    nmap
        19
    nmap  
       2021-02-25 11:17:53 +08:00
    技术原理是什么呢?
    iceorange
        20
    iceorange  
    OP
       2021-02-25 11:22:49 +08:00
    @nmap 简单说其实就是为需要代理的程序创建一个独立的 cgroup,通过 netfilter 把这个 cgroup 的网络流量全部重定向。
    Jirajine
        21
    Jirajine  
       2021-02-25 12:03:42 +08:00
    @iceorange #9 因为操作 iptables 不是原子性的,一个进程在添加 /删除规则的同时正好有其他进程也在添加 /删除规则,最终的结果就是乱七八糟。
    我看了一下你是用 pid 区分链名,多数情况下应该没有问题,但要是恰好有回收的 pid 导致重复就会导致非常令人困惑且难以排查的 bug 。
    另外你的大多数逻辑甚至连文件 IO 都不写代码而是用 shell 命令实现,与其把 shell 脚本嵌入到代码里,直接用 shell 脚本不方便吗?
    解析命令行参数和信号处理同样可以用库。至于检查错误,shell 脚本检查 shell 命令的错误当然比 rust 容易。
    iceorange
        22
    iceorange  
    OP
       2021-02-25 12:12:49 +08:00
    @Jirajine 真的很奇怪。
    1. 说乱七八糟不要凭感觉,需要具体的分析。为了避免误导其他人,我解释一下。在这种场景下,不存在 pid 回收的问题,因为 cproxy 是把自己的 pid 放入 cgroup,再 launch 子进程。删除链是 cproxy 进程进行删除,因此删除的时候不可能 pid 被回收。而且 pid 这个事情跟原子性完全没有任何关系,不要把不相关的事情放在一起,逻辑非常混乱。
    2. 您觉得 shell 脚本写更好,是您的 personal taste,争论这种 detail 没有任何意义,只会把讨论带偏。我觉得 rust 写更好。
    iceorange
        23
    iceorange  
    OP
       2021-02-25 12:21:38 +08:00
    @Jirajine 建议讨论的时候不要太多 “乱七八糟”、“十分不合适” 这种主观感觉。这对技术社区讨论是不利的。
    比如您觉得使用 iptables 有问题,那么讨论方向应该是使用了 iptables,比如多进程一起操作,可能会带来什么具体后果,这个后果会造成什么影响,对使用场景是否不可接受。在您的回复中这个后果叫做 “乱七八糟”,和“非常令人困惑且难以排查的 bug ”但又不明确是什么 bug 。这样讨论是没有意义的,对社区互相帮助提升技术有害。
    Jirajine
        24
    Jirajine  
       2021-02-25 12:35:40 +08:00
    @iceorange #22 结果“乱七八糟”哪里是主观感觉了,你的程序存在 race condition,运行的结果是不确定的,那就是乱七八糟啊。
    同样,这个后果造成的影响就是多数情况正常工作,偶发性的突然出现无法连接、没有回收等各种问题(出现冲突时时运行结果本身就是不明确的)。当然很容易让用户非常困惑并且难以排查(复现)。
    又看了下你这个 pid,你的程序可能会使用自己的 pid,也可能会使用目标程序的 pid,当然存在 pid 回收后再次分配导致你的名称冲突,进而因为缺乏原子性造成 race condition 。

    当然也不是说没有原子性就一定会有问题,但你需要考虑各种不同的情况,子进程嵌套调用、不同方式调用、僵尸进程、被 kill 后未回收等,这是非常 error prune 的。
    iceorange
        25
    iceorange  
    OP
       2021-02-25 12:37:43 +08:00
    @Jirajine 您开心就好 :)
    cathedrel
        26
    cathedrel  
       2021-02-25 19:36:10 +08:00
    @gfreezy 看起来很棒,我先点个 star 。
    releases 页面的 seeker-0.4.0-x86_64-unknown-linux-musl.tar.gz 这个文件是直接可以在 alpine linux 里运行的文件?
    gfreezy
        27
    gfreezy  
       2021-02-25 20:25:01 +08:00 via iPhone   ❤️ 1
    @cathedrel musl 打包出来的,普通 Linux 可以执行,没测试 alpine
    cathedrel
        28
    cathedrel  
       2021-02-25 20:59:24 +08:00
    @gfreezy 我在 alpine 里面试了一下,./seeker -h 可以运行,配置文件我该怎么写?“直接使用的 clash 的规则”,clash 我完全不懂。如果想让系统里的所有流量全部走代理,该怎么写?‘'DOMAIN,ALL,PROXY'’?
    gfreezy
        29
    gfreezy  
       2021-02-25 21:27:36 +08:00   ❤️ 1
    @cathedrel 对。有啥问题可以加 GitHub 页面上的 slack 聊吧。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   1520 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 30ms · UTC 17:03 · PVG 01:03 · LAX 10:03 · JFK 13:03
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.