透明代理

家庭网络配置

现在宽带入户,运营商提供的光猫, 除了有基本的拨号功能, 还在用户侧划分了子网(192.168.1.0/24), 并提供子网与公网之间的路由转发功能,在子网与公网间做了一定的隔离(比如入站DROP)。实际上是 光猫(PPPoE拨号连接ISP功能)+ 低配路由器(数据转发)+ 简单防火墙(包过滤)。因此本文中,将运营商提供的光猫 称为 “光猫路由器”。

一般家庭无线覆盖,都是 光猫路由器宽带入户+ 无线路由器。

————————————————————————————————————————————————————————–

配置1:(光猫路由LAN-无线路由器LAN)

光猫路由(入户路由器)配置: 用户侧网段192.168.1.0/24. 光猫LAN地址192.168.1.1. 光猫开启DHCP服务。DHCP范围 192.168.1.100-192.168.1.200, 其他地址用于绑定静态地址设备。

无线路由器配置: 无线路由器LAN口直连光猫路由器LAN口, 无线路由器WAN口连接方式不用管,用不到。 关闭无线路由器DHCP服务(使用静态IP)。 无线路由器LAN口配置为其他ip,比如192.168.1.250. 注意:光猫中也应该绑一下此无线路由器设备IP-MAC。 无线路由器设置无线SSID,加密方式,密码。

此时,家庭网络设备都在192.168.1.0/24网段,有线无线都在。DLNA投屏,NAS存储,内网共享打印机等也都应该正常。


配置2:(光猫路由LAN-无线路由器WAN,光猫和无线路由器各使用一个网段)

光猫路由(入户路由器)配置: LAN侧(用户侧)网段192.168.1.0/24. LAN地址192.168.1.1. 光猫路由开启DHCP服务。

无线路由器配置: 无线路由器WAN口连光猫路由器LAN口, 无线路由器WAN口连接方式选DHCP,即由光猫路由自动分配无线路由器的ip地址。 无线路由器的LAN侧网段选用一个其他网段,比如192.168.2.0/24. 无线路由器设置无线SSID,加密方式,密码。

此时,家庭网络设备尽量都放到192.168.2.0/24网段(连接到无线路由器下),DLNA投屏,NAS存储,内网共享打印机等才可正常访问。 或者,如果光猫路由和无线路由器都支持的话,配一下 192.168.1.0/24 和 192.168.2.0/24网段互访的静态路由,也可以。 .


配置3: (光猫路由LAN-无线路由WAN, 光猫使用桥接模式仅做光电信号转换, 无线路由器上PPPoE拨号)

这个需要管理员账号进光猫,改桥接模式。 无线路由器WAN口选择PPPoE拨号。 光猫管理员账号以及PPPoE账号信息,需要咨询运营商或者当地电信运维人员。

除非无线路由器性能确实比较强,或者用的是软路由配置透明代理等,没必要这么搞。略。


配置4: (光猫路由+无线MESH) 没用过,不懂,不会,略。应该和常规无线路由器差不多。


我的配置(配置4): (光猫路由LAN-无线AC-多个无线AP, 可看作是上面配置1 的扩展)

家里各房间距离较远, 使用单个家庭无线路由器,无线信号无法完全覆盖。后来改为了光猫路由器+ AC+多无线AP的连接方式。 无线管理器AC是Mercury水星的mac100. 无线AP是Mercury水星的macp1900

光猫路由,无线管理器AC,无线AP星型拓扑。

光猫路由(入户路由器)配置: 用户侧网段192.168.1.0/24. 光猫LAN地址192.168.1.1. 光猫关闭DHCP服务。

无线控制器AC配置: 无线控制器AC地址192.168.1.253. 无线控制器AC连接光猫路由器LAN口。 无线控制器AC设置默认网关192.168.1.1.默认DNS为阿里223.5.5.5,备用DNS为114.114.114.114. 无线控制器AC的DHCP服务。选择为所有设备分配IP。DHCP范围 192.168.1.100-192.168.1.200, 其他地址用于绑定静态地址设备。 无线路由器设置无线SSID,加密方式,密码。

此时,家庭网络设备都在192.168.1.0/24网段,有线无线都在。DLNA投屏,NAS存储,内网共享打印机等也都应该正常。

配置透明代理

以前一直以为配置家庭透明代理需要软路由,并设置光猫桥接的。

后来发现家里这台无线AC允许修改DHCP服务的网关与DNS。那么很好,可以用旁路由的方式来设置透明代理了。

之前按上节 配置4 的方式设置了家庭网络。 DHCP服务由无线控制器AC提供。 家里有一台Linux主机(archlinux),常年开着, 无线控制器为其MAC绑定了固定IP: 192.168.1.7。

这台Linux主机之前安装了v2ray-core代理。 没有自建vps,v2ray代理直接订购的搬瓦工的代理服务。 开始使用的是qv2ray客户端(GUI),但是默认不代理 命令行执行的指令。 如果要命令行yay -Sy安装软件,还需要明确指明代理

http_proxy=http://127.0.0.1:8889
https_proxy=http://127.0.0.1:8889
all_proxy=socks5://127.0.0.1:1089
ALL_PROXY=socks5://127.0.0.1:1089

我也不太清楚这几个环境变量是否需要大写,一般是大小写都配一遍。

