文章目录

本文将会搭建一个支持udp的,ssr转pptp服务

前言

先说一下这个服务存在的意义。一般ss服务的使用方式是在个人终端上安装相应的客户端程序,客户端会结合PAC和用户规则在本地开放socks服务,然后用户终端将流量转发至本地的socks服务端口。

这里有两个问题。

第一,需要安装客户端。

第二,ss客户端不支持udp。

  • 作为一个it极客,必然是要实现全网通的,所以代理这种事能在网关上做决不在终端做。我不太清楚目前市面上的路由器,或者第三方路由固件是否有针对socks5的代理支持,不过至少在我使用的routeros上并不支持socks5的代理通道,所以就有了一个想法,是否可以通过某种方式,将ss服务转换成pptp或者其他隧道,这样的话路由器也就可以直接连接。
  • ss的客户端并不支持udp,最起码MacOS版本的ssr客户端并不支持。也许已经有第三方的分支版本支持udp了,因为socks5本身是支持udp的,不过这个也和我们没关系了,因为在这我要实现的是透明代理,我不可能在路由器或者服务器安装一个ss客户端。

环境

  • 主路由:CCR1009-7G-1C-1S+PC
  • 服务器:Intel Nuc 8i3BEH & Centos7
  • 运行容器:docker ce。为什么要用docker,为了做服务隔离,在nuc上还运行着dns、homeassistant等其他服务,不过也因为我用了docker,也多踩了几个坑。

ss各组件

正向代理,需要客户端(此客户端指的不是ss客户端,而是任何用到网络的应用程序)手动指定代理配置,诸如Windows版QQ登录界面上的那个代理配置就是正向代理配置,配置完成之后,QQ软件就会使用指定的通道进行数据传输,这种代理方式只会对QQ软件本身生效。http,socks5此类的都是正向代理通道。

透明代理,不需要客户端配置,这种就类似直接架在某个网络出口的路由层,应用程序产生的网络流量会根据防火墙规则走相应的通道,由于应用程序并不知道网络被代理了,所以称透明代理。

  • ss-server:服务端
  • ss-local:客户端程序,提供socks5服务
  • ss-redir:本教程用到的组件,实际上也属于客户端程序,只不过它与iptables结合可以为我们提供透明代理的能力。iptables将对应的tcp,udp,icmp等流量转发到ss-redir的监听端口,ss-redir会将这些流量转发到远端ss-server。
  • ss-tunnel:本地端口转发工具。

创建docker镜像

准备centos容器

docker pull centos:latest
docker run -dit --privileged -p 1723:1723 [imageid](替换成你的centos镜像id)

注意,上述指令中的privileged项是必须的,因为我们要修改内核参数,必须加特权模式。1723端口为pptp端口。

进入容器

docker attach [containerid](替换成上述指令生成的容器id)

安装依赖

yum install https://dl.fedoraproject.org/pub/epel/epel-release-latest-7.noarch.rpm
yum install -y git pptpd pcre-devel openssl-devel zlib-devel asciidoc xmlto iproute net-tools telnet make gcc

编译&安装 ssr-libev

git clone https://github.com/shadowsocksr-backup/shadowsocksr-libev.git
make -j`nproc` && make install

准备ss-redir配置文件

ss-redir配置文件格式和ss-local一样

cd ~
vi ssr.conf

配置内容示例:

{
    "server":"",
    "server_port":,
    "local_address": "0.0.0.0",
    "local_port": 4111,
    "password": "",
    "method": "none",
    "protocol": "",
    "obfs": "",
    "obfs_param":""
}

准备容器的服务启动脚本

vi service.sh
#!/bin/sh
sysctl -w net.ipv4.ip_forward=1
/usr/sbin/pptpd
nohup /usr/local/bin/ss-redir -u -v -c /root/ssr.conf > /root/ssr.log &


#### 以下处理外部TCP数据包重定向至ss-redir ####
# 目标地址为保留地址直接跳出无需处理
iptables -t nat -A PREROUTING -d 127.0.0.0/24 -j RETURN
iptables -t nat -A PREROUTING -d 192.168.0.0/16 -j RETURN
iptables -t nat -A PREROUTING -d 10.42.0.0/16 -j RETURN
iptables -t nat -A PREROUTING -d 0.0.0.0/8 -j RETURN
iptables -t nat -A PREROUTING -d 10.0.0.0/8 -j RETURN
iptables -t nat -A PREROUTING -d 172.16.0.0/12 -j RETURN
iptables -t nat -A PREROUTING -d 224.0.0.0/4 -j RETURN
iptables -t nat -A PREROUTING -d 240.0.0.0/4 -j RETURN
iptables -t nat -A PREROUTING -d 169.254.0.0/16 -j RETURN
 # 余下数据包重定向至ss-redir
iptables -t nat -A PREROUTING -p tcp -j REDIRECT --to-ports 4111

#### 以下处理外部ICMP数据包重定向至ss-redir ####
iptables -t nat -A PREROUTING -p icmp -j REDIRECT --to-ports 4111

