V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
V2EX 提问指南
KasuganoSoras
V2EX  ›  问与答

一个关于 iptables 的问题

  •  
  •   KasuganoSoras · 2019-09-16 05:19:44 +08:00 · 4773 次点击
    这是一个创建于 1677 天前的主题,其中的信息可能已经有所发展或是发生改变。

    最近在研究用 OpenVPN + iptables 实现内网穿透,比传统 Frp 内网穿透有一个好处就是可以转发访客真实 IP,但是目前遇到一个问题,如何让 OpenVPN 客户端以非全局模式连接?目前内网穿透测试过已经没有问题,但是客户端的所有流量也会经过服务器,我的想法是让客户端只用于内网穿透,而不是代理上网。

    环境信息

    • OpenVPN 网段:10.8.0.0/24
    • OpenVPN 客户端 IP:10.8.0.181
    • OpenVPN 网关:10.8.0.1
    • 服务器外网 IP:123.123.123.123(假设)

    OpenVPN 服务器配置

    port 1194
    proto udp
    dev tun
    sndbuf 0
    rcvbuf 0
    ca ca.crt
    cert server.crt
    key server.key
    dh dh.pem
    auth SHA512
    tls-auth ta.key 0
    topology subnet
    server 10.8.0.0 255.255.255.0
    ifconfig-pool-persist ipp.txt
    push "redirect-gateway def1 bypass-dhcp"
    push "dhcp-option DNS 114.114.114.114"
    push "dhcp-option DNS 223.5.5.5"
    push "dhcp-option DNS 223.6.6.6"
    keepalive 10 120
    cipher AES-256-CBC
    user openvpn
    group openvpn
    persist-key
    persist-tun
    status openvpn-status.log
    log openvpn-tc.log
    verb 3
    crl-verify crl.pem
    client-config-dir /etc/openvpn/server/clients/
    script-security 2
    down-pre
    up /etc/openvpn/server/tc.sh
    down /etc/openvpn/server/tc.sh
    client-connect /etc/openvpn/server/tc.sh
    client-disconnect /etc/openvpn/server/tc.sh
    

    尝试过的操作

    服务器上 iptables 配置:

    iptables -t nat -A PREROUTING -d 123.123.123.123 -p tcp --dport 1:1023 -j DNAT --to-destination 10.8.0.181
    iptables -t nat -A PREROUTING -d 123.123.123.123 -p udp --dport 1:1023 -j DNAT --to-destination 10.8.0.181
    

    以上这个配置,内网穿透正常,可以转发真实 IP,但是客户端所有流量都会经过服务器。

    然后经过我一晚上摸索,做了以下尝试:

    注释掉 OpenVPN 服务器的这一行配置:

    push "redirect-gateway def1 bypass-dhcp"
    

    在服务器配置中增加一行:

    push "route 10.0.0.0 255.0.0.0"
    

    重新启动 OpenVPN 服务器和客户端,在客户端本地 curl 了一下 ip138,发现 IP 地址不再是服务器的 IP 了,也就说明此时是以非全局模式在运行的。

    但是接着又遇到个问题,我发现内网穿透无法连接了,经过 tcpdump 抓包发现数据包已经到达了客户端主机,但是在返回的时候出不去,于是又照着网上教程在服务器上加了一个 iptables 规则:

    iptables -t nat -A POSTROUTING -d 10.8.0.0/24 -j SNAT --to 10.8.0.1
    

    加入这个规则后,内网穿透正常了,但是不能转发真实 IP,显示的访客 IP 都是来自 10.8.0.1 也就是 OpenVPN 的网关 IP。

    有没有大佬知道如何解决这个问题?谢谢!

    第 1 条附言  ·  2019-09-16 06:00:51 +08:00

    补充:在 OpenVPN 客户端以全局模式启动的时候我看到它执行了一个这个:

    ip route add 0.0.0.0/1 via 10.8.0.1
    

    此时内网穿透可以转发真实 IP,但是客户端所有流量都走服务器。

    于是我试了下在客户端删除掉这条规则:

    ip route del 0.0.0.0/1
    

    此时就不再是全局模式了,但是内网穿透也连不上了。

    摸不着头脑.jpg

    第 2 条附言  ·  2019-09-16 07:59:04 +08:00

    研究了一晚上,到现在还没睡,实在想不到其他方法了……

    img

    29 条回复    2023-12-30 19:30:21 +08:00
    hangvane
        1
    hangvane  
       2019-09-16 08:12:24 +08:00   ❤️ 1
    同一个世界,同一个问题,我也遇到过了最后选择 frp https://www.v2ex.com/t/580901
    huangya
        2
    huangya  
       2019-09-16 08:23:42 +08:00 via iPhone
    请再详细描述一下你的内网穿透是什么意思。你最终的应用场景是什么
    KasuganoSoras
        3
    KasuganoSoras  
    OP
       2019-09-16 08:25:29 +08:00
    @hangvane #1 hhh 我就是做 Frp 的,因为苦于 Frp 不能转发访客真实 IP 所以想试试用 OpenVPN,结果又遇到了你遇到过的问题......
    KasuganoSoras
        4
    KasuganoSoras  
    OP
       2019-09-16 08:27:13 +08:00
    @huangya #2 就是和 Frp 一样的用法,只不过是想借助 VPN 协议可以转发访问者 IP 的特性(用 Frp 转发后的访问者 IP 全都是 127.0.0.1,目的就是解决这个问题),最终用途还是将内网的服务通过 VPN 暴露到公网。
    KasuganoSoras
        5
    KasuganoSoras  
    OP
       2019-09-16 08:29:05 +08:00
    目前遇到的技术难题就是,如何让 VPN 只作为内网穿透使用,而不是所有流量都经过服务器代理,同时还要能够转发访问者的真实 IP,这就是 1 楼之前遇到过的问题,没想到我又踩坑了。
    yibei
        6
    yibei  
       2019-09-16 08:41:45 +08:00 via iPhone
    @KasuganoSoras 这种只能对特定的端口进行转发才能实现吧
    paradislover
        7
    paradislover  
       2019-09-16 08:45:28 +08:00 via Android
    不推送网关,推送 route,客户端 iroute
    xduanx
        8
    xduanx  
       2019-09-16 08:55:18 +08:00
    客户端 tcpdump 的时候看到的源 IP 是什么
    如果不在 10.0.0.0 255.0.0.0 这里面(比如 192.168.0.0/16 或者 172.16.0.0/12 ),那就 push "route XXXX XXXX"
    KasuganoSoras
        9
    KasuganoSoras  
    OP
       2019-09-16 09:04:19 +08:00
    @paradislover #7 能具体说一下如何操作吗?看了下谷歌找的文档,不太理解这个 iroute 怎么用
    KasuganoSoras
        10
    KasuganoSoras  
    OP
       2019-09-16 09:05:13 +08:00
    @xduanx #8 客户端 tcpdump 看到的是访问者的源 IP
    zbinlin
        11
    zbinlin  
       2019-09-16 09:08:00 +08:00
    这是路由问题,你删除了 `ip route add 0.0.0.0/1 via 10.8.0.1` 就要加上 `ip route add 10.8.0.0/24 via 10.8.0.1` 呀
    KasuganoSoras
        12
    KasuganoSoras  
    OP
       2019-09-16 09:17:10 +08:00
    @zbinlin #11 我试了一下,还是之前的问题,OpenVPN 传递到客户端的 IP 地址是访客的源 IP,客户端这边由于没有了 0.0.0.0/1 这条规则,就没办法将数据包再传回给 VPN 网关,也就是上面说的数据只能进不能出的问题。
    huangya
        13
    huangya  
       2019-09-16 09:21:03 +08:00
    @KasuganoSoras 有看了下你的问题,基本弄清楚了需求。
    >但是接着又遇到个问题,我发现内网穿透无法连接了,经过 tcpdump 抓包发现数据包已经到达了客户端主机,但是在返回的时候出不去,于是又照着网上教程在服务器上加了一个 iptables 规则:

    前面步骤保留,在这里不要在服务器上添加 iptables 规则,建议在客户端上用策略路由。我写了一些没有验证过的命令,你可以在客户端试试看(用 root 权限执行),有什么问题可以后续交流。
    iptables -t mangle -A OUTPUT -p udp -m udp --sport 1:1023 -j MARK --set-xmark 0x1/0xffffffff
    iptables -t mangle -A OUTPUT -p tcp -m tcp --sport 1:1023 -j MARK --set-xmark 0x1/0xffffffff
    echo 1 openvpn-tun >>/etc/iproute2/rt_tables
    ip route add default via 192.168.5.1 dev tun0 table openvpn-tun
    ip rule add from all fwmark 0x1 lookup openvpn-tun
    huangya
        14
    huangya  
       2019-09-16 09:22:50 +08:00
    @huangya
    不好意思,有个命令 ip 写错了
    ip route add default via 192.168.5.1 dev tun0 table openvpn-tun
    改为
    ip route add default via 10.8.0.1 dev tun0 table openvpn-tun
    wwqgtxx
        15
    wwqgtxx  
       2019-09-16 09:23:06 +08:00
    提个尝试性建议,把你的 server 绑定在 tun 端口上,强制其在 tun 端口上收发数据,这样应该就能不受数据只进不出的影响(其实只是数据出的时候没走 OVN 而是直接从网卡出去了,所以被外层丢包)
    KasuganoSoras
        16
    KasuganoSoras  
    OP
       2019-09-16 09:27:39 +08:00
    @huangya #13 感谢感谢,我去测试一下

    @wwqgtxx #15 好的,可以尝试一下
    Meltdown
        17
    Meltdown  
       2019-09-16 09:31:34 +08:00 via Android
    用 docker 或者虚拟机跑服务,docker 或者虚拟机连 openvpn…
    KasuganoSoras
        18
    KasuganoSoras  
    OP
       2019-09-16 10:00:58 +08:00
    经过了 N 次尝试均以失败告终……甚至还两次把机器搞断网 😓
    算了,还是老实用全局模式吧……感谢各位提供的帮助,谢谢!
    acess
        19
    acess  
       2019-09-16 12:06:54 +08:00
    这个就是路由表里默认网关的问题吧……
    gowa
        20
    gowa  
       2019-09-16 12:14:04 +08:00 via Android
    楼主我有个疑问。vpn 你确定你是用的 隧道形式 还是 nat 形式。在 nat 形式 是在网络层 转发 你怎么可能获取真实 ip。 只有在隧道形式 对原始报文进行包裹的才可以
    neroxps
        21
    neroxps  
       2019-09-16 15:33:31 +08:00
    @gowa #20 一般 OPENVPN 都是 Route 模式,不需要 nat,要 nat 是因为双方内网地址是一样的情况下才需要配置 NAT 做地址转换,防止干扰出口。
    neroxps
        22
    neroxps  
       2019-09-16 15:45:46 +08:00
    楼主的问题感觉搞错方向,和 iptables 半毛钱关系都没有,楼主要的其实就是内网一台 openvpn 服务器提供 VPN 服务,然后让其访问内外的服务器而已。

    如果 openvpn 不是在内网默认路由上做的,那么需要在内网网关上加一个路由条目,告诉内网的所有服务器 VPN 的网络地址的网关是谁。

    例如 (这里假设 A 的内网地址是 192.168.2.x 网段,防止与 ovpn 环境内网冲突)外网 A 通过 openvpn 客户端访问 内网服务器 B,内外网关是 192.168.1.1,内网服务器是 192.168.1.2 内网 openvpn 服务器是 192.168.1.3 ovpn 地址池 10.10.10.0-254

    那么流量是这样的,首先 A 的 openvpn 连接上,配置文件上写上 `route 192.168.1.0 255.255.255.0` ,此时 ovpn 客户端连接成功后,会自动在客户端系统上添加一条路由条目

    **目的地址 192.168.1.0 网关是 ovpn 的服务器地址 10.10.10.1。**
    A 客户端的接口也多了一个地址
    **10.10.10.2 255.255.255.0**

    本地默认网关不变,依然 A 的内网 DHCP 默认网关。

    然后访问 192.168.1.2 内网服务器,发现网络不可达,因为数据包是先从 A ( 10.10.10.2 ) → ovpn ( 10.10.10.1 ) → B ( 192.168.1.2 ),B 是收到包了,但是他根本不知道 OVPN 10.10.10.x 段到底怎么去,所以他发给默认路由 192.168.1.1.但是 192.168.1.1 也不知道 10.10.10.x 的段到底怎么去,而且 10 开头也不是公网地址,所以网关把包丢弃了。

    故此,解决这个问题只需要在 B 服务器的网关路由上设置一个路由条目
    **目标网络 10.10.10.0 掩码 255.255.255.0 下一跳地址 192.168.1.3**

    这样网络就通了。
    neroxps
        23
    neroxps  
       2019-09-16 15:47:07 +08:00
    楼主的 ovpn 服务器虚拟地址池不应该设成内网 ip 一样,因为 ovpn 本质上是新建了一个 bridge 二层不通。
    KasuganoSoras
        24
    KasuganoSoras  
    OP
       2019-09-16 18:11:14 +08:00
    @neroxps #22 不是的,OpenVPN 服务端是搭建在一台拥有公网 IP 的服务器,然后客户端是运行在一个没有公网的家用电脑上,目的就是使用 VPN 将客户端电脑的某些端口映射到服务器端(也就和传统的端口映射一个原理,但是用的是 VPN 协议)。

    如果删掉了 0.0.0.0/1 via 10.8.0.1 这条规则,客户端就不再是全局模式了,同时服务器和客户端也能正常通信,但是外部访问者的数据包路线就变成这样了:

    (假设)访问者 11.22.33.44 》 OpenVPN 服务器的公网 IP ( 123.123.123.123 ) 》 通过 PREROUTING 转发给 OpenVPN 客户端 10.8.0.181 》 OpenVPN 客户端收到并处理请求 》 尝试返回数据包到源地址( 11.22.33.44 )》 缺失 0.0.0.0/1 路由规则(被删掉了)》 网络不可达(数据包只能接收无法返回)

    而如果把 0.0.0.0/1 via 10.8.0.1 这条规则加回去,就会让客户端本机的所有数据包走 OpenVPN 转发,那么又会回到问题的起点。唯一解决这个死循环的办法就是在服务器上添加个 POSTROUTING 规则,但那样又会让访问者的 IP 全部被 SNAT 转换为网关的 IP。

    除非有办法在客户端识别出某个数据包是来自哪个网卡设备的(比如 OpenVPN 的 tun0 ),然后在返回的时候让它走指定的网关再回去才可能实现,但是我谷歌找了很久也没有找到相关的内容……😓
    neroxps
        25
    neroxps  
       2019-09-16 18:18:32 +08:00
    你意思是你公网搭建了一个 ovpn 服务器,然后两个没有公网 IP 的客户端都连上去,实现互通??

    >>(假设)访问者 11.22.33.44 》 OpenVPN 服务器的公网 IP ( 123.123.123.123 ) 》 通过 PREROUTING 转发给 OpenVPN 客户端 10.8.0.181 》 OpenVPN 客户端收到并处理请求 》 尝试返回数据包到源地址( 11.22.33.44 )》 缺失 0.0.0.0/1 路由规则(被删掉了)》 网络不可达(数据包只能接收无法返回)

    那么你这个 ovpn 服务器当你连接起来之后,就是由三个网络。
    1. A 客户端本地网络 192.168.2.0 ovpn 地址池 IP 10.10.10.1
    2. B 客户端本地网络 192.168.1.0 ovpn 地址池 IP 10.10.10.2
    3. ovpn 服务器地址池网络 10.10.10.0

    理论上 你 A 访问 B 只需要访问 10.10.10.2 即可。应该是通的啊。你不应该访问 B 的局域网地址。因为 B 的局域网地址是不可达的。
    XiaoxiaoPu
        26
    XiaoxiaoPu  
       2019-09-16 18:33:02 +08:00
    huangya 的思路是对的,楼主可以把你尝试的配置具体发一下,看看是哪里有问题了
    KasuganoSoras
        27
    KasuganoSoras  
    OP
       2019-09-16 18:35:14 +08:00
    @neroxps #25 不是互通,https://zerohosts.com/cart.php?gid=14 其实就是这个(

    用 OpenVPN 实现内网穿透,把 A 客户端的 1000-1100 端口映射到服务器的 1001-1100,然后 B 客户端的 1101-1200 映射到服务器的 1101-1200,C 继续分配下 100 个端口,到大概就是这样一个操作。

    客户端数量是不定的,随时可能会增加。而且有个问题,如果配置过于复杂,可能有些小白用户不会用……如果想让普通用户去配置什么路由规则,那样难度是 ++++

    所以考虑众多因素后还是放弃了 x
    KasuganoSoras
        28
    KasuganoSoras  
    OP
       2019-09-16 18:38:57 +08:00
    @XiaoxiaoPu #26 你这么一说我倒是发现了一个地方有问题,那就是复制
    echo 1 openvpn-tun >>/etc/iproute2/rt_tables
    这一行的时候好像 1 和 openvpn-tun 连一块了,变成了 1openvpn-tun,我感觉是这个问题。。。
    我再去试一下
    RodHamlet
        29
    RodHamlet  
       110 天前
    @KasuganoSoras 大佬,有搞定 openvpn 非全局连内网和内网能显示 VPN 客户端真实 IP 了吗?前者我是用 iptables 实现了,但是后者就犯难了!不改显示的全是 openvpn 服务器的 IP ,改了客户端又不能正常连内网其他 IP !
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3633 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 32ms · UTC 05:02 · PVG 13:02 · LAX 22:02 · JFK 01:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.