而浏览器,可以打开系统proxy配置界面,与上面类似,也与windows类似。

有几个问题: qv2ray是用户级别的应用程序, 换个账号登录就没得了。 qv2ray定时更新subscription订阅功能好像失效了,代理总是用个几天都需要手动更新一下代理链接。 主机重启,都需要启动此应用。

而qv2ray是依赖于v2ray (v2ray-core)的, 就在想,要不直接用 v2ray (v2ray-core)得了, 而且还可以配置为系统级别的 systemd服务,开机自动启动。

而且我发现官方说, 通过subscription订阅链接更新,代理配置的功能不属于基础功能,各种GUI客户端可能不兼容,他们没做。 看了下qv2ray使用的v2ray-core配置,是存到了其他位置的。先向/etc/v2ray/config.json拷一份,作为模板进一步修改。具体怎么改,这就需要看官方文档了。。。,(推荐官网的新编白话文教程 https://guide.v2fly.org/ 当然,可能有几个fork版本的网站, 看起来基本上相同)

首先,使用qv2ray配置,拷贝到/etc/v2ray/config.json,关闭qv2ray,启动原生 v2ray服务:

systemctl start v2ray.service
systemctl enable v2ray.service

发现是可以正常使用的。

这里有个问题,这份配置是有时效性的。过个几天,配置中的vps服务器就会失效的。 需要向qv2ray那样更新vps服务器订阅信息。

这里存个配置模板,然后用shell脚本来更新订阅。订阅格式是v2rayN形式的。

[wangjm@archlinux ~]$ cat /usr/local/v2ray/config_template_bak_20221008_1446.json 
{
    "api": {
        "services": [
            "ReflectionService",
            "HandlerService",
            "LoggerService",
            "StatsService"
        ],
        "tag": "QV2RAY_API"
    },
    "dns": {
        "servers": [
            "8.8.4.4",
            "1.1.1.1",
            "8.8.8.8"
        ]
    },
    "fakedns": {
        "ipPool": "198.18.0.0/15",
        "poolSize": 65535
    },
    "inbounds": [
        {
            "listen": "127.0.0.1",
            "port": 15490,
            "protocol": "dokodemo-door",
            "settings": {
                "address": "127.0.0.1"
            },
            "sniffing": {
            },
            "tag": "QV2RAY_API_INBOUND"
        },
        {
            "listen": "0.0.0.0",
            "port": 8889,
            "protocol": "http",
            "settings": {
                "allowTransparent": true,
                "timeout": 300
            },
            "sniffing": {
            },
            "tag": "http_IN"
        },
        {
            "listen": "0.0.0.0",
            "port": 1089,
            "protocol": "socks",
            "settings": {
                "auth": "noauth",
                "ip": "127.0.0.1",
                "udp": true
            },
            "sniffing": {
            },
            "tag": "socks_IN"
        }
    ],
    "log": {
        "loglevel": "warning",
    "access": "/tmp/v2ray_access.log",
    "error": "/tmp/v2ray_error.log"
    },
    "outbounds": [
        {
            "protocol": "vmess",
            "sendThrough": "0.0.0.0",
            "settings": {
                "vnext": [
                    {
                        //"address": "104.245.99.207",
                        //"port": 18087,
            //insert vps address here
            //insert vps port here
                        "users": [
                            {
                                //"id": "4bc74eef-0889-4b6b-86ce-3d5e24a62db4",
                //insert vps user id here
                                "security": "aes-128-gcm"
                            }
                        ]
                    }
                ]
            },
            "streamSettings": {
                "tlsSettings": {
                    "disableSystemRoot": false
                },
                "xtlsSettings": {
                    "disableSystemRoot": false
                }
            },
            "tag": "PROXY"
        },
        {
            "protocol": "freedom",
            "sendThrough": "0.0.0.0",
            "settings": {
                "domainStrategy": "AsIs",
                "redirect": ":0"
            },
            "streamSettings": {
            },
            "tag": "DIRECT"
        },
        {
            "protocol": "blackhole",
            "sendThrough": "0.0.0.0",
            "settings": {
                "response": {
                    "type": "none"
                }
            },
            "streamSettings": {
            },
            "tag": "BLACKHOLE"
        }
    ],
    "policy": {
        "system": {
            "statsInboundDownlink": true,
            "statsInboundUplink": true,
            "statsOutboundDownlink": true,
            "statsOutboundUplink": true
        }
    },
    "routing": {
        "domainMatcher": "mph",
        "domainStrategy": "AsIs",
        "rules": [
            {
                "inboundTag": [
                    "QV2RAY_API_INBOUND"
                ],
                "outboundTag": "QV2RAY_API",
                "type": "field"
            },
            {
                "ip": [
                    "geoip:private"
                ],
                "outboundTag": "DIRECT",
                "type": "field"
            },
            {
                "ip": [
                    "geoip:cn"
                ],
                "outboundTag": "DIRECT",
                "type": "field"
            },
            {
                "domain": [
                    "geosite:cn"
                ],
                "outboundTag": "DIRECT",
                "type": "field"
            }
        ]
    },
    "stats": {
    }
}
[wangjm@archlinux ~]$ cat /usr/local/v2ray/update_v2ray_outbound_from_subscription.sh 
#!/bin/bash

echo "-----------------------start updating v2ray outbound setting from subscription link--------------------------------------------------------"
echo "start time: `date`"
echo

# a subscription link of v2rayN format
# https://github.com/2dust/v2rayN/wiki/%E8%AE%A2%E9%98%85%E5%8A%9F%E8%83%BD%E8%AF%B4%E6%98%8E
# NOTE: from my service provider: "noss=1" indicates no shadowsocks link (only v2ray/vmess service link returned)
URL_SUBSCRIPTION_V2RAY_N="https://justmysocks5.net/members/getsub.php?service=381845&id=4bc74eef-0889-4b6b-86ce-3d5e24a62db4&noss=1"
echo "v2rayN subscription link: $URL_SUBSCRIPTION_V2RAY_N"
echo

RESULT_ORIGIN=`curl $URL_SUBSCRIPTION_V2RAY_N`
echo
echo "original result of this api:"
echo "$RESULT_ORIGIN"
echo
# the result is encoded by base64, like this:
# dm1lc3M6Ly9leUp3Y3lJNklrcE5VeTB6T0RFNE5EVkFZek14Y3pNdWFtRnRhbUZ0Y3pNdWJtVjBPakU0TURnM0lpd2ljRzl5ZENJNklqRTRNRGczSWl3aWFXUWlPaUkwWW1NM05HVmxaaTB3T0RnNUxUUmlObUl0T0RaalpTMHpaRFZsTWpSaE5qSmtZalFpTENKaGFXUWlPakFzSW01bGRDSTZJblJqY0NJc0luUjVjR1VpT2lKdWIyNWxJaXdpZEd4eklqb2libTl1WlNJc0ltRmtaQ0k2SWpFM015NHlOREl1TVRJMkxqSXpOeUo5CnZtZXNzOi8vZXlKd2N5STZJa3BOVXkwek9ERTRORFZBWXpNeGN6UXVhbUZ0YW1GdGN6TXVibVYwT2pFNE1EZzNJaXdpY0c5eWRDSTZJakU0TURnM0lpd2lhV1FpT2lJMFltTTNOR1ZsWmkwd09EZzVMVFJpTm1JdE9EWmpaUzB6WkRWbE1qUmhOakprWWpRaUxDSmhhV1FpT2pBc0ltNWxkQ0k2SW5SamNDSXNJblI1Y0dVaU9pSnViMjVsSWl3aWRHeHpJam9pYm05dVpTSXNJbUZrWkNJNklqSXdOQzR4TWpRdU1UZ3pMalUzSW4wCnZtZXNzOi8vZXlKd2N5STZJa3BOVXkwek9ERTRORFZBWXpNeGN6VXVhbUZ0YW1GdGN6TXVibVYwT2pFNE1EZzNJaXdpY0c5eWRDSTZJakU0TURnM0lpd2lhV1FpT2lJMFltTTNOR1ZsWmkwd09EZzVMVFJpTm1JdE9EWmpaUzB6WkRWbE1qUmhOakprWWpRaUxDSmhhV1FpT2pBc0ltNWxkQ0k2SW5SamNDSXNJblI1Y0dVaU9pSnViMjVsSWl3aWRHeHpJam9pYm05dVpTSXNJbUZrWkNJNklqRXdOQzR5TkRVdU9Ua3VNakEzSW4wCnZtZXNzOi8vZXlKd2N5STZJa3BOVXkwek9ERTRORFZBWXpNeGN6Z3dNUzVxWVcxcVlXMXpNeTV1WlhRNk1UZ3dPRGNpTENKd2IzSjBJam9pTVRnd09EY2lMQ0pwWkNJNklqUmlZemMwWldWbUxUQTRPRGt0TkdJMllpMDRObU5sTFROa05XVXlOR0UyTW1SaU5DSXNJbUZwWkNJNk1Dd2libVYwSWpvaWRHTndJaXdpZEhsd1pTSTZJbTV2Ym1VaUxDSjBiSE1pT2lKdWIyNWxJaXdpWVdSa0lqb2lNak11T0RNdU1qSTBMamc1SW4w

RESULT_DECODED_ONCE=`echo $RESULT_ORIGIN | base64 -i -d`
echo "result of the first decoding:"
echo $RESULT_DECODED_ONCE
echo
# decode once, schema is visible, but values is still encoded, like this:
# vmess://eyJwcyI6IkpNUy0zODE4NDVAYzMxczMuamFtamFtczMubmV0OjE4MDg3IiwicG9ydCI6IjE4MDg3IiwiaWQiOiI0YmM3NGVlZi0wODg5LTRiNmItODZjZS0zZDVlMjRhNjJkYjQiLCJhaWQiOjAsIm5ldCI6InRjcCIsInR5cGUiOiJub25lIiwidGxzIjoibm9uZSIsImFkZCI6IjE3My4yNDIuMTI2LjIzNyJ9
# vmess://eyJwcyI6IkpNUy0zODE4NDVAYzMxczQuamFtamFtczMubmV0OjE4MDg3IiwicG9ydCI6IjE4MDg3IiwiaWQiOiI0YmM3NGVlZi0wODg5LTRiNmItODZjZS0zZDVlMjRhNjJkYjQiLCJhaWQiOjAsIm5ldCI6InRjcCIsInR5cGUiOiJub25lIiwidGxzIjoibm9uZSIsImFkZCI6IjIwNC4xMjQuMTgzLjU3In0
# vmess://eyJwcyI6IkpNUy0zODE4NDVAYzMxczUuamFtamFtczMubmV0OjE4MDg3IiwicG9ydCI6IjE4MDg3IiwiaWQiOiI0YmM3NGVlZi0wODg5LTRiNmItODZjZS0zZDVlMjRhNjJkYjQiLCJhaWQiOjAsIm5ldCI6InRjcCIsInR5cGUiOiJub25lIiwidGxzIjoibm9uZSIsImFkZCI6IjEwNC4yNDUuOTkuMjA3In0
# vmess://eyJwcyI6IkpNUy0zODE4NDVAYzMxczgwMS5qYW1qYW1zMy5uZXQ6MTgwODciLCJwb3J0IjoiMTgwODciLCJpZCI6IjRiYzc0ZWVmLTA4ODktNGI2Yi04NmNlLTNkNWUyNGE2MmRiNCIsImFpZCI6MCwibmV0IjoidGNwIiwidHlwZSI6Im5vbmUiLCJ0bHMiOiJub25lIiwiYWRkIjoiMjMuODMuMjI0Ljg5In0


# paste some '=' char at the end of every line  (because result of first decode is not aligned)
# cut (drop the part of schema, and drop extra '=' of every line)
# decode the part of value for the second time
RESULT_DECODED_TWICE=`echo "$RESULT_DECODED_ONCE" | xargs -I {} sh -c 'echo -n "{}===="| cut -c9-240 | base64 -d | xargs echo'`
echo "result of the second decoding:"
#echo $RESULT_DECODED_TWICE
echo $RESULT_DECODED_TWICE | xargs -d' ' -I {} echo "{}"
echo

# we get some config of json format, like this.
# {ps:JMS-381845@c31s3.jamjams3.net:18087,port:18087,id:4bc74eef-0889-4b6b-86ce-3d5e24a62db4,aid:0,net:tcp,type:none,tls:none,add:173.242.126.237}
# {ps:JMS-381845@c31s4.jamjams3.net:18087,port:18087,id:4bc74eef-0889-4b6b-86ce-3d5e24a62db4,aid:0,net:tcp,type:none,tls:none,add:204.124.183.57}
# {ps:JMS-381845@c31s5.jamjams3.net:18087,port:18087,id:4bc74eef-0889-4b6b-86ce-3d5e24a62db4,aid:0,net:tcp,type:none,tls:none,add:104.245.99.207}
# {ps:JMS-381845@c31s801.jamjams3.net:18087,port:18087,id:4bc74eef-0889-4b6b-86ce-3d5e24a62db4,aid:0,net:tcp,type:none,tls:none,add:23.83.224.89}
# {ps:JMS-381845@c31s3.jamjams3.net:18087,port:18087,id:4bc74eef-0889-4b6b-86ce-3d5e24a62db4,aid:0,net:tcp,type:none,tls:none,add:173.242.126.237}


V2RAY_OUTBOUND_ADDR=
V2RAY_OUTBOUND_PORT=
V2RAY_OUTBOUND_USER=
V2RAY_OUTBOUND_PROTOCOL=vmess

FIRST_JSON=
for i in $RESULT_DECODED_TWICE
do
    #echo $i
    FIRST_JSON=$i
    #only use the first config
    break
done

# only use the first config, like this:
# {ps:JMS-381845@c31s3.jamjams3.net:18087,port:18087,id:4bc74eef-0889-4b6b-86ce-3d5e24a62db4,aid:0,net:tcp,type:none,tls:none,add:173.242.126.237}
echo "only use the first config:"
echo $FIRST_JSON
echo


# reformat to entrys, like this:
# ps:JMS-381845@c31s3.jamjams3.net:18087
# port:18087
# id:4bc74eef-0889-4b6b-86ce-3d5e24a62db4
# aid:0
# net:tcp
# type:none
# tls:none
# add:173.242.126.237
CONFIG_ENTRYS=`echo $FIRST_JSON | awk -F '[,{}]' '{for(j=1;j<=NF;j++)  if($j != "")print $j}'`
echo "parses json config to entrys:"
echo "$CONFIG_ENTRYS"
echo


for k in $CONFIG_ENTRYS
do
    #echo $k
    if [[ "$k" == port* ]]; then 
        V2RAY_OUTBOUND_PORT=`echo $k | cut -d: -f2` 
    fi

    if [[ "$k" == id* ]]; then 
        V2RAY_OUTBOUND_USER=`echo $k | cut -d: -f2` 
    fi

    if [[ "$k" == add* ]]; then 
        V2RAY_OUTBOUND_ADDR=`echo $k | cut -d: -f2` 
    fi
done

echo "parsed vps address: $V2RAY_OUTBOUND_ADDR"
echo "parsed vps port: $V2RAY_OUTBOUND_PORT"
echo "parsed vps user id: $V2RAY_OUTBOUND_USER"
echo

# remove old config file
echo "remove old /etc/v2ray/config.json file"
rm -f /etc/v2ray/config.json

# copy the config_template.json file
echo "copy /usr/local/v2ray/config_template.json to /etc/v2ray/config.json"
cp /usr/local/v2ray/config_template.json /etc/v2ray/config.json
# insert config lines of vps address, port, user id.
echo "insert config lines of vps address, port, user id into /etc/v2ray/config.json"
sed -i "/\/\/insert vps address here/i \                        \"address\"\:\"$V2RAY_OUTBOUND_ADDR\"," /etc/v2ray/config.json
sed -i "/\/\/insert vps port here/i \                        \"port\"\:$V2RAY_OUTBOUND_PORT," /etc/v2ray/config.json
sed -i "/\/\/insert vps user id here/i \                                \"id\"\:\"$V2RAY_OUTBOUND_USER\"," /etc/v2ray/config.json
echo

#restart v2ray.service
echo "restart v2ray.service now"
systemctl restart v2ray.service
echo "restart tproxyrule.service now"
systemctl restart tproxyrule.service
echo


echo "sleep 5s"
echo
sleep 5s

#test connection
result_http_code=`curl -so /dev/null -w "%{http_code}"  google.com`
echo "result_http_code: $result_http_code"
echo

if [ "$result_http_code" != "301" ]
then
    echo "could not connect to google.com"
    echo "stop v2ray.service"
    systemctl stop v2ray.service
    echo "stop tproxyrule.service"
    systemctl stop tproxyrule.service
fi

echo
echo "end time: `date`"
echo "-----------------------ending of updating v2ray outbound setting from subscription link--------------------------------------------------------"

OK, 局域网 archlinux 192.168.1.7 就可以一直使用代理上网了。

配成定时任务,每小时自动更新一次。

[root@archlinux dnspod-shell]# crontab -l
09 * * * * /usr/local/v2ray/update_v2ray_outbound_from_subscription.sh >> /tmp/v2ray_update_outbound.log 2>&1

前面说了, 发现家里无线AC允许修改DHCP服务的网关与DNS。那么很好,可以用旁路由的方式来设置透明代理了。

之前按上节 配置4 的方式设置了家庭网络。 DHCP服务由无线控制器AC提供。

具体想法是, 家庭局域网中其他设备连入时, 由DHCP服务指定 网关地址为 192.168.1.7这台主机,DNS地址也为192.168.1.7这台主机。 然后通过这台主机转发所有局域网流量到 192.168.1.1. 这也就是他们说的旁路由吧。

转发过程中可以做一些手脚, 从而实现透明代理。

首先,192.168.1.7这台主机需要先开启转发:

vim /etc/sysctl
net.ipv4.ip_forward=1

重启。

安装iptables, 实现PREROUTING过滤TPROXY方式代理。本机出站 OUTPUT也代理一下。 结合v2ray TPROXY代理的支持,可以实现局域网全主机,全端口的tcp,udp代理。

具体的配置方式,见 官网 新白话文教程-应用篇-透明代理(TPROXY),https://guide.v2fly.org/app/tproxy.html#%E8%AE%BE%E7%BD%AE%E7%BD%91%E5%85%B3

配好之后,局域网中连接无线的手机, 可以打开 google.com.…

感觉美滋滋。

第二天,发现从公司无法连接到家里了。 (家里用的是电信宽带。打电话10000要客服给了ipv4公网地址,但是公网地址是经常变的。后来买了个域名配 DDNS做动态域名解析。动态域名解析用的是DnsPod提供的接口和免费服务。家里192.168.1.7这台主机定时每15分钟,运行一次脚本,获取一下家里主机的公网ip,然后调用DnsPod接口更新域名解析) 很显然,是动态域名解析失败了。

域名解析脚本用的是别人提供的。

[root@archlinux dnspod-shell]# git remote -v
origin  git@github.com:rehiy/dnspod-shell.git (fetch)
origin  git@github.com:rehiy/dnspod-shell.git (push)

定时任务

[root@archlinux dnspod-shell]# crontab -l
*/15 * * * * /usr/local/dnspod-shell/ddnspod.sh

忘记将日志重定向到单独的地方了。 那就看下定时任务的系统日志

journalctl -u cronie

然后发现是解析到了代理服务器的地址了。

查本机公网ip的流程,是向提供此服务网站发一个http请求,对方看下请求来源, 将这个请求来源写到报文里再发回来。 显然,提供公网IP查询服务的网站被当作是外网网站了,走了代理,对方返回了代理地址。

v2ray配置中,做如下修改: dns解析“v4.myip.la” 此域名时,也走国内DNS解析。 路由此“v4.myip.la”域名(对应的IP数据包时)走直连。

具体配置如下。

[wangjm@archlinux dnspod-shell]$ cat /usr/local/v2ray/config_template_bak_20221010.json 
{
    "inbounds": [
        {
            "tag":"transparent",
            "port": 12345,
            "protocol": "dokodemo-door",
            "settings": {
                "network": "tcp,udp",
                "followRedirect": true
            },
            "sniffing": {
                "enabled": true,
                "destOverride": [
                    "http",
                    "tls"
                ]
            },
            "streamSettings": {
                "sockopt": {
                    "tproxy": "tproxy", // 透明代理使用 TPROXY 方式
                    "mark":255
                }
            }
        },
        {
            "listen": "127.0.0.1",
            "port": 15490,
            "protocol": "dokodemo-door",
            "settings": {
                "address": "127.0.0.1"
            },
            "sniffing": {
            },
            "tag": "QV2RAY_API_INBOUND"
        },
        {
            "listen": "0.0.0.0",
            "port": 8889,
            "protocol": "http",
            "settings": {
                "allowTransparent": true,
                "timeout": 300
            },
            "sniffing": {
            },
            "tag": "http_IN"
        },
        {
            "listen": "0.0.0.0",
            "port": 1089,
            "protocol": "socks",
            "settings": {
                "auth": "noauth",
                "ip": "127.0.0.1",
                "udp": true
            },
            "sniffing": {
            },
            "tag": "socks_IN"
        }
    ],
    "log": {
        "loglevel": "warning",
    "access": "/tmp/v2ray_access.log",
    "error": "/tmp/v2ray_error.log"
    },
    "outbounds": [
        {
            "protocol": "vmess",
            "sendThrough": "0.0.0.0",
            "settings": {
                "vnext": [
                    {
                        //"address": "104.245.99.207",
                        //"port": 18087,
            //insert vps address here
            //insert vps port here
                        "users": [
                            {
                                //"id": "4bc74eef-0889-4b6b-86ce-3d5e24a62db4",
                //insert vps user id here
                                "security": "aes-128-gcm"
                            }
                        ]
                    }
                ]
            },
            "streamSettings": {
                "sockopt": {
                    "mark": 255
                },
                "tlsSettings": {
                    "disableSystemRoot": false
                },
                "xtlsSettings": {
                    "disableSystemRoot": false
                }
            },
            "tag": "proxy"
        },
        {
            "protocol": "freedom",
            "sendThrough": "0.0.0.0",
            "settings": {
                //"domainStrategy": "AsIs",
                "domainStrategy": "UseIP",
                "redirect": ":0"
            },
            "streamSettings": {
                "sockopt": {
                    "mark": 255
                }
            },
            "tag": "direct"
        },
        {
            "protocol": "blackhole",
            "sendThrough": "0.0.0.0",
            "settings": {
                "response": {
                    //"type": "none"
                    "type": "http"
                }
            },
            "streamSettings": {
            },
            "tag": "block"
        },
        {
            "tag": "dns-out",
            "protocol": "dns",
            "streamSettings": {
                "sockopt": {
                    "mark": 255
                }
            }  
        }
    ],
    "dns": {
        "servers": [
            {
                "address": "223.5.5.5", //中国大陆域名使用阿里的 DNS
                "port": 53,
                "domains": [
                    "geosite:cn",
                    "ntp.org",   // NTP 服务器
                    "justmysocks5.net", // 此处改为你 VPS 的域名
            "v4.myip.la" // ddns wan ip查询
                ]
            },
            {
                "address": "114.114.114.114", //中国大陆域名使用 114 的 DNS (备用)
                "port": 53,
                "domains": [
                    "geosite:cn",
                    "ntp.org",   // NTP 服务器
                    "justmysocks5.net", // 此处改为你 VPS 的域名
            "v4.myip.la" // ddns wan ip查询
                ]
            },
            {
                "address": "8.8.8.8", //非中国大陆域名使用 Google 的 DNS
                "port": 53,
                "domains": [
                    "geosite:geolocation-!cn"
                ]
            },
            {
                "address": "1.1.1.1", //非中国大陆域名使用 Cloudflare 的 DNS
                "port": 53,
                "domains": [
                    "geosite:geolocation-!cn"
            ]
            }
        ]
    },
    "policy": {
        "system": {
            "statsInboundDownlink": true,
            "statsInboundUplink": true,
            "statsOutboundDownlink": true,
            "statsOutboundUplink": true
        }
    },
    "routing": {
        "domainStrategy": "IPOnDemand",
        "rules": [
            { // 劫持 53 端口 UDP 流量,使用 V2Ray 的 DNS
                "type": "field",
                "inboundTag": [
                    "transparent"
                ],
                "port": 53,
                "network": "udp",
                "outboundTag": "dns-out" 
            },    
            { // 直连 123 端口 UDP 流量(NTP 协议)
                "type": "field",
                "inboundTag": [
                    "transparent"
                ],
                "port": 123,
                "network": "udp",
                "outboundTag": "direct" 
            },    
            {
                "type": "field", 
                "ip": [ 
                    // 设置 DNS 配置中的国内 DNS 服务器地址直连,以达到 DNS 分流目的
                    "223.5.5.5",
                    "114.114.114.114"
                ],
                "outboundTag": "direct"
            },
            {
                "type": "field",
                "ip": [ 
                    // 设置 DNS 配置中的国外 DNS 服务器地址走代理,以达到 DNS 分流目的
                    "8.8.8.8",
                    "1.1.1.1"
                ],
                "outboundTag": "proxy" // 改为你自己代理的出站 tag
            },
            { // 广告拦截
                "type": "field", 
                "domain": [
                    "geosite:category-ads-all"
                ],
                "outboundTag": "block"
            },
            { // BT 流量直连
                "type": "field",
                "protocol":["bittorrent"], 
                "outboundTag": "direct"
            },
            { // 直连中国大陆主流网站 ip 和 保留 ip
                "type": "field", 
                "ip": [
                    "geoip:private",
                    "geoip:cn"
                ],
                "outboundTag": "direct"
            },
            { // 直连中国大陆主流网站域名
                "type": "field", 
                "domain": [
                    "geosite:cn",
            "v4.myip.la" // ddns wan ip查询网址
                ],
                "outboundTag": "direct"
            }
        ]
    },
    "stats": {
    }
}

再从公网连接家里(192.168.1.7这台主机的ssh端口, 做了端口映射,映射到了公网ole12138.top:某个端口) 还是失败。(超时)

但是,这个时候,从公网ping ole12138.top 看到解析地址已经正确了。已经是正确的公网地址了。

然后, 想一下,整个链路,还有哪个地方会出问题。 公网PC->光猫路由网络侧公网IP:映射端口->192.168.1.7:22-> 没走TPROXY代理 (目标地址是内网地址,从V2RAY规则链return到上级链了,从默认路径继续往下走了)->192.168.1.7的sshd服务接收请求,返回握手数据 ->注意这里,返回握手数据时,可能v2ray路由白名单没匹配到,走了代理。

怎么改呢, 从公网任意IP都有可能发起这样的请求的, 建立对应的白名单是不现实的。 那就让 192.168.1.7:22端口的tcp出站数据都不走代理好了。

原来iptables的路由策略是这样的

    # 设置策略路由
    ip rule add fwmark 1 table 100 
    ip route add local 0.0.0.0/0 dev lo table 100
    
    # 代理局域网设备
    iptables -t mangle -N V2RAY
    iptables -t mangle -A V2RAY -d 127.0.0.1/32 -j RETURN
    iptables -t mangle -A V2RAY -d 224.0.0.0/4 -j RETURN 
    iptables -t mangle -A V2RAY -d 255.255.255.255/32 -j RETURN 
    iptables -t mangle -A V2RAY -d 192.168.0.0/16 -p tcp -j RETURN # 直连局域网,避免 V2Ray 无法启动时无法连网关的 SSH,如果你配置的是其他网段(如 10.x.x.x 等),则修改成自己的
    iptables -t mangle -A V2RAY -d 192.168.0.0/16 -p udp ! --dport 53 -j RETURN # 直连局域网,53 端口除外(因为要使用 V2Ray 的 DNS)
    iptables -t mangle -A V2RAY -j RETURN -m mark --mark 0xff    # 直连 SO_MARK 为 0xff 的流量(0xff 是 16 进制数,数值上等同与上面V2Ray 配置的 255),此规则目的是解决v2ray占用大量CPU(https://github.com/v2ray/v2ray-core/issues/2621)
    iptables -t mangle -A V2RAY -p udp -j TPROXY --on-ip 127.0.0.1 --on-port 12345 --tproxy-mark 1 # 给 UDP 打标记 1,转发至 12345 端口
    iptables -t mangle -A V2RAY -p tcp -j TPROXY --on-ip 127.0.0.1 --on-port 12345 --tproxy-mark 1 # 给 TCP 打标记 1,转发至 12345 端口
    iptables -t mangle -A PREROUTING -j V2RAY # 应用规则
    
    # 代理网关本机
    iptables -t mangle -N V2RAY_MASK 
    iptables -t mangle -A V2RAY_MASK -d 224.0.0.0/4 -j RETURN 
    iptables -t mangle -A V2RAY_MASK -d 255.255.255.255/32 -j RETURN 
    iptables -t mangle -A V2RAY_MASK -d 192.168.0.0/16 -p tcp -j RETURN # 直连局域网
    iptables -t mangle -A V2RAY_MASK -d 192.168.0.0/16 -p udp ! --dport 53 -j RETURN # 直连局域网,53 端口除外(因为要使用 V2Ray 的 DNS)
    iptables -t mangle -A V2RAY_MASK -j RETURN -m mark --mark 0xff    # 直连 SO_MARK 为 0xff 的流量(0xff 是 16 进制数,数值上等同与上面V2Ray 配置的 255),此规则目的是避免代理本机(网关)流量出现回环问题
    iptables -t mangle -A V2RAY_MASK -p udp -j MARK --set-mark 1   # 给 UDP 打标记,重路由
    iptables -t mangle -A V2RAY_MASK -p tcp -j MARK --set-mark 1   # 给 TCP 打标记,重路由
    iptables -t mangle -A OUTPUT -j V2RAY_MASK # 应用规则
    
    # 新建 DIVERT 规则,避免已有连接的包二次通过 TPROXY,理论上有一定的性能提升
    iptables -t mangle -N DIVERT
    iptables -t mangle -A DIVERT -j MARK --set-mark 1
    iptables -t mangle -A DIVERT -j ACCEPT
    iptables -t mangle -I PREROUTING -p tcp -m socket -j DIVERT

现在在V2RAY_MASK规则链中间加一条规则。

具体操作如下。

先看下当前的路由规则,并标出行号。

[root@archlinux ~]# iptables -t mangle -L --line-number
Chain PREROUTING (policy ACCEPT)
num  target     prot opt source               destination         
1    DIVERT     tcp  --  anywhere             anywhere             socket
2    V2RAY      all  --  anywhere             anywhere            

Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
num  target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination         
1    V2RAY_MASK  all  --  anywhere             anywhere            

Chain POSTROUTING (policy ACCEPT)
num  target     prot opt source               destination         

Chain DIVERT (1 references)
num  target     prot opt source               destination         
1    MARK       all  --  anywhere             anywhere             MARK set 0x1
2    ACCEPT     all  --  anywhere             anywhere            

Chain V2RAY (1 references)
num  target     prot opt source               destination         
1    RETURN     all  --  anywhere             localhost           
2    RETURN     all  --  anywhere             0.0.0.224.in-addr.arpa/4 
3    RETURN     all  --  anywhere             255.255.255.255     
4    RETURN     tcp  --  anywhere             192.168.0.0/16      
5    RETURN     udp  --  anywhere             192.168.0.0/16       udp dpt:!domain
6    RETURN     all  --  anywhere             anywhere             mark match 0xff
7    TPROXY     udp  --  anywhere             anywhere             TPROXY redirect 127.0.0.1:12345 mark 0x1/0xffffffff
8    TPROXY     tcp  --  anywhere             anywhere             TPROXY redirect 127.0.0.1:12345 mark 0x1/0xffffffff

Chain V2RAY_MASK (1 references)
num  target     prot opt source               destination         
1    RETURN     all  --  anywhere             0.0.0.224.in-addr.arpa/4 
2    RETURN     all  --  anywhere             255.255.255.255     
3    RETURN     tcp  --  anywhere             192.168.0.0/16      
4    RETURN     udp  --  anywhere             192.168.0.0/16       udp dpt:!domain
5    RETURN     all  --  anywhere             anywhere             mark match 0xff
6    MARK       udp  --  anywhere             anywhere             MARK set 0x1
7    MARK       tcp  --  anywhere             anywhere             MARK set 0x1

然后, 需要在 mangle表 V2RAY_MASK规则链 的 第5条 规则之前 插入(Insert) 一条规则。 来源是 192.168.0.0/16网段的 主机 的 22 端口的数据包, 都跳出 V2RAY_MASK规则链, 返回到 OUTPUT链


iptables -t mangle -I V2RAY_MASK 5 -s 192.168.0.0/16 -p tcp --sport 22 -j RETURN

再看下现在的规则:


# 再看下现在的规则
[root@archlinux ~]# iptables -t mangle -L --line-number
Chain PREROUTING (policy ACCEPT)
num  target     prot opt source               destination         
1    DIVERT     tcp  --  anywhere             anywhere             socket
2    V2RAY      all  --  anywhere             anywhere            

Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination         

Chain FORWARD (policy ACCEPT)
num  target     prot opt source               destination         

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination         
1    V2RAY_MASK  all  --  anywhere             anywhere            

Chain POSTROUTING (policy ACCEPT)
num  target     prot opt source               destination         

Chain DIVERT (1 references)
num  target     prot opt source               destination         
1    MARK       all  --  anywhere             anywhere             MARK set 0x1
2    ACCEPT     all  --  anywhere             anywhere            

Chain V2RAY (1 references)
num  target     prot opt source               destination         
1    RETURN     all  --  anywhere             localhost           
2    RETURN     all  --  anywhere             base-address.mcast.net/4 
3    RETURN     all  --  anywhere             255.255.255.255     
4    RETURN     tcp  --  anywhere             192.168.0.0/16      
5    RETURN     udp  --  anywhere             192.168.0.0/16       udp dpt:!domain
6    RETURN     all  --  anywhere             anywhere             mark match 0xff
7    TPROXY     udp  --  anywhere             anywhere             TPROXY redirect 127.0.0.1:12345 mark 0x1/0xffffffff
8    TPROXY     tcp  --  anywhere             anywhere             TPROXY redirect 127.0.0.1:12345 mark 0x1/0xffffffff

Chain V2RAY_MASK (1 references)
num  target     prot opt source               destination         
1    RETURN     all  --  anywhere             0.0.0.224.in-addr.arpa/4 
2    RETURN     all  --  anywhere             255.255.255.255     
3    RETURN     tcp  --  anywhere             192.168.0.0/16      
4    RETURN     udp  --  anywhere             192.168.0.0/16       udp dpt:!domain
5    RETURN     tcp  --  192.168.0.0/16       anywhere             tcp spt:ssh
6    RETURN     all  --  anywhere             anywhere             mark match 0xff
7    MARK       udp  --  anywhere             anywhere             MARK set 0x1
8    MARK       tcp  --  anywhere             anywhere             MARK set 0x1

可以看到Chain V2RAY_MASK多了一行

5    RETURN     tcp  --  192.168.0.0/16       anywhere             tcp spt:ssh

再从公网连接, 已经可以正常连上 家里 192.168.1.7这台主机了。

然后需要将当前的iptables规则保存,方便下次重启时通过服务恢复。

cp /etc/iptables/rules.v4 /etc/iptables/rules.v4_bak_20221012
mkdir -p /etc/iptables && iptables-save > /etc/iptables/rules.v4
systemctl stop tproxyrule.service 
systemctl start tproxyrule.service 
systemctl status tproxyrule.service 

过了几天,内网主机192.168.1.7:8888上配了gitlab, 也是遇到了类似ssh连不上的问题(超时,但是这次dns解析是没问题了),

类似的,配一下: 来源是 192.168.1.7/16网段的 主机 的 8888 端口的数据包, 都跳出 V2RAY_MASK规则链, 返回到 OUTPUT链, 然后保存iptables规则,方便重启 是通过服务恢复即可。


评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用 * 标注