#### 以下处理本机TCP数据包重定向至ss-redir ####
# 目标地址为保留地址直接跳出无需处理
iptables -t nat -A OUTPUT -d 127.0.0.0/24 -j RETURN
iptables -t nat -A OUTPUT -d 192.168.0.0/16 -j RETURN
iptables -t nat -A OUTPUT -d 10.42.0.0/16 -j RETURN
iptables -t nat -A OUTPUT -d 0.0.0.0/8 -j RETURN
iptables -t nat -A OUTPUT -d 10.0.0.0/8 -j RETURN
iptables -t nat -A OUTPUT -d 172.16.0.0/12 -j RETURN
iptables -t nat -A OUTPUT -d 224.0.0.0/4 -j RETURN
iptables -t nat -A OUTPUT -d 240.0.0.0/4 -j RETURN
iptables -t nat -A OUTPUT -d 169.254.0.0/16 -j RETURN
# 防回环处理
iptables -t nat -A OUTPUT -d [这里替换为你的server地址] -j RETURN
# 余下数据包重定向至ss-redir
iptables -t nat -A OUTPUT -p tcp -j REDIRECT --to-ports 4111

#### 以下处理本机ICMP数据包重定向至ss-redir ####
iptables -t nat -A OUTPUT -p icmp -j REDIRECT --to-ports 4111

#### 以下处理UDP数据包重定向至ss-redir ####
# 目标地址为保留地址直接跳出无需处理
iptables -t mangle -I PREROUTING -d 127.0.0.0/24 -j RETURN
iptables -t mangle -I PREROUTING -d 192.168.0.0/16 -j RETURN
iptables -t mangle -I PREROUTING -d 10.42.0.0/16 -j RETURN
iptables -t mangle -I PREROUTING -d 0.0.0.0/8 -j RETURN
iptables -t mangle -I PREROUTING -d 10.0.0.0/8 -j RETURN
iptables -t mangle -I PREROUTING -d 172.16.0.0/12 -j RETURN
iptables -t mangle -I PREROUTING -d 224.0.0.0/4 -j RETURN
iptables -t mangle -I PREROUTING -d 240.0.0.0/4 -j RETURN
iptables -t mangle -I PREROUTING -d 169.254.0.0/16 -j RETURN
iptables -t mangle -I PREROUTING -d 255.255.0.0/8 -j RETURN
# 防回环处理
iptables -t mangle -I PREROUTING -d [这里替换为你的server地址] -j RETURN

# 使用tproxy标记udp包重定向至ss-redir
ip rule add fwmark 0x01/0x01 table 100
ip route add local 0.0.0.0/0 dev lo table 100
iptables -t mangle -N SSUDP
iptables -t mangle -A SSUDP -p udp -j TPROXY --on-port 4111 --tproxy-mark 0x01/0x01
iptables -t mangle -A PREROUTING -j SSUDP

说明:上述脚本中最后的udp包转发使用了TProxy,为什么要使用TProxy,这篇文章已经说得很清楚了:
https://blog.csdn.net/weixin_33709364/article/details/86994942

至此,容器内部的服务搭建完毕。

打包镜像&准备容器

打包镜像

docker commit [containerid] ssr-to-pptp-server

docker容器的ip默认情况下是随机分配的,因为下文的某些原因,我们需要这个服务容器保持一个固定ip,所以要先建立一个docker网络

docker network create --subnet=172.18.0.0/16 mynet

接着,启动服务容器

docker run --name ssr-to-pptp-server -dit --privileged --net mynet --ip 172.18.0.2 --restart=unless-stopped -p 1723:1723 [镜像id] /root/service.sh

宿主机准备

安装pptpd

为什么在宿主机也要安装pptp服务?实际上,我们需要的并不是pptp服务本身,而是其中的nf_conntrack_pptp内核模块,只有加载了该模块,宿主机才能识别处理这些以PPTP GRE格式封装的数据包,然后转发至docker相应的容器里。

yum install pptpd

加载内核模块

modprobe nf_conntrack_pptp

然而问题又来了,docker并不处理GRE这种扩展形式的ip数据包,docker守护进程只监听转发标准的TCP UDP数据包到容器内部,所以我们仍然需要手动的将GRE包转发至指定容器,而此时就用到了上面新建的docker网络和容器ip了。

# 将目标为本机的GRE包做个DNAT路由到我们的服务容器
iptables -t nat -A PREROUTING -d 192.168.88.32/32 -p gre -j DNAT --to-destination 172.18.0.2

完整的脚本内容:

#!/bin/bash
modprobe nf_conntrack_pptp
iptables -t nat -A PREROUTING -d 192.168.88.32/32 -p gre -j DNAT --to-destination 172.18.0.2

Routeros配置

至此,这个转换服务准备完成,可以通过路由器PPTP拨号至宿主机ip建立隧道,然后根据路由器上的防火墙规则走代理。我目前只将一些常用的服务加入了路由器防火墙。谷歌这种拥有自己ip段的比较容易处理,其他的服务由于没有独立ip段或者没公开ip段不太好搞,不过目前GitHub上有一些GFW block list,例如上次提到的dnsmasq-china-list,将来可能写个程序定期拉一下这些block-list,然后用ros的API实现防火墙的自动化同步。

欢迎留言