v2ray配置指南

项目地址:https://github.com/v2fly/v2ray-core

项目地址:https://github.com/v2fly/v2ray-core

项目网站:https://www.v2fly.org/

官方教程:https://www.v2fly.org/guide/start.html

博客:https://toutyrater.github.io/

新白话文教程(社区版):https://guide.v2fly.org/

V2Ray 是 Project V 下的一个工具。Project V 是一个包含一系列构建特定网络环境工具的项目,而 V2Ray 属于最核心的一个。

  • 客户端:V2Ray 本身只是一个内核,V2Ray 上的图形客户端大多是调用 V2Ray 内核套一个图形界面的外壳,类似于 Linux 内核和 Linux 操作系统的关系
  • 分流:也许大家第一反应是 PAC,实际上无论是 Shadowsocks(特指 Shadowsocks-libev)还是 V2Ray 本身不支持 PAC,都是客户端加进来的
  • 加密方式:V2Ray(特指 VMess 协议)不像 Shadowsocks 那样看重对加密方式的选择,并且 VMess 的加密方式是由客户端指定的,服务器自适应。
  • 时间:使用 V2Ray 要保证时间准确,因为这是为了安全设计的。
  • 密码:V2Ray(VMess)只有 id(使用 UUID 的格式),作用类似于 Shadowsocks 的密码,但随机性远好于 Shadowsocks 的密码,只是不太方便记忆(安全和方便的矛盾)。
  • UDP 转发:VMess 是基于 TCP 的协议,对于 UDP 包 V2Ray 会转成 TCP 再传输的,即 UDP over TCP。要 UDP 转发功能在客户端的 socks 协议中开启 UDP 即可。

部署之前

时间是否准确

V2Ray 对于时间有比较严格的要求,要求服务器和客户端时间差绝对值不能超过90 秒,所以一定要保证时间足够准确。还好 V2Ray 并不要求时区一致。

安装

下载主程序安装脚本:

curl -O https://raw.githubusercontent.com/v2fly/fhs-install-v2ray/master/install-release.sh

安装 V2ray 主程序:

bash install-release.sh

安装完之后,使用以下命令启动 V2Ray:

systemctl start v2ray

设置开机自启动 V2Ray:

systemctl enable v2ray

配置之前关闭:

systemctl stop v2ray

更新:在 VPS,重新执行一遍安装脚本就可以更新了,在更新过程中会自动重启 V2Ray,配置文件保持不变。

bash install-release.sh

客户端安装

这里 (opens new window)下载 V2Ray 的 Windows 压缩包,如果是 32 位系统,下载 v2ray-windows-32.zip,如果是 64 位系统,下载 v2ray-windows-64.zip(下载速度慢或无法下载请考虑挂已有的翻墙软件来下载)。下载并且解压之后会有下面这些文件:

  • v2ray.exe 运行 V2Ray 的程序文件
  • wv2ray.exe 同 v2ray.exe,区别在于 wv2ray.exe 是后台运行的,不像 v2ray.exe 会有类似于 cmd 控制台的窗口。运行 V2Ray 时从 v2ray.exe 和 wv2ray.exe 中任选一个即可
  • config.json V2Ray 的配置文件,后面我们对 V2Ray 进行配置其实就是修改这个文件
  • v2ctl.exe V2Ray 的工具,有多种功能,除特殊用途外,一般由 v2ray.exe 来调用,用户不用太关心
  • geosite.dat 用于路由的域名文件
  • geoip.dat 用于路由的 IP 文件

实际上双击 v2ray.exe (或 wv2ray.exe) 就可以运行 V2Ray 了,V2Ray 会读取 config.json 中的配置与服务器连接。

V2Ray 将所有选择权交给用户,它不会自动设置系统代理,因此还需要在浏览器里设置代理。以火狐(Firefox)为例,点菜单 -> 选项 -> 高级 -> 设置 -> 手动代理设置,在 SOCKS Host 填上 127.0.0.1,后面的 Port 填 1080,再勾上使用 SOCKS v5 时代理 DNS (这个勾选项在旧的版本里叫做远程 DNS)。

基础

VMess

VMess 协议是由 V2Ray 原创并使用于 V2Ray 的加密传输协议,如同 Shadowsocks 一样为了对抗墙的深度包检测 (opens new window)而研发的。在 V2Ray 上客户端与服务器的通信主要是通过 VMess 协议通信。

本小节给出了 VMess 的配置文件,其实也就是服务器和客户端的基本配置文件,这是 V2Ray 能够运行的最简单的配置。

V2Ray 使用 inbound(传入) 和 outbound(传出) 的结构,这样的结构非常清晰地体现了数据包的流动方向,同时也使得 V2Ray 功能强大复杂的同时而不混乱,清晰明了。

V2Ray 做客户端,则 inbound 接收来自浏览器数据,由 outbound 发出去(通常是发到 V2Ray 服务器);V2Ray 做服务器,则 inbound 接收来自 V2Ray 客户端的数据,由 outbound 发出去(通常是如 Google 等想要访问的目标网站)。

以下给出了 VMess 的配置文件,包含客户端和服务器端,将你的配置替换成下面给出的配置,然后将服务器地址修改成你的就可以正常使用。修改完配置之后要重启 V2Ray 才能使用新配置生效。

注意

VMess 协议的认证基于时间,一定要保证服务器和客户端的系统时间相差要在 90 秒以内。

客户端配置

以下是客户端配置,将客户端的 config.json 文件修改成下面的内容,修改完成后要重启 V2Ray 才会使修改的配置生效。

json
{
  "inbounds": [
    {
      "port": 1080, // 监听端口
      "protocol": "socks", // 入口协议为 SOCKS 5
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      },
      "settings": {
        "auth": "noauth"  //socks的认证设置,noauth 代表不认证,由于 socks 通常在客户端使用,所以这里不认证
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "vmess", // 出口协议
      "settings": {
        "vnext": [
          {
            "address": "serveraddr.com", // 服务器地址,请修改为你自己的服务器 IP 或域名
            "port": 16823,  // 服务器端口
            "users": [
              {
                "id": "b831381d-6324-4d53-ad4f-8cda48b30811",  // 用户 ID,必须与服务器端配置相同
                "alterId": 0 // 此处的值也应当与服务器相同
              }
            ]
          }
        ]
      }
    }
  ]
}

在配置当中,有一个 id (在这里的例子是 b831381d-6324-4d53-ad4f-8cda48b30811),作用类似于 Shadowsocks 的密码(password), VMess 的 id 的格式必须与 UUID 格式相同。关于 id 或者 UUID 没必要了解很多,在这里只要清楚以下几点就足够了:

  • 相对应的 VMess 传入传出的 id 必须相同(如果你不是很明白这句话,那么可以简单理解成服务器与客户端的 id 必须相同)
  • 由于 id 使用的是 UUID 的格式,我们可以使用任何 UUID 生成工具生成 UUID 作为这里的 id。比如 UUID Generator (opens new window)这个网站,只要一打开或者刷新这个网页就可以得到一个 UUID,如下图。或者可以在 Linux 使用命令 cat /proc/sys/kernel/random/uuid 生成。

服务器配置

以下是服务器配置,将服务器 /usr/local/etc/v2ray 目录下的 config.json 文件修改成下面的内容,修改完成后要重启 V2Ray 才会使修改的配置生效。

