sshuttle 如何避免 TCP-over-TCP 诅咒?

How does sshuttle avoid of TCP-over-TCP curse?

sshuttle claims that it solves much discussed problem of TCP-over-TCP meltdown.

sshuttle assembles the TCP stream locally, multiplexes it statefully over an ssh session, and disassembles it back into packets at the other end. So it never ends up doing TCP-over-TCP. It’s just data-over-TCP, which is safe.

但从程序的角度来看,它维护与目标服务器的 TCP 连接以及它附带的所有内容(读取指数超时),这是关于其他 TCP 会话的分层,因为 SSH 还不能正常工作在 udp。这看起来很像 TCP-over-TCP。

这里有什么技巧? sshuttle真的解决问题了吗?

我尝试阅读 source code,但到目前为止没有找到答案。

更重要的是,他们到底是怎么做到的?如果想在准系统中重新实现它,应该在哪里寻找灵感?

如声明所述,它不是 TCP-over-TCP。

这是 TCP-over-TCP:

First application   
  First end of outer TCP connection  
  First end of inner TCP connection  
   Datagram/packet link  
  Send end of inner TCP connection  
  Second end of outer TCP connection  
Second application  

注意外部 TCP 连接是如何承载内部 TCP 连接的吗?

这就是他们正在做的事情:

First application   
 First end of outer TCP connection  
  Outer end of First TCP connection  
  Inner end of First TCP connection  
    Byte stream link  
  Inner end of Second TCP connection  
  Outer end of Second TCP connection  
Second application 

注意到没有通过内部 TCP 连接传输外部 TCP 连接吗?没有 TCP-over-TCP。

有四种明显的方法可以做到这一点:

  1. 诱导应用程序建立到已分配给系统的 IP 地址的 TCP 连接。
  2. 为系统分配应用程序尝试连接的 IP 地址。
  3. NAT 出站 TCP 连接到本地系统上的进程 运行。 ( 暗示这就是他们所做的)
  4. 使 OS 将 TCP 数据包路由到您,并通过您在用户 space 中实施 TCP 来终止它。

sshuttle 客户端设置防火墙规则(Linux 中的 iptables,这就是 sshuttle 客户端需要 root 权限的原因)将某些传出 TCP 连接重定向到本地端口(默认为 12300 ),启动sshuttle时可以看到这个过程:

firewall manager: starting transproxy.
>> iptables -t nat -N sshuttle-12300
>> iptables -t nat -F sshuttle-12300
>> iptables -t nat -I OUTPUT 1 -j sshuttle-12300
>> iptables -t nat -I PREROUTING 1 -j sshuttle-12300
>> iptables -t nat -A sshuttle-12300 -j RETURN --dest 127.0.0.0/8 -p tcp
>> iptables -t nat -A sshuttle-12300 -j REDIRECT --dest 0.0.0.0/0 -p tcp --to-ports 12300 -m ttl ! --ttl 42

并在 sshuttle 退出时删除 iptables nat 规则,

>> iptables -t nat -D OUTPUT -j sshuttle-12300
>> iptables -t nat -D PREROUTING -j sshuttle-12300
>> iptables -t nat -F sshuttle-12300
>> iptables -t nat -X sshuttle-12300

TCP 内容被拾取并通过 ssh 连接多路复用到 sshuttle 服务器,然后再次解复用为连接。 client.py 中的函数 onaccept_tcpin 执行多路复用:

def onaccept_tcp(listener, method, mux, handlers):
    global _extra_fd
    try:
        sock, srcip = listener.accept()
    except socket.error as e:
        if e.args[0] in [errno.EMFILE, errno.ENFILE]:
            debug1('Rejected incoming connection: too many open files!\n')
            # free up an fd so we can eat the connection
            os.close(_extra_fd)
            try:
                sock, srcip = listener.accept()
                sock.close()
            finally:
                _extra_fd = os.open('/dev/null', os.O_RDONLY)
            return
        else:
            raise

    dstip = method.get_tcp_dstip(sock)
    debug1('Accept TCP: %s:%r -> %s:%r.\n' % (srcip[0], srcip[1],
                                              dstip[0], dstip[1]))
    if dstip[1] == sock.getsockname()[1] and islocal(dstip[0], sock.family):
        debug1("-- ignored: that's my address!\n")
        sock.close()
        return
    chan = mux.next_channel()
    if not chan:
        log('warning: too many open channels.  Discarded connection.\n')
        sock.close()
        return
    mux.send(chan, ssnet.CMD_TCP_CONNECT, b'%d,%s,%d' %
             (sock.family, dstip[0].encode("ASCII"), dstip[1]))
    outwrap = MuxWrapper(mux, chan)
    handlers.append(Proxy(SockWrapper(sock, sock), outwrap))
    expire_connections(time.time(), mux)

您可以在ssnet.py中看到数据是如何打包的。

我在 redsocks 中看到了相同的策略(我的意思是设置防火墙规则),旨在将任何 TCP 连接重定向到 SOCKS 或 HTTPS 代理。