VPS相关配置,Trojan及V2RAY脚本

警告
本文最后更新于 2020-05-08,文中内容可能已过时。

目前在用的科学上网工具有两款:TrojanV2Ray。V2Ray的使用方式有很多,这里不作详细记录。Trojan的优势在于配置相对简单、采用的科学上网的方法不易被发现、速度快。V2Ray的优势在于功能齐全,搭配其他工具可以实现从简单到复杂的各种科学上网方式,缺点是某些方式速度较慢。综合两者的优缺点,我一般将两者搭配使用,Trojan负责科学上网,V2Ray负责路由功能。

是否在Debian/Ubuntu官方仓库中

  • Trojan: ✔ (ubuntu从19.04开始)
  • V2Ray: ✘

V2Ray的官方安装脚本在国内下载速度很慢,经常失败。结合上面信息,应选择优先安装Trojan,然后在终端下开启科学上网模式,再安装V2Ray安装流程全自动,一切都很Nice。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
function trojan_environment_varibles() {
    TROJAN_PREFIX=/directory/to/trojan/real/config.json
    TROJAN_CERT=$TROJAN_PREFIX/cert.pem
    TROJAN_CERT_PATH_IN_CONFIG=$TROJAN_CERT
    TROJAN_CONFIG=$TROJAN_PREFIX/config.json
    # trojan模板配置文件
    TROJAN_CONFIG_EXAMPLE=$TROJAN_PREFIX/config_example.json
    
    TROJAN_SERVER=$SERVER_NAME
}

function trojan_install_for_old_distributor() {
    # 当官方仓库中不含trojan时安装流程
    # 证书 位于 /usr/local/etc/ssl/cert.pem
    # 配置文件 位于 /usr/local/etc/trojan/config.json
    # 新建trojan用户,由trojan用户开启服务
}

function trojan_install() {
    # 设置trojan相关环境变量
    trojan_environment_varibles
    # 若trojan配置文件不存在,则从模板配置文件复制一份
    [ ! -f $TROJAN_CONFIG ] && cp $TROJAN_CONFIG_EXAMPLE $TROJAN_CONFIG
    if ! command -v trojan > /dev/null 2>&1; then
        TROJAN_CONFIG=/path/to/模板配置文件
        TROJAN_CERT=/path/to/本地证书
        if 满足条件:Ubuntu及版本在19.04及以上; then
            sudo apt install trojan
            sudo ln -sf $TROJAN_CONFIG /etc/trojan/config.json
        else
            trojan_install_for_old_distributor
            TROJAN_CERT_PATH_IN_CONFIG=/usr/local/etc/ssl/cert.pem
        fi
    fi
    # 设置配置文件中的证书路径
    if $TROJAN_CONFIG中不含$TROJAN_CERT_PATH_IN_CONFIG; then
        sed -i "s|\"cert.*|\"cert\": \"$TROJAN_CERT_PATH_IN_CONFIG\",|" $TROJAN_CONFIG
    fi
    trojan_first_start
}

这里只讨论Trojan在本地的配置以及相关脚本。Trojan的客户端配置中有三项在切换VPS时需要修改,分别是

配置选项 VPS
remote_addr IP/域名
sni 域名
cert 本地证书路径

因为使用了CloudFlare家的泛域名证书,因此在多个VPS上可以使用同一套证书,意味着即使在配置文件中本地证书路径使用了软链接地址,也无需对其指向的证书文件做任何修改。因此在切换VPS时,真正需要修改的只有两个地方:remote_addrsni

remote_addr选择的规则为

分类 DNS Proxy
remote_addr IP或者域名 IP

原因在于remote_addr必须解析到VPS自己的IP

根据上面的分析,采用模板配置文件的方式,选取特定的VPS后,用sed直接修改配置文件中上述两个选项的值,再用service命令重启服务。

VPS的相关信息,都存放在文件server_info.ini中,如下所示:

1
2
3
4
5
6
7
8
9
[cloudcone]
ip = xxx.xxx.xxx.xxx
domain = www.example.com
dns_proxy = true

[racknerd]
ip = xxx.xxx.xxx.xxx
domain = www.example2.com
dns_proxy = false

借助awk定义两个函数用于读取ini文件中的服务器信息。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
SERVER_INFO_FILE=/path/to/server_info.ini
# 收集VPS入口信息
function read_section_ini() {
    INI_FILE=$1
    awk -F '=' '{
        if ($1 ~ /\[/) {
            gsub(/[\[\]]/, "", $1)
            print $1
        }
    }' $INI_FILE
}    

