UP | HOME

TLS

Table of Contents

从 v1.19 起引入了 TLS,TLS 中文译名是传输层安全

基础

注册域名

如果已经注册有域名了可以跳过

TLS 需要一个域名,域名有免费的和有付费的,如果不舍得为一个域名每年花点钱,用个免费域名也可以,但总体来说付费的会优于免费的

为了方便,在本文中忽略如何注册购买域名了

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

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

生成证书

TLS 是证书认证机制,所以使用 TLS 需要证书,证书也有免费付费的

这里使用免费证书,证书认证机构为 Let's Encrypt 

证书的生成有许多方法,这里使用的是比较简单的方法:使用 acme.sh 脚本生成

本部分说明部分内容参考于acme.sh README 

证书有两种,一种是 ECC 证书(内置公钥是 ECDSA 公钥),一种是 RSA 证书(内置 RSA 公钥)

简单来说,同等长度 ECC 比 RSA 更安全,也就是说在具有同样安全性的情况下,ECC 的密钥长度比 RSA 短得多(加密解密会更快)

但问题是 ECC 的兼容性会差一些,Android 4.x 以下和 Windows XP 不支持。只要设备不是非常老的老古董,建议使用 ECC 证书

acme

安装

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

$ curl  https://get.acme.sh | sh
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
100   671  100   671    0     0    680      0 --:--:-- --:--:-- --:--:--   679
% Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
Dload  Upload   Total   Spent    Left  Speed
100  112k  100  112k    0     0   690k      0 --:--:-- --:--:-- --:--:--  693k
[Fri 30 Dec 01:03:32 GMT 2016] Installing from online archive.
[Fri 30 Dec 01:03:32 GMT 2016] Downloading https://github.com/Neilpang/acme.sh/archive/master.tar.gz
[Fri 30 Dec 01:03:33 GMT 2016] Extracting master.tar.gz
[Fri 30 Dec 01:03:33 GMT 2016] Installing to /home/user/.acme.sh
[Fri 30 Dec 01:03:33 GMT 2016] Installed to /home/user/.acme.sh/acme.sh
[Fri 30 Dec 01:03:33 GMT 2016] Installing alias to '/home/user/.profile'
[Fri 30 Dec 01:03:33 GMT 2016] OK, Close and reopen your terminal to start using acme.sh
[Fri 30 Dec 01:03:33 GMT 2016] Installing cron job
no crontab for user
no crontab for user
[Fri 30 Dec 01:03:33 GMT 2016] Good, bash is found, so change the shebang to use bash as preferred.
[Fri 30 Dec 01:03:33 GMT 2016] OK
[Fri 30 Dec 01:03:33 GMT 2016] Install success!
安装成功后执行 source ~/.bashrc 以确保脚本所设置的命令别名生效

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

$ sudo apt-get install openssl cron socat curl

生成证书

执行以下命令生成证书:

$ ~/.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-256 、ec-384、2048、3072、4096、8192

带有 ec 表示生成的是 ECC 证书,没有则是 RSA 证书

在安全性上 256 位的 ECC 证书等同于 3072 位的 RSA 证书

更新证书

由于 Let's Encrypt 的证书有效期只有 3 个月,因此需要 90 天至少要更新一次证书

acme.sh 脚本会每 60 天自动更新证书。也可以手动更新:

$ ~/.acme.sh/acme.sh --renew -d mydomain.com --force --ecc
由于本例中将证书生成到 /etc/v2ray/ 文件夹,更新证书之后还得把新证书生成到 /etc/v2ray

安装证书和密钥

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

$ 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 的手册

配置

服务器

{
    "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": {}
        }
    ]
}

客户端

