【首创】巧用V2Ray隐藏Web服务器

2020-09-01 Technical Salty Fish 1条

想从公网访问NAS?买不起群晖?不想做内网穿透?动态IP没法备案?怕被查水表封宽带?看过来!

NAS的网络困境

我是一个NAS玩家,说白了就是有一台放在家里的私人文件服务器。NAS最大的问题就是公网访问。如果一台NAS只有在家的时候能用,那必然是非常的不爽。想从公网访问家里的NAS,方法无非是两种:

  1. 内网穿透(包括ngrok、FRP、群晖AnyConnect等)。原理是从NAS向一台具有公网IP,能被公网访问的外部服务器发起连接,通过这条连接建立隧道,外网服务器把收到的访问全部转发到隧道里从而到达内网的NAS。
  2. 端口映射。原理是在网关处设置转发规则,到达网关的数据包会被根据端口映射规则转发给NAS处理。

内网穿透的优点是不需要家里有公网IP,也不需要拥有网关控制权;缺点则是稳定性和速度。端口映射的优缺点正好相反。如果你是移动宽带,则不需要考虑端口映射方案,因为移动家庭宽带没有公网IP。公网IP是采用端口映射方案的前提条件,因为没有公网IP意味着还存在上级NAT,而普通用户是不具备运营商NAT网关的控制权的。

为了追求稳定性,我采用端口映射的方案。内网穿透在此不多加赘述。电信/联通宽带用户如果发现没有公网IP,都可以打电话要求分配。

如果你使用自己的路由器,查询是否具有公网IP时,要在光猫上查看。现代光猫默认配置为NAT模式,相当于一个在路由器上级的网关。

确认光猫获取到了公网IP(而非100开头的运营商级内网IP)后,需要在光猫上进行端口映射或者将光猫设置为桥接模式(建议后者)。打电话给客服要求桥接光猫即可,如果ISP不同意,可以考虑自行破解光猫或更换自购光猫,此处也不加赘述。

因此,现在假设你使用电信或联通宽带,已经获得了公网IP并桥接了光猫,具体表现为路由器拨号且路由器WAN口IP为公网IP。

自建Web服务的问题

众所周知,在我国境内架设Web服务器(HTTP(S)协议)需要备案,而普通家庭宽带的动态IP是无法备案的。据网上资料显示,在不备案的情况下将NAS开放到公网有被查水表的风险。一旦被查水表,ISP就会切断宽带并要求你下线Web服务后再申请恢复。第二次查到Web服务则会永久停止服务。

运营商获知你有私自架设Web服务的方式据网友分析有二:从DDNS下手,或扫描开放的端口。被查水表的网友多使用国内DDNS服务,如花生壳。因此为了我们网络连接的安全,首先要确保不使用国内DDNS。然而即使使用了国外的DDNS来解析域名,第二个问题依然存在。运营商只需要对每一个开放的端口建立HTTP(S)连接就能确认此端口是否非法提供Web服务。

应对方案和难题

应对的思路也很简单,就是根据HTTP包头部信息屏蔽所有不使用域名建立的连接。比如你的公网IP是1.2.3.4,域名是example.com,那么只接受Host字段值为example.com的连接,这样当使用HTTP(S)协议访问1.2.3.4时,数据包被直接丢弃,就不会识别到Web服务的存在了。

不建立1.2.3.4的虚拟主机并不能实现这一方案。当访问1.2.3.4时,请求依然会到达Web服务器。没有资料表明有Web服务器具有根据访问地址过滤请求的功能,只要这个请求到达了Web服务器,服务器就一定会接受连接并以HTTP(S)协议作出回应,这样就暴露了。因此我们需要一个能够解析HTTP包的防火墙。

V2Ray的妙用

普通的防火墙软件也并不具备这一功能。因此我想到了V2Ray这一神器。V2Ray本是用来做梯子的,为了实现分流内建了一个路由模块,可以嗅探HTTP(S)数据包里的目标地址并根据匹配规则做出对应的反应。这一功能的使用情景通常是对本地发出的数据包进行分流,但我们只要将这一功能“反向”应用,就能实现对发往本地的数据包进行分流。

完整的实现思路如下:

  1. 数据包到达V2Ray对0.0.0.0监听的端口,进入V2Ray。
  2. V2Ray嗅探出Host字段的值,并进行路由判断。
  3. V2Ray把数据包转发给Web服务器对127.0.0.1监听的端口,或者将其丢弃。

配置文件样例

我知道你们想要什么(滑稽):

{
  "routing": {
    "domainStrategy": "AsIs",
    "rules": [
      {
        "inboundTag": "https_filter",
        "type": "field",
        "domain": [
          "domain:【你的域名】”
        ],
        "outboundTag": "https_in"
      }
    ]
  },
  "inbounds": [
    {
      "sniffing": {
        "enabled": true,
        "destOverride": [
          "http",
          "tls"
        ]
      },
      "listen": "0.0.0.0",
      "protocol": "dokodemo-door",
      "settings": {
        "address": "",
        "port": 0
      },
      "tag": "https_filter",
      "port": 2333
    }
  ],
  "outbounds": [
    {
      "tag": "reject",
      "protocol": "blackhole"
    },
    {
      "tag": "https_in",
      "protocol": "freedom",
      "settings": {
        "domainStrategy": "AsIs",
        "redirect": "127.0.0.1:443"
      }
    }
  ]
}

此处创建了两个出口,reject采用blackhole协议,用于处理被拒绝的连接,https_in采用freedom,用于转发被接受的连接。入口采用dokodemo-door协议,并开启嗅探功能。路由规则只有一条,即匹配目标是预设域名的连接并交由https_in处理。由于reject是第一个出口,所有不符合这条规则的访问都会被拒绝。这个拒绝发生在V2Ray,而非Web服务器,V2Ray直接丢弃了收到的数据包,因此完全不会暴露Web服务器的存在。

配置完V2Ray后,记得将你要开放的公网端口转发到NAS的2333端口(按照此处的配置文件),而非443端口(默认的Web服务器端口)。这样所有访问Web服务器的请求都会先经过V2Ray的过滤。

注:dokodemo-door的设计初衷是用于简单端口转发的,因此自带转发功能,即settings中的配置。笔者推测这个转发相当于一个与dokodemo-door绑定的outbound,如果不用路由模块“拦截”来自dokodemo-door的流量,则这个入口收到的数据包会被自动通过此”outbound“转发。当配置了相应的路由模块时,自带转发不生效,但是缺少settings.addresssettings.port会导致V2Ray不认这个inbound,因此settings不可省略,但是值填什么并没有关系。

效果

配置好端口转发并启动V2Ray和Web服务器后,可以尝试用浏览器直接访问IP地址(记得加上https://前缀和端口)(都2020年了不会还有人不用HTTPS吧?),应该能看到类似这样的画面:

connection_refused.png

curl连接应该得到这样的提示:

curl: (35) LibreSSL SSL_connect: SSL_ERROR_SYSCALL in connection to 【你的IP:你的公网端口】

使用你设置的域名应该可以正常访问到网站。