luckyyyyy / blog

William Chan's Blog
https://williamchan.me/
172 stars 28 forks source link

使用 nginx 搭建自己的 shadowsocks 集群 并利用 DDNS 来避免在公司被 GFW 的干扰 #39

Open luckyyyyy opened 4 years ago

luckyyyyy commented 4 years ago

由于众所周知的原因,程序员一般需要自己的科学上网方式,除大厂的基础建设外,我们普通程序员有时候也需要会使用自己的科学上网方式。 今天就来介绍一下使用 nginx 来搭建 shadowsocks 集群。

当然我们也可以使用 HAProxy 来搭建,目前最新消息 HAProxy2 已经支持 Lua + UDP 啦,后面我会单独写一篇 HAProxy 的配置方式。

机场采购

首选既然是集群,必须要有一个好的机场,自费的话成本太高,那么建议采购一些还不错的机场。 目前我个人使用的还不错 可以发给大家做参考 https://wmsxwd888.men/auth/register?code=QpBC

配置 nginx

编译时一定要加入--with-stream参数来确保支持 stream 转发。 配置可以参考下方的配置方式。

stream {
    log_format proxy '$remote_addr [$time_local] '
                 '$protocol $status "$bytes_sent/$bytes_received" '
                 '$session_time "$server_addr -> $upstream_addr" '
                 '"$upstream_bytes_sent/$upstream_bytes_received" "$upstream_connect_time"';
    #access_log /mnt/sda1/logs/nginx-access_log proxy;
    #error_log /mnt/sda1/logs/nginx-error.log;

    access_log off;
    error_log off;

    upstream ss_group {
        #hash $remote_addr consistent;
        #hash $upstream_addr consistent;
        include server.conf;
    }
    server {
        listen 10088;
        listen 10088 udp;
        proxy_pass ss_group;
    }
}
# server.conf
server server_ip:port fail_timeout=1s max_fails=2;
server server_ip:port fail_timeout=1s max_fails=2;

端口转发

如果家里有电信光猫最高权限的,可以设置光猫内的端口转发,否则可以打电话把光猫改为桥接模式,然后在路由器中 PPPoE 拨号,然后设置 10088 端口对外开放,配合我之前的 DDNS 就可以在公司中使用家里的集群转发啦。

这样做的好处是避免公司人多,出口容易引起 GFW 的注意和干扰,在 SS 配置中连回家中,然后由家中转发出去,就非常完美的避开人多这一点了。

自动生成 server.conf 脚本

使用我上面推荐的服务商,可以使用我这个脚本来自动更新 SS 服务器的更新,选择了最优的 hk 和 tw 的节点。

<?php
$link = 'SERVER_IP';
$path = '/usr/local/nginx/conf/server.conf';

$stream_opts = [
    "ssl" => [
        "verify_peer"=>false,
        "verify_peer_name"=>false,
    ]
];
echo 'DOWNLOAD...', PHP_EOL;
$txt = json_decode(base64_decode(substr(file_get_contents($link, false, stream_context_create($stream_opts)), 6)));
$servers = '';
$flags = STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT;
$port = '1737';
$afds = $wfds = [];
if ($txt && $txt->servers) {
    foreach ($txt->servers as $i => $value) {
        if (!preg_match("/中国|俄罗斯|免费|官网/i",$value->remarks) && (strstr($value->server, 'hk') || strstr($value->server, 'tw')) ) {
            $fd = @stream_socket_client('tcp://' . $value->server . ':' . $port, $errno, $error, 1, $flags);
            if ($fd === false) {
                echo 'server: ' . $value->server . ':' . $port . ' fail', PHP_EOL;
            } else {
                $afds[$i] = $wfds[$i] = $fd;
            }
        }
    }
    if (count($wfds) === 0) {
        echo 'FAIL COUNT 0', PHP_EOL;
        return;
    }
    sleep(2);
    $rfds = $xfds = [];
    if (@stream_select($rfds, $wfds, $xfds, 0, 0) !== false) {
        foreach ($afds as $i => $fd) {
            // 连接超时
            if (!isset($wfds[$i])) {
                $value = $txt->servers[$i];
                echo 'server: ' . $value->server . ':' . $port . ' fail', PHP_EOL;
            }
        }
        foreach ($wfds as $i => $fd) {
            echo 'server: ' . $value->server . ':' . $port . ' success', PHP_EOL;
            $value = $txt->servers[$i];
            $servers .= 'server ' . $value->server . ':' . $port . ' fail_timeout=1s max_fails=2;' . PHP_EOL;
        }
    }
    // close
    foreach ($afds as $fd) {
        @fclose($fd);
    }
}
echo 'UPDATE...', PHP_EOL;
if ($servers) {
    file_put_contents($path, $servers);
    echo 'DONE', PHP_EOL;
} else {
    echo 'FAIL', PHP_EOL;
}