文章 八月 07, 2018

“工匠精神” 精心打造一个代理服务器

文章字数 14k 阅读约需 12 mins. 阅读次数 0

前言

首先,本文会涉及众多代理软件的名字,但因为本人是在国外,用代理软件是回国的,是一名爱国青年。所以请大家不要把代理的方向反转来用。

在国外很多时候打开国内的网站非常缓慢,而且时不时有境外 IP 受限的问题。而代理软件的 obfs 或者 Websocks 能让我冒认成其他软件的服务器干些嘿嘿嘿的事情之外,还能帮助我突破一些的网络限制,比如我们学校的 Wi-Fi ,除了 443 和 80 端口,其他端口很多都是不通的。经过代理后就不会有域名和端口被限制的问题,还能保障安全,随便连各种 Wi-Fi 。

从而有了这篇文章,整个代理实现了:obfs + 服务器 IP 分流 + UDP 转发 + 去广告 + IPv6 支持 。

代理入口

Shadowsocks

之所以选择 Shadowsocks 作为代理入口是因为 Shadowsocks 都发展有一段时间了,各平台的客户端也比较完善,用起来比较顺手。而目前 V2ray 的手机客户端目前还不稳定,如果直接用 V2ray 作为入口的话,就可以直接在 V2ray 实现 UDP 转发和 IP 分流,也能用 Caddy 和 Nginx 直接代理它的 Websocks 不需要再套 SNIPROXY,整个过程更加简洁。但截止目前 2017-11-15 ,IOS 端的 ShadowRay 还是很不稳定,丢失 VPN 图标经常发生,而且 IPv6 支持并不完美(不支持直接打开 IPv6 地址),也不支持 UDP 转发。综合以上的考虑,决定使用 Shadowsocks 作为代理的入口。

配置 Shadowsocks

相信大家对于这个已经不陌生了,我也不过多介绍。

推荐使用 秋水逸冰 的一键安装脚本,Shadowsocks-libev 版
https://teddysun.com/

Shadowsocks 优化

除了基本的安装,当然还要有一些其他的优化,比如打开 BBR 和 TCP Fast Open ,还有一些 sysctl 的优化,我直接放上来。

原本我是参考小数派里面一篇很好的文章的,里面有详细写到每一项的作用,但 “不知道为什么” ,这文章后来被删除了。

所以如果大家要了解以下 sysctl 设置的作用的话,可以 Google 一下。

net.ipv6.conf.all.accept_ra = 2
net.ipv4.tcp_fastopen = 3
net.ipv4.tcp_congestion_control = bbr
net.core.default_qdisc = fq
fs.file-max = 1024000
kernel.msgmnb = 65536
kernel.msgmax = 65536
kernel.shmmax = 68719476736
kernel.shmall = 4294967296
net.core.rmem_max = 12582912
net.core.wmem_max = 12582912
net.ipv4.tcp_rmem = 10240 87380 12582912
net.ipv4.tcp_wmem = 10240 87380 12582912
net.ipv4.ip_forward = 1
net.ipv4.tcp_syncookies = 1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 30
net.ipv4.tcp_keepalive_time = 1200
net.ipv4.tcp_mtu_probing = 1
net.ipv4.conf.all.accept_source_route = 1n
et.ipv4.conf.default.accept_source_route = 1
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.lo.send_redirects = 0
net.ipv4.conf.all.rp_filter = 0
net.ipv4.conf.default.rp_filter = 0
net.ipv4.conf.lo.rp_filter = 0
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv6.conf.all.accept_source_route = 1
net.ipv6.conf.default.accept_source_route = 1
net.ipv6.conf.all.accept_redirects = 0
net.ipv6.conf.default.accept_redirects = 0
net.ipv6.conf.all.autoconf = 1
net.ipv6.conf.all.forwarding = 1

Shadowsocks 配置文件

因为我们需要兼容到 WEB 服务器的运行,然后用 443 端口来做代理 TCP 和 UDP 的 Shadowsocks 流量。我们要分别跑两个 Shadowsocks 服务,一个是 TCP + obfs server 的,另一个是 UDP Only 的,因为 SNIPROXY 不代理 UDP 流量。

TCP SERVER:

{
    "server":["[::1]","127.0.0.1"],
    "server_port":8988,
    "local_address":"127.0.0.1",
    "local_port":1080,
    "password":"stevenkwan.me",
    "timeout":600,
    "method":"chacha20-ietf-poly1305"
}

因为服务还需要经过 SNIPROXY 代理,所以监听 IPv4 的 127.0.0.1 即可,顺带提一下 IPv6 里是 [::1]

在运行的时候,还需要加上以下的参数:

-6 -c /xxxxxx/ss-tls.conf -d 127.0.0.1:53 --plugin obfs-server --plugin-opts "obfs=tls;fast-open"