json
{
  "inbounds": [
    {
      "port": 16823, // 服务器监听端口
      "protocol": "vmess",    // 主传入协议
      "settings": {
        "clients": [
          {
            "id": "b831381d-6324-4d53-ad4f-8cda48b30811",  // 用户 ID,客户端与服务器必须相同
            "alterId": 0
          }
        ]
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",  // 主传出协议
      "settings": {}
    }
  ]
}

原理简析

根据上文给出的配置,在这里简单的介绍一下 V2Ray 的工作原理。

无论是客户端还是服务器,配置文件都由两部分内容组成: inboundsoutbounds。V2Ray 没有使用常规代理软件的 C/S(即客户端/服务器)结构,它既可以当做服务器也可以作为客户端。于是我们可以从另一个角度来理解,认为每一个 V2Ray 都是一个节点,inbound 是关于如何与上一个节点连接的配置,outbound 是关于如何与下一个节点连接的配置。对于第一个节点,inbound 与浏览器连接;对于最后一个节点,outbound与目标网站连接。inboundsoutboundsinboundoutbound 的集合,意味着每一个 V2Ray 节点都可以有多个入口和出口。本例当中的入口和出口都只有一个,这是为了便于说明和理解。

客户端

客户端配置中的 inbounds,port 为 1080,即 V2Ray 监听了一个端口 1080,协议是 socks。之前我们已经把浏览器的代理设置好了(SOCKS Host: 127.0.0.1,Port: 1080),假如访问了 google.com,浏览器就会发出一个数据包打包成 socks 协议发送到本机(127.0.0.1 指的本机,localhost)的 1080 端口,这个时候数据包就会被 V2Ray 接收到。

再看 outbounds,protocol 是 vmess,说明 V2Ray 接收到数据包之后要将数据包打包成 VMess (opens new window)协议并且使用预设的 id 加密(这个例子 id 是 b831381d-6324-4d53-ad4f-8cda48b30811),然后发往服务器地址为 serveraddr.com 的 16823 端口。服务器地址 address 可以是域名也可以是 IP,只要正确就可以了。

在客户端配置的 inbounds 中,有一个 "sniffing" 字段,V2Ray 手册解释为“流量探测,根据指定的流量类型,重置所请求的目标”,这话不太好理解,简单说这东西就是从网络流量中识别出域名。这个 sniffing 有两个用处:

  1. 解决 DNS 污染;
  2. 对于 IP 流量可以应用后文提到的域名路由规则;
  3. 识别 BT 协议,根据自己的需要拦截或者直连 BT 流量(后文有一节专门提及)。

如果这段话不懂,没关系,照着写吧。

服务器

接着看服务器,服务器配置的 id 是 b831381d-6324-4d53-ad4f-8cda48b30811,所以 V2Ray 服务器接收到客户端发来的数据包时就会尝试用 b831381d-6324-4d53-ad4f-8cda48b30811 解密,如果解密成功再看一下时间对不对,对的话就把数据包发到 outbound 去,outbound.protocol 是 freedom(freedom 的中文意思是自由,在这里姑且将它理解成直连吧),数据包就直接发到 google.com 了。

实际上数据包的流向就是:

plain
{浏览器} <--(socks)--> {V2Ray 客户端 inbound <-> V2Ray 客户端 outbound} <--(VMess)-->  {V2Ray 服务器 inbound <-> V2Ray 服务器 outbound} <--(Freedom)--> {目标网站}

配置中还有一个 alterId 参数,在之前的版本中建议设置为 30 到 100 之间,在 v4.28.1 版本之后必须设置为 0 以启用 VMessAEAD


注意事项

  • 为了让浅显地介绍 V2Ray 的工作方式,本节中关于原理简析的描述有一些地方是错误的。但我知识水平又不够,还不知道该怎么改,暂且将错就错。正确的工作原理在用户手册的 VMess 协议 (opens new window)有详细的说明。
  • VMess 协议可以设定加密方式,但 VMess 不同的加密方式对于过墙没有明显差别,本节没有给出相关配置方式(因为这不重要,默认情况下 VMess 会自己选择一种比较合适的加密方式),具体配置可见 V2Ray 手册 (opens new window),不同加密方式的性能可参考性能测试

Shadowsocks

本节讲述 Shadowsocks 的配置。

其实,作为一个代理工具集合,V2Ray 集成有 Shadowsocks 模块。用 V2Ray 配置成 Shadowsocks 服务器或者 Shadowsocks 客户端都是可以的,兼容 Shadowsocks-libev, go-shadowsocks2 等基于 Shadowsocks 协议的客户端。

配置与 VMess 大同小异,客户端服务器端都要有入口和出口,只不过是协议(protocol)和相关设置(settings)不同,不作过多说明,直接给配置,如果你配置过 Shadowsocks,对比之下就能够明白每个参数的意思(配置还有注释说明呢)。

客户端配置

json
{
  "inbounds": [
    {
      "port": 1080, // 监听端口
      "protocol": "socks", // 入口协议为 SOCKS 5
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      },
      "settings": {
        "auth": "noauth"  // 不认证
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "shadowsocks",
      "settings": {
        "servers": [
          {
            "address": "serveraddr.com", // Shadowsocks 的服务器地址
            "method": "aes-128-gcm", // Shadowsocks 的加密方式
            "ota": true, // 是否开启 OTA,true 为开启
            "password": "sspasswd", // Shadowsocks 的密码
            "port": 1024  
          }
        ]
      }
    }
  ]
}

#服务器配置

json
{
  "inbounds": [
    {
      "port": 1024, // 监听端口
      "protocol": "shadowsocks",
      "settings": {
        "method": "aes-128-gcm",
        "ota": true, // 是否开启 OTA
        "password": "sspasswd"
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",  
      "settings": {}
    }
  ]
}

#注意事项

  • 因为协议漏洞,Shadowsocks 已放弃 OTA(一次认证) 转而使用 AEAD,V2Ray 的 Shadowsocks 协议已经跟进 AEAD,但是仍然兼容 OTA。建议使用 AEAD (method 为 aes-256-gcm、aes-128-gcm、chacha20-poly1305 即可开启 AEAD), 使用 AEAD 时 OTA 会失效;
  • Shadowsocks 已经弃用 simple-obfs,可使用基于 V2Ray 的新版混淆插件(但也可以使用 V2Ray 的 Websocket/http2 + TLS );
  • 可以使用 V2Ray 的传输层配置(详见高级篇),但如果这么设置了将与原版 Shadowsocks 不兼容(兼容 Shadowsocks 新增的 v2ray-plugin (opens new window)插件)。

HTTP

本节举例 HTTP(S)代理的配置. 在早期的 V2Ray 中不支持 HTTP(S)作为出站协议的,但大家对 HTTP(S)出站支持的呼声比较高,于是在最近的版本(V4.21.1)中推出了 HTTP(S)出站的支持. 配置与 VMess 大同小异,客户端服务器端都要有入口和出口,只不过是协议(protocol)和相关设置(settings)不同,不作过多说明,直接给配置

配置

客户端配置

json
{
  "inbounds": [
    {
      "port": 1080, // 监听端口
      "protocol": "socks", // 入口协议为 SOCKS 5
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      },
      "settings": {
        "auth": "noauth"  // 不认证
      }
    }
  ],
  "outbounds": [
      {
        "protocol": "http",
        "settings": {
          "servers": [
            {
              "address": "192.168.108.1",//服务器IP
              "port": 1024,//服务器端口
              "users": [
                {
                  "Username": "my-username",//将my-username改为你的用户名.
                  "Password": "my-password" //将my-password改为你的密码
                }
              ] 
            }
          ]
        },
        "streamSettings": {
          "security": "none", //如果是HTTPS代理,需要將none改為tls
          "tlsSettings": {
            "allowInsecure": false
            //检测证书有效性
        }
      }
    }
  ]
}

服务器配置

json
{
  "inbounds": [
    {
      "port": 1024, // 监听端口
      "protocol": "http",
      "settings": {
        "timeout:":0,
        "accounts":[
          {
            "user":"my-username",
            "pass":"my-password"
          }
        ],
        "allowTransparent":false,
        "userLevel":0
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",  
      "settings": {}
    }
  ]
}

注意事项

  • HTTP(S) 出站规则存在的意义是方便只能使用 http proxy 对外访问内部网络中用户联网使用,如果需要在使用 HTTP 代理联网的前提下翻墙请阅读前置代理 (opens new window).
  • HTTP(S) 出站规则可以作为对外访问的配置,但 http proxy 协议没有对传输加密,不适宜经公网中传输,且因不支持 udp 传输将会导致 core 功能受限(Routing 过程的的 DNS 查询不可用).

DNS 服务

V2Ray 内置的 DNS 服务,其解析的 IP 往往先是用来匹配路由规则,如果配置不当,请求会在 DNS 请求上耗费一定时间。

路由 routing 的"domainStrategy"的几种模式都跟 DNS 功能密切相关,所以在此专门说一下。

  • "AsIs",当终端请求一个域名时,进行路由里面的 domain 进行匹配,不管是否能匹配,直接按路由规则走。
  • "IPIfNonMatch", 当终端请求一个域名时,进行路由里面的 domain 进行匹配,若无法匹配到结果,则对这个域名进行 DNS 查询,用结果的 IP 地址再重新进行 IP 路由匹配。
  • "IPOnDemand", 当匹配时碰到任何基于 IP 的规则,将域名立即解析为 IP 进行匹配。

可见,AsIs是最快的,但是分路由的结果最不精确;而IPOnDemand是最精确的,但是速度是最慢的。

DNS 流程

img

基础配置

json
{
 "dns": {
   "servers": [
     "1.1.1.1",
     "localhost"
   ]
 }
}

DNS 模块的基础使用并没有什么特别复杂的地方,就是指定一个或几个 DNS 服务器,v2ray 会依次使用(查询失败时候会查询下一个)。其中”localhost”的意义是特殊的,作用是本地程序用系统的 DNS 去发请求,而不是 V2ray 直接跟 DNS 服务器通信,这个通信不受 Routing 等模块的控制。

进阶配置

虽然这是基本篇,但是不想在高级那边再开一篇 DNS 了,就写这。

客户端分流配置

json
{
 "dns": {
   "servers": [
     {
       "address": "119.29.29.29",
       "port": 53,
       "domains": [
         "geosite:cn"
       ],
       "expectIPs": [
         "geoip:cn"
       ]
     },
     {
       "address": "8.8.8.8",
       "port": 53,
       "domains": [
         "geosite:geolocation-!cn",
         "geosite:speedtest",
         "ext:h2y.dat:gfw"
       ]
     },
     "1.1.1.1",
     "localhost"
   ]
 }
}

DNS 服务是可以用来分流的,大致思路是,”哪些域名要去哪个 DNS 服务器解析,并希望得到属于那里的 IP 地址“。 配置的规则跟路由模式中用的是相似的,详细使用还需参考官方文档。

上面的配置思路这里解释一下:国内域名匹配到 domains 里面,使用 119.29.29.29 进行查询,并期待得到国内的 IP 地址;如果得到的地址并不是国内的,则进行下一个 DNS 服务器进行查询,并使用新的结果。不是国内的域名会匹配到第二个配置, 使用 8.8.8.8 进行查询,这次不需要期待特别的 IP 了,可直接使用返回的;如果以上过程都有问题,则直接查询 1.1.1.1,再不行让本地 DNS 试试吧。

服务器配置

json
{
 "dns": {
   "servers": [
     "https+local://1.1.1.1/dns-query",
     "localhost"
   ]
 }
}

服务端的 DNS 一般无需复杂配置。如果配置了,应注意freedom的 outbound 应配置了"domainStrategy""UseIP" | "UseIPv4" | "UseIPv6"几种的时候才会使用内置 DNS,默认的AsIs是交给操作系统去解析和连接。

新版本 4.22.0+ 后加入的 DOH 功能,部署在服务器端时候可以简单使用。

对外开放 v2ray 的 DNS 服务

Kitsunebi 的作者在 《漫谈各种黑科技式 DNS 技术在代理环境中的应用》 (opens new window)中介绍了通过Dokodemo入站协议和DNS出站协议开放 v2ray DNS 的方法,可以充分发挥 v2ray 内置 DNS 的强大能力,例如让系统其它不经代理的联网程序获得 DNS 级别广告过滤的能力,以及在透明代理中接管系统 DNS 等。

该方法的核心思想是使用Dokodemo入站协议接收 DNS 请求流量,转发至DNS出站协议。而DNS出站协议会拦截Type AType AAAA的 DNS 查询并交由 v2ray 内置 DNS 处理,从而返回查询结果;除此之外的查询流量,会根据Dokodemo的配置发送至目标 DNS 服务器。

客户端基本配置

json
{
    "inbounds": [
        {
            "tag": "dns-in",
            "port": 53,
            "protocol": "dokodemo-door",
            "settings": {
                "address": "8.8.8.8",
                "port": 53,
                "network": "tcp,udp",
                "userLevel": 1
            }
        }
    ],
    "outbounds": [
        {
            "protocol": "dns",
            "tag": "dns-out"
        }
    ],
    "routing": {
        "rules": [
            {
                "type": "field",
                "inboundTag": [
                    "dns-in"
                ],
                "outboundTag": "dns-out"
            }
        ]
    }
}

然而,这样配置存在一定问题。对于非Type AType AAAA的 DNS 查询,v2ray 将会直连转发至dns-in中所设置的目标 DNS 服务器。而当这个域名被污染时,返回的自然是被污染的结果;如果希望 v2ray 内置 DNS 承担 DNSCrypt-Proxy 的作用,这样无疑会导致 DNS 查询内容的泄露。

解决方法有两种:

  1. dns-out中,添加proxySettings的配置,使得非Type AType AAAA的 DNS 查询经由remote-proxy-out转发至远端解析。此时,目标为8.8.8.8:53的非Type AType AAAA的 DNS 查询流量通过代理转发至远端,并通过远端 v2ray 的freedom出站,发往8.8.8.8:53从而返回未经污染的结果。
{
    "outbounds": [
        {
            "protocol": "dns",
            "tag": "dns-out",
            "proxySettings": {
                "tag": "remote-proxy-out"
            }
        }
    ]
}
  1. dns-in 中设置其它可靠的 DNS 服务器。例如,仅利用 v2ray 内置 DNS 实现 DNS 分流,而解析则使用 dnscrypt-proxy 实现。

如果非 Type AType AAAA 的 DNS 查询需求较少,可以无视上述改进。

DNS over HTTPS

V2Ray 4.22.0 新加入的功能,也没特殊配置的地方,就是上述配置里面的 DNS 地址写成 DOH 服务地址。一般只在服务端使用 https+local 模式,而墙内目前似乎没有稳定的 DOH 提供商,只有 1.1.1.1 一家可用,而且效果并不稳定 在中国大陆可以使用阿里云 DNS 提供的 DoH 服务解析境内域名:https://dns.alidns.com/dns-queryhttps://223.5.5.5/dns-query

json
{
 "dns": {
   "servers": [
     "https+local://1.1.1.1/dns-query",
     "localhost"
   ]
 }
}

DOH 服务商不像传统 DNS 那么成熟,目前网上提供 DOH 的服务商可以参考 curl - DNS over HTTPS(opens new window)

注意,多数服务商的 DOH 的 tls 证书是没有对 IP 地址签发认证的,必须写实际的域名,但也有一些 DoH 提供商可以直接使用 IP 作为主机名访问,例如 CloudFlare 的 1.1.1.1 和阿里云公共 DNS 的 223.5.5.5

DOH 把 DNS 请求融入到常见的 https 流量当中,完全使用 DOH 可以避免出入口 ISP 知道你访问的域名。 但需要注意,只有在客户端、服务端都使用 DOH 协议(客户端使用 https 模式,服务端使用 https+local 模式)时候,VPS 出口上才不会出现传统的 UDP DNS 请求。

DOH 的解析时间比传统的 UDP 要高不少,把 V2Ray 的 log level 设置为 info 可以看到具体的域名解析耗时值。

plain
2019/11/28 17:34:55 [Info] v2ray.com/core/app/dns: UDP:1.1.1.1:53 got answere: www.msn.com. TypeA -> [204.79.197.203] 8.9953ms
...
2019/11/28 17:42:50 [Info] v2ray.com/core/app/dns: DOH:1.1.1.1:443 got answere: lp-push-server-849.lastpass.com. TypeA -> [192.241.186.205] 182.1171ms

以下列出解析耗时参考值:

  • 美国 VPS UDP 1.1.1.1:1ms~5ms
  • 美国 VPS DOHL 1.1.1.1:10ms~100ms
  • V2Ray 客户端 国内 UDP 1.1.1.1:200ms~1s
  • V2Ray 客户端 国内 DOH 1.1.1.1:200ms~3s

但是实际中因为网络原因之类问题,也可能出现 DOH 耗时比 UDP 还小的。个人感觉这个耗时虽然有区别,但都是较小的间隙,实际使用很少有察觉。

大家按需选择使用即可。

参考阅读

详见 《漫谈各种黑科技式 DNS 技术在代理环境中的应用》 (opens new window),这篇文章为 Kitsunebi 的作者所写,很详细地分析了 V2Ray 关于 DNS 的机制及一些独有的骚操作,如果你有关于透明代理的需求,我认为很值得一看。如果没有那就随意吧。

DNS 答疑

问:DOH 是什么

答:DOH 是 DNS Over HTTPS 的简称,作用是用 https 请求来完成 DNS 请求。V2ray-core 4.22.0 后加入的功能。DOH 在历史上经历过多个草案版本,V2ray 支持的是 RFC8484。

问:DOHL 又是什么

答:DOHL 不是个标准名称,只是 V2ray 内的一个称呼。V2ray 有两种 DOH 工作模式,DOHL 是指配置中写https+local://1.1.1.1/dns-query,程序直接发出而不经过 routing 规则;而写成https://1.1.1.1/dns-query则会根据路由规则走。

问:DNS 都要走 HTTPS,那不是很慢

答:对。传统的 UDP 基本会在 10ms 级别响应,而 DOH 的响应时间一般都在 100ms+。在 DOHL 模式下受益于 http2 的长链接,也能有数十 ms 的响应。不过个人来说,这些 ms 级别的延时是没什么感觉。

问:DOH 走 HTTPS 可以防污染,传统 DNS 走代理也不受污染,两者有区别吗

答:效果没区别。一定要说区别就是:DOH 后出口 ISP 也不知道 DNS 查询的内容了。DOH 设计的主要目的是解决私隐问题,并不是什么厉害的黑科技。

问:V2ray 的 DNS 配置好像很复杂

答:V2ray 的 DNS 主要是两个作用,用户比较容易混淆:

  • 一是在出口 freedom 协议的 outbound 处,就跟普通代理服务器一样,代理的请求在此处终结,可称为“终结 DNS”;
  • 二是在匹配 routing 规则前需要对终端请求的域名进行解析成 IP,再根据 IP 做 routing 规则匹配,可称为“匹配 DNS”。

其中“终结 DNS”可交给操作系统处理(freedom 的domainStrategy:AsIs),也可使用内置 DNS 的结果。

而“匹配 DNS”都由内置 DNS 处理。“匹配 DNS”在整个代理过程中并不是必不可少的,当使用 routing 的 “domainStrategy”:AsIs 时,甚至不会使用“匹配 DNS”功能;即使是其他模式,也只影响效率:如果内置 DNS 配置不正确,每个请求都会询问一次错误的“匹配 DNS”,直至超时后才转发,但整个转发过程数据是正常的。

问:DNS 里面还有”domians”…“expectIPs”

答:理解“终结 DNS”和“匹配 DNS”两大作用后,根据需要,按照文档选用这些参数即可。

问:压根没写 DNS 配置,好像也是正常的

答:不写 dns 配置,会默认一个"localhost"配置,作为内置代理。在客户端侧,内置 DNS 的作用只有“匹配 DNS”,即使本地 DNS 返回了个污染的结果,往往也不影响 v2ray 走代理的流程,所以“也正常”。

问:文档推荐在服务器侧使用 DOHL,在墙内客户端侧使用 DOHL 模式可以吗

答:可以使用一些境内可用的服务商,比如https+local://1.1.1.1/dns-queryhttps+local://dns.rubyfish.cn/dns-queryhttps+local://dns.alidns.com/dns-query。但是如果这些服务在境内不可用,就只能走 DOH 模式,或者自建 DOH 服务。

自建 DOH 也不算复杂,V2ray 也能使用非标准端口和自定义的路径,甚至“隐藏”在一个个人网站后(类似 tls+ws+web)模式。这些玩法有待大家去挖掘。

问:V2ray 作为 DNS 服务端时候算什么模式

答:V2ray 可以通过 dokodemo 的 inbound 提供 DNS 服务回应,使用内置 DNS 服务的配置。这个过程最终是以 UDP 形式发到出口的外部 DNS 请求的,不属于上述的“终结 DNS”和“匹配 DNS”的过程。

问:在透明代理的作用中,DNS 是怎样工作的

答:取决于透明代理的形式。有些是把 DNS 作为普通流量直接转发到代理,这不受 V2ray 的 DNS 机制影响;有些通过中间组件如 redsocks,把子网内的 DNS 请求转换成 socks5 请求,这也类似流量直走,不受 V2ray 的 DNS 机制影响;有些是使用 V2ray 作为 DNS 服务端,受内置服务器的缓存和匹配影响。

日志文件

使用一个软件总是不可避免出现一些问题,比如说你用着某个软件突然间崩溃了,兴冲冲向开发者反馈说软件有崩溃现象。开发者问你日志,你没有;问你详细情况,你支支吾吾说不出来。比较和蔼的开发者可能会跟你说:好的,我知道了,这个问题会解决的。内心独白却是:mdzz,啥也说不出来,日志也没有还瞎 bb。

对于软件开发者来说使用查看日志是一种非常有效的调试手段。普通用户使用日志可以知道软件的运行状况,并且当软件出现异常时提供日志给开发者可以令开发者更加容易找到问题的根源,加快修复问题。

配置

客户端配置

json
{
  "log": {
    "loglevel": "warning", // 日志级别
    "access": "D:\\v2ray\\access.log",  // 这是 Windows 系统的路径
    "error": "D:\\v2ray\\error.log"
  },
  "inbounds": [
    {
      "port": 1080,
      "protocol": "socks",
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      },
      "settings": {
        "auth": "noauth"
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "vmess",
      "settings": {
        "vnext": [
          {
            "address": "serveraddr.com",
            "port": 16823,  
            "users": [
              {
                "id": "b831381d-6324-4d53-ad4f-8cda48b30811",  
                "alterId": 64
              }
            ]
          }
        ]
      }
    }
  ]
}

服务器配置

json
{
  "log": {
    "loglevel": "warning",
    "access": "/var/log/v2ray/access.log", // 这是 Linux 的路径
    "error": "/var/log/v2ray/error.log"
  },
  "inbounds": [
    {
      "port": 16823,
      "protocol": "vmess",   
      "settings": {
        "clients": [
          {
            "id": "b831381d-6324-4d53-ad4f-8cda48b30811",  
            "alterId": 64
          }
        ]
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",  
      "settings": {}
    }
  ]
}

依次看 log 的选项:

  • loglevel:日志级别,分别有 5 个,本例中设定的是 warning
    • debug:最详细的日志信息,专用于软件调试
    • info:比较详细的日志信息,可以看到 V2Ray 详细的连接信息
    • warning:警告信息。轻微的问题信息,经我观察 warning 级别的信息大多是网络错误。推荐使用 warning
    • error:错误信息。比较严重的错误信息。当出现 error 时该问题足以影响 V2Ray 的正常运行
    • none:空。不记录任何信息
  • access:访问记录输出路径
    • "": 输出至控制台即 stdout
    • “none”: 不输出
    • “/path/access.log”: 输出至/path/access.log
  • error:错误记录输出路径
    • "": 输出至控制台即 stdout
    • “none”: 不输出
    • “/path/error.log”: 输出至/path/error.log

提示

需要注意的一点是,在 json 中,反斜杠 \ 是转义字符,因此 Windows 操作系统目录的 \ 符号在配置中要使用 \\ 来表示。

路由功能

本小节将介绍路由功能的使用。V2Ray 的一大特点就是内置了路由功能,用大白话说就是可以根据自己的实际情况制定一些规则来满足自己的上网需求,最简单最常见的就是直连国内网站、拦截特定站点以及代理被墙网站。

路由简介

先简单举几个例子,都是客户端的。

json
{
  "log": {
    "loglevel": "warning",
    "access": "D:\\v2ray\\access.log",
    "error": "D:\\v2ray\\error.log"
  },
  "inbounds": [
    {
      "port": 1080,
      "protocol": "socks",
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      },
      "settings": {
        "auth": "noauth"  
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "vmess",
      "settings": {
        "vnext": [
          {
            "address": "serveraddr.com",
            "port": 16823,  
            "users": [
              {
                "id": "b831381d-6324-4d53-ad4f-8cda48b30811",  
                "alterId": 64
              }
            ]
          }
        ]
      }
    }
  ]
}

像上面这个配置就是前面 VMess 的客户端配置文件,假如改一下 outbound 的内容,变成这样:

json
{
  "log": {
    "loglevel": "warning",
    "access": "D:\\v2ray\\access.log",
    "error": "D:\\v2ray\\error.log"
  },
  "inbounds": [
    {
      "port": 1080,
      "protocol": "socks",
      "settings": {
        "auth": "noauth"  
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom", //原来是 VMess,现在改成 freedom
      "settings": {
      }
    }
  ]
}

如果修改成这个配置重启客户端之后,你会发现这个时候浏览器设不设置代理其实是一样的,像 Google 这类被墙的网站没法访问了,taobao 这种国内网站还是跟平常一样能上。如果是前面的介绍 VMess,数据包的流向是:

plain
{浏览器} <--(socks)--> {V2Ray 客户端 inbound <-> V2Ray 客户端 outbound} <--(VMess)-->  {V2Ray 服务器 inbound <-> V2Ray 服务器 outbound} <--(Freedom)--> {目标网站}

但因为现在 V2Ray 客户端的 outbound 设成了 freedom,freedom 就是直连,所以呢修改后数据包流向变成了这样:

plain
{浏览器} <--(socks)--> {V2Ray 客户端 inbound <-> V2Ray 客户端 outbound} <--(Freedom)--> {目标网站}

V2Ray 客户端从 inbound 接收到数据之后没有经过 VPS 中转,而是直接由 freedom 发出去了,所以效果跟直接访问一个网站是一样的。

再来看下面这个:

json
{
  "log":{
    "loglevel": "warning",
    "access": "D:\\v2ray\\access.log",
    "error": "D:\\v2ray\\error.log"
  },
  "inbounds": [
    {
      "port": 1080,
      "protocol": "socks",
      "settings": {
        "auth": "noauth"  
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "blackhole",
      "settings": {
      }
    }
  ]
}

这样的配置生效之后,你会发现无论什么网站都无法访问。这是为什么呢?blackhole 是黑洞的意思,在 V2Ray 这里也差不多相当于是一个黑洞,就是说 V2Ray 从 inbound 接收到数据之后发到 outbound,因为 outbound 是 blackhole,来什么吞掉什么,就是不转发到服务器或者目标网站,相当于要访问什么就阻止访问什么。

到这儿为止,总共介绍了 4 种出口协议:用于代理的 VMess 和 Shadowsocks 协议,用于直连的 freedom 协议,以及用于阻止连接的 blackhole 协议。我们可以利用这几种协议再配合路由功能可以灵活地根据自己的需求针对不同网站进行代理、直连或者拦截。举个简单的例子,比较大众的需求是被墙网站走代理,国内网站直连,其他一些不喜欢的则拦截(比如说百度的高精度定位)。

等等!你这里有 VMess、freedom 和 blackhole 3 个出口,难道要运行 3 个 V2Ray 吗?

当然不是!在 V2Ray 的配置中,outbounds 是出口协议的集合,你可以在里面放任意多个出口协议,不仅 3 个,300 个都可以。下面给出放 3 个出口协议配置的例子。

json
{
  "log": {
    "loglevel": "warning",
    "access": "D:\\v2ray\\access.log",
    "error": "D:\\v2ray\\error.log"
  },
  "inbounds": [
    {
      "port": 1080,
      "protocol": "socks",
      "settings": {
        "auth": "noauth"  
      }
    }
  ],
  "outbounds": [ 
    {
      "protocol": "vmess", // 出口协议
      "settings": {
        "vnext": [
          {
            "address": "serveraddr.com", // 服务器 IP 地址
            "port": 16823,  // 服务器端口
            "users": [
              {
                "id": "b831381d-6324-4d53-ad4f-8cda48b30811",  // 用户 ID,须与服务器端配置相同
                "alterId": 64
              }
            ]
          }
        ]
      }
    },
    {
      "protocol": "freedom",
      "settings": {}
    },
    {
      "protocol": "blackhole",
      "settings": {}
    }
  ]
}

当然这个配置只是包含了多个出口协议而已,在包含多个出口协议的情况下,只会以 outbounds 中的第一个出口作为默认的出口。要达到上面说的被墙网站走代理,国内网站直连,其他特殊网站拦截的效果,还得加入路由功能的配置。关于路由功能的配置见后面两小节。

小结

现在对本章作个总结。

配置文件格式

V2Ray 的配置文件格式就像这样:

json
{
  "log": {},
  "inbounds": [],
  "outbounds": [],
  "routing": {},
  "transport": {},
  "dns": {},
  "reverse": {},
  "policy": {},
  "stats": {},
  "api": {}
}

总的来说,V2Ray 的配置有 10 个字段,每个字段都可以进一步展开成具体的配置。这些配置字段当中,本章有涉及到前面 4 项,关于 dnstransportreverse 将在后文说明。而 apipolicystats 的内容暂未提供,对这些感兴趣的仔细研究手册吧。配置文件的详细信息可以参考手册 (opens new window)

要理解 V2Ray 的工作模式,首先得抛开客户端和服务器的概念(教程中说客户端和服务器是习惯了),我们更应该以中转节点的概念来理解。 V2Ray 只是一个转发数据的软件,只要它从入口当中接收到数据包,不管 V2Ray 对这些数据包做了什么(加密、解密、协议转换等),到最后肯定是要把这些数据包从出口发出去。每一个运行的 V2Ray 都是一个节点,它从上一个节点接收数据,发送到下一个节点,在这样由多个节点组成的代理链中,首节点和末节点就是我们常说的客户端和服务器。更广义地说,每个节点对于上一个节点来说是服务器,对于下一个节点来说是客户端。

协议

无论是出口还是入口,我们首先要明确的是协议,只有协议对了才能正常通信。

V2Ray 的传入协议有 HTTP、SOCKS、VMess、Shadowsocks、Dokodemo-door;传出协议有 VMess、Shadowsocks、Blackhole、Freedom、SOCKS。

在 inbounds 和 outbounds 当中,无论使用了什么协议,inbounds 或者 outbounds 的配置格式都是一样的,区别只在于不同的协议对应的 settings 内容不一样。

高级

Mux

Mux 意为多路复用(multiplexing),在目前的科学上网工具中仅 V2Ray 有此功能(2018-03-15 注:也有其他软件实现了类似的功能)。它能够将多条 TCP 连接合并成一条,节省资源,提高并发能力。

听众:呃?什么鬼?

好吧,翻译成人话就是:

从前,有一个人叫小白,他是骑行爱好者,还是网购狂人、DIY 玩家,因此手中有点闲钱总会网购,也喜欢自己买配件组装自行车。有一次他组装自行车,在网上骑行之家买了头盗、手套、码表,在 x 诺专卖店买了指拨、变速器,在 x 特专卖买了车架,在 xx 车行买了刹车、踏板、坐垫,在 xxx 买了轮组、曲柄……

4 天后……plainplainplainplainplainplainplain
9 点小白手机响了,接通,小白:喂,你好。对方:你好,申x快递,来取一下包裹。小白兴兴冲地取快递了。
20 分钟后,小白:喂,你好。对方:你好,韵x,过来取快递。小白又去了。
又过 15 分钟,小白:喂,你好。对方:你好,天x,来取快递。小白又去了。
又过半个小时,小白:什么快递?对方:圆x,快点来。小白心里:我X。
10 分钟后……

如果是你是小白,你累不累? 电脑也差不多,但要干的活要小白多得多:

浏览器:我要看 V2Ray 配置指南。plainplainplainplainplain
电脑:好,我发起一条 TCP 连接。
Telegram:我要进 V2Ray 的 Telegram 群向大佬学习。
电脑:好,发起了连接。
浏览器:我要看 V2Ray 的手册。
电脑:好。
浏览器:我要 Google 搜索 V2Ray 的教程。
电脑:好。
浏览器:我要……

如果正常的上网连接可以使用上面小白的例子类比的话,那么,V2Ray 的 Mux 就是:

小黑也与小白一样自行组装自行车,也是网购配件,但他无论什么东西都从 xx 车行这店里买。

4 天后,小黑接起电话:你好。plainplainplainplainplainplainplainplain
对方:你好,顺x,来取一下快递。
小黑顺路买了瓶饮料:大哥,天气这么热,喝点水解解渴。嘿嘿,这箱子太沉,辛苦一下帮帮我搬到屋里吧。

Mux 实质上不能提高网速,但对并发连接比较有效,如浏览图片较多的网页,看直播等。从使用效果来说,V2Ray 的 Mux 应该类似于 Shadowsocks 的 TCP Fast Open,因为两者的目的都是减小握手时间,只是实现方式不一样。只是 TCP Fast Open 要设置系统内核才能打开,而 Mux 是纯粹在软件层面上实现,从配置难易度上 V2Ray 较好一些。(2018-09-19 注:刚更新这段话没多久,V2Ray 就加入了对 TCP Fast Open 的支持,感觉要学不动了~)

配置

Mux 只需在客户端开启,服务器会自动识别,所以只给客户端的配置。也就是只要在 outbound 或 outboundDetour 加入 "mux": {"enabled": true} 即可:

json
{
  "inbounds": [
    {
      "port": 1080, // 监听端口
      "protocol": "socks", // 入口协议为 SOCKS 5
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      },
      "settings": {
        "auth": "noauth"  // 不认证
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "vmess", // 出口协议
      "settings": {
        "vnext": [
          {
            "address": "serveraddr.com", // 服务器地址,请修改为你自己的服务器 ip 或域名
            "port": 16823,  // 服务器端口
            "users": [
              {
                "id": "b831381d-6324-4d53-ad4f-8cda48b30811",  // 用户 ID,必须与服务器端配置相同
                "alterId": 64 // 此处的值也应当与服务器相同
              }
            ]
          }
        ]
      },
      "mux": {"enabled": true}
    }
  ]
}

mKCP

V2Ray 引入了 KCP (opens new window)传输协议,并且做了一些不同的优化,称为 mKCP。如果你发现你的网络环境丢包严重,可以考虑一下使用 mKCP。由于快速重传的机制,相对于常规的 TCP 来说,mKCP 在高丢包率的网络下具有更大的优势,也正是因为此, mKCP 明显会比 TCP 耗费更多的流量,所以请酌情使用。要了解的一点是,mKCP 与 KCPTUN 同样是 KCP 协议,但两者并不兼容。

在此我想纠正一个概念。基本上只要提起 KCP 或者 UDP,大家总会说”容易被 Qos“。Qos 是一个名词性的短语,中文意为服务质量,试想一下,你跟人家说一句”我的网络又被服务质量了“是什么感觉。其次,哪怕名词可以动词化,这么使用也是不合适的,因为 Qos 区分网络流量优先级的,就像马路上划分人行道、非机动车道、快车道、慢车道一样,哪怕你牛逼到运营商送你一条甚至十条专线,是快车道中的快车道,这也是 Qos 的结果。

配置

mKCP 的配置比较简单,只需在服务器的 inbounds 和 客户端的 outbounds 添加一个 streamSettings 并设置成 mkcp 即可。

服务器配置

json
{
  "inbounds": [
    {
      "port": 16823,
      "protocol": "vmess",
      "settings": {
        "clients": [
          {
            "id": "b831381d-6324-4d53-ad4f-8cda48b30811",
            "alterId": 64
          }
        ]
      },
      "streamSettings": {
        "network": "mkcp", //此处的 mkcp 也可写成 kcp,两种写法是起同样的效果
        "kcpSettings": {
          "uplinkCapacity": 5,
          "downlinkCapacity": 100,
          "congestion": true,
          "header": {
            "type": "none"
          }
        }
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "settings": {}
    }
  ]
}

#客户端配置

json
{
  "inbounds": [
    {
      "port": 1080,
      "protocol": "socks",
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
    },
      "settings": {
        "auth": "noauth"
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "vmess",
      "settings": {
        "vnext": [
          {
            "address": "serveraddr.com",
            "port": 16823,
            "users": [
              {
                "id": "b831381d-6324-4d53-ad4f-8cda48b30811",
                "alterId": 64
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "mkcp",
        "kcpSettings": {
          "uplinkCapacity": 5,
          "downlinkCapacity": 100,
          "congestion": true,
          "header": {
            "type": "none"
          }
        }
      }
    }
  ]
}

#说明

在上面的配置当中,与之前相比主要的变化在于多了一个 streamSettings,包含有不少参数:

  • network: 网络的选择,要像上面的配置写成 kcp 或 mkcp 才会启用 mKCP

  • kcpSettings

    : 包含一些关于 mKCP 设置的参数,有

    • uplinkCapacity: 上行链路容量,将决定 V2Ray 向外发送数据包的速率。单位为 MB

    • downlinkCapacity:下行链路容量,将决定 V2Ray 接收数据包的速率。单位同样是 MB

    • header

      :对于数据包的伪装

      • type:要伪装成的数据包类型

客户端的上行对于服务器来说是下行,同样地客户端的下行是服务器的上行,mKCP 设置当中服务器和客户端都有 uplinkCapacity 和 downlinkCapacity,所以客户端的上传速率由服务器的 downlinkCapacity 和客户端的 uplinkCapacity 中的最小值决定,客户端的下载速率也是同样的道理。因此,建议将服务器和客户端的 downlinkCapacity 设成一个很大的值,然后分别修改两端的 uplinkCapacity 以调整上下行速率。

还有一个 header 参数可以对 mKCP 进行伪装,这是 mKCP 的一个优势。具体的伪装类型在 type 参数设置,type 可以设置成 utp、srtp、wechat-video、dtls、wireguard 或者 none,这几个分别将 mKCP 数据伪装成 BT 下载、视频通话、微信视频通话、dtls、wireguard(一种新型 VPN)以及不进行伪装。这里的 type 参数,客户端与服务器要一致。还有要时刻记住伪装仅仅是伪装。

至于上述配置里有但是我没有说明的参数,是 V2Ray 的默认值,我个人建议是保持默认。如果你需要了解或者修改,请参考手册。

#更新历史

动态端口

V2Ray 提供了一个叫动态端口的功能。顾名思义,就是可以动态变化通信端口,该功能的初衷是为了应对电信服务运营商可能会对长时间大流量的单个端口进行限速。也许是用的人比较少,到目前为止没有证据可以动态端口对于科学上网是加分项还是减分项。

#注意

根据实际使用来看,动态端口功能是面向 vmess 协议的特性,其他协议似乎不支持该特性。

#基本动态端口

服务器 inbound 的端口作为主端口,在 inboundDetour 开动态监听的端口,客户端不用额外设定,客户端会先与服务器的主端口通信协商下一个使用的端口号。

#服务器配置

json
{
  "inbounds":[
  { //主端口配置
      "port": 37192,
      "protocol": "vmess",
      "settings": {
        "clients": [
          {
            "id": "d17a1af7-efa5-42ca-b7e9-6a35282d737f",
            "alterId": 64
          }
        ],
        "detour": { //绕行配置,即指示客户端使用 dynamicPort 的配置通信
          "to": "dynamicPort"
        }
      }
    },
    {
      "protocol": "vmess",
      "port": "10000-20000", // 端口范围
      "tag": "dynamicPort",  // 与上面的 detour to 相同
      "settings": {
        "default": {
          "alterId": 64
        }
      },
      "allocate": {            // 分配模式
        "strategy": "random",  // 随机开启
        "concurrency": 2,      // 同时开放两个端口,这个值最大不能超过端口范围的 1/3
        "refresh": 3           // 每三分钟刷新一次
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "settings": {}
    }
  ]
}

#客户端配置

json
{
  "inbounds": [
    {
      "port": 1080,
      "protocol": "socks",
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      },
      "settings": {
        "auth": "noauth"
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "vmess",
      "settings": {
        "vnext": [
          {
            "address": "1.2.3.4",
            "port": 37192,
            "users": [
              {
                "id": "d17a1af7-efa5-42ca-b7e9-6a35282d737f",
                "alterId": 64
              }
            ]
          }
        ]
      }
    }
  ]
}

#动态端口使用 mKCP

在对应的 inbounds 和 outbounds 加入 streamSettings 并将 network 设置为 kcp 即可。

#服务器配置

json
{
  "inbounds": [
    {
      "port": 37192,
      "protocol": "vmess",
      "settings": {
        "clients": [
          {
            "id": "d17a1af7-efa5-42ca-b7e9-6a35282d737f",
            "level": 1,
            "alterId": 64
          }
        ],
        "detour": {
          "to": "dynamicPort"
        }
      },
      "streamSettings": {
        "network": "kcp"
      }
    },
    {
      "protocol": "vmess",
      "port": "10000-20000", // 端口范围
      "tag": "dynamicPort",
      "settings": {
        "default": {
          "level": 1,
          "alterId": 32
        }
      },
      "allocate": {            // 分配模式
        "strategy": "random",  // 随机开启
        "concurrency": 2,      // 同时开放两个端口
        "refresh": 3           // 每三分钟刷新一次
      },
      "streamSettings": {
        "network": "kcp"
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "settings": {}
    }
  ]
}

#客户端配置

json
{
  "inbounds": [
    {
      "port": 1080,
      "protocol": "socks",
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      },
      "settings": {
        "auth": "noauth"
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "vmess",
      "settings": {
        "vnext": [
          {
            "address": "1.2.3.4",
            "port": 37192,
            "users": [
              {
                "id": "d17a1af7-efa5-42ca-b7e9-6a35282d737f",
                "alterId": 64
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "kcp"
      }
    }
  ]
}

代理转发

V2Ray 提供了代理转发功能,利用它可以实现中转(在没有中转服务器操作权限的情况下)。

#基本代理转发

使用代理转发可以实现由一个 Shadowsocks 服务器或者 V2Ray(VMess) 服务器来中转你的网络流量,并且中转服务器只能看到你加密的数据而不知道原始的数据是什么。

以下面的配置说明,它的工作原理是:

  1. 你在 Twitter 发了个帖子 f**k GFW,由 V2Ray 代理
  2. V2Ray 客户端收到浏览器发出的 f**k GFW 的帖子后,首先由对其进行加密(VMess,id: b12614c5-5ca4-4eba-a215-c61d642116ce,目的服务器: 1.1.1.1:8888)
  3. 加密后数据包将被转到 transit 这个 outbound 中,在这里数据包又会加密一次(Shadowsocks, password: password, 服务器: 2.2.2.2:1024)
  4. 两次加密后的数据包被发送到了 Shadowsocks 服务器,该服务器收到后解包后得到仍是加密的数据包(步骤 2 中加密后的数据包),然后将数据包发到 VMess 服务器。即便这个 Shadowsocks 服务器的主人是个偷窥狂魔,他也没办法看到你的原始数据。
  5. VMess 服务器收到 Shadowsocks 服务器发来的数据包,解密得到原始的数据包,然后把你这个帖子发到 Twitter 的网站中。

只要第 5 步中的服务器是自己掌控的就不用担心别人看到你的上网的内容。

客户端:

json
{
  "outbounds": [
    {
      "protocol": "vmess",
      "settings": { // settings 的根据实际情况修改
        "vnext": [
          {
            "address": "1.1.1.1",
            "port": 8888,
            "users": [
              {
                "alterId": 64,
                "id": "b12614c5-5ca4-4eba-a215-c61d642116ce"
              }
            ]
          }
        ]
      },
      "proxySettings": {
          "tag": "transit"  // 这里的 tag 必须跟作为代理 VPS 的 tag 一致,这里设定的是 "transit"
        }
    },
    {
      "protocol": "shadowsocks",
      "settings": {
        "servers": [
          {
            "address": "2.2.2.2",
            "method": "aes-256-cfb",
            "ota": false,
            "password": "password",
            "port": 1024
          }
        ]
      },
      "tag": "transit"
    }
  ]
}

#链式代理转发

如果你有多个 Shadowsocks 或 VMess 账户,那么你可以这样:

json
{
  "outbounds": [
    {
      "protocol": "vmess",
      "settings": { // settings 的根据实际情况修改
        "vnext": [
          {
            "address": "1.1.1.1",
            "port": 8888,
            "users": [
              {
                "alterId": 64,
                "id": "b12614c5-5ca4-4eba-a215-c61d642116ce"
              }
            ]
          }
        ]
      },
      "tag": "DOUS",
      "proxySettings": {
          "tag": "DOSG"  
        }
    },
    {
      "protocol": "shadowsocks",
      "settings": {
        "servers": [
          {
            "address": "2.2.2.2",
            "method": "aes-256-cfb",
            "ota": false,
            "password": "password",
            "port": 1024
          }
        ]
      },
      "tag": "AliHK"
    },
    {
      "protocol": "shadowsocks",
      "settings": {
        "servers": [
          {
            "address": "3.3.3.3",
            "method": "aes-256-cfb",
            "ota": false,
            "password": "password",
            "port": 3442
          }
        ]
      },
      "tag": "AliSG",
      "proxySettings": {
          "tag": "AliHK"  
      }
    },
    {
      "protocol": "vmess",
      "settings": {
        "vnext": [
          {
            "address": "4.4.4.4",
            "port": 8462,
            "users": [
              {
                "alterId": 64,
                "id": "b27c24ab-2b5a-433e-902c-33f1168a7902"
              }
            ]
          }
        ]
      },
      "tag": "DOSG",
      "proxySettings": {
          "tag": "AliSG"  
      }
    },
  ]
}

那么数据包经过的节点依次为: PC -> AliHK -> AliSG -> DOSG -> DOUS -> 目标网站

这样的代理转发形成了一条链条,我称之为链式代理转发。

注意:如果你打算配置(动态)链式代理转发,应当明确几点:

  • 性能。链式代理使用了多个节点,可能会造成延时、带宽等网络性能问题,并且客户端对每一个加解密的次数取决于代理链的长度,理论上也会有一定的影响。
  • 安全。前文提到,代理转发会一定程度上提高安全性,但安全取决于最弱一环,并不意味着代理链越长就会越安全。如果你需要匿名,请考虑成熟的匿名方案。 另外,使用了代理转发 streamSettings 会失效,即只能是非 TLS、无 HTTP 伪装的 TCP 传输协议。

HTTP 伪装

2018-03-16 注:个人建议不要使用 HTTP 伪装) V2Ray 自 v2.5 版本开始提供 HTTP 伪装功能,后经作者不断完善,到现在已经非常成熟稳定了。V2Ray 的 HTTP 伪装功能可以可以将 V2Ray 的流量伪装成正常的 HTTP 协议的。这里给出一个 HTTP 伪装的服务器端与客户端配置文件示例。

配置中关于 HTTP 头字段的内容及含义,Wikipedia (opens new window)有简要的说明,可参阅。

配置

从 V2Ray 的实现角度来说,使用 HTTP 伪装的同时完全可以使用动态端口。但我个人并不建议这么做,因为从实际情况来看,基本上不会有人在一个服务器上开使用多个端口的 Web 服务。如果你觉得 HTTP 伪装的配置过于复杂不懂得如何修改,那请直接使用下面的配置即可。

服务器

json
{
  "log" : {
    "access": "/var/log/v2ray/access.log",
    "error": "/var/log/v2ray/error.log",
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "port": 80, //推荐80端口,更好地迷惑防火墙(好吧实际上并没有什么卵用
      "protocol": "vmess",
      "settings": {
        "clients": [
          {
            "id": "b831381d-6324-4d53-ad4f-8cda48b30811",
            "level": 1,
            "alterId": 64
          }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "tcpSettings": {
          "header": { // header 这一项是关于数据包伪装的设置,可自定义合理的内容,但要确保服务器与客户端一致
            "type": "http",
            "response": {
              "version": "1.1",
              "status": "200",
              "reason": "OK",
              "headers": {
                "Content-Type": ["application/octet-stream", "application/x-msdownload", "text/html", "application/x-shockwave-flash"],
                "Transfer-Encoding": ["chunked"],
                "Connection": ["keep-alive"],
                "Pragma": "no-cache"
              }
            }
          }
        }
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "settings": {}
    },
    {
      "protocol": "blackhole",
      "settings": {},
      "tag": "blocked"
    }
  ],
  "routing": {
    "strategy": "rules",
    "settings": {
      "rules": [
        {
          "type": "field",
          "ip": [
            "geoip:private"
          ],
          "outboundTag": "blocked"
        }
      ]
    }
  }
}

#客户端

json
{
  "log": {
    "loglevel": "warning"
  },
  "inbounds": [
    {
      "port": 1080,
      "protocol": "socks",
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      },
      "settings": {
        "auth": "noauth"
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "vmess",
      "settings": {
        "vnext": [
          {
            "address": "serveraddr.com",
            "port": 80,
            "users": [
              {
                "id": "b831381d-6324-4d53-ad4f-8cda48b30811",
                "alterId": 64
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "tcpSettings": {
          "header": {  //这里的 header 要与服务器保持一致
            "type": "http",
            "request": {
              "version": "1.1",
              "method": "GET",
              "path": ["/"],
              "headers": {
                "Host": ["www.cloudflare.com", "www.amazon.com"],
                "User-Agent": [
                  "Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.75 Safari/537.36",
                          "Mozilla/5.0 (iPhone; CPU iPhone OS 10_0_2 like Mac OS X) AppleWebKit/601.1 (KHTML, like Gecko) CriOS/53.0.2785.109 Mobile/14A456 Safari/601.1.46"
                ],
                "Accept-Encoding": ["gzip, deflate"],
                "Connection": ["keep-alive"],
                "Pragma": "no-cache"
              }
            }
          }
        }
      }
    },
    {
      "protocol": "freedom",
      "settings": {},
      "tag": "direct"
    }
  ],
  "routing": {
    "strategy": "rules",
    "settings": {
      "domainStrategy": "IPIfNonMatch",
      "rules": [
        {
          "type": "field",
          "ip": [
            "geoip:private"
          ],
          "outboundTag": "direct"
        },
        {
          "type": "chinasites",
          "outboundTag": "direct"
        },
        {
          "type": "chinaip",
          "outboundTag": "direct"
        }
      ]
    }
  }
}

TLS

从 v1.19 起引入了 TLS,TLS 中文译名是传输层安全,如果你没听说过,请 Google 了解一下。以下给出些我认为介绍较好的文章链接:

SSL/TLS 协议运行机制的概述(opens new window)

传输层安全协议(opens new window)

#注册一个域名

如果已经注册有域名了可以跳过。 TLS 需要一个域名,域名有免费的和有付费的,如果你不舍得为一个域名每年花点钱,用个免费域名也可以,但总体来说付费的会优于免费的。为了方便,在本文中我就忽略如何注册购买域名了。关于如何获取域名,具体搜索相关文章教程。

注册好域名之后务必记得添加一个 A 记录指向你的 VPS!

以下假设注册的域名为 mydomain.me,请将之替换成自己的域名。

#证书生成

TLS 是证书认证机制,所以使用 TLS 需要证书,证书也有免费付费的,同样的这里使用免费证书,证书认证机构为 Let’s Encrypt (opens new window)。 证书的生成有许多方法,这里使用的是比较简单的方法:使用 acme.sh (opens new window)脚本生成,本部分说明部分内容参考于acme.sh README (opens new window)

证书有两种,一种是 ECC 证书(内置公钥是 ECDSA 公钥),一种是 RSA 证书(内置 RSA 公钥)。简单来说,同等长度 ECC 比 RSA 更安全,也就是说在具有同样安全性的情况下,ECC 的密钥长度比 RSA 短得多(加密解密会更快)。但问题是 ECC 的兼容性会差一些,Android 4.x 以下和 Windows XP 不支持。只要您的设备不是非常老的老古董,建议使用 ECC 证书。

以下只给出 ECC 证书的部分。

证书生成只需在服务器上操作。

#安装 acme.sh

执行以下命令,acme.sh 会安装到 ~/.acme.sh 目录下。

plain
$ curl  https://get.acme.sh | sh

安装成功后执行 source ~/.bashrc 以确保脚本所设置的命令别名生效。

如果安装报错,那么可能是因为系统缺少 acme.sh 所需要的依赖项 (opens new window),acme.sh 的依赖项主要是 socat,我们通过以下命令来安装这些依赖项,然后重新安装一遍 acme.sh:

plain
$ sudo apt-get install openssl cron socat curl

#使用 acme.sh 生成证书

#证书生成

执行以下命令生成证书:

以下的命令会临时监听 80 端口,请确保执行该命令前 80 端口没有使用

plain
$ ~/.acme.sh/acme.sh --issue -d mydomain.me --standalone --keylength ec-256 --force
[Fri Dec 30 08:59:12 HKT 2016] Standalone mode.
[Fri Dec 30 08:59:12 HKT 2016] Single domain='mydomain.me'
[Fri Dec 30 08:59:12 HKT 2016] Getting domain auth token for each domain
[Fri Dec 30 08:59:12 HKT 2016] Getting webroot for domain='mydomain.me'
[Fri Dec 30 08:59:12 HKT 2016] _w='no'
[Fri Dec 30 08:59:12 HKT 2016] Getting new-authz for domain='mydomain.me'
[Fri Dec 30 08:59:14 HKT 2016] The new-authz request is ok.
[Fri Dec 30 08:59:14 HKT 2016] mydomain.me is already verified, skip.
[Fri Dec 30 08:59:14 HKT 2016] mydomain.me is already verified, skip http-01.
[Fri Dec 30 08:59:14 HKT 2016] mydomain.me is already verified, skip http-01.
[Fri Dec 30 08:59:14 HKT 2016] Verify finished, start to sign.
[Fri Dec 30 08:59:16 HKT 2016] Cert success.
-----BEGIN CERTIFICATE-----
MIIEMTCCAxmgAwIBAgISA1+gJF5zwUDjNX/6Xzz5fo3lMA0GCSqGSIb3DQEBCwUA
MEoxCzAJBgNVBAYTAlVTMRYwFAYDVQQKEw1MZXQncyBFbmNyeXB0MSMwIQYDVQQD
ExpMZXQncyBFbmNyeXB0IEF1dGhvcml0eSBYMzAeFw0xNjEyMjkyMzU5MDBaFw0x
NzAzMjkyMzU5MDBaMBcxFTATBgNVBAMTDHdlYWtzYW5kLmNvbTBZMBMGByqGSM49
****************************************************************
4p40tm0aMB837XQ9jeAXvXulhVH/7/wWZ8/vkUUvuHSCYHagENiq/3DYj4a85Iw9
+6u1r7atYHJ2VwqSamiyTGDQuhc5wdXIQxY/YQQqkAmn5tLsTZnnOavc4plANT40
zweiG8vcIvMVnnkM0TSz8G1yzv1nOkruN3ozQkLMu6YS7lk/ENBN7DBtYVSmJeU2
VAXE+zgRaP7JFOqK6DrOwhyE2LSgae83Wq/XgXxjfIo1Zmn2UmlE0sbdNKBasnf9
gPUI45eltrjcv8FCSTOUcT7PWCa3
-----END CERTIFICATE-----
[Fri Dec 30 08:59:16 HKT 2016] Your cert is in  /root/.acme.sh/mydomain.me_ecc/mydomain.me.cer
[Fri Dec 30 08:59:16 HKT 2016] Your cert key is in  /root/.acme.sh/mydomain.me_ecc/mydomain.me.key
[Fri Dec 30 08:59:16 HKT 2016] The intermediate CA cert is in  /root/.acme.sh/mydomain.me_ecc/ca.cer
[Fri Dec 30 08:59:16 HKT 2016] And the full chain certs is there:  /root/.acme.sh/mydomain.me_ecc/fullchain.cer

--keylength 表示密钥长度,后面的值可以是 ec-256ec-3842048307240968192,带有 ec 表示生成的是 ECC 证书,没有则是 RSA 证书。在安全性上 256 位的 ECC 证书等同于 3072 位的 RSA 证书。

#证书更新

由于 Let’s Encrypt 的证书有效期只有 3 个月,因此需要 90 天至少要更新一次证书,acme.sh 脚本会每 60 天自动更新证书。也可以手动更新。

手动更新证书,执行:

plain
$ ~/.acme.sh/acme.sh --renew -d mydomain.com --force --ecc

由于本例中将证书生成到 /etc/v2ray/ 文件夹,更新证书之后还得把新证书生成到 /etc/v2ray。

#安装证书和密钥

将证书和密钥安装到 /etc/v2ray 中:

plain
$ sudo ~/.acme.sh/acme.sh --installcert -d mydomain.me --ecc \
                          --fullchain-file /etc/v2ray/v2ray.crt \
                          --key-file /etc/v2ray/v2ray.key

注意:无论什么情况,密钥(即上面的 v2ray.key)都不能泄漏,如果你不幸泄漏了密钥,可以使用 acme.sh 将原证书吊销,再生成新的证书,吊销方法请自行参考 acme.sh 的手册(opens new window)

#配置 V2Ray

#服务器

json
{
  "inbounds": [
    {
      "port": 443, // 建议使用 443 端口
      "protocol": "vmess",    
      "settings": {
        "clients": [
          {
            "id": "23ad6b10-8d1a-40f7-8ad0-e3e35cd38297",  
            "alterId": 64
          }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "security": "tls", // security 要设置为 tls 才会启用 TLS
        "tlsSettings": {
          "certificates": [
            {
              "certificateFile": "/etc/v2ray/v2ray.crt", // 证书文件
              "keyFile": "/etc/v2ray/v2ray.key" // 密钥文件
            }
          ]
        }
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "settings": {}
    }
  ]
}

#客户端

json
{
  "inbounds": [
    {
      "port": 1080,
      "protocol": "socks",
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      },
      "settings": {
        "auth": "noauth"
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "vmess",
      "settings": {
        "vnext": [
          {
            "address": "mydomain.me", // tls 需要域名,所以这里应该填自己的域名
            "port": 443,
            "users": [
              {
                "id": "23ad6b10-8d1a-40f7-8ad0-e3e35cd38297",
                "alterId": 64
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "tcp",
        "security": "tls" // 客户端的 security 也要设置为 tls
      }
    }
  ]
}

#验证

一般来说,按照以上步骤操作完成,V2Ray 客户端能够正常联网说明 TLS 已经成功启用。但要是有个可靠的方法来验证是否正常开启 TLS 无疑更令人放心。 验证的方法有很多,我仅介绍一种小白化一点的,便是 Qualys SSL Labs’s SSL Server Test (opens new window)

注意:使用 Qualys SSL Labs’s SSL Server Test 要求使用 443 端口,意味着你服务器配置的 inbound.port 应当是 443

打开 Qualys SSL Labs’s SSL Server Test (opens new window),在 Hostname 中输入你的域名,点提交,过一会结果就出来了。 img

img 这是对于你的 TLS/SSL 的一个总体评分,我这里评分为 A,看来还不错。有这样的界面算是成功了。

img 这是关于证书的信息。从图中可以看出,我的这个证书有效期是从 2016 年 12 月 27 号到 2017 年的 3 月 27 号,密钥是 256 位的 ECC,证书签发机构是 Let’s Encrypt,重要的是最后一行,TrustedYes,表明我这个证书可信。

#温馨提醒

V2Ray 的 TLS 不是伪装或混淆,这是完整、真正的 TLS。因此才需要域名和证书。后文提到的 WS(WebSocket) 也不是伪装。

WebSocket

WebSocket 的配置其实很简单,就跟 mKCP 一样把 network 一改就行了。话不多说,直接上配置。

##配置

服务器配置

json
{
  "inbounds": [
    {
      "port": 16823,
      "protocol": "vmess",
      "settings": {
        "clients": [
          {
            "id": "b831381d-6324-4d53-ad4f-8cda48b30811",
            "alterId": 64
          }
        ]
      },
      "streamSettings": {
        "network":"ws"
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "settings": {}
    }
  ]
}

客户端配置

json
{
  "inbounds": [
    {
      "port": 1080,
      "protocol": "socks",
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      },
      "settings": {
        "auth": "noauth"
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "vmess",
      "settings": {
        "vnext": [
          {
            "address": "serveraddr.com",
            "port": 16823,
            "users": [
              {
                "id": "b831381d-6324-4d53-ad4f-8cda48b30811",
                "alterId": 64
              }
            ]
          }
        ]
      },
      "streamSettings":{
        "network":"ws"
      }
    }
  ]
}

WebSocket + TLS + Web

前文分别提到过 TLS 和 WebSocket 的配置方法,而本文搭配 Web 服务并同时实现 TLS 和 WebSocket。关于 Web 的软件本文给出了 Nginx,Caddy 和 Apache 三个例子,三选一即可,也可以选用其它的软件。

很多新手一接触 V2Ray 就想搞 WebSocket + TLS + Web 或 WebSocket + TLS + Web + CDN,我就想问 ssh 和 vim/nano 用利索了没,步子这么大不怕扯到蛋吗?使用 Nginx/Caddy/Apache 是因为 VPS 已经有 Nginx/Caddy/Apache 可以将 V2Ray 稍作隐藏,使用 WebSocket 是因为搭配 Nginx/Caddy/Apache 只能用 WebSocket,使用 TLS 是因为可以流量加密,看起来更像 HTTPS。 也许 WebSocket + TLS + Web 的配置组合相对较好,但不意味着这样的配置适合任何人。因为本节涉及 Nginx/Caddy/Apache,只给出了配置示例而不讲具体使用方法

注意: V2Ray 的 Websocket + TLS 配置组合并不依赖 Nginx/Caddy/Apache,只是能与其搭配使用而已,没有它们也可以正常使用。

配置

服务器配置

这次 TLS 的配置将写入 Nginx/Caddy/Apache 配置中,由这些软件来监听 443 端口(443 比较常用,并非 443 不可),然后将流量转发到 V2Ray 的 WebSocket 所监听的内网端口(本例是 10000),V2Ray 服务器端不需要配置 TLS。

服务器 V2Ray 配置
json
{
  "inbounds": [
    {
      "port": 10000,
      "listen":"127.0.0.1",//只监听 127.0.0.1,避免除本机外的机器探测到开放了 10000 端口
      "protocol": "vmess",
      "settings": {
        "clients": [
          {
            "id": "b831381d-6324-4d53-ad4f-8cda48b30811",
            "alterId": 0
          }
        ]
      },
      "streamSettings": {
        "network": "ws",
        "wsSettings": {
        "path": "/ray"
        }
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "settings": {}
    }
  ]
}
证书配置

Nginx 配置和 Apache 配置中使用的是域名和证书使用 TLS 小节的举例,请替换成自己的。因为 Caddy 会自动申请证书并自动更新,所以使用 Caddy 不用指定证书、密钥。

注意: 如果你有的 VPS 上有架设网页,请使用 webroot 模式生成证书而不是 TLS 小节中提到的 standalone 模式。以下仅就两种模式的些微不同举例,相同部分参照 TLS 小节。本例中使用的是 ECC 证书,若要生成 RSA 证书,删去 --keylength ec-256--ecc 参数即可。详细请参考 acmesh-official/acme.sh (opens new window)

证书生成

plain
$ ~/.acme.sh/acme.sh --issue -d mydomain.me --webroot /path/to/webroot --keylength ec-256

安装证书和密钥

plain
acme.sh --install-cert -d mydomain.com --ecc \
        --key-file       /etc/v2ray/v2ray.key \
        --fullchain-file /etc/v2ray/v2ray.crt \
        --reloadcmd     "service nginx force-reload"
Nginx 配置
plain
server {
  listen 443 ssl;
  listen [::]:443 ssl;
  
  ssl_certificate       /etc/v2ray/v2ray.crt;
  ssl_certificate_key   /etc/v2ray/v2ray.key;
  ssl_session_timeout 1d;
  ssl_session_cache shared:MozSSL:10m;
  ssl_session_tickets off;
  
  ssl_protocols         TLSv1.2 TLSv1.3;
  ssl_ciphers           ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384;
  ssl_prefer_server_ciphers off;
  
  server_name           mydomain.me;
  location /ray { # 与 V2Ray 配置中的 path 保持一致
    if ($http_upgrade != "websocket") { # WebSocket协商失败时返回404
        return 404;
    }
    proxy_redirect off;
    proxy_pass http://127.0.0.1:10000; # 假设WebSocket监听在环回地址的10000端口上
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
    proxy_set_header Host $host;
    # Show real IP in v2ray access.log
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }
}

客户端配置

json
{
  "inbounds": [
    {
      "port": 1080,
      "listen": "127.0.0.1",
      "protocol": "socks",
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      },
      "settings": {
        "auth": "noauth",
        "udp": false
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "vmess",
      "settings": {
        "vnext": [
          {
            "address": "mydomain.me",
            "port": 443,
            "users": [
              {
                "id": "b831381d-6324-4d53-ad4f-8cda48b30811",
                "alterId": 64
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "ws",
        "security": "tls",
        "wsSettings": {
          "path": "/ray"
        }
      }
    }
  ]
}

注意事项

  • V2Ray 自 4.18.1 后支持 TLS1.3,如果开启并强制 TLS1.3 请注意 v2ray 客户端版本.

  • 较低版本的 nginx 的 location 需要写为 /ray/ 才能正常工作

  • 如果在设置完成之后不能成功使用,可能是由于 SElinux 机制(如果你是 CentOS 7 的用户请特别留意 SElinux 这一机制)阻止了 Nginx 转发向内网的数据。如果是这样的话,在 V2Ray 的日志里不会有访问信息,在 Nginx 的日志里会出现大量的 “Permission Denied” 字段,要解决这一问题需要在终端下键入以下命令:

    plain
    setsebool -P httpd_can_network_connect 1
  • 请保持服务器和客户端的 wsSettings 严格一致,对于 V2Ray,/ray/ray/ 是不一样的

  • 较低版本的系统/浏览器可能无法完成握手. 如 Chrome 49/XP SP3, Safari 8/iOS 8.4, Safari 8/OS X 10.10 及更低的版本. 如果你的设备比较旧, 则可以通过在配置中添加较旧的 TLS 协议以完成握手.

其他的话

  1. 开启了 TLS 之后 path 参数是被加密的,GFW 看不到;
  2. 主动探测一个 path 产生 Bad request 不能证明是 V2Ray;
  3. 不安全的因素在于人,自己的问题就不要甩锅,哪怕我把示例中的 path 改成一个 UUID,依然有不少人原封不动地 COPY;
  4. 使用 Header 分流并不比 path 安全, 不要迷信。

WebSocket + BrowserForwarder

通过 WebSocket 的配置,服务端可以用一个真正的 HTTP 服务器接收 V2Ray 数据。而 V2Ray 4.37.0 加入的浏览器转发 (BrowserForwarder) 模块,则可以让真正的网页浏览器发送 V2Ray 数据。

如果说 HTTP 服务器隐藏了 V2Ray 服务器,那么使用浏览器转发,浏览器隐藏了 V2Ray 客户端。示意图如下:

[V2Ray client] --> [Browser] --> {Internet} --> [HTTP server] --> [V2Ray server] --> [github.com]

然而,最 tricky 的地方也正与浏览器有关。由于在日常使用中,浏览器往往是连上代理之后访问网络的。但是这里,浏览器成了 V2Ray 的代理服务器。为了尽可能让读者明白,我们重新绘制上面的图如下:

[Browser A] --> [V2Ray client] --> [Browser B] --> {Internet} --> [HTTP server] --> [V2Ray server] --> [github.com]

Browser A 是面向用户的浏览器, Browser B 则仅仅用于转发 V2Ray 流量。

如果不想用两个浏览器的话,就需要注意配置路由规则,确保浏览器是直连科学服务器的。以下配置假定用户使用同一个浏览器上网以及转发。

配置

在实际应用场景中,浏览器转发模块一般用在客户端。不需要额外对服务器进行额外配置。任何使用 WebSocket 作为传输配置的客户端都可以配置为使用浏览器转发。不限于 WebSocket + TLS + Web 配置方式。

客户端配置

我们在 WebSocket + TLS + Web 客户端配置的基础上进行修改。

json
{
  "inbounds": [
    {
      "port": 1080,
      "protocol": "socks",
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      },
      "settings": {
        "auth": "noauth"
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "vless",
      "settings": {
        "vnext": [
          {
            "address": "yourserver",
            "port": 443,
            "users": [
              {
                "id": "your-id",
                "encryption": "none"
              }
            ]
          }
        ]
      },
      "streamSettings":{
        "network": "ws",
        "wsSettings": {
          "path": "/yourpath",
          "useBrowserForwarding": true
        },
        "security": "tls",
        "tlsSettings": {
            "serverName": "yourserver",
            "allowInsecure": false
        }
      }
    },
    {
      "protocol": "freedom",
      "settings": {},
      "tag": "direct"
    }
  ],
  "routing": {
    "rules": [
      {
        "type": "field",
        "ip": ["127.0.0.1"],
        "outboundTag": "direct"
      },
      {
        "type": "field",
        "domain": ["yourserver"],
        "outboundTag": "direct"
      }
    ]
  },
  "browserForwarder": {
    "listenAddr": "127.0.0.1",
    "listenPort": 8080
  }
}

说明:

  • browserForwarderlistenPort 所指定的端口上(本例是 8080 )监听,稍后用于转发流量的浏览器需要访问此端口;

  • ws 设置中的 "useBrowserForwarding": true 指示此 WebSocket 要由浏览器转发;

  • 设置了直连

    127.0.0.1

    和你的科学服务器的路由规则;

    • 如果不直连 127.0.0.1, V2Ray 无法与本机浏览器进行连接;
    • 如果不直连你的科学服务器, 流量将会一直在 V2Ray 和你的浏览器之间兜圈子,你访问任何需要走 ws 的网站都会 timeout ;
    • 如果你有别的分流手段可以实现上述目的,例如使用 Privoxy ,或者使用另一个不走代理的浏览器作为转发服务器,那么可以不需要此处的分流规则;
  • 也可以使用 vmess 协议取代 vless 协议。

为了使浏览器转发模块工作,还必须下载 (opens new window)与当前 V2Ray 相同版本的 v2ray-extra.zip 。并将 browserforwarder 目录其解压到资源文件路径 (opens new window)中。为了简单起见,直接解压到 v2ray 所在目录,类似下面的列表:

.
├── browserforwarder
│   ├── index.html
│   └── index.js
├── v2ray.exe
└── wv2ray.exe

作为转发服务器的浏览器配置

打开一个现代网络浏览器,例如 Firefox 或是 Chromium ,访问 http://127.0.0.1:8080 ,使 V2Ray 连接上你的浏览器 (好吧这样说有点奇怪,明明是浏览器在连接 V2Ray 客户端) 。如连接成功, V2Ray 日志中应有类似于

2022/02/01 15:07:01 reflective server connected

的输出。

HTTP/2

简单地说 HTTP/2 是 HTTP/1.1 的升级版(目前大多数网页还是 HTTP/1.1),点击这里 (opens new window)可以直观地体会到 HTTP/2 相比于 HTTP/1.1 的提升(不代表 V2Ray 中 HTTP/2 相对于 TCP 的提升就是这样的)。HTTP/2 协议一般简称为 h2。

在 v2ray 中使用 h2,经常被用户们用来跟 websocket 方式做比较。从理论上来说,HTTP/2 在首次连接时,不像 websocket 需完成 upgrade 请求;v2ray 客户端和服务端之间一般直接通信,较少中间层代理。但是,在配合 CDN、Nginx/Caddy/Apache 等服务组件作为前置分流代理的应用场景上,h2 没有 websocket 方式灵活,因为很多代理并不提供 h2 协议的后端支持。实际使用中,websocket 和 h2 的方式,在体验上很可能没有明显区别,用户可自行根据需要选择。

配置

与其它的传输层协议一样在 streamSettings 中配置,不过要注意的是使用 HTTP/2 要开启 TLS。

服务器配置

json
{
  "inbounds": [
    {
      "port": 443,
      "protocol": "vmess",
      "settings": {
        "clients": [
          {
            "id": "b831381d-6324-4d53-ad4f-8cda48b30811",
            "alterId": 64
          }
        ]
      },
      "streamSettings": {
        "network": "h2", // h2 也可写成 http,效果一样
        "httpSettings": { //此项是关于 HTTP/2 的设置
          "path": "/ray"
        },
        "security": "tls", // 配置tls
        "tlsSettings": {
          "certificates": [
            {
              "certificateFile": "/etc/v2ray/v2ray.crt", // 证书文件,详见 tls 小节
              "keyFile": "/etc/v2ray/v2ray.key" // 密钥文件
            }
          ]
        }
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "freedom",
      "settings": {}
    }
  ]
}

客户端配置

json
{
  "inbounds": [
    {
      "port": 1080,
      "listen": "127.0.0.1",
      "protocol": "socks",
      "sniffing": {
        "enabled": true,
        "destOverride": ["http", "tls"]
      },
      "settings": {
        "auth": "noauth",
        "udp": false
      }
    }
  ],
  "outbounds": [
    {
      "protocol": "vmess",
      "settings": {
        "vnext": [
          {
            "address": "mydomain.me",
            "port": 443,
            "users": [
              {
                "id": "b831381d-6324-4d53-ad4f-8cda48b30811",
                "alterId": 64
              }
            ]
          }
        ]
      },
      "streamSettings": {
        "network": "h2",
        "httpSettings": { //此项是关于 HTTP/2 的设置
          "path": "/ray"
        },
        "security": "tls"
      }
    }
  ]
}

HTTP/2+TLS+WEB

V2RayClient --|-> Caddy -> V2RayServer -> Internet

流量经 Caddy 通过本地回环(lo)转发给 V2Ray ,如直接使用浏览器访问 V2Ray Path 将会返回 502 Bad Gateway,直接打开域名或访问其他路径与通过 HTTP/2 访问普通网站一般无二。每一个请求都是真实的 HTTP/2 PUT,正所谓真实是最完美的伪装。

CDN

H2 流量理论上跟 ws 一样可以被 CDN 转发。 但是遗憾的是, Cloudflare 只支持与源服务器进行 HTTP/1.x 通信。 Cloudflare only uses HTTP/1.x between the origin web server and Cloudflare.(opens new window)

缺陷

服务端配置

建议的部署顺序

先配置 Web 服务器,待 Web 服务器正常工作后部署 V2Ray。

本文之示例配置尚未使用 h2c,故 V2Ray 和 Caddy 均需要配置 SSL 证书。

需要自行修改的内容已使用 标注,部署时请注意修改。

TAG说明
服务器的域名
V2Ray 在本地回环(lo)中监听的端口。
VMess 用户的主 ID。必须是一个合法的 UUID 。

以“/”开头的 HTTP 路径,客户端与服务端必须一致。
指向证书/密钥的绝对路径
指向 Web 页面根目录的绝对路径

前期准备

  • 注册域名并正确配置域名解析
  • 准备好 SSL 证书 (如果需要使用 Caddy 管理 SSL 证书,请自行修改 Caddyfile)
  • 完成 V2Ray 和 Web 服务器(例如 Caddy)的安装
  • 准备一些人畜无害的 HTML 页面用来挡刀

Web 服务器

此处需要使用能转发 HTTP/2 流量的 Web 服务器。本文以 Caddy 1 的配置文件 Caddyfile 为例,其他 Web 服务器同理。

WARNING

NGINX 不能向后端转发 HTTP2 流量。1

下面的配置假定你已经拥有现成的 SSL 证书,如果需要使用 Caddy 管理 SSL 证书,请自行修改 tls 部分。

Caddyfile
Caddyfile (Caddy v2)
Caddyfile (Caddy v2 & H2C)

#V2Ray

下列配置不包含 log 部分。

V2Ray config.json
V2Ray Server(H2C) config.json
V2Ray Client(H2C) config.json

基于 Nginx 的简单 TLS 分流

本节提供了基于协议数据的统一的代理分流方案,其使用 Nginx 作为前端对基于 TLS 承载的数据进行分流,简化了现有的 TCP+TLS+Web 方案并同时支持分流到 Trojan 或 V2Ray 的 HTTP2。

#目的

看到有人根据 Trojan 原理基于 V2Ray 做了个类似功能的定制即 TCP + TLS + Web,就是在 TLS 层上传输 VMess 或者其他比如 http Web 流量。 本人好奇,遂群里有如下互动:

Q: TCP + TLS + Web 为啥需要 Web 前需要 HAProxy 啊,nginx 也有这种功能啊 非要前面 HAProxy,后面再弄个 nginx/httpd。 对于个人使用没必要吧。 当然你搭建商业的除外

A: 不要再问这种问题了,你觉得可以就自己搭,搭成了可以写给教程 pr

Q: nginx 的 stream 块不行吗

A: 不要再问这种问题了,你觉得可以就自己搭,搭成了可以写给教程 pr

所以目的很简单,就是去掉那个 HAProxy,只用 Nginx 来分流,这样更适用于个人 vps 的搭建。 另外的一个目的,也作为 Trojan 的前端,即 Nginx 也可以分流到 Trojan 后端。

#可行性分析

所有的 TLS 处理在 Nginx 截止,后端代理只进行明文协议的解析。有些域名既可以指向网站,也可以放到我们的代理客户端的 SNI 中处理,故本篇不考虑 SNI 的分流。

#基于 TLS 握手后的数据包分流

由于原理是根据 TLS 握手后的数据进行分流,对 Nginx 采用 stream 的配置方式,具体如下:

  • 如果是 HTTP/1.1,直接路由到某个正常的网站,这要求我们没有把代理协议承载在 HTTP/1.1 上;
  • 如果是 HTTP/2,就路由到 HTTP/2 代理后端比如 V2Ray 或 Trojan。尽管 Trojan-Go 支持 HTTP2,但鉴于 Trojan-GFW 项目组对引入 HTTP/2 的反对,所以我们的实现也不考虑 Trojan-Go 的 HTTP/2 实现。这就要求 V2Ray 支持对其他来源的 HTTP/2 网站请求的返回即自动回落,但目前尚没有支持。
  • 如果是纯 VMess 协议数据即常说的 TCP+TLS+VMess+Web,直接转发到 V2Ray,这同样需要 V2Ray 在解析失败的情况下返回一个普通网页以实现伪装,算是另一种自动回落机制。目前 V2Ray 没有相关实现,但有个开源实现 (opens new window)可供参考或使用。
  • 如果是纯 Trojan 协议数据,直接转发到 Trojan 后端,这里 Trojan 实现了协议自动回落。

#与 Trojan 的比较

#主动探测

对于 Trojan,所有没有提供正确数据结果或密码的请求都会被转至一个预设的出口,包括普通 HTTPS 流量或重放、伪造的流量;但是目前 V2Ray 没有这样的功能,即上面所提的 协议自动回落 功能,不过社区在做了。

#被动探测

Trojan 对抗被动探测方法和本篇所描述的方案基本一致,即所有代理流量表现形如 HTTPS 或 WebSocket。本方案中所有 TLS 的功能都被放到了 Nginx 上,其他功能还在后端 Trojan 或 V2Ray 上。

#具体实现

WARNING

本节提供的安装示例基于 Debian,请酌情做必要的修改。

#安装 OpenResty (Nginx 分支)

我们采用 luajit 来实现,OpenResty 安装参考详见官方指导 (opens new window)

bash
sudo systemctl disable nginx
sudo systemctl stop nginx
sudo apt-get -y install --no-install-recommends wget gnupg ca-certificates
wget -O - https://openresty.org/package/pubkey.gpg | sudo apt-key add -

# add this to /etc/apt/sources.list
codename=`grep -Po 'VERSION="[0-9]+ \(\K[^)]+' /etc/os-release`

echo "deb http://openresty.org/package/debian $codename openresty" \
    | sudo tee /etc/apt/sources.list.d/openresty.list

# end
sudo apt-get update
sudo apt-get -y install openresty

#Nginx 配置

plain
{
worker_processes  auto;
error_log  logs/error.log  info;
events {
    worker_connections  1024;
    use epoll;
    multi_accept on;
}
stream {
    resolver 127.0.0.1;
    lua_add_variable $VMess;

    server {
        listen  443 ssl reuseport backlog=4096;
        listen [::]:443 ssl reuseport;

        ssl_certificate_key   /privatekey.pem;
        ssl_certificate       /fullchain.pem;


        ssl_session_timeout 1d;
        ssl_session_cache shared:SSL:20m;
        ssl_protocols TLSv1.1 TLSv1 TLSv1.2;
        ssl_ciphers ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv3:+EXP;
        ssl_prefer_server_ciphers on;

        # 16k
        proxy_buffer_size 256k;
        # 16k
        # preread_buffer_size 4k;
        preread_buffer_size 58;

        preread_by_lua_block {
            local sock, err = ngx.req.socket()
            if sock then
               -- ngx.say("got the request socket")
            else
                ngx.say("failed to get the request socket: ", err)
            end

            local data, err = sock:peek(16)
            local datal, err = sock:peek(58)
            if string.match(data, "HTTP/2.0") then
                -- maybe faked http2 to detect us ,so need parse the body to 协议自动回落 to normal url
                -- or by VMess
                -- maybe we use Trojan-go http2,but now giveup

        -- for V2Ray's tcp +TLS +h2c
                ngx.var.VMess = "10008"
            elseif string.match(data, "HTTP") then
            -- for normal http req
                ngx.var.VMess = "8080"
            elseif string.byte(datal:sub(57), 1, 2) == 13 then
            -- for Trojan
                ngx.var.VMess = "453"
            else
            -- for V2Ray's tcp+TLS +web
                ngx.var.VMess = "10007"
            end
        }
         proxy_pass 127.0.0.1:$VMess;
  }# server block
}

#创建 Nginx 目录 ‘a’

修改 a 中 conf/nginx.conf 为如上配置,这样可以不污染 /usr/local/openresty/nginx 下的内容。

bash
rsync -av /usr/local/openresty/nginx/[conf,html,logs] a
cd a
sudo openresty -p .
sudo openresty -p . -s reload

#V2Ray 配置

可以参考 HTTP2+TLS+Web (opens new window)TCP+TLS+Web (opens new window)的服务器配置。 对于 HTTP2+TLS+Web,注意这里 HTTP2 的 TLS 在 Nginx 实现,即 h2c 配置。本篇的改动是采用了 Nginx 作为前端。

对于 TCP+TLS+Web,不同的是去掉 HAProxy,并且基于协议内容分流而不是 SNI。相对 TCP+TLS 分流器 (opens new window),本方案更简洁。

#Trojan-Go

关闭了 TLS 处理的 Trojan-Go 版本目前位于 dev 分支 (opens new window),需要自行编译相应架构的客户端或者服务器端。

Trojan-Go 服务端配置如下:

json
{
    "run_type": "server",
    "local_addr": "127.0.0.1",
    "local_port": 453,
    "remote_addr": "127.0.0.1",
    "remote_port": 80,
    "password": [
        "xxxx"
    ],
    "ssl": {
        "serve_plain_text": true
    }
}

客户端使用正常的 Trojan 配置即可。

#性能测试

#vmessping 延迟测试

TCP+TLS+Web
--- VMess ping statistics ---
10 requests made, 10 success, total time 26.515340739s
rtt min/avg/max = 993/1748/2544 ms

TLS+WebSocket+CDN
--- VMess ping statistics ---
10 requests made, 10 success, total time 30.647379082s
rtt min/avg/max = 1215/2161/7160 ms

TLS+h2c
--- VMess ping statistics ---
10 requests made, 10 success, total time 20.977310488s
rtt min/avg/max = 616/1194/3263 ms

可见延迟表现最佳的是 TLS + h2c。 按理说 TCP+TLS (即 TCP+TLS+Web,没有 HTTP 开销) 应该比 TLS+h2c 更好,这里为什么性能还弱呢?

不推荐的配置

也许有一部分朋友发现了,高级篇的内容关于传输层的,各种配置的组合,可以搭配出非常多的配置。但是,有一些组合是我认为不值得或者是冗余的(仅代表个人意见),以下给出。

  • TLS + KCP

这是相当一部分人喜欢的组合。选用 KCP 的原因是为了在某些恶劣的网络环境下拥有比较好的上网体验。而使用 TLS 的原因大约有两种考虑:一是认为 TLS 拥有与 HTTPS 一样的特征不容易被墙;二是觉得 TLS 具有更好的加密效果不容易被墙。对于第一点,尽管 HTTPS 是基于 TLS,但并不等同与 TLS,因此 TLS 与 HTTPS 的特征一样的说法是错误的;对于第二点,使用更强的加密算法而被墙的几率更小这个观点并未得到论证。然而这并不是我不推荐的理由,真正的原因的是不使用 TLS 并没什么坏处,额外使用 TLS 也没有足够的好处。

  • TLS + HTTP 伪装

我并没有测试过这个组合,不清楚最外层是 TLS 还是 HTTP 伪装。无论哪一种,处于内层的配置将会失去其意义。

  • 单纯使用 WebSocket

理论上,单纯使用 WebSocket 会比 TCP 性能差一些,所以如果不是搭配 CDN、nginx 或者在 PaaS 上使用,那还是使用 TCP 吧。

评论

评论加载中……