Total Pageviews

Saturday, 10 October 2020

使用v2ray进行反向代理/内网穿透

  (related post: https://briteming.blogspot.com/2019/12/v2ray.html)

最开始用ngrok,难用。然后准备搞frp,结果一直没时间咕咕咕,现在v2有了反向代理功能,终于可以研究一下了。

为什么需要反向代理和内网穿透,这里不多说了,反正点进来看的都是有这需求的。

这里只说感想:v2ray的反向代理配合路由真的可以玩出很多骚操作!

为方便理解和统一称呼,我们把没有公网IP的主机,即欲访问的内网服务器称为bridge;把具有公网IP的主机,即外网直接访问的服务器称为portal;所有的外网设备都称为client

v2ray反向代理的大致工作原理如下:

在 bridge 和 portal 中配置各一个V2Ray。

bridge 会向 portal 主动建立连接,此连接的目标地址可以自行设定。portal 会收到两种连接,一是由 bridge 发来的连接,二是公网用户发来的连接。portal 会自动将两类连接合并。于是 bridge 就可以收到公网流量了。

bridge 在收到公网流量之后,会将其原封不动地发给主机 A 中的网页服务器。当然,这一步需要路由的协作。

因此,bridge 需要两个 outbound ,portal 需要两个inbound ,它们都需要配置相应的路由。

ps:本文省略了部分基础内容,详情请参考:直接查看官方文档-

https://v2ray.com/chapter_02/reverse.html

pss:本文中所有实例都是实验通过的,如有其他思路欢迎在评论里面讨论。


基础反向代理

用到反向代理最常见的场景就是想要在外面访问家中的NAS,此时家中的NAS即为bridge。

在本场景中,client 不需要安装 v2ray 客户端,直接通过浏览器进行访问。

bridge的配置:

{

  "log": {

    "access": "",

    "error": "",

    "loglevel": "warning"

  },

  "reverse": {

    "bridges": [

      {

        "tag": "bridge",

        "domain": "test.ailitonia.com"

      }

    ]

  },

  "outbounds": [

    {

      "tag": "bridgeout",

      "protocol": "freedom",

      "settings": {

        "redirect": "127.0.0.1:80"  // 将所有流量转发到网页服务器

      }

    },

    {

      "protocol": "vmess",

      "settings": {

        "vnext": [

          {

            "address": "portal的IP地址",

            "port": 4096,

            "users": [

              {

                "id": "134b53ca-b0cc-44a7-a28f-4214842c2fd6",

                "alterId": 64

              }

            ]

          }

        ]

      },

      "tag": "interconn"

    }

  ],

  "routing": {

    "rules": [

      {

        "type": "field",

        "inboundTag": [

          "bridge"

        ],

        "domain": [

          "full:test.ailitonia.com"

        ],

        "outboundTag": "interconn"

      },

      {

        "type": "field",

        "inboundTag": [

          "bridge"

        ],

        "outboundTag": "bridgeout"

      }

    ]

  }

}

 

portal的配置:

{

  "log": {

    "access": "",

    "error": "",

    "loglevel": "warning"

  },

  "reverse": {

    "portals": [

      {

        "tag": "portal",

        "domain": "test.ailitonia.com"

      }

    ]

  },

  "inbounds": [

    {

      "tag": "portalin",

      "port": 80,

      "protocol": "dokodemo-door",

      "settings": {

        "address": "127.0.0.1",  // 将所有流量转发到网页服务器,与bridge上redirect配置相同,或任选其一配置

        "port": 80,

        "network": "tcp"

      }

    },

    {

      "port": 4096,

      "tag": "interconn",

      "protocol": "vmess",

      "settings": {

        "clients": [

          {

            "id": "134b53ca-b0cc-44a7-a28f-4214842c2fd6",

            "alterId": 64

          }

        ]

      }

    }

  ],

  "routing": {

    "rules": [

      {

        "type": "field",

        "inboundTag": [

          "portalin"

        ],

        "outboundTag": "portal"

      },

      {

        "type": "field",

        "inboundTag": [

          "interconn"

        ],

        "outboundTag": "portal"

      }

    ]

  }

}

 

client 无需配置。


配置好 bridge 和 portal 的 V2Ray 后,先后运行 bridge 和 portal 的 V2Ray,访问 portal 的 IP 或域名,这时就能内网穿透访问内网服务器了。


注意在本场景下,brigde 配置中 outbounds 的 redirect 和 portal 配置中 inbounds 的转发端口要配置相同,或者就不配置 bridge 的。


全局反向代理(反向翻墙代理)

这个场景适用于:想要从国外翻回国内,但国内VPS贼贵,于是家中路由器上搭建;可以利用反向代理在校外访问校园网的资源;在以上等等场景之外,同样能访问家中内网里的NAS等设备。


在本场景中,client 等效于在 bridge 网络中。


在本场景中,client 需要安装 v2ray 客户端并进行相应的配置。


bridge配置:

{

  "log": {

    "access": "",

    "error": "",

    "loglevel": "warning"

  },

  "reverse": {

    "bridges": [

      {

        "tag": "bridge",

        "domain": "test.ailitonia.com"

      }

    ]

  },

  "outbounds": [

    {

      "tag": "bridgeout",

      "protocol": "freedom"

    },

    {

      "protocol": "vmess",

      "settings": {

        "vnext": [

          {

            "address": "portal的IP地址",

            "port": 4096,

            "users": [

              {

                "id": "134b53ca-b0cc-44a7-a28f-4214842c2fd6",

                "alterId": 64

              }

            ]

          }

        ]

      },

      "tag": "interconn"

    }

  ],

  "routing": {

    "rules": [

      {

        "type": "field",

        "inboundTag": [

          "bridge"

        ],

        "domain": [

          "full:test.ailitonia.com"

        ],

        "outboundTag": "interconn"

      },

      {

        "type": "field",

        "inboundTag": [

          "bridge"

        ],

        "outboundTag": "bridgeout"

      }

    ]

  }

}

 

portal的配置:

{

  "log": {

    "access": "",

    "error": "",

    "loglevel": "warning"

  },

  "reverse": {

    "portals": [

      {

        "tag": "portal",

        "domain": "test.ailitonia.com"

      }

    ]

  },

  "inbounds": [

    {

      "tag": "portalin",

      "port": 5001,

      "protocol": "vmess",  // 用于client连接

      "settings": {

        "clients": [

          {

            "id": "89682891-3d57-4cef-abbb-fbac5937ba29",

            "alterId": 64

          }

        ]

      }

    },

    {

      "port": 4096,

      "tag": "interconn",

      "protocol": "vmess",

      "settings": {

        "clients": [

          {

            "id": "134b53ca-b0cc-44a7-a28f-4214842c2fd6",

            "alterId": 64

          }

        ]

      }

    }

  ],

  "routing": {

    "rules": [

      {

        "type": "field",

        "inboundTag": [

          "portalin"

        ],

        "outboundTag": "portal"

      },

      {

        "type": "field",

        "inboundTag": [

          "interconn"

        ],

        "outboundTag": "portal"

      }

    ]

  }

}

 

client配置:

{

  "log": {

    "access": "",

    "error": "",

    "loglevel": "warning"

  },

  "inbounds": [

    {

      "port": 1080,

      "listen": "127.0.0.1",

      "protocol": "socks"

    }

  ],

  "outbounds": [

    {

      "protocol": "vmess",  // 对应portal中tag为portalin的inbound

      "settings": {

        "vnext": [

          {

            "address": "portal的IP",

            "port": 5001,

            "users": [

              {

                "id": "89682891-3d57-4cef-abbb-fbac5937ba29",

                "alterId": 64

              }

            ]

          }

        ]

      }

    }

  ]

}

 

配置好 bridge、portal 和 client 的 v2ray 后,先后运行 bridge 和 portal 的 v2ray,再在 client 上运行v2ray即可。


本场景与第一个场景相比,主要区别有两个:

bridge 和 portal 均没有配置端口转发

portal 中原来的 dokodemo-door 协议变成了 vmess

client 需要运行 v2ray 客户端,配置方法等同于连接到一个正常的 vmess 服务器

在第一个场景中,由于 portal 中的对于 client 的入站协议是 dokodemo-door,这时 v2ray 会将收到的流量原封不动地转发,因此相当于一个端口映射,我们通过指定端口的方式,将内网服务器的端口通过反向代理的方式暴露给外网。


而在本场景中,portal 对于 client 的入站协议变成了 vmess,因此 client 也需要配置 v2ray客户端;同时由于以上原因,portal 将不是一个单纯的“端口转发机”,而是成为了一个v2ray梯子,只不过这个梯子的出口是你自家的内网而已;同时内网设备将不会直接暴露于公网中,而需要通过连接 v2ray 客户端后才能访问。


在本场景的实际使用中,在 client 看来,portal 和 bridge 就像成为了一个整体,通过 v2ray 客户端连接后访问任何网站就如同是从 bridge 上访问的一样,就如同置身于家中的内网环境中。此时如要访问家中的设备,直接在浏览器中访问192.168.1.x这样的内网地址就行了(家中NAS设备记得配置静态地址,不然你懂的……)。


分流反向代理

以上两个场景基本就是绝大部分的使用场景了,但如果有些奇怪的需求,比如:我想在 client 上配置 v2ray 后,既能同时访问家中的内网设备,又能同时利用海外的服务器翻墙呢?


先解释一下为什么会有这种需求。对于身在海外的人来说,翻回国内和访问国内内网设备的需求是同向的,因此直接适用场景二即可;但对于身在国内的人来说,需要同时翻出去和访问国内内网设备(以及身在海外要翻回国内同时还要访问海外内网设备的),这两个需求的方向是不同的,因此直接使用场景二中的方法就不靠谱了。


因此,我们需要使用一点小手段,来分流我们的流量,这个方法就是:路由。当然,在这里只是提出一种思路,和一个例子,通过这种方法甚至还能实现身在国内能同时同时翻出去和访问国内内网设备并能访问海外内网设备(真有这么闲到蛋疼的人吗)。


前面开篇就说到,利用路由可以在反向代理上能玩出很多骚操作,这个方法的重点在于如何区分我们想要进行反向代理的流量和爬梯子的流量。


那如何区分呢?答案是:端口。通过配置 portal 和 client 的路由配置,我们可以做到正常翻墙的同时,用特定端口访问我们自己的内网设备!(考验各位记忆力的时候到了!)


首先假设自己是个有钱人,家里面有NAS、有服务器、有智能家具、有一堆可以联网的设备,我们想要从外网访问它们。假设你有个刷了 openwrt 的路由器并且把 v2ray 装好了,现在这个路由器就是 bridge 了。


bridge配置:

{

  "log": {

    "access": "",

    "error": "",

    "loglevel": "warning"

  },

  "reverse": {

    "bridges": [

      {

        "tag": "bridge",

        "domain": "test.ailitonia.com"

      }

    ]

  },

  "outbounds": [

    {

      "tag": "bridgeout1",  // 内网设备1

      "protocol": "freedom",

      "settings": {

        "redirect": "127.0.0.1:21"  // 内网设备1的内网地址与端口

      }

    },

    {

      "tag": "bridgeout2",  // 内网设备2

      "protocol": "freedom",

      "settings": {

        "redirect": "192.168.1.110:22"  // 内网设备2的内网地址与端口

      }

    },

    {

      "tag": "bridgeout3",  // 内网设备3

      "protocol": "freedom",

      "settings": {

        "redirect": "192.168.1.120:80"  // 内网设备3的内网地址与端口

      }

    },

    {

      "protocol": "vmess",

      "settings": {

        "vnext": [

          {

            "address": "portal的IP地址",

            "port": 4096,

            "users": [

              {

                "id": "134b53ca-b0cc-44a7-a28f-4214842c2fd6",

                "alterId": 64

              }

            ]

          }

        ]

      },

      "tag": "interconn"

    }

  ],

  "routing": {

    "rules": [

      {

        "type": "field",

        "inboundTag": [

          "bridge"

        ],

        "domain": [

          "full:test.ailitonia.com"

        ],

        "outboundTag": "interconn"

      },

      {

        "type": "field",

        "inboundTag": [

          "bridge"

        ],

        "port": "5001",  // 为内网设备1分配访问端口,须在portal分配的端口范围中

        "outboundTag": "bridgeout1"  // 对应内网设备1

      },

      {

        "type": "field",

        "inboundTag": [

          "bridge"

        ],

        "port": "5002",  // 为内网设备2分配访问端口,须在portal分配的端口范围中

        "outboundTag": "bridgeout2"  // 对应内网设备2

      },

      {

        "type": "field",

        "inboundTag": [

          "bridge"

        ],

        "port": "5003",  // 为内网设备3分配访问端口,须在portal分配的端口范围中

        "outboundTag": "bridgeout3"  //对应内网设备3

      }

    ]

  }

}

 

portal配置:

{

  "log": {

    "access": "",

    "error": "",

    "loglevel": "warning"

  },

  "reverse": {

    "portals": [

      {

        "tag": "portal",

        "domain": "test.ailitonia.com"

      }

    ]

  },

  "inbounds": [

    {

      "tag": "portalin",  // 与client连接

      "port": 5001,

      "protocol": "vmess",

      "settings": {

        "clients": [

          {

            "id": "89682891-3d57-4cef-abbb-fbac5937ba29",

            "alterId": 64

          }

        ]

      }

    },

    {

      "port": 4096,

      "tag": "interconn",  // 与bridge连接

      "protocol": "vmess",

      "settings": {

        "clients": [

          {

            "id": "134b53ca-b0cc-44a7-a28f-4214842c2fd6",

            "alterId": 64

          }

        ]

      }

    }

  ],

  "outbounds": [

    {

      "tag": "crossfire",  // 正常流量出口

      "protocol": "freedom"

    }

  ],

  "routing": {

    "rules": [

      {

        "type": "field",

        "inboundTag": [

          "portalin"

        ],

        "ip": "111.111.111.111",  // 指定一个用来进行内网穿透的ip

        "port": "5001-5100",  // 指定一个进行内网穿透的端口范围

        "outboundTag": "portal"  // 对应内网穿透连接

      },

      {

        "type": "field",

        "inboundTag": [

          "interconn"

        ],

        "outboundTag": "portal"  // 对应bridge连接

      },

      {

        "type": "field",

        "inboundTag": [

          "portalin"

        ],

        "outboundTag": "crossfire"  //对应翻墙连接

      }

    ]

  }

}

 

client的配置与第二个场景中的相同:

{

  "log": {

    "access": "",

    "error": "",

    "loglevel": "warning"

  },

  "inbounds": [

    {

      "port": 1080,

      "listen": "127.0.0.1",

      "protocol": "socks"

    }

  ],

  "outbounds": [

    {

      "protocol": "vmess",

      "settings": {

        "vnext": [

          {

            "address": "portal的IP",

            "port": 5001,

            "users": [

              {

                "id": "89682891-3d57-4cef-abbb-fbac5937ba29",

                "alterId": 64

              }

            ]

          }

        ]

      }

    }

  ]

}

 

配置好 bridge、portal 和 client 的 v2ray 后,先后运行 bridge 和 portal 的 v2ray,再在 client 上运行v2ray即可。


此时 client 可以正常翻墙,portal 即为梯子。如果想要访问内网设备,在浏览器中输入 portal 配置中的指定的ip,加上 bridge 为内网设备分配的端口号即可。如在本例中,如果我想要访问家中内网设备3,只需要连接上 v2ray 后直接访问 111.111.111.111:5003 就行了。


在本例中,我们使用路由配置将家中的内网ip映射到一个(xjb写的)ip上,同时将内网设备的不同端口也映射到了这个(xjb写的)ip的不同端口上。


在本场景中,内网设备同样不会直接暴露于公网中,需要通过连接 v2ray 客户端后才能访问。而与场景二的不同在于,场景二中连接 v2ray 客户端后相当于处于家中内网环境,访问内网设备是通过直接访问内网ip进行的;而本例在连接 v2ray 客户端后是处于翻墙后的状态,访问内网设备需要通过访问特定的(那个xjb写的)ip以及为内网设备分配的端口进行。


这个xjb写的ip地址其实不写也是可以的,但这样的话像本例中访问任意ip的5001-5100端口都会被转发到内网去了……不过为了方便好记,这个(xjb写的)ip当然是越简单越好,不过意外事故还是有可能的,比如政府网上办事/公司/学校给的一些ip地址刚好和你用的撞车(怎么可能),这时改改就行了,反正就改几个数字耗不了多少时间(远程登陆费的是时间)。端口号也按个人喜好分配,也是方便记忆的最好。这个端口号和 v2ray 客户端连接的端口也没有关系,一样也是可以的。


还有一点就是映射端口时,和上一个场景一样,家中设备要记得配置静态地址,不然的话后果自己体会。当然这个方法配置起来比较麻烦,毕竟需要把家中每个设备都单独配置 outbound,不过这个方法用起来是最舒服的,值得用点时间去写配置文件。


而且本例中 client 的配置,就是个白板配置,完全可以再加点国内直连啊,广告过滤啊什么的,多的就不在这里详说了……


基础反向代理·改

看了上面这个实例,估计有的人又有新想法了。


基础的反向代理一个ip就对应一个设备,利用率太低了。既然上面可以使用路由配置实现类似端口映射的效果,那能不能直接在最简单的反向代理中实现呢,毕竟不是随时随地用到的所有设备上都装了 v2ray 吧,能在任何设备上直接访问不是更方便吗嘛?


答案当然是不可以啦!因为 portal 的入站协议是 dokodemo-door,是必须要指定一个转发的端口的,因此是不能只通过一个 inbound 就搞定多个端口的转发的。


所以在这个场景中的配置其实和路由完全没有一点关系,只需要给每一个内网设备都配置一个 inbound 就行了(其实就是第一个例子在portal上多加了几个inbound而已,没什么改动)(和上面那个例子中每个设备都配置一个 outbound 一样都极不优雅)


(为什么把这段放在这个位置?我也是写到这才想起来还有这茬的)


bridge配置:

{

  "log": {

    "access": "",

    "error": "",

    "loglevel": "warning"

  },

  "reverse": {

    "bridges": [

      {

        "tag": "bridge",

        "domain": "test.ailitonia.com"

      }

    ]

  },

  "outbounds": [

    {

      "tag": "bridgeout",

      "protocol": "freedom"

    },

    {

      "protocol": "vmess",

      "settings": {

        "vnext": [

          {

            "address": "portal的IP地址",

            "port": 4096,

            "users": [

              {

                "id": "134b53ca-b0cc-44a7-a28f-4214842c2fd6",

                "alterId": 64

              }

            ]

          }

        ]

      },

      "tag": "interconn"

    }

  ],

  "routing": {

    "rules": [

      {

        "type": "field",

        "inboundTag": [

          "bridge"

        ],

        "domain": [

          "full:test.ailitonia.com"

        ],

        "outboundTag": "interconn"

      },

      {

        "type": "field",

        "inboundTag": [

          "bridge"

        ],

        "outboundTag": "bridgeout"

      }

    ]

  }

}

 

portal配置:

{

  "log": {

    "access": "",

    "error": "",

    "loglevel": "warning"

  },

  "reverse": {

    "portals": [

      {

        "tag": "portal",

        "domain": "test.ailitonia.com"

      }

    ]

  },

  "inbounds": [

    {

      "tag": "device1",  // 内网设备1

      "port": 5001,  // 访问端口

      "protocol": "dokodemo-door",

      "settings": {

        "address": "127.0.0.1",  // 内网ip

        "port": 80,  // 设备开放端口

        "network": "tcp"

      }

    },

    {

      "tag": "device2",  // 内网设备2

      "port": 5002,

      "protocol": "dokodemo-door",

      "settings": {

        "address": "192.168.1.100",

        "port": 80,

        "network": "tcp"

      }

    },

    {

      "tag": "device3",  // 内网设备3

      "port": 5003,

      "protocol": "dokodemo-door",

      "settings": {

        "address": "192.168.1.200",

        "port": 21,

        "network": "tcp"

      }

    },

    {

      "port": 4096,

      "tag": "interconn",

      "protocol": "vmess",

      "settings": {

        "clients": [

          {

            "id": "134b53ca-b0cc-44a7-a28f-4214842c2fd6",

            "alterId": 64

          }

        ]

      }

    }

  ],

  "routing": {

    "rules": [

      {

        "type": "field",

        "inboundTag": [  // 前面所有inbound的tag都要写

          "device1",

          "device2",

          "device3"

        ],

        "outboundTag": "portal"

      },

      {

        "type": "field",

        "inboundTag": [

          "interconn"

        ],

        "outboundTag": "portal"

      }

    ]

  }

}

 

client 无需配置。


配置好 bridge 和 portal 的 V2Ray 后,先后运行 bridge 和 portal 的 V2Ray,访问 portal 的 IP 或域名+端口号,这时就能内网穿透访问内网设备了。


基础反向代理·二改

在面对多个内网设备的情况下,如何优雅简单地写配置文件并简单优雅地访问一直是个问题,因为有多个设备的话想要分别访问就不可避免的要为其挨个单独设置一个 inbound 或 outbound。况且使用端口号来访问对应设备对记忆起来也是个说大不大说小不小的麻烦。虽说本文第二例的配置就挺简单的,但毕竟作为梯子才是v2ray的主业,内网穿透只是兼职而已。所以想要翻墙同时内网穿透,跟简单优雅的配置仿佛就是鱼与熊掌不可兼得的难题。


在认识到这个现状之后,我突然醒悟了:复杂的配置是为了简单优雅的使用而服务的口牙。只要用起来方便,配置复杂点又有什么关系呢(这是邪道好孩子不要学)。


v2ray的路由配置目前毕竟只能实现一些简单的功能,前面的使用特定ip访问也是一种取巧的方法。在本例中将展示使用 nginx 配合 v2ray 实现的反向代理。


使用建议:最好有个自己的域名。


首先准备工作:先把 nginx 装上:Nginx安装与配置

注意:本例使用的web服务器启用了SSL,并且由于使用了nginx转发,请注意bridge和portal端口不同.

装好 nginx 后随便上传个模板主页充当门面。

接下来先开始配置 v2ray。

bridge配置:

{

  "log": {

    "access": "",

    "error": "",

    "loglevel": "warning"

  },

  "reverse": {

    "bridges": [

      {

        "tag": "bridge",

        "domain": "test.ailitonia.com"

      }

    ]

  },

  "outbounds": [

    {

      "tag": "bridgeout",

      "protocol": "freedom"

    },

    {

      "protocol": "vmess",

      "settings": {

        "vnext": [

          {

            "address": "portal的域名",

            "port": 443,

            "users": [

              {

                "id": "134b53ca-b0cc-44a7-a28f-4214842c2fd6",

                "alterId": 64

              }

            ]

          }

        ]

      },

  "streamSettings": {

        "network": "ws",  //为了使用nginx反代这里需要使用ws

        "security": "tls",

        "tlsSettings": {

          "allowInsecure": false

        },

        "wsSettings": {

          "path": "/interconnpath"

        }

      },

      "tag": "interconn"

    }

  ],

  "routing": {

    "rules": [

      {

        "type": "field",

        "inboundTag": [

          "bridge"

        ],

        "domain": [

          "full:test.ailitonia.com"

        ],

        "outboundTag": "interconn"

      },

      {

        "type": "field",

        "inboundTag": [

          "bridge"

        ],

        "outboundTag": "bridgeout"

      }

    ]

  }

}

 

portal配置:

{

  "log": {

    "access": "",

    "error": "",

    "loglevel": "warning"

  },

  "reverse": {

    "portals": [

      {

        "tag": "portal",

        "domain": "test.ailitonia.com"

      }

    ]

  },

  "inbounds": [

    {

      "tag": "device1",

      "port": 5001,

      "protocol": "dokodemo-door",

      "settings": {

        "address": "127.0.0.1",

        "port": 80,

        "network": "tcp"

      }

    },

    {

      "tag": "device2",

      "port": 5002,

      "protocol": "dokodemo-door",

      "settings": {

        "address": "192.168.1.100",

        "port": 80,

        "network": "tcp"

      }

    },

    {

      "tag": "device3",

      "port": 5003,

      "protocol": "dokodemo-door",

      "settings": {

        "address": "192.168.1.200",

        "port": 21,

        "network": "tcp"

      }

    },

    {

      "port": 4096,

      "tag": "interconn",

      "protocol": "vmess",

      "settings": {

        "clients": [

          {

            "id": "134b53ca-b0cc-44a7-a28f-4214842c2fd6",

            "alterId": 64

          }

        ]

      },

      "streamSettings": {

        "network": "ws",  //为了使用nginx反代这里需要使用ws

        "wsSettings": {

          "path": "/interconnpath"

        }

      }

    }

  ],

  "routing": {

    "rules": [

      {

        "type": "field",

        "inboundTag": [

          "device1",

          "device2",

          "device3"

        ],

        "outboundTag": "portal"

      },

      {

        "type": "field",

        "inboundTag": [

          "interconn"

        ],

        "outboundTag": "portal"

      }

    ]

  }

}

 

nginx配置:

upstream nas {

  server 127.0.0.1:5001;

}

upstream htpc {

  server 127.0.0.1:5002;

}

upstream ssh {

  server 127.0.0.1:5003;

}

server {

...

    location /interconnpath {

        proxy_redirect off;

        proxy_pass http://127.0.0.1:4096;  #WebSocket监听端口

        proxy_http_version 1.1;

        proxy_set_header Upgrade $http_upgrade;

        proxy_set_header Connection "upgrade";

        proxy_set_header Host $http_host;

    }

    location /nas/ {

        proxy_redirect off;

        proxy_set_header Host $host;

        proxy_set_header X-Real-IP $remote_addr;

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_pass http://nas;

    }

    location /htpc/ {

        proxy_redirect off;

        proxy_set_header Host $host;

        proxy_set_header X-Real-IP $remote_addr;

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_pass http://htpc;

    }

    location /ssh/ {

        proxy_redirect off;

        proxy_set_header Host $host;

        proxy_set_header X-Real-IP $remote_addr;

        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_set_header X-Forwarded-Proto $scheme;

        proxy_pass http://ssh;

    }

...

}

 

反向代理的底层传输配置

有的人可能看到这里还在纠结为什么 v2ray 的反向代理在 bridge 和 portal 之间需要两条连接,现在就先别纠结了,因为实际上这里虽然有两个连接,但在实际的通信中这两条连接实际上是只通过一条连接,也就是上面例子中 tag 为 interconn 的那对出/入站协议进行的。

换句话说,我们可以为反向代理的连接配置底层传输方式,甚至还可以用其他的协议比如shadowsocks来传输反向代理的流量.

对于有极端强迫症的人以及有特殊需求的人来说,这是个还算有价值的方案。

这里依然以上面第三个实例为基础,但本例中,我们在 bridge 和 portal 之间使用 shadowsocks 协议和 QUIC 传输方式;在 client 和 portal 之间使用 vmess 协议和 WebSocket 传输方式,并且 client 和 portal 之间的连接使用CDN进行中转。使用tls和CDN时需要提前准备一个域名并解析到 portal 上。


ps:所有的协议和底层传输都是可以改的,不是只有这种组合,这个例子(无法形容的奇葩组合方式)只是用于展示本方案可能性,并不是一个好的配置组合(好孩子千万不要直接照抄哦)。


pss:如果之前对这方面没有了解,建议先看看V2Ray完全配置指南/WebSocket+TLS+Web部分


bridge配置:

{

  "log": {

    "access": "",

    "error": "",

    "loglevel": "warning"

  },

  "reverse": {

    "bridges": [

      {

        "tag": "bridge",

        "domain": "test.ailitonia.com"

      }

    ]

  },

  "outbounds": [

    {

      "tag": "bridgeout1",

      "protocol": "freedom",

      "settings": {

        "redirect": "127.0.0.1:21"

      }

    },

    {

      "tag": "bridgeout2",

      "protocol": "freedom",

      "settings": {

        "redirect": "192.168.1.110:22"

      }

    },

    {

      "tag": "bridgeout3",

      "protocol": "freedom",

      "settings": {

        "redirect": "192.168.1.120:80"

      }

    },

    {

      "protocol": "shadowsocks",

      "settings": {

        "servers": [

          {

            "address": "portal的ip",

            "port": 4096,

            "method": "aes-128-cfb",

            "password": "87654321"

          }

        ]

      },

      "streamSettings": {  // 底层传输配置,使用quic

        "network": "quic",

        "quicSettings": {

          "security": "aes-128-gcm",

          "key": "",

          "header": {

            "type": "utp"

          }

        }

      },

      "tag": "interconn"

    }

  ],

  "routing": {

    "rules": [

      {

        "type": "field",

        "inboundTag": [

          "bridge"

        ],

        "domain": [

          "full:test.ailitonia.com"

        ],

        "outboundTag": "interconn"

      },

      {

        "type": "field",

        "inboundTag": [

          "bridge"

        ],

        "port": "5001",

        "outboundTag": "bridgeout1"

      },

      {

        "type": "field",

        "inboundTag": [

          "bridge"

        ],

        "port": "5002",

        "outboundTag": "bridgeout2"

      },

      {

        "type": "field",

        "inboundTag": [

          "bridge"

        ],

        "port": "5003",

        "outboundTag": "bridgeout3"

      }

    ]

  }

}

 


portal配置:

{

  "log": {

    "access": "",

    "error": "",

    "loglevel": "warning"

  },

  "reverse": {

    "portals": [

      {

        "tag": "portal",

        "domain": "test.ailitonia.com"

      }

    ]

  },

  "inbounds": [

    {

      "tag": "portalin",

      "port": 5001,  // 注意在web服务器上配置转发

      "protocol": "vmess",

      "settings": {

        "clients": [

          {

            "id": "89682891-3d57-4cef-abbb-fbac5937ba29",

            "alterId": 64

          }

        ]

      },

      "streamSettings": {  // 底层传输配置,client配置应与其相同

        "network": "ws",

        "wsSettings": {

          "path": "/portalin",

          "headers": {

            "Host": "ailitonia.com"

          }

        }

      }

    },

    {

      "port": 4096,

      "tag": "interconn",

      "protocol": "shadowsocks",

      "settings": {

        "method": "aes-128-cfb",

        "password": "87654321"

      },

      "streamSettings": {  // 底层传输配置,应与bridge配置相同

        "network": "quic",

        "quicSettings": {

          "security": "aes-128-gcm",

          "key": "",

          "header": {

            "type": "utp"

          }

        }

      }

    }

  ],

  "outbounds": [

    {

      "tag": "crossfire",

      "protocol": "freedom"

    }

  ],

  "routing": {

    "rules": [

      {

        "type": "field",

        "inboundTag": [

          "portalin"

        ],

        "ip": "111.111.111.111",

        "port": "5001-5100",

        "outboundTag": "portal"

      },

      {

        "type": "field",

        "inboundTag": [

          "interconn"

        ],

        "outboundTag": "portal"

      },

      {

        "type": "field",

        "inboundTag": [

          "portalin"

        ],

        "outboundTag": "crossfire"

      }

    ]

  }

}


使用CDN时应当配置web服务器(如Nginx、Caddy)分流。

使用QUIC时记得服务器防火墙放行udp


client配置:

{

  "log": {

    "access": "",

    "error": "",

    "loglevel": "warning"

  },

  "inbounds": [

    {

      "port": 1080,

      "listen": "127.0.0.1",

      "protocol": "socks"

    }

  ],

  "outbounds": [

    {

      "protocol": "vmess",

      "settings": {

        "vnext": [

          {

            "address": "portal的域名",

            "port": 443,

            "users": [

              {

                "id": "89682891-3d57-4cef-abbb-fbac5937ba29",

                "alterId": 64

              }

            ]

          }

        ]

      },

      "streamSettings": {

        "network": "ws",

        "security": "tls",

        "tlsSettings": {

          "allowInsecure": false

        },

        "wsSettings": {

          "path": "/portalin",

          "headers": {

            "Host": "ailitonia.com"

          }

        }

      }

    }

  ]

}


配置好 bridge、portal 和 client 的 v2ray 后,先后运行 bridge 和 portal 的 v2ray,再在 client 上运行v2ray即可。

访问方式与“分流反向代理”中相同。


反向代理的负载均衡

v2ray在V4.4的时候不声不响地放出了路由中负载均衡的配置,相比于之前在同一个 outbound 的 vnext 写入多个服务器的配置方式,在路由中配置负载均衡不仅不需要各服务器的协议与底层传输方式配置相同,还能配合其他路由设置实现更加灵活的分流。虽然目前的负载均衡器似乎只有简单的随机策略,但已经足够我们使用了。

依旧是本文的第三个实例中,这回我们有了十多台服务器,我们要在这十多台服务器上配置负载均衡,同时还要能保证在负载均衡后不仅翻墙要正常,而且反向代理的内网设备也要能正常访问.

负载均衡的配置只涉及到 bridge 和 client,而 portal 的配置与第三个例子中的没有区别,只是需要在很多服务器上都配置一遍而已。

from https://ailitonia.com/archives/%E4%BD%BF%E7%94%A8v2ray%E8%BF%9B%E8%A1%8C%E5%8F%8D%E5%90%91%E4%BB%A3%E7%90%86-%E5%86%85%E7%BD%91%E7%A9%BF%E9%80%8F/

---------

使用V2ray反向代理实现内网穿透


1. 前言

笔者在很久之前感叹过:在IPv6成为基础设施标配前,NAT依旧会是常态,这个常态已经持续几十年了。没想到现在的IPv6带着NAT一起来,内网穿透的玩法也更多了。上周wireguard突然失效,笔者以为是哪里配置出了问题,仔细排查后发现,国内访问VPS的UDP端口已经被封。

笔者的wireguard网络已经稳定运行了很长时间,但最后还是逃不过屏蔽,在国内的环境下,只有合规的流量才能大隐隐于网。笔者在对比了几个可用方案:OpenVPN、frp、inlets和v2ray之后,选择相对熟悉的v2ray来实现内网穿透,这里稍作记录。

2. 内网穿透架构

这里我们不会考虑对NAT动手脚,而是选择使用一个带公网IP的中心节点完成流量转发,以笔者的三个设备为例:

  • 设备A:MacBook Air,放置在公司内
  • 设备B:MacBook Pro,放置在家里
  • 设备C:VPS,托管在云厂商

如果需要在公司访问家里的设备,并执行一些ssh登录,vscode远程开发等,需要设备B主动连接设备C,建立一个传输隧道,当设备A需要访问家里的服务时,请求经过设备C转发到达设备B:

公网访问
公网隧道
公网隧道
设备A
Macbook Air
设备B
MacBook Pro
设备C
VPS

这个结构在使用wireguard与v2ray时都是一致的,区别在于:

  • wireguard使用无连接状态的UDP构建三层网络,在逻辑上所有设备都处于一个子网下,设备之间使用内网IP互相访问
  • v2ray使用有连接状态的TCP构建隧道,利用流量探测和路由规则在应用层实现内网穿透,设备之间需要经过代理才能互相访问

v2ray相比wireguard会有更多的开销,但好处在于v2ray属于应用层实现,只要在Go语言支持的OS和硬件下都可以运行,而且WebSocket能受益于TLS加密和CDN加速。

3. v2ray配置

v2ray定义了reverse对象来设置关于反向代理的参数。以Air访问Pro为例:

  • 我们使用vless作为传输协议,ws+tls作为传输方式
  • B与C之间通过 pro.v2ray.com 域名标记隧道流量
  • 子路径 ProTunnel 用于B与C之间建立传输隧道
  • 子路径 ProForward 供A连接C来使用B的网络环境
test.example.com/ProForward
pro.v2ray.com + test.example.com/ProTunnel
pro.v2ray.com + test.example.com/ProTunnel
设备A
Macbook Air
设备B
MacBook Pro
设备C
VPS

并且为了更好利用WebSocket特性,减少端口暴露,在C上部署了nginx作为前端,处理TLS流量,并将访问子路径的请求转发到对应v2ray服务,如下所示:

设备C: VPS
https://test.example.com/ProForward
https://test.example.com/ProTunnel
nginx
v2ray
http://127.0.0.1:9090/ProForward
http://127.0.0.1:9091/ProTunnel
设备A
Macbook Air
设备B
MacBook Pro

设备A、B、C的V2ray配置文件如下:

设备A

这里只是简单将输入端的 pro-forward 路由到输出端的 pro-forward

设备B

这里定义了一个reverse对象的bridges字段来标记隧道流量,在routing规则中:

  • 从 pro 输入的流量中,只允许匹配预定义的 pro.v2ray.com 域名流量,即隧道流量,输出到 pro-tunnel
  • 其余从 pro 输入的流量,全部输出到 direct 来使用宿主机的网络

设备C

这里定义了一个reverse对象的portals字段来标记隧道流量,与需要被访问的B中的bridges域名一致,并在inboounds中启用流量嗅探,在routing规则中:

  • 令 pro-forward 输出到 pro,让经过该代理的请求可以转发到内网的设备B,进而使用B的网络
  • 从 pro-tunnel 输入的流量中,只允许匹配预定义的 pro.v2ray.com 域名流量,即隧道流量,从 pro 输出

4. 通过代理访问内网服务

需要访问设备B的内网服务时,只需要设置代理即可。

笔者的两台内网设备都已经启用了ssh登录,如果在需要设备A上通过登录设备B,则操作如下:

使用桌面浏览器时,例如Chrome配合SwitchyOmega插件,就能像访问国外网站一样经过代理访问内网网站。使用手机时,也可以通过编辑代理工具的自定义规则来分流,实现按域名或者IP访问内网服务。

5. 更进一步

由于是在应用层通过路由规则进行转发,v2ray的内网穿透架构不像使用VPN时那么直观,但如果熟悉结构后,配置可以进一步简化,如下所示:

设备C: VPS
test.example.com/AirForward
test.example.com/ProForward
air.v2ray.com + test.example.com/Tunnel
pro.v2ray.com + test.example.com/Tunnel
nginx
v2ray
http://127.0.0.1:8080/Tunnel
http://127.0.0.1:9090/AirFoward
http://127.0.0.1:9091/ProFoward
设备A
Macbook Air
设备B
MacBook Pro
用户
  • 设备A和设备B通过公共的Tunnel子路径和内部域名与设备C建立长连接
  • 用户通过AirForward子路径连接代理时,所有流量导向设备A
  • 用户通过ProForward子路径连接代理时,所有流量导向设备B

通过这种方式我们可以固定隧道入口,要暴露新设备到公网时,只需添加内部域名和转发路径即可,设备C此时的配置文件如下:

  • 从 tunnel 输入的流量中
    • 匹配预定义的 air.v2ray.com 域名流量,从 air 输出
    • 匹配预定义的 pro.v2ray.com 域名流量,从 pro 输出
  • 令 air-forward 输出到 air,让经过该代理的请求可以转发到内网的设备A,进而使用A的网络
  • 令 pro-forward 输出到 pro,让经过该代理的请求可以转发到内网的设备B,进而使用B的网络

笔者测试时使用了xray的镜像,进入容器后,可以看到设备A(MacBook Air)与设备B(MacBook Pro)建立的长连接,本地端口为8080,对应上述tunnel的配置:

当我们从MacBook Pro使用AirForward子路径代理登录到Macbook Air时,可以看到新增了本地端口为9090的长连接,对应上述air-foward的配置:

接着进行套娃操作,在MacBook Air使用ProForward子路径代理登录MacBook Pro,可以看到又新增了一个长连接,本地端口为9091,对应上述pro-forward的配置:

套娃操作可以反复持续下去,一直到网络延迟增加至无法登录为止。

No comments:

Post a Comment