如何使用 namespaced-openvpn 为每个应用程序建立 VPN?

如何使用 namespaced-openvpn 为每个应用程序建立 VPN?

这可能是一个相当新手的问题,但是......

我在 Ubuntu 18.04.3 LTS 上,我有一个VPN我想用它做一些应用程序。我查看了namespaced-openvpn并且成功了!需要注意的是,我无法本地连接到此应用程序(出于管理目的)。

有没有办法将 VPN 隧道内的某个端口转发到本地主机VPN 隧道不会将其用于其他目的并因此泄露过多信息吗?

我已经可以做的事情:

  • 跑步namespaced-openvpn-脚本:sudo ./namespaced-openvpn --namespace vpn --config /etc/openvpn/vpn-xxxxx.ovpn
  • 在网络命名空间内执行应用程序:sudo ip netns exec vpn application

我想要实现的目标:

  • 1234从真实的本地主机(enp3s0以太网适配器)连接到在 VPN 隧道内启动的应用程序(例如在端口)
  • 转发此端口,以便我可以从 LAN 中的其他计算机访问它。

答案1

很多年过去了,但万一有人碰巧遇到这个问题,我希望过去几天我也能遇到。
希望这对某人有帮助。

首先,这里是 namespaced-vpn,它完成了使一切运行起来的 90% 的工作。
https://github.com/slingamn/namespaced-openvpn

这里有一个几乎与此完全相同的问题的答案,尽管我不明白做什么以及为什么要这样做,但它对我帮助很大。 https://unix.stackexchange.com/questions/391193/how-to-forward-traffic-between-linux-network-namespaces

我将尝试使用我在搜索中找到的零碎信息,解释我经过大量尝试和错误后得出的结论。
我认为,与将连接转发到您的服务(而不是从运行服务的 PC)最相关的部分是这个(请注意 PREROUTING 后面的感叹号):

iptables -t nat -A PREROUTING ! -s 10.0.0.0/24 -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.0.0.2

其中,10.0.0.0/24代表您不想转发的 IP 范围。
这主要是为了防止路由循环,即任何可通过10.0.0.0/24子网访问的服务都会被路由回自身。--
dport 80 仅转发寻求端口 80 的数据包,即 Web 服务器连接。
10.0.0.2是已移动到服务器正在运行的命名空间内的 veth 接口。
这使得10.0.0.0/24寻求端口 80 服务的传入数据包(不是来自子网的数据包)的目标 IP 被转换为10.0.0.2。示例:192.168.1.200:80变成10.0.0.2:80,这大概是服务器所在命名空间内的接口。

我在多个命名空间中启动并运行了多个 vpn 隧道。如果我不将 PREROUTING 规则添加到我的 iptables,我将无法从 LAN 上的 PC 外部访问资源。

如果您希望从自己的电脑上访问它,则必须添加一条规则,例如:
iptables --table nat --append OUTPUT --destination 192.168.1.2 -p tcp -m tcp --dport 80 -j DNAT --to-destination 10.0.0.2
因此,如果您在服务器所在的电脑上输入浏览器192.168.1.2来访问您的服务器,它将被路由到您的服务器10.0.0.2

我将尝试解释我的一部分设置...

命名空间示例:ip netns add ns_example

创建 veth 对。一端将留在 root/normal 命名空间中(我称之为host),另一端将进入带有 VPN 的命名空间。
ip link add dev ihost type veth peer name ivpn

将 veth 对的 vpn 端移至目标命名空间:
ip link set dev ivpn netns ns_example

将 ips 分配给您创建的 veth 对。

我使用 /32 是因为我只想分配这个确切的 IP,而不是一个范围。否则,当我启动设备时,一些路由会自动插入到我的ip route表中,这会扰乱我其他命名空间的路由。

分配给主机端。
ip addr add 10.0.0.1/32 dev ihost

要分配给已移至命名空间的 vpn 端,必须在命令前加上前缀 ..
ip netns exec TARGET_NAMESPACE

因此,要将 IP 分配给 VPN 端:
ip netns exec ns_example ip addr add 10.0.0.2/32 dev ivpn

现在启动接口。

ip link set dev ihost up

ip netns exec ns_example ip link set dev ivpn up

这里我添加了路由和 iptables 来使流量在它们之间流动。
从根命名空间,要到达 VPN 端(10.0.0.2),请使用接口 ihost 并将源地址重写为根命名空间端(10.0.0.1)。
ip route add 10.0.0.2/32 dev ihost src 10.0.0.1

在 VPN 命名空间内,从相反的角度做同样的事情。
ip netns exec ns_example ip route add 10.0.0.1/32 dev ivpn src 10.0.0.2

现在,我添加相同的操作,iptables这些操作与之分开ip route,但可以完成类似的任务。

任何数据包10.0.0.2的源地址都会被重写为10.0.0.1
iptables --table nat --append POSTROUTING --destination 10.0.0.2/32 --jump SNAT --to-source 10.0.0.1