-6 优先使用 IPv6 地址。

-d 指定 DNS 服务器,这里指定为本机的 dnsmasq 服务器,下面去广告会用到。

UDP Only SERVER:

{
    "server":["[::0]","0.0.0.0"],
    "server_port":443,
    "local_address":"127.0.0.1",
    "local_port":443,
    "password":"stevenkwan.me",
    "timeout":600,
    "method":"chacha20-ietf-poly1305"
}

这里的 UDP 是需要直接暴露在公网的 443 端口。

在运行的时候,还需要加上以下的参数:

-6 -U -c /xxxx/udp-443.conf -d 127.0.0.1:53

-U 大写的 U 代表只开启 UDP 转发不开启 TCP 转发,如果写成小写的话,是两个同时开始。有 WEB Server 的话就会提示端口已经被占用。

兼容 WEB

SNIPROXY

SNIPROXY 的设置非常简单,直接上配置文件。

需要注意的是,启用前,要先将 WEB Server 改成非 443 端口。

user daemon

pidfile /tmp/sniproxy.pid

error_log {
    syslog daemon
    priority notice
}

listen 443 {
    protocol tls
    reuseport no
    table https
    fallback [::1]:445
}

table https {
    www.google.com [::1]:8988
}

fallback 指向你的 WEB Server 端口即可。

table https 里面写你 Shadowsoks 里使用的 obfs 域名。

中转服务器

V2ray

除了我上面说的 V2ray 客户端不足,V2ray 还是有很多优势的,适合用来 “长距离” “翻山越岭” 。它支持 mkcp 和 mux 。

我曾经用过 Shadowsocks 作为中转服务器,那速度真的太慢了。

这里我用到了 mkcp 和 动态端口。

国内服务器配置文件:

{
  "inbound": {
    "port": 8888,
    "protocol": "vmess",
    "streamSettings": {
      "network": "kcp"
    },
    "settings": {
      "clients": [
        {
          "id": "xxxxxxxxxx",
          "level": 1,
          "alterId": 32
        }
      ],
      "detour": {
        "to": "detour-kcp"
      }
    }
  },
  "inboundDetour": [
    {
      "protocol": "vmess",
      "port": "8889-9999",
      "tag": "detour-kcp",
      "settings": {},
      "allocate": {
        "strategy": "random",
        "concurrency": 2,
        "refresh": 5
      },
      "streamSettings": {
        "network": "kcp"
      }
    }
  ],
  "outbound": {
    "protocol": "freedom",
    "settings": {}
  },
  "outboundDetour": [
    {
      "protocol": "blackhole",
      "settings": {},
      "tag": "blocked"
    }
  ],
  "routing": {
    "strategy": "rules",
    "settings": {
      "rules": [
        {
          "type": "field",
          "ip": [
            "0.0.0.0/8",
            "10.0.0.0/8",
            "100.64.0.0/10",
            "127.0.0.0/8",
            "169.254.0.0/16",
            "172.16.0.0/12",
            "192.0.0.0/24",
            "192.0.2.0/24",
            "192.168.0.0/16",
            "198.18.0.0/15",
            "198.51.100.0/24",
            "203.0.113.0/24",
            "::1/128",
            "fc00::/7",
            "fe80::/10"
          ],
          "outboundTag": "blocked"
        }
      ]
    }
  },
  "transport": {
    "kcpSettings": {
      "mtu": 1400,
      "tti": 20,
      "uplinkCapacity": 10,
      "downlinkCapacity": 10,
      "congestion": false,
      "readBufferSize": 2,
      "writeBufferSize": 2,
      "header": {
        "type": "none"
      }
    }
  }
}

请自行修改配置文件的端口、动态端口范围、客户 ID 。

国外服务器配置文件:

{
  "inbound": {
    "protocol": "dokodemo-door",
    "port": 1080,
    "settings": {
      "network": "tcp,udp",
      "timeout": 30,
      "followRedirect": true
    }
  },
  "outbound": {
    "protocol": "vmess",
    "streamSettings": {
      "network": "kcp"
    },
    "mux": {
      "enabled": true,
      "concurrency": 8
    },
    "settings": {
      "vnext": [
        {
          "address": "8.8.8.8",
          "port": 8888,
          "users": [
            {
              "id": "xxxxxxxxxx",
              "alterId": 32,
              "security": "aes-128-gcm"
            }
          ]
        }
      ]
    }
  },
  "transport": {
    "kcpSettings": {
      "mtu": 1400,
      "tti": 20,
      "uplinkCapacity": 100,
      "downlinkCapacity": 100,
      "congestion": false,
      "readBufferSize": 2,
      "writeBufferSize": 2,
      "header": {
        "type": "none"
      }
    }
  }
}

