commit c7bd82d7e46c23e499b5e033294410533339a5c6 Author: Samuel Huang Date: Mon Aug 16 00:04:22 2021 +1000 Init import diff --git a/Dockerfile.amd64 b/Dockerfile.amd64 new file mode 100644 index 0000000..b27e6ae --- /dev/null +++ b/Dockerfile.amd64 @@ -0,0 +1,32 @@ +FROM golang:1.16-alpine as builder + +ARG XRAYVER='v1.4.2' + +RUN apk add --no-cache bash git build-base + +WORKDIR /go/src/XTLS/Xray-core +RUN git clone https://github.com/XTLS/Xray-core.git . && \ + git checkout ${XRAYVER} && \ + go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main + + +FROM alpine:3.14 + +COPY --from=builder /go/src/XTLS/Xray-core/xray /usr/local/bin/ + +RUN apk update && apk add bash nginx openssl curl socat jq moreutils +RUN cd /root; curl -sSL "https://github.com/acmesh-official/acme.sh/archive/refs/tags/2.9.0.tar.gz"|tar zxvf - +RUN cd /root; mv acme.sh-2.9.0 .acme.sh + +ADD run.sh /run.sh +ADD server-ltx.sh /server-ltx.sh +ADD server-ltt.sh /server-ltt.sh +ADD server-lttw.sh /server-lttw.sh +ADD server-mtt.sh /server-mtt.sh +ADD server-mttw.sh /server-mttw.sh +ADD server-ttt.sh /server-ttt.sh +ADD server-tttw.sh /server-tttw.sh + +RUN chmod 755 /*.sh + +ENTRYPOINT ["/run.sh"] diff --git a/Dockerfile.arm b/Dockerfile.arm new file mode 100644 index 0000000..df4299d --- /dev/null +++ b/Dockerfile.arm @@ -0,0 +1,32 @@ +FROM arm32v6/golang:1.16-alpine as builder + +ARG XRAYVER='v1.4.2' + +RUN apk add --no-cache bash git build-base + +WORKDIR /go/src/XTLS/Xray-core +RUN git clone https://github.com/XTLS/Xray-core.git . && \ + git checkout ${XRAYVER} && \ + go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main + + +FROM arm32v6/alpine:3.14 + +COPY --from=builder /go/src/XTLS/Xray-core/xray /usr/local/bin/ + +RUN apk update && apk add bash nginx openssl curl socat jq moreutils +RUN cd /root; curl -sSL "https://github.com/acmesh-official/acme.sh/archive/refs/tags/2.9.0.tar.gz"|tar zxvf - +RUN cd /root; mv acme.sh-2.9.0 .acme.sh + +ADD run.sh /run.sh +ADD server-ltx.sh /server-ltx.sh +ADD server-ltt.sh /server-ltt.sh +ADD server-lttw.sh /server-lttw.sh +ADD server-mtt.sh /server-mtt.sh +ADD server-mttw.sh /server-mttw.sh +ADD server-ttt.sh /server-ttt.sh +ADD server-tttw.sh /server-tttw.sh + +RUN chmod 755 /*.sh + +ENTRYPOINT ["/run.sh"] diff --git a/Dockerfile.arm64 b/Dockerfile.arm64 new file mode 100644 index 0000000..8039fa7 --- /dev/null +++ b/Dockerfile.arm64 @@ -0,0 +1,32 @@ +FROM arm64v8/golang:1.16-alpine as builder + +ARG XRAYVER='v1.4.2' + +RUN apk add --no-cache bash git build-base + +WORKDIR /go/src/XTLS/Xray-core +RUN git clone https://github.com/XTLS/Xray-core.git . && \ + git checkout ${XRAYVER} && \ + go build -o xray -trimpath -ldflags "-s -w -buildid=" ./main + + +FROM arm64v8/alpine:3.14 + +COPY --from=builder /go/src/XTLS/Xray-core/xray /usr/local/bin/ + +RUN apk update && apk add bash nginx openssl curl socat jq moreutils +RUN cd /root; curl -sSL "https://github.com/acmesh-official/acme.sh/archive/refs/tags/2.9.0.tar.gz"|tar zxvf - +RUN cd /root; mv acme.sh-2.9.0 .acme.sh + +ADD run.sh /run.sh +ADD server-ltx.sh /server-ltx.sh +ADD server-ltt.sh /server-ltt.sh +ADD server-lttw.sh /server-lttw.sh +ADD server-mtt.sh /server-mtt.sh +ADD server-mttw.sh /server-mttw.sh +ADD server-ttt.sh /server-ttt.sh +ADD server-tttw.sh /server-tttw.sh + +RUN chmod 755 /*.sh + +ENTRYPOINT ["/run.sh"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4077a25 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2021 Samuel Huang + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..f94c9db --- /dev/null +++ b/README.md @@ -0,0 +1,89 @@ +# server-xray + +Yet another unofficial [xray](https://github.com/XTLS/Xray-core) server container with x86 and arm/arm64 (Raspberry Pi) support. + +![docker-build](https://github.com/samuelhbne/server-xray/workflows/docker-buildx-latest/badge.svg) + +## [Optional] How to build server-xray docker image + +```shell +$ git clone https://github.com/samuelhbne/server-xray.git +$ cd server-xray +$ docker build -t samuelhbne/server-xray:amd64 -f Dockerfile.amd64 . +... +``` + +### NOTE1 + +- Please replace "amd64" with the arch match the current box accordingly. For example: "arm64" for AWS ARM64 platform like A1, t4g instance or 64bit Ubuntu on Raspberry Pi. "arm" for 32bit Raspbian. + +## How to start the container + +```shell +$ docker run --rm -it samuelhbne/server-xray:amd64 +server-xray -- [-r|--request-domain ] [-c|--cert-path ] [-k|--hook ] + -k|--hook [Optional] DDNS update or notifing URL to be hit. Multiple allowed + -r|--request-domain [Optional] Domain name to request for letsencrypt cert. Multiple allowed + -c|--cert-path [Optional] Reading TLS certs from folder //. Multiple allowed + --ltx p=1443,d=domain0.com,u=uuid[:level[:email]][,f=[fallback-host]:fb-port:[fb-path]] + --ltt p=2443,d=domain1.com,u=uuid[:level[:email]][,f=[fallback-host]:fb-port:[fb-path]] + --lttw p=3443,d=domain2.com,u=uuid[:level[:email]][,f=[fallback-host]:fb-port:[fb-path]],w=/webpath + --mtt p=4443,d=domain3.com,u=uuid[:level[:email]][,f=[fallback-host]:fb-port:[fb-path]] + --mttw p=5443,d=domain4.com,u=uuid[:level[:email]][,f=[fallback-host]:fb-port:[fb-path]],w=/webpath + --ttt p=6443,d=domain5.com,u=passwd[:email][,f=[fallback-host]:fb-port:[fb-path]] + --tttw p=7443,d=domain5.com,u=passwd[:email][,f=[fallback-host]:fb-port:[fb-path]],w=/webpath + --stdin Read XRay config from stdin instead of auto generation + +$ docker run --name server-xray -p 8443:443 -d samuelhbne/server-xray:amd64 --ltx p=443,d=mydomain.duckdns.org,u=bec24d96-410f-4723-8b3b-46987a1d9ed8,f=:80 -k https://duckdns.org/update/mydomain/c9711c65-db21-4f8c-a790-2c32c93bde8c -r mydomain.duckdns.org +... +``` + +### NOTE2 + +- Please replace "amd64" with the arch match the current box accordingly. For example: "arm64" for AWS ARM64 platform like A1, t4g instance or 64bit Ubuntu on Raspberry Pi. "arm" for 32bit Raspbian. +- Please replace "8443" with the TCP port number you want to listen. +- Please replace "bec24d96-410f-4723-8b3b-46987a1d9ed8" with the uuid you want to set for Xray client auth. +- Please replace mydomain.duckdns.org with the domain-name for Letsencrypt cert request. +- You can optionally assign a HOOK-URL to update the DDNS domain-name pointing to the current server public IP address. + +## How to verify if server-xray is running properly + +Try to connect the server from v2ray compatible mobile app like [v2rayNG](https://github.com/2dust/v2rayNG) for Android or [Shadowrocket](https://apps.apple.com/us/app/shadowrocket/id932747118) for iOS with the host-name, port, UUID etc. set above. Or verify it from Ubuntu / Debian / Raspbian client host follow the instructions below. + +### Please run the following instructions from Ubuntu / Debian / Raspbian client host for verifying + +```shell +$ docker run --rm -it samuelhbne/proxy-xray:amd64 +proxy-xray -- [options] + --ltx uuid@xray-host:port + --ltt uuid@xray-host:port + --lttw uuid@xray-host:port:/webpath + --mtt uuid@xray-host:port + --mttw uuid@xray-host:port:/webpath + --ttt password@xray-host:port + --tttw password@xray-host:port:/webpath + --stdin Read XRay config from stdin instead of auto generation + +$ docker run --name proxy-xray -p 1080:1080 -p 65353:53/udp -p 8123:8123 -d samuelhbne/proxy-xray:amd64 --ltx bec24d96-410f-4723-8b3b-46987a1d9ed8@nvda.duckdns.org:8443 +... + +$ curl -sSx socks5h://127.0.0.1:1080 http://ifconfig.co +12.34.56.78 +``` + +### NOTE4 + +- First we ran proxy-xray as SOCKS5 proxy that tunneling traffic through your Xray server. +- Then launching curl with client-IP address query through the proxy. +- This query was sent through your server with server-xray running. +- You should get the public IP address of your server with server-xray running if all good. +- Please have a look over the sibling project [proxy-xray](https://github.com/samuelhbne/proxy-xray) for more details. + +## How to stop and remove the running container + +```shell +$ docker stop server-xray +... +$ docker rm server-xray +... +``` diff --git a/run.sh b/run.sh new file mode 100755 index 0000000..64d6082 --- /dev/null +++ b/run.sh @@ -0,0 +1,132 @@ +#!/bin/bash + +DIR=`dirname $0` +DIR="$(cd $DIR; pwd)" +XCONF=/tmp/server-xray.json + +usage() { + echo "server-xray -- [-r|--request-domain ] [-c|--cert-path ] [-k|--hook ]" + echo " -k|--hook [Optional] DDNS update or notifing URL to be hit. Multiple allowed" + echo " -r|--request-domain [Optional] Domain name to request for letsencrypt cert. Multiple allowed" + echo " -c|--cert-path [Optional] Reading TLS certs from folder //. Multiple allowed" + echo " --ltx p=1443,d=domain0.com,u=uuid[:level[:email]][,f=[fallback-host]:fb-port:[fb-path]]" + echo " --ltt p=2443,d=domain1.com,u=uuid[:level[:email]][,f=[fallback-host]:fb-port:[fb-path]]" + echo " --lttw p=3443,d=domain2.com,u=uuid[:level[:email]][,f=[fallback-host]:fb-port:[fb-path]],w=/webpath" + echo " --mtt p=4443,d=domain3.com,u=uuid[:level[:email]][,f=[fallback-host]:fb-port:[fb-path]]" + echo " --mttw p=5443,d=domain4.com,u=uuid[:level[:email]][,f=[fallback-host]:fb-port:[fb-path]],w=/webpath" + echo " --ttt p=6443,d=domain5.com,u=passwd[:email][,f=[fallback-host]:fb-port:[fb-path]]" + echo " --tttw p=7443,d=domain5.com,u=passwd[:email][,f=[fallback-host]:fb-port:[fb-path]],w=/webpath" +# echo " --ssa port=8443,user=password1:method1[,user=password2:method2]" +# echo " --sst port=9443,user=passwd,method=xxxx" + echo " --stdin Read XRay config from stdin instead of auto generation" +} + +TEMP=`getopt -o k:r:c:d --long hook:,request-domain:,cert-path:,ltx:,ltt:,lttw:,mtt:,mttw:,ttt:,tttw:,ssa:,sst:stdin,debug -n "$0" -- $@` +if [ $? != 0 ] ; then usage; exit 1 ; fi + +eval set -- "$TEMP" +while true ; do + case "$1" in + -k|--hook) + HOOKURL+=("$2") + shift 2 + ;; + -r|--request-domain) + CERTDOMAIN+=("$2") + shift 2 + ;; + -c|--cert-path) + CERTPATH+="$2" + shift 2 + ;; + -d|--debug) + DEBUG=1 + shift 1 + ;; + --ltx|--ltt|--lttw|--mtt|--mttw|--ttt|--tttw) + SVC=`echo $1|tr -d '\-\-'` + SVCMD+=("$DIR/server-${SVC}.sh $2") + shift 2 + ;; + --stdin) + STDINCONF=1 + shift 2 + ;; + --) + shift + break + ;; + *) + echo "Get: $1" + usage; + exit 1 + ;; + esac +done + +if [ -n "${HOOKURL}" ]; then + for URL in "${HOOKURL[@]}" + do + echo "curl -sSL $URL" + curl -sSL "$URL" + echo + done + echo "Wait 10s for hook updates..." + sleep 10 +fi + +if [ -n "${CERTDOMAIN}" ]; then + for DOMAIN in "${CERTDOMAIN[@]}" + do + TRY=0 + while [ ! -f "/root/.acme.sh/${DOMAIN}/fullchain.cer" ] || [ ! -f "/root/.acme.sh/${DOMAIN}/${DOMAIN}.key" ] + do + echo "Requesting TLS cert for ${DOMAIN} ..." + /root/.acme.sh/acme.sh --issue --standalone -d ${DOMAIN} + ((TRY++)) + if [ "${TRY}" -ge 3 ]; then + echo "Requesting TLS cert for ${DOMAIN} failed. Check log please." + exit 3 + fi + echo "Wait 10 seconds before checking cert again..." + sleep 10 + done + done +fi + +echo '{"log":{"loglevel":"warning"}, "inbounds":[], "outbounds":[{"protocol":"freedom"}]}' |jq .|sponge $XCONF + +xopt="xconf=$XCONF" +CERTPATH+=("/root/.acme.sh") +for cp in "${CERTPATH[@]}" +do + xopt="$xopt,certpath=$cp" +done + +if [ -n "${SVCMD}" ]; then + for svcmd in "${SVCMD[@]}" + do + svcmd="$svcmd,$xopt" + $svcmd + if [[ $? -ne 0 ]]; then + echo + echo "Command failed: $svcmd" + exit 1 + fi + done + if [ "${DEBUG}" = 1 ]; then + cat $XCONF |jq '.log.loglevel |="debug"' |sponge $XCONF + echo + cat $XCONF + echo + fi + exec /usr/local/bin/xray -c $XCONF +else + if [ "${STDINCONF}" = "1" ]; then + exec /usr/local/bin/xray + else + echo "Mode selection option missing." + usage + exit 1 + fi +fi diff --git a/server-ltt.sh b/server-ltt.sh new file mode 100755 index 0000000..58f151a --- /dev/null +++ b/server-ltt.sh @@ -0,0 +1,160 @@ +#!/bin/bash + +usage() { + echo "Usage: server-ltt ,,,,[,fallback=www.baidu.com:443:/html][,fallback=:2443:/websocket2]" +} + +options=(`echo $1 |tr ',' ' '`) +for option in "${options[@]}" +do + kv=(`echo $option |tr '=' ' '`) + case "${kv[0]}" in + x|xconf) + xconf="${kv[1]}" + ;; + c|certpath) + certpath+=("${kv[1]}") + ;; + p|port) + port="${kv[1]}" + ;; + d|domain) + domain="${kv[1]}" + ;; + u|user) + xuser+=("${kv[1]}") + ;; + f|fallback) + fallback+=("${kv[1]}") + ;; + esac +done + +if [ -z "${certpath}" ]; then + echo "Error: certpath undefined." + usage + exit 1 +fi + +if [ -z "${xconf}" ]; then + echo "Error: xconf undefined." + usage + exit 1 +fi + +if [ -z "${port}" ]; then + echo "Error: port undefined." + usage + exit 1 +fi + +if [ -z "${domain}" ]; then + echo "Error: domain undefined." + usage + exit 1 +fi + +if [ -z "${xuser}" ]; then + echo "Error: user undefined." + usage + exit 1 +fi + +XCONF=$xconf +cat $XCONF |jq --arg port "${port}" '.inbounds +=[{"port":($port|tonumber), "protocol":"vless", "settings":{"clients":[]}}]' |sponge $XCONF + +for xu in "${xuser[@]}" +do + IFS=':' + uopt=(${xu}) + uopt=(${uopt[@]}) + + if [ -z "${uopt[0]}" ]; then + echo "Incorrect user format: ${xu}" + echo "Correct user format: user=[:level:email]" + echo "Like: user=805b2209-c26f-48d6-ba52-07b7d894f962:0:me@g.cn" + echo "Like: user=805b2209-c26f-48d6-ba52-07b7d894f962::me@g.cn" + echo "user=805b2209-c26f-48d6-ba52-07b7d894f962:0" + echo "user=805b2209-c26f-48d6-ba52-07b7d894f962" + exit 1 + fi + if [ -z "${uopt[1]}" ]; then + uopt[1]=0 + fi + if [ -z "${uopt[2]}" ]; then + uopt[2]="nobody@g.cn" + fi + cat $XCONF |jq --arg port "${port}" --arg uid "${uopt[0]}" --arg level "${uopt[1]}" --arg email "${uopt[2]}" \ + '( .inbounds[] | select(.port == ($port|tonumber)) | .settings.clients ) += [ {"id":$uid, "level":($level|tonumber), "email":$email} ] ' \ + |sponge $XCONF +done + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .settings.decryption ) += "none" ' \ +|sponge $XCONF + +for fb in "${fallback[@]}" +do + IFS=':' + fopt=(${fb}) + fopt=(${fopt[@]}) + + fhost="${fopt[0]}" + fport="${fopt[1]}" + fpath="${fopt[2]}" + + if [ -z "${fport}" ]; then + echo "Incorrect fallback format: ${fb}" + echo "Correct fallback: fallback=[host]<:port>[:path]" + echo "Like: fallback=baidu.com:443:/websocket" + echo "Like: fallback=:1443:/websocket" + echo "Like: fallback=:1443" + exit 1 + fi + + if [ -z "${fhost}" ]; then + if [ -z "${fpath}" ]; then + Jfallback=`echo '{}' |jq --arg fport "${fport}" --arg fpath "${fpath}" '. += {"dest":($fport|tonumber), "xver":1}'` + else + Jfallback=`echo '{}' |jq --arg fport "${fport}" --arg fpath "${fpath}" '. += {"dest":($fport|tonumber), "path":$fpath, "xver":1}'` + fi + else + if [ -z "${fpath}" ]; then + fdest="${fhost}:${fport}" + Jfallback=`echo '{}' |jq --arg fdest "${fdest}" --arg fpath "${fpath}" '. += {"dest":$fdest, "xver":1}'` + else + Jfallback=`echo '{}' |jq --arg fdest "${fdest}" --arg fpath "${fpath}" '. += {"dest":$fdest, "path":$fpath, "xver":1}'` + fi + fi + + cat $XCONF |jq --arg port "${port}" --argjson jfallback "$Jfallback" \ + '( .inbounds[] | select(.port == ($port|tonumber)) | .settings.fallbacks ) += [ $jfallback ] ' \ + |sponge $XCONF +done + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings ) += {"network":"tcp", "security":"tls" } ' \ +|sponge $XCONF + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings ) += {"tlsSettings":{"alpn":["http/1.1"]} } ' \ +|sponge $XCONF + +for certroot in "${certpath[@]}" +do + if [ -f "${certroot}/${domain}/fullchain.cer" ] && [ -f "${certroot}/${domain}/${domain}.key" ]; then + fullchain="${certroot}/${domain}/fullchain.cer" + prvkey="${certroot}/${domain}/${domain}.key" + break + fi +done + +if [ ! -f "${fullchain}" ] || [ ! -f "${prvkey}" ]; then + echo "TLS cert missing?" + echo "Abort." + exit 2 +fi + +cat $XCONF |jq --arg port "${port}" --arg fullchain "${fullchain}" --arg prvkey "${prvkey}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings.tlsSettings ) += {"certificates":[{"certificateFile":$fullchain, "keyFile":$prvkey}]} ' \ +|sponge $XCONF diff --git a/server-lttw.sh b/server-lttw.sh new file mode 100755 index 0000000..6b57746 --- /dev/null +++ b/server-lttw.sh @@ -0,0 +1,173 @@ +#!/bin/bash + +usage() { + echo "Usage: server-lttw ,,,,,[,fallback=www.baidu.com:443:/html][,fallback=:2443:/websocket2]" +} + +options=(`echo $1 |tr ',' ' '`) +for option in "${options[@]}" +do + kv=(`echo $option |tr '=' ' '`) + case "${kv[0]}" in + x|xconf) + xconf="${kv[1]}" + ;; + c|certpath) + certpath+=("${kv[1]}") + ;; + p|port) + port="${kv[1]}" + ;; + d|domain) + domain="${kv[1]}" + ;; + u|user) + xuser+=("${kv[1]}") + ;; + w|wpath) + wspath="${kv[1]}" + ;; + f|fallback) + fallback+=("${kv[1]}") + ;; + esac +done + +if [ -z "${certpath}" ]; then + echo "Error: certpath undefined." + usage + exit 1 +fi + +if [ -z "${xconf}" ]; then + echo "Error: xconf undefined." + usage + exit 1 +fi + +if [ -z "${port}" ]; then + echo "Error: port undefined." + usage + exit 1 +fi + +if [ -z "${domain}" ]; then + echo "Error: domain undefined." + usage + exit 1 +fi + +if [ -z "${xuser}" ]; then + echo "Error: user undefined." + usage + exit 1 +fi + +if [ -z "${wspath}" ]; then + echo "Error: wspath undefined." + usage + exit 1 +fi + +XCONF=$xconf +cat $XCONF |jq --arg port "${port}" '.inbounds +=[{"port":($port|tonumber), "protocol":"vless", "settings":{"clients":[]}}]' |sponge $XCONF + +for xu in "${xuser[@]}" +do + IFS=':' + uopt=(${xu}) + uopt=(${uopt[@]}) + + if [ -z "${uopt[0]}" ]; then + echo "Incorrect user format: ${xu}" + echo "Correct user format: user=[:level:email]" + echo "Like: user=805b2209-c26f-48d6-ba52-07b7d894f962:0:me@g.cn" + echo "Like: user=805b2209-c26f-48d6-ba52-07b7d894f962::me@g.cn" + echo "user=805b2209-c26f-48d6-ba52-07b7d894f962:0" + echo "user=805b2209-c26f-48d6-ba52-07b7d894f962" + exit 1 + fi + if [ -z "${uopt[1]}" ]; then + uopt[1]=0 + fi + if [ -z "${uopt[2]}" ]; then + uopt[2]="nobody@g.cn" + fi + cat $XCONF |jq --arg port "${port}" --arg uid "${uopt[0]}" --arg level "${uopt[1]}" --arg email "${uopt[2]}" \ + '( .inbounds[] | select(.port == ($port|tonumber)) | .settings.clients ) += [ {"id":$uid, "level":($level|tonumber), "email":$email} ] ' \ + |sponge $XCONF +done + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .settings.decryption ) += "none" ' \ +|sponge $XCONF + +for fb in "${fallback[@]}" +do + IFS=':' + fopt=(${fb}) + fopt=(${fopt[@]}) + + fhost="${fopt[0]}" + fport="${fopt[1]}" + fpath="${fopt[2]}" + + if [ -z "${fport}" ]; then + echo "Incorrect fallback format: ${fb}" + echo "Correct fallback: fallback=[host]<:port>[:path]" + echo "Like: fallback=baidu.com:443:/websocket" + echo "Like: fallback=:1443:/websocket" + echo "Like: fallback=:1443" + exit 1 + fi + + if [ -z "${fhost}" ]; then + if [ -z "${fpath}" ]; then + Jfallback=`echo '{}' |jq --arg fport "${fport}" --arg fpath "${fpath}" '. += {"dest":($fport|tonumber), "xver":1}'` + else + Jfallback=`echo '{}' |jq --arg fport "${fport}" --arg fpath "${fpath}" '. += {"dest":($fport|tonumber), "path":$fpath, "xver":1}'` + fi + else + if [ -z "${fpath}" ]; then + fdest="${fhost}:${fport}" + Jfallback=`echo '{}' |jq --arg fdest "${fdest}" --arg fpath "${fpath}" '. += {"dest":$fdest, "xver":1}'` + else + Jfallback=`echo '{}' |jq --arg fdest "${fdest}" --arg fpath "${fpath}" '. += {"dest":$fdest, "path":$fpath, "xver":1}'` + fi + fi + + cat $XCONF |jq --arg port "${port}" --argjson jfallback "$Jfallback" \ + '( .inbounds[] | select(.port == ($port|tonumber)) | .settings.fallbacks ) += [ $jfallback ] ' \ + |sponge $XCONF +done + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings ) += {"network":"ws", "security":"tls" } ' \ +|sponge $XCONF + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings ) += {"tlsSettings":{"alpn":["http/1.1"]} } ' \ +|sponge $XCONF + +cat $XCONF |jq --arg port "${port}" --arg wspath "${wspath}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings ) += {"wsSettings":{"path":$wspath}} ' \ +|sponge $XCONF + +for certroot in "${certpath[@]}" +do + if [ -f "${certroot}/${domain}/fullchain.cer" ] && [ -f "${certroot}/${domain}/${domain}.key" ]; then + fullchain="${certroot}/${domain}/fullchain.cer" + prvkey="${certroot}/${domain}/${domain}.key" + break + fi +done + +if [ ! -f "${fullchain}" ] || [ ! -f "${prvkey}" ]; then + echo "TLS cert missing?" + echo "Abort." + exit 2 +fi + +cat $XCONF |jq --arg port "${port}" --arg fullchain "${fullchain}" --arg prvkey "${prvkey}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings.tlsSettings ) += {"certificates":[{"certificateFile":$fullchain, "keyFile":$prvkey}]} ' \ +|sponge $XCONF diff --git a/server-ltx.sh b/server-ltx.sh new file mode 100755 index 0000000..4faafb0 --- /dev/null +++ b/server-ltx.sh @@ -0,0 +1,160 @@ +#!/bin/bash + +usage() { + echo "Usage: server-ltx ,,,,[,fallback=www.baidu.com:443:/html][,fallback=:2443:/websocket2]" +} + +options=(`echo $1 |tr ',' ' '`) +for option in "${options[@]}" +do + kv=(`echo $option |tr '=' ' '`) + case "${kv[0]}" in + x|xconf) + xconf="${kv[1]}" + ;; + c|certpath) + certpath+=("${kv[1]}") + ;; + p|port) + port="${kv[1]}" + ;; + d|domain) + domain="${kv[1]}" + ;; + u|user) + xuser+=("${kv[1]}") + ;; + f|fallback) + fallback+=("${kv[1]}") + ;; + esac +done + +if [ -z "${certpath}" ]; then + echo "Error: certpath undefined." + usage + exit 1 +fi + +if [ -z "${xconf}" ]; then + echo "Error: xconf undefined." + usage + exit 1 +fi + +if [ -z "${port}" ]; then + echo "Error: port undefined." + usage + exit 1 +fi + +if [ -z "${domain}" ]; then + echo "Error: domain undefined." + usage + exit 1 +fi + +if [ -z "${xuser}" ]; then + echo "Error: user undefined." + usage + exit 1 +fi + +XCONF=$xconf +cat $XCONF |jq --arg port "${port}" '.inbounds +=[{"port":($port|tonumber), "protocol":"vless", "settings":{"clients":[]}}]' |sponge $XCONF + +for xu in "${xuser[@]}" +do + IFS=':' + uopt=(${xu}) + uopt=(${uopt[@]}) + + if [ -z "${uopt[0]}" ]; then + echo "Incorrect user format: ${xu}" + echo "Correct user format: user=[:level:email]" + echo "Like: user=805b2209-c26f-48d6-ba52-07b7d894f962:0:me@g.cn" + echo "Like: user=805b2209-c26f-48d6-ba52-07b7d894f962::me@g.cn" + echo "user=805b2209-c26f-48d6-ba52-07b7d894f962:0" + echo "user=805b2209-c26f-48d6-ba52-07b7d894f962" + exit 1 + fi + if [ -z "${uopt[1]}" ]; then + uopt[1]=0 + fi + if [ -z "${uopt[2]}" ]; then + uopt[2]="nobody@g.cn" + fi + cat $XCONF |jq --arg port "${port}" --arg uid "${uopt[0]}" --arg level "${uopt[1]}" --arg email "${uopt[2]}" \ + '( .inbounds[] | select(.port == ($port|tonumber)) | .settings.clients ) += [ {"id":$uid, "flow":"xtls-rprx-direct", "level":($level|tonumber), "email":$email} ] ' \ + |sponge $XCONF +done + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .settings.decryption ) += "none" ' \ +|sponge $XCONF + +for fb in "${fallback[@]}" +do + IFS=':' + fopt=(${fb}) + fopt=(${fopt[@]}) + + fhost="${fopt[0]}" + fport="${fopt[1]}" + fpath="${fopt[2]}" + + if [ -z "${fport}" ]; then + echo "Incorrect fallback format: ${fb}" + echo "Correct fallback: fallback=[host]<:port>[:path]" + echo "Like: fallback=baidu.com:443:/websocket" + echo "Like: fallback=:1443:/websocket" + echo "Like: fallback=:1443" + exit 1 + fi + + if [ -z "${fhost}" ]; then + if [ -z "${fpath}" ]; then + Jfallback=`echo '{}' |jq --arg fport "${fport}" --arg fpath "${fpath}" '. += {"dest":($fport|tonumber), "xver":1}'` + else + Jfallback=`echo '{}' |jq --arg fport "${fport}" --arg fpath "${fpath}" '. += {"dest":($fport|tonumber), "path":$fpath, "xver":1}'` + fi + else + if [ -z "${fpath}" ]; then + fdest="${fhost}:${fport}" + Jfallback=`echo '{}' |jq --arg fdest "${fdest}" --arg fpath "${fpath}" '. += {"dest":$fdest, "xver":1}'` + else + Jfallback=`echo '{}' |jq --arg fdest "${fdest}" --arg fpath "${fpath}" '. += {"dest":$fdest, "path":$fpath, "xver":1}'` + fi + fi + + cat $XCONF |jq --arg port "${port}" --argjson jfallback "$Jfallback" \ + '( .inbounds[] | select(.port == ($port|tonumber)) | .settings.fallbacks ) += [ $jfallback ] ' \ + |sponge $XCONF +done + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings ) += {"network":"tcp", "security":"xtls" } ' \ +|sponge $XCONF + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings ) += {"xtlsSettings":{"alpn":["http/1.1"]} } ' \ +|sponge $XCONF + +for certroot in "${certpath[@]}" +do + if [ -f "${certroot}/${domain}/fullchain.cer" ] && [ -f "${certroot}/${domain}/${domain}.key" ]; then + fullchain="${certroot}/${domain}/fullchain.cer" + prvkey="${certroot}/${domain}/${domain}.key" + break + fi +done + +if [ ! -f "${fullchain}" ] || [ ! -f "${prvkey}" ]; then + echo "TLS cert missing?" + echo "Abort." + exit 2 +fi + +cat $XCONF |jq --arg port "${port}" --arg fullchain "${fullchain}" --arg prvkey "${prvkey}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings.xtlsSettings ) += {"certificates":[{"certificateFile":$fullchain, "keyFile":$prvkey}]} ' \ +|sponge $XCONF diff --git a/server-mtt.sh b/server-mtt.sh new file mode 100755 index 0000000..4910248 --- /dev/null +++ b/server-mtt.sh @@ -0,0 +1,160 @@ +#!/bin/bash + +usage() { + echo "Usage: server-mtt ,,,,[,fallback=www.baidu.com:443:/html][,fallback=:2443:/websocket2]" +} + +options=(`echo $1 |tr ',' ' '`) +for option in "${options[@]}" +do + kv=(`echo $option |tr '=' ' '`) + case "${kv[0]}" in + x|xconf) + xconf="${kv[1]}" + ;; + c|certpath) + certpath+=("${kv[1]}") + ;; + p|port) + port="${kv[1]}" + ;; + d|domain) + domain="${kv[1]}" + ;; + u|user) + xuser+=("${kv[1]}") + ;; + f|fallback) + fallback+=("${kv[1]}") + ;; + esac +done + +if [ -z "${certpath}" ]; then + echo "Error: certpath undefined." + usage + exit 1 +fi + +if [ -z "${xconf}" ]; then + echo "Error: xconf undefined." + usage + exit 1 +fi + +if [ -z "${port}" ]; then + echo "Error: port undefined." + usage + exit 1 +fi + +if [ -z "${domain}" ]; then + echo "Error: domain undefined." + usage + exit 1 +fi + +if [ -z "${xuser}" ]; then + echo "Error: user undefined." + usage + exit 1 +fi + +XCONF=$xconf +cat $XCONF |jq --arg port "${port}" '.inbounds +=[{"port":($port|tonumber), "protocol":"vmess", "settings":{"clients":[]}}]' |sponge $XCONF + +for xu in "${xuser[@]}" +do + IFS=':' + uopt=(${xu}) + uopt=(${uopt[@]}) + + if [ -z "${uopt[0]}" ]; then + echo "Incorrect user format: ${xu}" + echo "Correct user format: user=[:level:email]" + echo "Like: user=805b2209-c26f-48d6-ba52-07b7d894f962:0:me@g.cn" + echo "Like: user=805b2209-c26f-48d6-ba52-07b7d894f962::me@g.cn" + echo "user=805b2209-c26f-48d6-ba52-07b7d894f962:0" + echo "user=805b2209-c26f-48d6-ba52-07b7d894f962" + exit 1 + fi + if [ -z "${uopt[1]}" ]; then + uopt[1]=0 + fi + if [ -z "${uopt[2]}" ]; then + uopt[2]="nobody@g.cn" + fi + cat $XCONF |jq --arg port "${port}" --arg uid "${uopt[0]}" --arg level "${uopt[1]}" --arg email "${uopt[2]}" \ + '( .inbounds[] | select(.port == ($port|tonumber)) | .settings.clients ) += [ {"id":$uid, "level":($level|tonumber), "email":$email} ] ' \ + |sponge $XCONF +done + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .settings.decryption ) += "none" ' \ +|sponge $XCONF + +for fb in "${fallback[@]}" +do + IFS=':' + fopt=(${fb}) + fopt=(${fopt[@]}) + + fhost="${fopt[0]}" + fport="${fopt[1]}" + fpath="${fopt[2]}" + + if [ -z "${fport}" ]; then + echo "Incorrect fallback format: ${fb}" + echo "Correct fallback: fallback=[host]<:port>[:path]" + echo "Like: fallback=baidu.com:443:/websocket" + echo "Like: fallback=:1443:/websocket" + echo "Like: fallback=:1443" + exit 1 + fi + + if [ -z "${fhost}" ]; then + if [ -z "${fpath}" ]; then + Jfallback=`echo '{}' |jq --arg fport "${fport}" --arg fpath "${fpath}" '. += {"dest":($fport|tonumber), "xver":1}'` + else + Jfallback=`echo '{}' |jq --arg fport "${fport}" --arg fpath "${fpath}" '. += {"dest":($fport|tonumber), "path":$fpath, "xver":1}'` + fi + else + if [ -z "${fpath}" ]; then + fdest="${fhost}:${fport}" + Jfallback=`echo '{}' |jq --arg fdest "${fdest}" --arg fpath "${fpath}" '. += {"dest":$fdest, "xver":1}'` + else + Jfallback=`echo '{}' |jq --arg fdest "${fdest}" --arg fpath "${fpath}" '. += {"dest":$fdest, "path":$fpath, "xver":1}'` + fi + fi + + cat $XCONF |jq --arg port "${port}" --argjson jfallback "$Jfallback" \ + '( .inbounds[] | select(.port == ($port|tonumber)) | .settings.fallbacks ) += [ $jfallback ] ' \ + |sponge $XCONF +done + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings ) += {"network":"tcp", "security":"tls" } ' \ +|sponge $XCONF + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings ) += {"tlsSettings":{"alpn":["http/1.1"]} } ' \ +|sponge $XCONF + +for certroot in "${certpath[@]}" +do + if [ -f "${certroot}/${domain}/fullchain.cer" ] && [ -f "${certroot}/${domain}/${domain}.key" ]; then + fullchain="${certroot}/${domain}/fullchain.cer" + prvkey="${certroot}/${domain}/${domain}.key" + break + fi +done + +if [ ! -f "${fullchain}" ] || [ ! -f "${prvkey}" ]; then + echo "TLS cert missing?" + echo "Abort." + exit 2 +fi + +cat $XCONF |jq --arg port "${port}" --arg fullchain "${fullchain}" --arg prvkey "${prvkey}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings.tlsSettings ) += {"certificates":[{"certificateFile":$fullchain, "keyFile":$prvkey}]} ' \ +|sponge $XCONF diff --git a/server-mttw.sh b/server-mttw.sh new file mode 100755 index 0000000..e0d6f47 --- /dev/null +++ b/server-mttw.sh @@ -0,0 +1,173 @@ +#!/bin/bash + +usage() { + echo "Usage: server-mttw ,,,,,[,fallback=www.baidu.com:443:/html][,fallback=:2443:/websocket2]" +} + +options=(`echo $1 |tr ',' ' '`) +for option in "${options[@]}" +do + kv=(`echo $option |tr '=' ' '`) + case "${kv[0]}" in + x|xconf) + xconf="${kv[1]}" + ;; + c|certpath) + certpath+=("${kv[1]}") + ;; + p|port) + port="${kv[1]}" + ;; + d|domain) + domain="${kv[1]}" + ;; + u|user) + xuser+=("${kv[1]}") + ;; + w|wpath) + wspath="${kv[1]}" + ;; + f|fallback) + fallback+=("${kv[1]}") + ;; + esac +done + +if [ -z "${certpath}" ]; then + echo "Error: certpath undefined." + usage + exit 1 +fi + +if [ -z "${xconf}" ]; then + echo "Error: xconf undefined." + usage + exit 1 +fi + +if [ -z "${port}" ]; then + echo "Error: port undefined." + usage + exit 1 +fi + +if [ -z "${domain}" ]; then + echo "Error: domain undefined." + usage + exit 1 +fi + +if [ -z "${xuser}" ]; then + echo "Error: user undefined." + usage + exit 1 +fi + +if [ -z "${wspath}" ]; then + echo "Error: wspath undefined." + usage + exit 1 +fi + +XCONF=$xconf +cat $XCONF |jq --arg port "${port}" '.inbounds +=[{"port":($port|tonumber), "protocol":"vmess", "settings":{"clients":[]}}]' |sponge $XCONF + +for xu in "${xuser[@]}" +do + IFS=':' + uopt=(${xu}) + uopt=(${uopt[@]}) + + if [ -z "${uopt[0]}" ]; then + echo "Incorrect user format: ${xu}" + echo "Correct user format: user=[:level:email]" + echo "Like: user=805b2209-c26f-48d6-ba52-07b7d894f962:0:me@g.cn" + echo "Like: user=805b2209-c26f-48d6-ba52-07b7d894f962::me@g.cn" + echo "user=805b2209-c26f-48d6-ba52-07b7d894f962:0" + echo "user=805b2209-c26f-48d6-ba52-07b7d894f962" + exit 1 + fi + if [ -z "${uopt[1]}" ]; then + uopt[1]=0 + fi + if [ -z "${uopt[2]}" ]; then + uopt[2]="nobody@g.cn" + fi + cat $XCONF |jq --arg port "${port}" --arg uid "${uopt[0]}" --arg level "${uopt[1]}" --arg email "${uopt[2]}" \ + '( .inbounds[] | select(.port == ($port|tonumber)) | .settings.clients ) += [ {"id":$uid, "level":($level|tonumber), "email":$email} ] ' \ + |sponge $XCONF +done + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .settings.decryption ) += "none" ' \ +|sponge $XCONF + +for fb in "${fallback[@]}" +do + IFS=':' + fopt=(${fb}) + fopt=(${fopt[@]}) + + fhost="${fopt[0]}" + fport="${fopt[1]}" + fpath="${fopt[2]}" + + if [ -z "${fport}" ]; then + echo "Incorrect fallback format: ${fb}" + echo "Correct fallback: fallback=[host]<:port>[:path]" + echo "Like: fallback=baidu.com:443:/websocket" + echo "Like: fallback=:1443:/websocket" + echo "Like: fallback=:1443" + exit 1 + fi + + if [ -z "${fhost}" ]; then + if [ -z "${fpath}" ]; then + Jfallback=`echo '{}' |jq --arg fport "${fport}" --arg fpath "${fpath}" '. += {"dest":($fport|tonumber), "xver":1}'` + else + Jfallback=`echo '{}' |jq --arg fport "${fport}" --arg fpath "${fpath}" '. += {"dest":($fport|tonumber), "path":$fpath, "xver":1}'` + fi + else + if [ -z "${fpath}" ]; then + fdest="${fhost}:${fport}" + Jfallback=`echo '{}' |jq --arg fdest "${fdest}" --arg fpath "${fpath}" '. += {"dest":$fdest, "xver":1}'` + else + Jfallback=`echo '{}' |jq --arg fdest "${fdest}" --arg fpath "${fpath}" '. += {"dest":$fdest, "path":$fpath, "xver":1}'` + fi + fi + + cat $XCONF |jq --arg port "${port}" --argjson jfallback "$Jfallback" \ + '( .inbounds[] | select(.port == ($port|tonumber)) | .settings.fallbacks ) += [ $jfallback ] ' \ + |sponge $XCONF +done + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings ) += {"network":"ws", "security":"tls" } ' \ +|sponge $XCONF + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings ) += {"tlsSettings":{"alpn":["http/1.1"]} } ' \ +|sponge $XCONF + +cat $XCONF |jq --arg port "${port}" --arg wspath "${wspath}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings ) += {"wsSettings":{"path":$wspath}} ' \ +|sponge $XCONF + +for certroot in "${certpath[@]}" +do + if [ -f "${certroot}/${domain}/fullchain.cer" ] && [ -f "${certroot}/${domain}/${domain}.key" ]; then + fullchain="${certroot}/${domain}/fullchain.cer" + prvkey="${certroot}/${domain}/${domain}.key" + break + fi +done + +if [ ! -f "${fullchain}" ] || [ ! -f "${prvkey}" ]; then + echo "TLS cert missing?" + echo "Abort." + exit 2 +fi + +cat $XCONF |jq --arg port "${port}" --arg fullchain "${fullchain}" --arg prvkey "${prvkey}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings.tlsSettings ) += {"certificates":[{"certificateFile":$fullchain, "keyFile":$prvkey}]} ' \ +|sponge $XCONF diff --git a/server-ttt.sh b/server-ttt.sh new file mode 100755 index 0000000..ce9bdc0 --- /dev/null +++ b/server-ttt.sh @@ -0,0 +1,155 @@ +#!/bin/bash + +usage() { + echo "Usage: server-ttt ,,,,[,fallback=www.baidu.com:443:/html][,fallback=:2443:/websocket2]" +} + +options=(`echo $1 |tr ',' ' '`) +for option in "${options[@]}" +do + kv=(`echo $option |tr '=' ' '`) + case "${kv[0]}" in + x|xconf) + xconf="${kv[1]}" + ;; + c|certpath) + certpath+=("${kv[1]}") + ;; + p|port) + port="${kv[1]}" + ;; + d|domain) + domain="${kv[1]}" + ;; + u|user) + xuser+=("${kv[1]}") + ;; + f|fallback) + fallback+=("${kv[1]}") + ;; + esac +done + +if [ -z "${certpath}" ]; then + echo "Error: certpath undefined." + usage + exit 1 +fi + +if [ -z "${xconf}" ]; then + echo "Error: xconf undefined." + usage + exit 1 +fi + +if [ -z "${port}" ]; then + echo "Error: port undefined." + usage + exit 1 +fi + +if [ -z "${domain}" ]; then + echo "Error: domain undefined." + usage + exit 1 +fi + +if [ -z "${xuser}" ]; then + echo "Error: user undefined." + usage + exit 1 +fi + +XCONF=$xconf +cat $XCONF |jq --arg port "${port}" '.inbounds +=[{"port":($port|tonumber), "protocol":"trojan", "settings":{"clients":[]}}]' |sponge $XCONF + +for xu in "${xuser[@]}" +do + IFS=':' + uopt=(${xu}) + uopt=(${uopt[@]}) + + if [ -z "${uopt[0]}" ]; then + echo "Incorrect user format: ${xu}" + echo "Correct user format: user=[:email]" + echo "Like: user=trojan_pass:me@g.cn" + echo "Like: user=trojan_pass" + exit 1 + fi + if [ -z "${uopt[1]}" ]; then + uopt[1]="nobody@g.cn" + fi + cat $XCONF |jq --arg port "${port}" --arg password "${uopt[0]}" --arg email "${uopt[1]}" \ + '( .inbounds[] | select(.port == ($port|tonumber)) | .settings.clients ) += [ {"password":$password, "email":$email} ] ' \ + |sponge $XCONF +done + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .settings.decryption ) += "none" ' \ +|sponge $XCONF + +for fb in "${fallback[@]}" +do + IFS=':' + fopt=(${fb}) + fopt=(${fopt[@]}) + + fhost="${fopt[0]}" + fport="${fopt[1]}" + fpath="${fopt[2]}" + + if [ -z "${fport}" ]; then + echo "Incorrect fallback format: ${fb}" + echo "Correct fallback: fallback=[host]<:port>[:path]" + echo "Like: fallback=baidu.com:443:/websocket" + echo "Like: fallback=:1443:/websocket" + echo "Like: fallback=:1443" + exit 1 + fi + + if [ -z "${fhost}" ]; then + if [ -z "${fpath}" ]; then + Jfallback=`echo '{}' |jq --arg fport "${fport}" --arg fpath "${fpath}" '. += {"dest":($fport|tonumber), "xver":1}'` + else + Jfallback=`echo '{}' |jq --arg fport "${fport}" --arg fpath "${fpath}" '. += {"dest":($fport|tonumber), "path":$fpath, "xver":1}'` + fi + else + if [ -z "${fpath}" ]; then + fdest="${fhost}:${fport}" + Jfallback=`echo '{}' |jq --arg fdest "${fdest}" --arg fpath "${fpath}" '. += {"dest":$fdest, "xver":1}'` + else + Jfallback=`echo '{}' |jq --arg fdest "${fdest}" --arg fpath "${fpath}" '. += {"dest":$fdest, "path":$fpath, "xver":1}'` + fi + fi + + cat $XCONF |jq --arg port "${port}" --argjson jfallback "$Jfallback" \ + '( .inbounds[] | select(.port == ($port|tonumber)) | .settings.fallbacks ) += [ $jfallback ] ' \ + |sponge $XCONF +done + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings ) += {"network":"tcp", "security":"tls" } ' \ +|sponge $XCONF + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings ) += {"tlsSettings":{"alpn":["http/1.1"]} } ' \ +|sponge $XCONF + +for certroot in "${certpath[@]}" +do + if [ -f "${certroot}/${domain}/fullchain.cer" ] && [ -f "${certroot}/${domain}/${domain}.key" ]; then + fullchain="${certroot}/${domain}/fullchain.cer" + prvkey="${certroot}/${domain}/${domain}.key" + break + fi +done + +if [ ! -f "${fullchain}" ] || [ ! -f "${prvkey}" ]; then + echo "TLS cert missing?" + echo "Abort." + exit 2 +fi + +cat $XCONF |jq --arg port "${port}" --arg fullchain "${fullchain}" --arg prvkey "${prvkey}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings.tlsSettings ) += {"certificates":[{"certificateFile":$fullchain, "keyFile":$prvkey}]} ' \ +|sponge $XCONF diff --git a/server-tttw.sh b/server-tttw.sh new file mode 100755 index 0000000..9ebbf17 --- /dev/null +++ b/server-tttw.sh @@ -0,0 +1,168 @@ +#!/bin/bash + +usage() { + echo "Usage: server-tttw ,,,,,[,fallback=www.baidu.com:443:/html][,fallback=:2443:/websocket2]" +} + +options=(`echo $1 |tr ',' ' '`) +for option in "${options[@]}" +do + kv=(`echo $option |tr '=' ' '`) + case "${kv[0]}" in + x|xconf) + xconf="${kv[1]}" + ;; + c|certpath) + certpath+=("${kv[1]}") + ;; + p|port) + port="${kv[1]}" + ;; + d|domain) + domain="${kv[1]}" + ;; + u|user) + xuser+=("${kv[1]}") + ;; + w|wpath) + wspath="${kv[1]}" + ;; + f|fallback) + fallback+=("${kv[1]}") + ;; + esac +done + +if [ -z "${certpath}" ]; then + echo "Error: certpath undefined." + usage + exit 1 +fi + +if [ -z "${xconf}" ]; then + echo "Error: xconf undefined." + usage + exit 1 +fi + +if [ -z "${port}" ]; then + echo "Error: port undefined." + usage + exit 1 +fi + +if [ -z "${domain}" ]; then + echo "Error: domain undefined." + usage + exit 1 +fi + +if [ -z "${xuser}" ]; then + echo "Error: user undefined." + usage + exit 1 +fi + +if [ -z "${wspath}" ]; then + echo "Error: wspath undefined." + usage + exit 1 +fi + +XCONF=$xconf +cat $XCONF |jq --arg port "${port}" '.inbounds +=[{"port":($port|tonumber), "protocol":"trojan", "settings":{"clients":[]}}]' |sponge $XCONF + +for xu in "${xuser[@]}" +do + IFS=':' + uopt=(${xu}) + uopt=(${uopt[@]}) + + if [ -z "${uopt[0]}" ]; then + echo "Incorrect user format: ${xu}" + echo "Correct user format: user=[:email]" + echo "Like: user=trojan_pass:me@g.cn" + echo "Like: user=trojan_pass" + exit 1 + fi + if [ -z "${uopt[1]}" ]; then + uopt[1]="nobody@g.cn" + fi + cat $XCONF |jq --arg port "${port}" --arg password "${uopt[0]}" --arg email "${uopt[1]}" \ + '( .inbounds[] | select(.port == ($port|tonumber)) | .settings.clients ) += [ {"password":$password, "email":$email} ] ' \ + |sponge $XCONF +done + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .settings.decryption ) += "none" ' \ +|sponge $XCONF + +for fb in "${fallback[@]}" +do + IFS=':' + fopt=(${fb}) + fopt=(${fopt[@]}) + + fhost="${fopt[0]}" + fport="${fopt[1]}" + fpath="${fopt[2]}" + + if [ -z "${fport}" ]; then + echo "Incorrect fallback format: ${fb}" + echo "Correct fallback: fallback=[host]<:port>[:path]" + echo "Like: fallback=baidu.com:443:/websocket" + echo "Like: fallback=:1443:/websocket" + echo "Like: fallback=:1443" + exit 1 + fi + + if [ -z "${fhost}" ]; then + if [ -z "${fpath}" ]; then + Jfallback=`echo '{}' |jq --arg fport "${fport}" --arg fpath "${fpath}" '. += {"dest":($fport|tonumber), "xver":1}'` + else + Jfallback=`echo '{}' |jq --arg fport "${fport}" --arg fpath "${fpath}" '. += {"dest":($fport|tonumber), "path":$fpath, "xver":1}'` + fi + else + if [ -z "${fpath}" ]; then + fdest="${fhost}:${fport}" + Jfallback=`echo '{}' |jq --arg fdest "${fdest}" --arg fpath "${fpath}" '. += {"dest":$fdest, "xver":1}'` + else + Jfallback=`echo '{}' |jq --arg fdest "${fdest}" --arg fpath "${fpath}" '. += {"dest":$fdest, "path":$fpath, "xver":1}'` + fi + fi + + cat $XCONF |jq --arg port "${port}" --argjson jfallback "$Jfallback" \ + '( .inbounds[] | select(.port == ($port|tonumber)) | .settings.fallbacks ) += [ $jfallback ] ' \ + |sponge $XCONF +done + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings ) += {"network":"ws", "security":"tls" } ' \ +|sponge $XCONF + +cat $XCONF |jq --arg port "${port}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings ) += {"tlsSettings":{"alpn":["http/1.1"]} } ' \ +|sponge $XCONF + +cat $XCONF |jq --arg port "${port}" --arg wspath "${wspath}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings ) += {"wsSettings":{"path":$wspath}} ' \ +|sponge $XCONF + +for certroot in "${certpath[@]}" +do + if [ -f "${certroot}/${domain}/fullchain.cer" ] && [ -f "${certroot}/${domain}/${domain}.key" ]; then + fullchain="${certroot}/${domain}/fullchain.cer" + prvkey="${certroot}/${domain}/${domain}.key" + break + fi +done + +if [ ! -f "${fullchain}" ] || [ ! -f "${prvkey}" ]; then + echo "TLS cert missing?" + echo "Abort." + exit 2 +fi + +cat $XCONF |jq --arg port "${port}" --arg fullchain "${fullchain}" --arg prvkey "${prvkey}" \ +'( .inbounds[] | select(.port == ($port|tonumber)) | .streamSettings.tlsSettings ) += {"certificates":[{"certificateFile":$fullchain, "keyFile":$prvkey}]} ' \ +|sponge $XCONF