#!/usr/bin/env bash # TCPeasy bootstrap 1.0.0. Pinned release; no credentials leave the target server. set -euo pipefail base=https://tcpeasy.zhln.dev server=''; port=''; sni=''; target='' fail() { printf 'TCPeasy: %s\n' "$*" >&2; exit 1; } while (($#)); do case "$1" in --server|--port|--sni|--target) (($# >= 2)) || fail "Missing value for $1" case "$1" in --server) server=$2;; --port) port=$2;; --sni) sni=$2;; --target) target=$2;; esac shift 2;; --help) echo 'Run as root on Debian/Ubuntu with systemd. Options: --server IPv4 --port 443 --sni one.one.one.one --target 1.1.1.1:443'; exit 0;; *) fail "Unknown option: $1";; esac done [[ $(uname -s) == Linux && $(id -u) == 0 ]] || fail 'Run this command as root on the Linux server, not on your Mac.' [[ -d /run/systemd/system ]] || fail 'Requires a running systemd (Debian/Ubuntu VPS).' command -v curl >/dev/null || fail 'Install curl first: apt-get update && apt-get install -y curl ca-certificates' case $(uname -m) in x86_64) platform=linux-64; xhash=0eec3ed4163c273d8c8bf821f7d219e257921cf584aa3fb970715e458e631978;; aarch64|arm64) platform=linux-arm64-v8a; xhash=d1304946c486d98e6b886e9b931eaaaebb23835b30e217638037b7f2721d4e9c;; *) fail 'Supported architectures: x86_64 and arm64.';; esac ipv4() { local a b c d rest [[ $1 =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] || return 1 IFS=. read -r a b c d rest <<< "$1" local n for n in "$a" "$b" "$c" "$d"; do [[ ${#n} -le 3 ]] && ((10#$n <= 255)) || return 1; done } [[ -z $server ]] || ipv4 "$server" || fail 'Invalid --server IPv4.' [[ -z $port ]] || { [[ $port =~ ^[0-9]{1,5}$ ]] && ((10#$port >= 1 && 10#$port <= 65535)); } || fail 'Port must be 1–65535.' [[ -z $sni || $sni =~ ^[A-Za-z0-9.-]{1,253}$ ]] || fail 'Invalid SNI hostname.' [[ -z $target || $target =~ ^[A-Za-z0-9.-]{1,253}:[0-9]{1,5}$ ]] || fail 'Target must be hostname:port.' if [[ -n $target ]]; then target_port=${target##*:} ((10#$target_port >= 1 && 10#$target_port <= 65535)) || fail 'Invalid target port.' fi # Reinstall keeps the current endpoint if no explicit override is supplied. if [[ -f /etc/tcpeasy/client.private.json ]] && command -v python3 >/dev/null; then [[ -n $server ]] || server=$(python3 -c 'import json; print(json.load(open("/etc/tcpeasy/client.private.json"))["server"])') [[ -n $port ]] || port=$(python3 -c 'import json; print(json.load(open("/etc/tcpeasy/client.private.json"))["port"])') fi fetch() { curl --proto '=https' --tlsv1.2 --fail --show-error --silent --location --connect-timeout 10 --max-time 180 --retry 2 "$1" -o "$2"; } if [[ -z $server ]]; then for endpoint in https://api.ipify.org https://ipv4.icanhazip.com; do server=$(curl -4 --proto '=https' --fail --silent --show-error --connect-timeout 5 --max-time 10 "$endpoint" 2>/dev/null) || server='' ipv4 "$server" && break done fi ipv4 "$server" || fail 'Cannot detect public IPv4. Add --server YOUR_SERVER_IP.' port=${port:-443}; sni=${sni:-one.one.one.one}; target=${target:-1.1.1.1:443} [[ $port =~ ^[0-9]{1,5}$ ]] && ((10#$port >= 1 && 10#$port <= 65535)) || fail 'Invalid port.' work=$(mktemp -d /var/tmp/tcpeasy-download.XXXXXXXX) chmod 700 "$work" trap 'rm -rf "$work"' EXIT printf 'Downloading TCPeasy 1.3.0 and Xray 26.10.21 for %s…\n' "$platform" fetch "$base/releases/1.3.0/TCPeasy-server-1.3.0.tar.gz" "$work/server.tar.gz" printf '%s %s\n' 42933fc173b0852913da201aa53a48ffde375db333831064f876bf499be6b5cb "$work/server.tar.gz" | sha256sum --check --status || fail 'Server archive checksum mismatch.' fetch "$base/releases/xray/26.10.21/Xray-$platform.zip" "$work/xray.zip" printf '%s %s\n' "$xhash" "$work/xray.zip" | sha256sum --check --status || fail 'Xray checksum mismatch.' mkdir "$work/server" tar -xzf "$work/server.tar.gz" -C "$work/server" --no-same-owner TCPEASY_XRAY_ARCHIVE="$work/xray.zip" bash "$work/server/install.sh" --server "$server" --port "$port" --sni "$sni" --target "$target" printf '\nReady. Copy the vless:// link into TCPeasy on Mac and select TCPeasy mode.\nShow the link again: tcpeasy key\n'