这里我们将 V2ray 的 inbound 设置为透明代理,这样 iptables 就能把国内流量转发到 V2ray 上。

更详细的设置 V2ray 设置可以参考他们家的官网 https://www.v2ray.com

IP 分流

iptables

要根据 IP 来分流,除了 iptables 之外,也没其他什么软件能做到了。但是单纯靠 iptables 来分流的话,会造成性能低下,这时候我们需要 ipset 。

下面这篇文章测试了在使用 ipset 和不使用 ipset 时 iptables 的性能。

http://blog.csdn.net/dog250/article/details/41171643

ipset

在这里推荐大家用这个项目来获取中国的 IP 地址,每月更新的。

https://github.com/17mon/china_ip_list

这里我只转发了 TCP 流量:

ipset -N chnroute hash:net maxelem 65536
ipset add chnroute $IP
iptables -t nat -N V2RAY
iptables -t nat -A V2RAY -p tcp -m set --match-set chnroute dst -j REDIRECT --to-port 1080
iptables -t nat -A OUTPUT -p tcp -j V2RAY

将上面的 $IP 换成你需要添加的 IP 地址,推荐大家用脚本来添加,这样可以设置成每月任务,自动更新。

更新前,通常需要先移除旧规则:

iptables -t nat -D OUTPUT -p tcp -j V2RAY
iptables -t nat -F V2RAY
iptables -t nat -X V2RAY
ipset destroy chnroute

UDP

在上面配置 Shadowsocks 服务端的时候,我们已经做好了完整的 UDP 转发支持。但客户端支不支持,却又是另一回事,我们可以通过以下方法来测试。

测试 UDP

我们在服务器上打开一个 screen 运行 nc -lu 20000 来监听 20000 端口的 UDP 。

然后在需要测试的设备上,发一个 UDP 包到你的服务器上。

如果服务器能收到这个 UDP 包,那么就可以再新开一个 screen 运行 netstat -nu | grep 20000 来查看来源 IP ,通过来源 IP 来判断是否走了代理。

截止 2017-11-15 ,IOS 客户端就只有 Shadowrocket 支持 UDP 转发,Surge 和 ShadowRay 均为直连。

IPv6 支持

在上面配置 Shadowsocks 服务端的时候,已经做好了完整的 IPv6 支持,大家可以打开一下网站里面的 “技术信息” 来测试 IPv6 。

http://test-ipv6.com

截止 2017-11-15 ,IOS 客户端就只有 Surge 支持直接打开 IPv6 地址,Shadowrocket 和 ShadowRay 均失败。

去广告 & 境外受限

自建 DNS 服务器除了能达到去广告的目的外,还能解决部分国内服务商将域名在国外解析成 127.0.0.1 的问题(如网易云音乐)。

关于如何更好的解除境外受限,大家可以参考一下以下项目。

https://github.com/uku/Unblock-Youku

DNS 去广告的效果也是非常好的,因为是从根源上返回一个 fake IP ,无论是 HTTP 和 HTTPS 都支持。

但域名列表是需要经常更新的,大家可以写个脚本来定期更新。

这里推荐一下这个更新广告域名的项目:

https://github.com/StevenBlack/hosts

还有个小问题是,大多数的去广告 hosts 文件都是把 IP 指向了 127.0.0.1 或者 0.0.0.0 ,但我服务器上是有 WEB Server 的,这会造成浏览器在尝试连接时,会等待结果而不是立即断开连接。我也试过把 IP 地址改成美国国防部的保留 IP 地址,然后通过 iptables 把所有出去的包 drop 了,效果也是不理想。推荐大家把 IP 指向 224.0.0.0 等特殊 IP 地址,实测不会拖慢网页加载速度。(以上结论暂时还没有进行过详细的测试,仅凭实测,如有错误,欢迎指出)

dnsmasq 配置

port=53
no-resolv
server=2001:4860:4860::8888
server=8.8.8.8
server=2620:0:ccc::2
server=208.67.222.222
all-servers
listen-address=127.0.0.1,::1
addn-hosts=/etc/dnsmasq_host/hosts

这里我设置了 4 个 DNS 上有地址,是 Google Public DNS 和 OpenDNS 的 IPv4 和 IPv6 地址,用了 all-servers 选项去并发查询这 4 个服务器,取最先返回结果的。

如果 Shadowsocks 开启了 IPv6 优先的话,hosts 文件也要加入 IPv6 的地址,推荐使用 ‘FF00::’ 。

因为 DNS 服务器只是给本机使用的,安全起见可以只监听 127.0.0.1

addn-hosts 则为去广告所用的 hosts 文件。

大功告成~!

至此为此,安全性和各种功能都有了,可以无拘无束的安心上网了。

原文链接:https://stevenkwan.me/post/perfect-proxy-server.html

– EOF –

0%