# 获取特定VPS的信息
function read_ini() {
    INI_FILE=$1
    SECTION=${2:-cloudcone}
    KEY=${3:-ip}
    awk -F '=' '{
    if ($1 ~ /\[/)
        if ($1 ~ /\['$SECTION'/) {
            a = 1
        } else {
            a = 0
        }
    else
        if (a == 1 && $1 ~ /'$KEY'/) {
            gsub(/[[:blank:]]/, "", $2)
            print $2
        }
    }' $INI_FILE
}

VPS_SERVERS=($(read_section_ini $SERVER_INFO_FILE))

接下来就涉及到TrojanV2Ray相关的脚本了,同样定义两个函数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
function choose_server_for_trojan() {
    TROJAN_SERVER=$1
    TROJAN_CONFIG=/path/to/trojan的模板配置文件
    # 获取选定VPS的具体信息
    dns_proxy=$(read_ini $SERVER_INFO_FILE $TROJAN_SERVER dns_proxy)
    ip=$(read_ini $SERVER_INFO_FILE $TROJAN_SERVER ip)
    sni=$(read_ini $SERVER_INFO_FILE $TROJAN_SERVER sni)
    if $dns_proxy; then
        remote_addr=$ip
    else
        remote_addr=$sni
    fi
    # 修改模板配置文件
    sed -i "s|\"remote_addr.*|\"remote_addr\": \"$remote_addr\",|" $TROJAN_CONFIG
    sed -i "s|\"sni.*|\"sni\": \"$sni\",|" $TROJAN_CONFIG
    # 重启Trojan服务
    sudo service trojan restart
    sudo service trojan status
}

function choose_server_for_v2ray() {
    # V2Ray相关配置修改
}

# 核心逻辑部分
function change_server() {
    # 可用的VPS都存放在数组变量 VPS_SERVERS 中,传入的参数为数组下标,
    # 用于选取特定 VPS
    VPS_SERVERS=($(read_section_ini $SERVER_INFO_FILE))
    SERVER_ID=${1:-0}
    SERVER_NAME=$(VPS_SERVERS[$SERVER_ID])
    if [ $SERVER_ID -ge 0 ] && [ $SERVER_ID -lt ${#VPS_SERVERS[*]} ]; then
        choose_server_for_trojan $SERVER_NAME
        choose_server_for_v2ray $SERVER_NAME
    else
        echo "错误:VPS服务器不存在"
        echo "尝试:'$COMMAND -i' 查看可用服务器"
    fi
}

都到这个程度了,干脆写成一个简单的命令行程序。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# 除了上面提到的函数外,再定义三个函数,用于实现相应的命令参数
function show_help() {
    echo "欢迎使用本脚本切换VPS服务器"
    echo "用法:vps_server 选项"
    echo "选项:"
    echo "  -h [--help]         显示本帮助"
    echo "  -s [--server=val]   通过val选择不同VPS服务器"
    echo "  -i [--info]         显示可用服务器汇总信息"
    echo "  -c [--current]      显示当前使用的VPS服务器"
}


function show_servers_info() {
    # shellcheck disable=SC2128,2207
    [ -z "$VPS_SERVERS" ] && VPS_SERVERS=($(read_section_ini $SERVER_INFO_FILE))
    echo "可用的服务器有:"
    for i in "${!VPS_SERVERS[@]}"; do
        echo "    $i ): ${VPS_SERVERS[i]}"
    done
    echo ""
    echo "服务器端安装完成:"
    echo "    将证书下载至客户端所在主机并放置在正确路径"   
    echo "    客户端配置文件中需指定证书(或其软链接)路径"
    echo "    若CloudFlare中开启了代理,则客户端配置文件中只能使用IP。"
    echo ""
    echo "客户端安装完成:"
    echo "    从服务器上下载证书并安装"
    echo "    配置文件中需指定证书绝对路径"
    echo "    |             |       |    remote_addr   |"
    echo "    |             |       |   IP   |   域名  |"
    echo "    | CloudFlare  |  DNS  |    ✔   |    ✔    |"
    echo "    |             | Proxy |    ✔   |    ✘    |" 
    echo ""
}

function get_current_server() {
    awk -F ':' '{
        if ($1 ~ /\"sni/) {
            gsub(/\"/, "", $2)
            print $2
        }
    }' $TROJAN_PREFIX/config.json
}

# while循环与case结构处理命令参数
while [[ $# -ge 1 ]]; do
    case "$1" in
        -h|--help )
            show_help
            ;;
        -i|--info)
            show_servers_info
            ;; 
        -c|--current)
            VPS=$(get_current_server)
            echo "当前VPS服务器:"
            echo "    ${VPS%%.*}"
            ;;
        -s|--server) 
            shift
            if [[ $# -ge 1 ]]; then
                ID=$1
            else
                ID=0
            fi
            change_server $ID
            ;;
        *)
            echo "错误:未识别选项 '$1'"
            echo "尝试 '$COMMAND -h|--help' 查看帮助信息"
            ;;
    esac
    shift
done