完全相同的东西,但是视角和内部命名空间相反。
ip netns exec ns_example iptables --table nat --append POSTROUTING --destination 10.0.0.1/32 --jump SNAT --to-source 10.0.0.2

如果我理解正确的话,源地址重写只是为了确保数据包知道如何返回命名空间,反之亦然。

现在,例如:要访问端口 8080 上的命名空间中的 torrent webui,我会这样做。
任何数据包都会发送到我的 LAN ip(例如:我192.168.1.2:8080在浏览器中输入)。它将把目的地重写到我的 torrent 客户端正在运行的命名空间(例如:)10.0.0.2
iptables --table nat --append OUTPUT --destination 192.168.1.2/32 --protocol tcp --match tcp --dport 8080 --jump DNAT --to-destination 10.0.0.2
此规则专门仅转发端口 8080,并且此规则适用于您访问正在运行服务的 PC 上的服务。网络上的任何其他 PC 都192.168.1.2:8080需要我在答案顶部提到的 PREROUTING 规则(只需将 --dport 80 更改为 --dport 8080 或您需要的任何端口/协议)。

对于使用此方法和 namespaced-vpn.py 脚本的用户。如果您必须重置 vpn,则可能会遇到问题。当命名空间 vpn 检测到目标命名空间内除lo环回接口以外的接口时,它会抛出异常。
当我使用 namespaced-vpn.py 时,当我的目标命名空间已经存在并且它有我的 veth 对时,以下是抛出异常的部分。

if os.path.exists(os.path.join('/var/run/netns', namespace)):
        adapters = _adapter_names(namespace)
        if adapters != [b'lo']:
            LOG.error('Namespace %s already has adapters %s, exiting.' % (namespace, adapters))
            raise Exception

以下是我使用过的一个示例修改。
allowed_adapters = [b'lo', b'ivpn']
if os.path.exists(os.path.join('/var/run/netns', namespace)):
     adapters = _adapter_names(namespace)
     # Iterate and ensure all are in allowed list
     for a in adapters:
         if not a.split(b'@')[0] in allowed_adapters:
            LOG.error('Namespace %s has non-whitelisted adapters %s, exiting.' % (namespace, adapters))
            raise Exception

只需确保在 `def setup_namespace(namespace)` 函数底部为每个你认为已经存在的接口添加这样的调用。用设备名称替换 LO
subprocess.check_call(_enter_namespace_cmd(namespace) + [IP_CMD, 'link', 'set', 'LO', 'up'])

例子:
subprocess.check_call(_enter_namespace_cmd(namespace) + [IP_CMD, 'link', 'set', 'ivpn', 'up'])

对于运行连接到 VPN 接口的 Bittorrent 客户端的用户,您可能会遇到客户端因某种原因随机停止连接的问题。例如,您早上醒来发现所有精彩的下载在您离开 10 分钟后都冻结了。
我相信这个问题是由于 VPN 软件 IE openvpn 因某种原因(错误、ping 重启等)重新连接到 VPN 提供商并被分配了新的 IP 地址。VPN 接口可能需要离线一秒钟才能重置配置,这会导致客户端出现故障(qBittorrent 肯定会这样做)。我开始研究的一个“修复”是编写一个脚本,用于检测 VPN 接口何时收到新 IP 地址或因某种原因离线。该脚本应该优雅地(重要:优雅地避免损坏某些东西/文件系统错误)退出 Torrent 客户端,然后重新启动它。我还没有一个可行的脚本,但这似乎是解决这个问题的一个不错的方法。

答案2

好的。似乎这个问题太复杂了,没有人能真正回答,或者可能之前已经回答过了,只是没人愿意回答。

无论如何:我又花了一天时间研究这个问题,发现既然我只对为应用程序的 Web 界面公开一个端口感兴趣,那么我还不如使用socat。 所以我做了。

创建 namespaced-openvpn 连接并启动该网络命名空间内的应用程序后,我在专用终端中运行了以下命令:

sudo socat tcp-listen:PORT-TO-FORWARD-TO,fork,reuseaddr exec:'ip netns exec vpn socat STDIO tcp-connect\:127.0.0.1\:PORT-TO-LISTEN-TO',nofork

笔记:

  • PORT-TO-FORWARD-TO是我希望端口转发到的端口(在根网络命名空间中)
  • PORT-TO-LISTEN-TO是应用程序的端口,我想将其公开到根命名空间
  • 127.0.0.1我必须使用实际的本地主机 IP(而不是localhost名称,因为它似乎无法正确解析)
  • :确保转义单引号内的双点字符 ( )
  • 我不确定这是否会导致 DNS 泄漏;据我检查,不会,但我不能确定,因为我不知道如何测试它

剩下要做的事情:

  • 自动执行所有命令以启动应用程序并在启动时转发端口:我目前不需要这个,但如果/当我找到如何做到这一点时,我会在这里添加它

相关内容