Total Pageviews

Thursday, 14 May 2020

利用websocks翻墙

在linux vps上。首先安装python3.8.0环境,然后,
pip3 install -U websocks
(会生成可执行文件websocks)
websocks server --userpass myusername:mypassword 0.0.0.0 2146
不过此命令是运行在前台的,我们可以利用systemd把它运行为service:
nano /etc/systemd/system/websocks.service
cat /etc/systemd/system/websocks.service
[Unit]
After=network.target

[Service]
ExecStart=/usr/local/python-3.8.0/bin/websocks server --userpass myusername:mypassword 0.0.0.0 2146
Restart=always

[Install]
WantedBy=multi-user.target

然后运行:
systemctl start websocks
systemctl enable websocks

服务器端搭建完成。

在本地机器Mac上。首先安装python3.8环境:brew install @python3.8
然后,pip3 install -U websocks
(会生成可执行文件websocks)
websocks client --policy PROXY --server-url ws://myusername:mypassword@vps-public-ip:2146 127.0.0.1 3278
不要关闭此terminal.

然后设置浏览器的socks5代理服务器地址为127.0.0.1 ,端口为3278,浏览器即可翻墙。

因为ws不是加密连接,所以运行上面的命令:
websocks client --policy PROXY --server-url ws://myusername:mypassword@vps-public-ip:2146 127.0.0.1 3278有可能翻墙失败。解决办法:使用nginx设置一个域名反向代理http://127.0.0.1:2146,并且替该域名申请和加上证书。具体就是在nginx的配置文件中加上一个server段如下:
server {
listen 3150 ssl;
server_name mydomain.com;
location / {
proxy_pass http://127.0.0.1:2146;
        proxy_set_header Host   $host;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

ssl_certificate /root/.acme.sh/mydomain.com/fullchain.cer;
ssl_certificate_key /root/.acme.sh/mydomain.com/mydomain.com.key;
}

然后重启nginx.
然后在本地机器上,运行:
websocks client --policy PROXY --server-url wss://myusername:mypassword@mydomain.com:3150 127.0.0.1 3278
不要关闭此terminal.
然后设置浏览器的socks5代理服务器地址为127.0.0.1 ,端口为3278,浏览器即可翻墙。

项目地址:https://github.com/abersheeran/websocks
https://github.com/abersheeran/websocks/issues/6
https://github.com/abersheeran/websocks/wiki

类似项目:https://briteming.blogspot.com/2018/04/haskellwstunnel-by-erebe.html
-----------------------------------------------------

使用WebSocket进行网络穿透

网络穿透的本质就是代理而想要稳定的翻墙必须要把代理伪装成一个正常的网络请求而这一点上在拜读了Shadowsocks的源码之后我觉得它还不够因为Shadowsocks的连接只能让GFW知道这个是个未知的协议虽然SSR的混淆做的比较好然而Breakwa11都删库了他那个神仙代码我实在是没法维护还是自己写吧
在之前的一篇绕开校园网计费里我写了一个Sock5代理但单纯的Socks5代理是无法翻墙的因为GFW能轻易的分析出你是一个代理从而封掉你的海外IP
在研究完了Shadowsocks的混淆代码之后我把目光盯上了WebSocket

WebSocket

WebSocket是一种基于TCP的长连接它会使用一次HTTP的报文握手在那之后便是WebSocket自身规定的方式进行通讯了它是个在各种网站都可能被用到的协议既然它常见那么就安全

为什么要使用WebSocket

使用WebSocket的原因除了它常见以外还有就是可以利用各种CDN服务将你的WebSocket进行代理而让真实的服务器IP隐藏在重重CDN之后哪怕真实的服务器IP被封锁了你的代理也不会失效
并且标准Websocket的协议设计使得客户端发送到服务端的信息都有一次性的掩码进行混淆使得我们不需要做二次处理只需要稍微处理一下服务器发送到客户端的数据即可

WebSocket 浅析

在使用 WebSocket 之前先了解一下 WebSocket

HTTP 握手

客户端发送到服务端的 HTTP 请求中请求头必须包含如下四个字段Connection: Upgrade 指定此次请求需要从 HTTP 协议升级Upgrade 指定此次请求升级到 websocket Sec-WebSocket-Version 指定 WebSocket 的版本(一般都是 13)Sec-WebSocket-Key 是一个 16 byte 长度的随机字符串经过 base64 处理后的结果
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
Sec-WebSocket-Version: 13
服务端视情况决定是否允许升级到 websocket如果不同意则返回一个常规 HTTP 响应即可如果同意则返回状态码为 101 的 HTTP 响应
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: HSmrc0sMlYUkAGmm5OPpG2HaGWk=
Sec-WebSocket-Accept 的值为客户端发送的 Sec-WebSocket-Key 拼接 "258EAFA5-E914-47DA-95CA-C5AB0DC85B11" 的字符串的 sha-1 值的 base64 编码结果——用伪代码编写应该是 base64encode(sha1(SecWebSocketKey + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"))
握手阶段完成后后续所有的数据传输都与 HTTP 协议无关了使用纯粹的 WebSocket 报文进行交互

WebSocket 报文结构

  • Fin1bit用于标记当前数据帧是不是最后一个数据帧单条消息可能会分成多个数据帧来传递如果只需要一个数据帧第一个数据帧也就是最后一个
  • RSV1RSV2RSV3各自1bit用于扩展用途一般全部置0即可
  • Opcode4bit操作码用于描述此数据帧的类型
    • 0x0标示当前数据帧为分片的数据帧也就是当一个消息需要分成多个数据帧来传送的时候需要将opcode设置位0x0
    • 0x1标示当前数据帧传递的内容是文本编码为UTF-8
    • 0x2标示当前数据帧传递的内容是二进制数据
    • 0x8标示请求关闭连接
    • 0x9标示此数据帧为ping
    • 0xA标示此数据帧为pong仅在接收ping帧后发送pong
    • 0x3~0x7以及0xB~0xF留作其他用途
  • MASK1bit标示数据有没有使用掩码RFC中规定服务端发送给客户端的数据帧不能使用掩码客户端发送给服务端的数据帧必须使用掩码
  • Payload length7bit的无符号整数用于标识Payload data的长度
    值在0-125之间时这就是Payload data的长度
    值为126使用后续2bytes也就是16 bit的无符号整数来标识Payload data的长度
    值为127使用后续8bytes也就是64 bit的无符号整数用于标识Payload data的长度
  • Masking-key数据掩码如果Mask位为0则该部分可以省略如果Mask位为1则为32 bit (4 byte)的掩码

自定义协议

当一个WebSocket连接建立起来之后我们需要自定义协议来让服务端知道需要干什么

身份验证

当客户端与服务端建立连接时, 需将 Basic 格式的身份认证信息通过 Authorization 头发送到服务器
服务端响应应按照HTTP标准如身份验证失败则返回401身份验证成功但被限制访问则返回403成功则返回101

请求代理

当有数据流需要走代理时应做如下的请求

客户端请求

客户端发送一个如下的数据包给服务端用以约定此连接是代理TCP还是UDP当约定为UDP代理时DST.ADDRDST.PORT为任意值均可服务端无需解析此部分
{
    "VERSION": 1,
    "CMD": "TCP" | "UDP",
    "ADDR": "目标的地址",
    "PORT": "目标的端口"
}
  • VERSION: 协议版本号此处为'01'
  • CMD: 指定代理方式
    1. "TCP"
    2. "UDP"
  • ADDR: 目标的地址
  • PORT: 目标的端口(number类型)

服务端响应

服务端根据客户端请求进行响应——如果是TCP方法则尝试连接客户端指定的地址和端口如果是UDP方法则视自身情况而定是否同意UDP转发
当服务端对此次请求处理完毕后视情况而定回复一个数据包
{
    "VERSION": 1,
    "STATUS": "SUCCESS"
}
  • STATUS: 为SUCCESS客户端可进行下一步否则客户端认为服务器无法做到此次请求的要求立刻关闭连接

数据传输

当进行完以上两步后可进行正式的数据传输数据传输阶段使用二进制帧

TCP

对于TCP方法客户端直接将数据发送到此WebSocket连接即可而服务端也只需要单纯的转发

UDP

对于UDP方法客户端需要将UDP数据进行封装
+------+----------+----------+----------+
| ATYP | DST.ADDR | DST.PORT |   DATA   |
+------+----------+----------+----------+
|  1   | Variable |    2     | Variable |
+------+----------+----------+----------+
  • ATYP: 指定DST.ADDR的类型
    • IPV4: X'01'
    • 域名: X'03'
    • IPV6: X'04'
  • DST.ADDR: 该数据包渴望到达的目标地址
  • DST.PORT: 该数据包渴望到达的目标端口
  • DATA: 实际要传输的数据
而服务端进行解包转发后所接收到的UDP包也解析为同样格式再发给客户端

题外话

本协议实现的开始只是我想突破学校的网络封锁后来想着用了别人的软件那么久也该自己写一个以防不测了于是就有了此文
代码实现在websocks
但现在已经不住学校寝室了所以网络环境不再受限且 UDP 跑在 WebSocket 上的确延迟很高于是我又编写了使用WebSocket进行网络穿透(续)仅实现 TCP 的转发至于 UDP 协议正在使用 UDP 协议重构中
------

使用WebSocket进行网络穿透(续)

实际使用中UDP转发并不常用并且使用WebSocket转发UDP包也会导致较高的延迟我当时之所以有这个需求是因为学校网络限制
所以为了更简单的实现更短的延迟在此重新设计一种新的基于WebSocketTCP转发协议
代码实现在websocks

协商

当客户端与服务端建立连接时, 需将 Basic 格式的身份认证信息通过 Authorization 头发送到服务器
服务端响应应按照HTTP标准如身份验证失败则返回401身份验证成功但被限制访问则返回403成功则返回101

转发

为了复用单条 WebSocket 连接, 约定如下三种状态, 组成一次完整的请求周期如下

请求连接

websocket使用文本帧传递JSON格式的字符串请求连接的服务器HOSTPORT
{
    "HOST": "example.com",
    "PORT": 443
}
服务器成功或失败之后返回一个状态
{
    "ALLOW": true/false
}
若成功, 则转至转发状态.
若失败, 则直接转至结束连接状态.

转发

在此过程中, 仅使用二进制帧, 进行转发数据

结束连接

客户端或服务端主动断开连接双方均需使用文本帧发送或者响应一次
{
    "STATUS": "CLOSED"
}
当结束连接之后客户端可重新使用此条WebSocket连接进行如上周期
------------------------

另一个项目:websocks-by-lzjluzijie

优点

  • 使用WS+TLS,十分安全且不易被检测,和普通HTTPS网站一样.
  • 可以搭配使用cloudflare这类cdn,完全不怕被墙!

服务端命令:

./websocks server -l 0.0.0.0:2333 -p /xyz
服务端命令是运行在前台的,我们可以利用systemd来把该命令运行为service:
nano /etc/systemd/system/websocks-by-lzjluzijie.service
cat /etc/systemd/system/websocks-by-lzjluzijie.service
[Unit]
After=network.target

[Service]
ExecStart=/root/websocks_Linux_x86_64/websocks server -l 0.0.0.0:2333 -p /xyz
Restart=always

[Install]
WantedBy=multi-user.target
然后,运行:
systemctl start websocks-by-lzjluzijie
systemctl enable websocks-by-lzjluzijie 

caddy webserver的Caddyfile:

https://mydomain.com:1443 {
  proxy /xyz localhost:2333 {
    websocket
  }
}
橘红色文字的意思是用caddy webserver做反向代理,代理websocks的服务器端地址localhost:2333
当然,也可用nginx做反向代理,代理websocks的服务器端地址localhost:2333 ,具体的server
段内容如下:
server {
listen 1443 ssl;
server_name mydomain.com;
location /xyz {
proxy_pass http://127.0.0.1:2333;
        proxy_set_header Host   $host;
        proxy_http_version 1.1;
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "upgrade";
    }

ssl_certificate /root/.acme.sh/mydomain.com/fullchain.cer;
ssl_certificate_key /root/.acme.sh/mydomain.com/mydomain.com.key;
}
 
然后重启nginx.

服务端搭建完成。

from https://github.com/lzjluzijie/websocks 
https://github.com/lzjluzijie/websocks/releases (编译失败,于是下载作者提供的
可执行文件来使用。) 
服务器端程序 https://github.com/lzjluzijie/websocks/releases/download/v0.15.1/websocks_Linux_x86_64.tar.gz 
客户端程序 https://github.com/lzjluzijie/websocks/releases/download/v0.15.1/websocks_Darwin_x86_64.tar.gz

客户端命令:

./websocks client -l :2081 -s wss://mydomain.com:1443/xyz
 
不要关闭此终端,设置浏览器的socks5 proxy的地址为127.0.0.1 ,端口为2081,浏览器即可翻墙。 
------------

Websocks开发记录

作为一个 App

一开始软件结构是仿照 LightSocks 的,分为了 server 和 local 两部分。这样做缺点很明显——麻烦,不仅写代码麻烦,编译也麻烦,运行更麻烦。

于是,我把这两部分利用 cli 合到了一起。这样可以一个软件同时担任 client 和 server 的角色,还可以加入其它功能,比如生成证书,打开官网之类的~

伪装混淆

众所周知,TLS 是个十分安全的协议,可以保护数据不被 ISP 或者 GFW 获取到,这是选择 HTTPS 作为底层协议的原因。但是 TLS 在 Client Hello 阶段是明文的,server name 会作为明文发给服务端,这样就会暴露要访问的域名。但我们也可以利用这个特性达到伪装的目的。

来抓包对比一下。首先可以看到在伪装前,即使是启用了 TLS,访问的主机名依然可以被检测到:


 而通过设置 TlsConfig.ServerName 可以达到伪装的目的:


 

WebSocket

一开始用的 WebSocket 包是官方包,虽然简单方便,但是总有些问题,比如套上 CF 就用不了(其实可能是别的原因)。

后来看了很多其它 go 写的代理工具比如v2raygost,基本上用的都是用的第三方库,甚至 go 官方推荐用它,我也选择换成了github.com/gorilla/websocket这个库:


 from https://halu.lu/%E7%AC%94%E8%AE%B0/websocks-%E5%BC%80%E5%8F%91%E8%AE%B0%E5%BD%95/

 


No comments:

Post a Comment