{
    "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。因此才需要域名和证书

后文提到的 WS(WebSocket) 也不是伪装

TCP + TLS + Web

新手建议使用 TLS 分流器 

背景

目前 Vmess + WebSocket + TLS (以下简称 wss)方式,因其特征如同 HTTPS 流量

可以隐藏 V2Ray 路径,主动侦测会得到正常 HTTP 网站响应,具有良好的伪装能力,目前被广泛用于反审查

但是如此强大的伪装能力,需要付出严重的性能代价:TLS 1.3 握手需要消耗 1-rtt,WS 握手也需要消耗 1-rtt,增大了握手延迟

V2Ray 增加了 mux 以减少握手的发生,然而实际使用中 mux 体验并不好,很多用户选择关闭

最近兴起了一个新的反审查工具 Trojan,这个工具将一个类似 Socks 的协议直接通过 TLS 传输,并将认证失败的流量交由 Web 服务器处理。降低 WS 延迟的同时,提供与 wss 方式一样的伪装能力

但是该工具较为年轻,没有路由功能,各平台图形化客户端也不完善

因此,这里尝试用 V2Ray 实现类似功能,即 Vmess + TCP + TLS 并网站伪装,省下 WS 的握手延迟

原理

HaProxy 监听 443 端口,处理 TLS 之后,将 HTTP 流量交由 Web 服务器处理,非 HTTP 流量交由 V2Ray 按 Vmess 处理

实现

本次方案使用 HaProxy,Nginx(Web 服务器的使用不是本教程的重点),V2Ray

  1. 安装 HaProxy

    apt install haproxy
    
    为了较好的支持 TLS1.3,HaProxy 版本应大于 1.8.15,OpenSSl 版本应大于 1.1.1
    
    如果使用的发行版仓库自带的版本较低,需要自行编译安装
    
  2. 安装 Web 服务器,Nginx

    apt install nginx
    
  3. 安装 V2Ray,可以使用官方脚本官方脚本
  4. 修改 V2Ray 配置文件,以 Vmess + TCP 方式 监听 40001 端口

    {
        "inbounds": [
            {
                "protocol": "vmess",
                "listen": "127.0.0.1",
                "port": 40001,
                "settings": {
                    "clients": [
                        {
                            "id": "f2435e5c-9ad9-4367-836a-8341117d0a5f"
                        }
                    ]
                },
                "streamSettings": {
                    "network": "tcp"
                }
            }
        ],
        "outbounds": [
            {
                "protocol": "freedom"
            }
        ]
    }
    
  5. 修改 Web 服务器配置文件,部署 HTTP 服务于 8080 端口:

    # Nginx 在 http{} 里面添加
    
    server {
        listen 8080;
        server_name example.com;
        root /var/www/html;
    }
    
    /var/www/html 是静态网站目录
    
    实际服务请根据需要部署,也可以用 httpd 之类的替代
    
    似乎很多 Trojan 教程直接监听 80 端口,其实很多 HTTPS 网站 80 端口通常是重定向到 HTTPS
    
  6. 修改 HaProxy 配置文件:

    global
    log /dev/log local0
    log /dev/log local1 notice
    chroot /var/lib/haproxy
    stats socket /run/haproxy/admin.sock mode 660 level admin expose-fd listeners
    stats timeout 30s
    user haproxy
    group haproxy
    daemon
    ca-base /etc/ssl/certs
    crt-base /etc/ssl/private
    
    # 仅使用支持 FS 和 AEAD 的加密套件
    ssl-default-bind-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-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
    # 禁用 TLS 1.2 之前的 TLS
    ssl-default-bind-options no-sslv3 no-tlsv10 no-tlsv11
    
    tune.ssl.default-dh-param 2048
    
    defaults
    log global
    # 我们需要使用 tcp 模式
    mode tcp
    option dontlognull
    timeout connect 5s
    # 空闲连接等待时间,这里使用与 V2Ray 默认 connIdle 一致的 300s
    timeout client  300s
    timeout server  300s
    
    frontend tls-in
    # 监听 443 tls,tfo 根据自身情况决定是否开启,证书放置于 /etc/ssl/private/example.com.pem
    bind *:443 tfo ssl crt /etc/ssl/private/example.com.pem
    tcp-request inspect-delay 5s
    tcp-request content accept if HTTP
    # 将 HTTP 流量发给 web 后端
    use_backend web if HTTP
    # 将其他流量发给 vmess 后端
    default_backend vmess
    
    backend web
    server server1 127.0.0.1:8080
    
    backend vmess
    server server1 127.0.0.1:40001
    
    与 Nginx 不同,HaProxy 的证书和密钥放于同一个文件
    
    可以使用命令 cat example.com.crt example.com.key > example.com.pem 合成证书
    
  7. 重启服务

    systemctl restart haproxy
    systemctl restart nginx
    systemctl restart v2ray
    
  8. 客户端连接 example.com:443 vmess tls 即可

    {
        "inbounds": [
            {
                "port": 1080,
                "listen": "127.0.0.1",
                "protocol": "socks"
            }
        ],
        "outbounds": [
            {
                "protocol": "vmess",
                "settings": {
                    "vnext": [
                        {
                            "address": "example.com",
                            "port": 443,
                            "users": [
                                {
                                    "id": "f2435e5c-9ad9-4367-836a-8341117d0a5f",
                                    "security": "none"
                                }
                            ]
                        }
                    ]
                },
                "streamSettings": {
                    "network": "tcp",
                    "security": "tls"
                }
            }
        ]
    }
    

效果

tQyKPD45fmAFl9x.jpg

测试工具为 vmessping ,可见 Vmess + TCP + TLS(左)延迟低于 Vmess + WSS(右)

讨论

  • HaProxy,V2Ray,Nginx 都是支持 Domain Socket 的,流量较大或数据包较多时使用 ds 可以提高性能

    这里不做展开,可以参考这篇文章 https://gist.github.com/liberal-boy/b2d5597285b4202b6d607faaa1078d27
    
  • 可以使用这个工具 开启 OCSP Stapling 减少客户端验证证书的时间
  • 该方法的隐蔽性是否比 wss 低?
    • 中间人看来,该方法在建立 TLS 连接后,比 wss 少一次握手,即 TLS 建立后直接发送请求并获得响应,该行为是符合正常的 HTTPS 请求的
    • 主动探测时:
      • 如 TLS 建立后发送 HTTP 请求,则被发给 Web 服务器按正常 HTTP 请求处理
      • 如发送非 HTTP 请求,会被发给 V2Ray 处理,如 Vmess 认证失败,连接将被关闭,向 HTTPS 服务器发送非 HTTPS 请求,连接被关闭是正常的行为。

TCP + TLS 分流器

这是 TCP + TLS + Web 的简易实现

不需要处理 HaProxy 和 OpenSSL 的版本问题,也不需要自己申请证书,也不需要额外安装 Web 服务器

实现

  1. 安装 V2Ray,可以使用官方脚本官方脚本
  2. 安装 TLS 分流器 ,见 安装说明
  3. 修改 TLS 分流器配置文件,位于 /etc/tls-shunt-proxy/config.yaml

    listen: 0.0.0.0:443
    vhosts:
      # 将 example.com 改为你的域名
      - name: example.com
        tlsoffloading: true
        managedcert: true
        alpn: h2,http/1.1
        # 如果不需要兼容 tls12, 可改为 tls13
        protocols: tls12,tls13
        http:
          handler: fileServer
          # /var/www/html 是静态网站目录
          args: /var/www/html
          default:
            handler: proxyPass
            args: 127.0.0.1:40001
    
  4. 修改服务器 V2Ray 配置文件 /etc/v2ray/config.json ,同 TCP + TLS + Web 方式

    {
        "inbounds": [
            {
                "protocol": "vmess",
                "listen": "127.0.0.1",
                "port": 40001,
                "settings": {
                    "clients": [
                        {
                            "id": "f2435e5c-9ad9-4367-836a-8341117d0a5f"
                        }
                    ]
                },
                "streamSettings": {
                    "network": "tcp"
                }
            }
        ],
        "outbounds": [
            {
                "protocol": "freedom"
            }
        ]
    }
    
  5. 重启服务

    systemctl restart tls-shunt-proxy
    systemctl restart v2ray
    
  6. 客户端连接 example.com:443 vmess tls 即可

    {
        "inbounds": [
            {
                "port": 1080,
                "listen": "127.0.0.1",
                "protocol": "socks"
            }
        ],
        "outbounds": [
            {
                "protocol": "vmess",
                "settings": {
                    "vnext": [
                        {
                            "address": "example.com",
                            "port": 443,
                            "users": [
                                {
                                    "id": "f2435e5c-9ad9-4367-836a-8341117d0a5f",
                                    "security": "none"
                                }
                            ]
                        }
                    ]
                },
                "streamSettings": {
                    "network": "tcp",
                    "security": "tls"
                }
            }
        ]
    }
    

Domain Socket

相比 TCP,Domain Socket (以下简称 DS) 更为高效

根据测试反馈,速度超过 50Mbps 时,通常会有较明显的性能差距

DS 仅限分流器与服务端 V2Ray 连接,客户端连接服务器仍然使用 TCP, 即:

	      TLS over TCP                DS
客户端 V2Ray --------------- TLS 分流器 -------- 服务端 V2Ray
  1. 修改分流器配置文件 /etc/tls-shunt-proxy/config.yaml

    listen: 0.0.0.0:443
    vhosts:
      # 将 example.com 改为你的域名
      - name: example.com
        tlsoffloading: true
        managedcert: true
        alpn: h2,http/1.1
        # 如果不需要兼容 tls12, 可改为 tls13
        protocols: tls12,tls13
        http:
          handler: fileServer
          # /var/www/html 是静态网站目录
          args: /var/www/html
          default:
            handler: proxyPass
            args: unix:@v2ray.sock
    
  2. 修改服务器 V2Ray 配置文件 /etc/v2ray/config.json

    {
        "inbounds": [
            {
                "protocol": "vmess",
                "listen": "127.0.0.1",
                "port": 40001,
                "settings": {
                    "clients": [
                        {
                            "id": "f2435e5c-9ad9-4367-836a-8341117d0a5f"
                        }
                    ]
                },
                "streamSettings": {
                    "network": "ds",
                    "dsSettings": {
                        "path": "@v2ray.sock",
                        "abstract": true
                    }
    
                }
            }
        ],
        "outbounds": [
            {
                "protocol": "freedom"
            }
        ]
    }
    
  3. 重启服务

    systemctl daemon-reload
    systemctl restart v2ray
    systemctl restart tls-shunt-proxy
    

其他

TLS 分流器还可以实现 vmess + TLS + Web 和 trojan 共享端口

具体配置参数请参阅项目 https://github.com/liberal-boy/tls-shunt-proxy/blob/master/README.md
Next: Websocket Previous:代理转发 Home:传输