【问题标题】:UDP reverse tunnelingUDP反向隧道
【发布时间】:2020-08-26 07:20:53
【问题描述】:

我遇到了一个很难解决的问题,如果有人能提供帮助,我将不胜感激。

我在防火墙后面的本地网络中有一个 VPN 服务器,它只允许出站连接。 我的目标是进行“UDP 性别更改”,并通过创建反向隧道使 VPN UDP 端口可用于我可以转发端口的外部服务器。使用 TCP 隧道执行此操作非常简单,并且可以通过使用诸如 socat、nc 甚至 ssh 隧道之类的工具轻松完成。但是,VPN 应始终由 UDP 数据包携带,以避免 TCP 崩溃问题(TCP over TCP)。

使用 socat/nc 创建的 UDP 反向隧道不起作用,因为 UDP 是无连接协议。这意味着“client client”和“listen listen”配置将仅在客户端首先发送数据包时才允许数据传输(在反向连接中是不可能的)。

我错过了什么吗?是否有任何实用程序可以在不使用第二个 VPN 连接的情况下完成此任务(例如,通过使用某些标头进行面向 UDP 连接)? 非常感谢

【问题讨论】:

  • 您是否搜索过“UDP 打孔”或任何 NAT 穿越协议以查看它们的作用? webrtc 是另一个似乎面临类似问题的常见系统,可能是一个地方看看
  • 嗨!谢谢您的回答。我知道 UDP 打孔,但不幸的是,我找不到任何允许将其用于反向连接的工具。
  • webrtc 和 bittorrent 都以一种巧妙的方式使用打孔来获得类似的结果。隧道的技术方面对我来说很清楚。我想要找到的是,我的问题是否可以通过使用现有工具(nc、socat、(...)而据我所知不是)或者唯一可能的解决方案是编写一个自定义隧道。
  • 你不能只ping UDP数据包到/从任一端的已知端口吗?这可能足以强制一个相对愚蠢的 NATing 防火墙为您创建转发规则。不确定你是否对事情有那么大的控制权,但可能是从某个地方开始
  • 这就是我试图用 socat 做的事情。真正的问题是连接的“反向”部分。我可以建立连接,但第一个数据包必须由客户端发送。有什么方法可以发送(并丢弃服务器端)一个自动“hello”数据包,以建立 UDP 连接?

标签: udp tunneling socat


【解决方案1】:

我在 Python 中想过这样的事情:

import socket
from select import select

# immediately useful parameters are:
#   REMOTE_SERVER_NAME other network server (which will be resolved by DNS)
#   LOCAL_SERVER_PORT where forward network traffic to

REMOTE_SERVER_NAME = '8.8.8.8'
LOCAL_SERVER_PORT = 20
LOCAL_SERVER_NAME = '127.0.0.1'
REMOTE_PORT = 9990
LOCAL_PORT = REMOTE_PORT + 1

sock_remote = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock_remote.bind(('', REMOTE_PORT))
sock_remote.connect((REMOTE_SERVER_NAME, REMOTE_PORT))

sock_local = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock_local.bind(('', LOCAL_PORT))
sock_local.connect((LOCAL_SERVER_NAME, LOCAL_SERVER_PORT))

sockets = (sock_remote, sock_local)
for s in sockets:
    s.setblocking(0)

# loop forever forwarding packets between the connections
while True:
    avail, _, _ = select((sock_local, sock_remote), (), (), timeout=100)

    # send a keep alive message every timeout
    if not avail:
        sock_remote.send(b'keep alive')
        continue

    for s in avail:
        # something from the local server, forward it on
        if s is sock_local:
            msg = sock_local.recv(8192)
            sock_remote.send(msg)

        # something from the remote server
        if s is sock_remote:
            msg = sock_remote.recv(8192)
            # don't forward keep alives to local system
            if msg != b'keep alive':
                sock_local.send(msg)

即在任一服务器上运行此程序(将REMOTE_SERVER_NAME 更改为指向适当的位置),它将在它们之间转发数据包,每 100 秒发送一个“保持活动”数据包。您的本地进程会将 UDP 数据包发送到LOCAL_PORT,这些数据包将转发到远程服务器,远程服务器将接收这些数据包并将它们发送到另一端的LOCAL_SERVER_PORT。有 8 个流程需要担心,所以命名变得很尴尬:

DMZ <=> VPN <=> python <=> NAT <=> internet <=> NAT <=> python <=> VPN <=> DMZ

您也许可以使用 sock_local.recvfrom 检测到 LOCAL_SERVER_NAMELOCAL_SERVER_PORT 并将 addrinfo 隐藏起来,但我认为为了便于理解我会将它们留在里面

希望你了解 Python!但我很难用语言来表达它

【讨论】:

  • 您好,非常感谢您的回答。在双方使用已知端口使其在我的应用程序中不可行,但它使我朝着正确的方向前进,并且通过一些修改,我能够使其在我的环境中工作。我将很快将其发​​布在另一个答案中,如果您能检查它是否有可能的改进,我将不胜感激。
【解决方案2】:

我改编了 Sam Mason 的答案以执行 UDP 反向连接。 这是配置:

HOST UDP-LISTEN <=> python cc <=> NAT <=> internet <=> server <=> python ll UDP-LISTEN

主机监听一个 UDP 端口。您可以通过 python 脚本使用反向连接从任何有权访问服务器的第三个客户端访问主机。

第一部分是听听脚本。它将在 CC_PORT 上侦听客户端脚本,并在 SERVICE_PORT 上转发任何内容。它还会丢弃来自 cc 脚本的 keepalive。

import socket
from select import select

CC_PORT = 8881
SERVICE_PORT = 8880
LISTEN_IP='x.x.x.x'

sock_cc = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock_cc.bind((LISTEN_IP,CC_PORT))

sock_serv = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock_serv.bind((LISTEN_IP,SERVICE_PORT))

sockets = (sock_cc, sock_serv)
for s in sockets:
    s.setblocking(0)

client_serv=None
client_cc=None

# loop forever forwarding packets between the connections
while True:
    avail, _, _ = select((sock_cc, sock_serv), (), (), 1)

    # send a keep alive message every timeout
    if not avail:
        continue

    for s in avail:
        # something from the local server, forward it on
        if s is sock_cc:
            msg = sock_cc.recvfrom(8192)
            client_cc=msg[1]
            if client_serv is not None:
                if msg[0] != b'keep alive':
                    sock_serv.sendto(msg[0], client_serv)

        # something from the remote server
        if s is sock_serv:
            msg = sock_serv.recvfrom(8192)
            client_serv=msg[1]
            if client_cc is not None:
                sock_cc.sendto(msg[0], client_cc)

客户端客户端脚本

import socket

from select import select

DEST_IP = 'x.x.x.x' 
DEST_PORT = 8881 
LOCAL_IP = '127.0.0.1' 
LOCAL_SERVICE = 8800

sock_remote = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock_remote.connect((DEST_IP,DEST_PORT)) sock_local = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock_local.connect((LOCAL_IP,LOCAL_SERVICE)) sockets = (sock_remote, sock_local) for s in sockets:
    s.setblocking(0)

# loop forever forwarding packets between the connections while True:
    avail, _, _ = select((sock_local, sock_remote), (), (), 1)

    # send a keep alive message every timeout
    if not avail:
        sock_remote.send(b'keep alive')
        continue

    for s in avail:
        # something from the local server, forward it on
        if s is sock_local:
            msg = sock_local.recv(8192)
            sock_remote.send(msg)

        # something from the remote server
        if s is sock_remote:
            msg = sock_remote.recv(8192)
            # don't forward keep alives to local system
            if msg != b'keep alive':
                sock_local.send(msg)

再次感谢 Sam Mason 帮助我解决了这个问题。

【讨论】:

  • 是的,我以为你会想要这样的东西!它使它变得更加复杂,我担心前几个数据包对启动很重要,所以不认为它值得包含在我发布的版本中。可能会在风格上进行一些改进,但看起来功能上还不错
  • 这很好,但是这样机器 A 必须到达机器 B,如果 A 和 B 都在防火墙后面,那么唯一的方法是使用 webrtc/stun/turn 来连接两者:D跨度>
猜你喜欢
  • 1970-01-01
  • 2012-01-26
  • 2021-06-19
  • 2012-06-06
  • 1970-01-01
  • 2010-09-27
  • 1970-01-01
  • 2022-01-18
  • 1970-01-01
相关资源
最近更新 更多