From c40a4daef47f3ed76e5bece495eb88e329db0012 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=94=AC=E5=93=A5=E4=BE=83=E4=BE=83=E4=BE=83ygkkk?= <121604513+yonggekkk@users.noreply.github.com> Date: Mon, 30 Dec 2024 16:33:33 +0800 Subject: [PATCH] Create serv00.sh --- serv00.sh | 1155 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1155 insertions(+) create mode 100644 serv00.sh diff --git a/serv00.sh b/serv00.sh new file mode 100644 index 0000000..eebadd4 --- /dev/null +++ b/serv00.sh @@ -0,0 +1,1155 @@ +#!/bin/bash + +# 定义颜色 +re="\033[0m" +red="\033[1;91m" +green="\e[1;32m" +yellow="\e[1;33m" +purple="\e[1;35m" +red() { echo -e "\e[1;91m$1\033[0m"; } +green() { echo -e "\e[1;32m$1\033[0m"; } +yellow() { echo -e "\e[1;33m$1\033[0m"; } +purple() { echo -e "\e[1;35m$1\033[0m"; } +reading() { read -p "$(red "$1")" "$2"; } +USERNAME=$(whoami) +HOSTNAME=$(hostname) +[[ "$HOSTNAME" == "s1.ct8.pl" ]] && export WORKDIR="domains/${USERNAME}.ct8.pl/logs" || export WORKDIR="domains/${USERNAME}.serv00.net/logs" +[ -d "$WORKDIR" ] || (mkdir -p "$WORKDIR" && chmod 777 "$WORKDIR") +#ps aux | grep $(whoami) | grep -v "sshd\|bash\|grep" | awk '{print $2}' | xargs -r kill -9 > /dev/null 2>&1 + +read_ip() { +cat ip.txt +reading "请输入上面三个IP中的任意一个 (建议默认回车自动选择可用IP): " IP +if [[ -z "$IP" ]]; then +IP=$(grep -m 1 "可用" ip.txt | awk -F ':' '{print $1}') +if [ -z "$IP" ]; then +IP=$(head -n 1 ip.txt | awk -F ':' '{print $1}') +fi +fi +green "你选择的IP为: $IP" +} + +read_uuid() { + reading "请输入统一的uuid密码 (建议回车默认随机): " UUID + if [[ -z "$UUID" ]]; then + UUID=$(uuidgen -r) + fi + green "你的uuid为: $UUID" +} + +read_reym() { + yellow "方式一:回车使用CF域名,支持proxyip+非标端口反代ip功能 (推荐)" + yellow "方式二:输入 s 表示使用Serv00自带域名,不支持proxyip功能 (推荐)" + yellow "方式三:支持其他域名,注意要符合reality域名规则" + reading "请输入reality域名 【请选择 回车 或者 s 或者 输入域名】: " reym + if [[ -z "$reym" ]]; then + reym=www.speedtest.net + elif [[ "$reym" == "s" || "$reym" == "S" ]]; then + reym=$USERNAME.serv00.net + fi + green "你的reality域名为: $reym" +} + +read_vless_port() { + while true; do + reading "请输入vless-reality端口 (面板开放的tcp端口): " vless_port + if [[ "$vless_port" =~ ^[0-9]+$ ]] && [ "$vless_port" -ge 1 ] && [ "$vless_port" -le 65535 ]; then + green "你的vless-reality端口为: $vless_port" + break + else + yellow "输入错误,请重新输入面板开放的TCP端口" + fi + done +} + +read_hy2_port() { + while true; do + reading "请输入hysteria2端口 (面板开放的UDP端口): " hy2_port + if [[ "$hy2_port" =~ ^[0-9]+$ ]] && [ "$hy2_port" -ge 1 ] && [ "$hy2_port" -le 65535 ]; then + green "你的hysteria2端口为: $hy2_port" + break + else + yellow "输入错误,请重新输入面板开放的UDP端口" + fi + done +} + +read_vmess_port() { + while true; do + reading "请输入vmess-ws端口 (面板开放的tcp端口): " vmess_port + if [[ "$vmess_port" =~ ^[0-9]+$ ]] && [ "$vmess_port" -ge 1 ] && [ "$vmess_port" -le 65535 ]; then + green "你的vmess端口为: $vmess_port" + break + else + yellow "输入错误,请重新输入面板开放的tcp端口" + fi + done +} + +install_singbox() { +if [[ -e $WORKDIR/list.txt ]]; then +yellow "已安装sing-box,请先选择2卸载,再执行安装" && exit +fi +yellow "请确保在Serv00网页设置中已开放3个端口:2个tcp端口、1个udp端口" +sleep 2 + cd $WORKDIR + echo + read_ip + echo + read_reym + echo + read_uuid + echo + read_vless_port + echo + read_vmess_port + echo + read_hy2_port + echo + sleep 2 + argo_configure + echo + download_and_run_singbox + get_links +} + +uninstall_singbox() { + reading "\n确定要卸载吗?【y/n】: " choice + case "$choice" in + [Yy]) + ps aux | grep $(whoami) | grep -v "sshd\|bash\|grep" | awk '{print $2}' | xargs -r kill -9 2>/dev/null + rm -rf $WORKDIR serv00.sh + crontab -l | grep -v "serv00keep" >rmcron + crontab rmcron >/dev/null 2>&1 + rm rmcron + + clear + green "已完全卸载" + ;; + [Nn]) exit 0 ;; + *) red "无效的选择,请输入y或n" && menu ;; + esac +} + +kill_all_tasks() { +reading "\n清理所有进程并清空所有安装内容,将退出ssh连接,确定继续清理吗?【y/n】: " choice + case "$choice" in + [Yy]) + ps aux | grep $(whoami) | grep -v "sshd\|bash\|grep" | awk '{print $2}' | xargs -r kill -9 2>/dev/null + crontab -l | grep -v "serv00keep" >rmcron + crontab rmcron >/dev/null 2>&1 + rm rmcron + find ~ -type f -exec chmod 644 {} \; 2>/dev/null + find ~ -type d -exec chmod 755 {} \; 2>/dev/null + find ~ -type f -exec rm -f {} \; 2>/dev/null + find ~ -type d -empty -exec rmdir {} \; 2>/dev/null + find ~ -exec rm -rf {} \; 2>/dev/null + killall -9 -u $(whoami) + ;; + *) menu ;; + esac +} + +# Generating argo Config +argo_configure() { + while true; do + yellow "方式一:Argo临时隧道 (无需域名,推荐)" + yellow "方式二:Argo固定隧道 (需要域名,需要CF设置提取Token)" + echo -e "${red}注意:${purple}Argo固定隧道使用Token时,需要在cloudflare后台设置隧道端口,该端口必须与vmess-ws的tcp端口一致)${re}" + reading "输入 g 表示使用Argo固定隧道,回车跳过表示使用Argo临时隧道 【请选择 g 或者 回车】: " argo_choice + if [[ "$argo_choice" != "g" && "$argo_choice" != "G" && -n "$argo_choice" ]]; then + red "无效的选择,请输入 g 或回车" + continue + fi + if [[ "$argo_choice" == "g" || "$argo_choice" == "G" ]]; then + reading "请输入argo固定隧道域名: " ARGO_DOMAIN + green "你的argo固定隧道域名为: $ARGO_DOMAIN" + reading "请输入argo固定隧道密钥(Json或Token。当你粘贴Token时,必须以ey开头): " ARGO_AUTH + green "你的argo固定隧道密钥为: $ARGO_AUTH" + else + green "使用Argo临时隧道" + fi + break +done + + if [[ $ARGO_AUTH =~ TunnelSecret ]]; then + echo $ARGO_AUTH > tunnel.json + cat > tunnel.yml << EOF +tunnel: $(cut -d\" -f12 <<< "$ARGO_AUTH") +credentials-file: tunnel.json +protocol: http2 + +ingress: + - hostname: $ARGO_DOMAIN + service: http://localhost:$vmess_port + originRequest: + noTLSVerify: true + - service: http_status:404 +EOF + fi +} + +# Download Dependency Files +download_and_run_singbox() { + ARCH=$(uname -m) && DOWNLOAD_DIR="." && mkdir -p "$DOWNLOAD_DIR" && FILE_INFO=() + if [ "$ARCH" == "arm" ] || [ "$ARCH" == "arm64" ] || [ "$ARCH" == "aarch64" ]; then + FILE_INFO=("https://github.com/eooce/test/releases/download/arm64/sb web" "https://github.com/eooce/test/releases/download/arm64/bot13 bot") + elif [ "$ARCH" == "amd64" ] || [ "$ARCH" == "x86_64" ] || [ "$ARCH" == "x86" ]; then + FILE_INFO=("https://github.com/yonggekkk/Cloudflare_vless_trojan/releases/download/serv00/sb web" "https://github.com/yonggekkk/Cloudflare_vless_trojan/releases/download/serv00/server bot") + else + echo "Unsupported architecture: $ARCH" + exit 1 + fi +declare -A FILE_MAP +generate_random_name() { + local chars=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890 + local name="" + for i in {1..6}; do + name="$name${chars:RANDOM%${#chars}:1}" + done + echo "$name" +} + +download_with_fallback() { + local URL=$1 + local NEW_FILENAME=$2 + + curl -L -sS --max-time 2 -o "$NEW_FILENAME" "$URL" & + CURL_PID=$! + CURL_START_SIZE=$(stat -c%s "$NEW_FILENAME" 2>/dev/null || echo 0) + + sleep 1 + CURL_CURRENT_SIZE=$(stat -c%s "$NEW_FILENAME" 2>/dev/null || echo 0) + + if [ "$CURL_CURRENT_SIZE" -le "$CURL_START_SIZE" ]; then + kill $CURL_PID 2>/dev/null + wait $CURL_PID 2>/dev/null + wget -q -O "$NEW_FILENAME" "$URL" + echo -e "\e[1;32mDownloading $NEW_FILENAME by wget\e[0m" + else + wait $CURL_PID + echo -e "\e[1;32mDownloading $NEW_FILENAME by curl\e[0m" + fi +} + +for entry in "${FILE_INFO[@]}"; do + URL=$(echo "$entry" | cut -d ' ' -f 1) + RANDOM_NAME=$(generate_random_name) + NEW_FILENAME="$DOWNLOAD_DIR/$RANDOM_NAME" + + if [ -e "$NEW_FILENAME" ]; then + echo -e "\e[1;32m$NEW_FILENAME already exists, Skipping download\e[0m" + else + download_with_fallback "$URL" "$NEW_FILENAME" + fi + + chmod +x "$NEW_FILENAME" + FILE_MAP[$(echo "$entry" | cut -d ' ' -f 2)]="$NEW_FILENAME" +done +wait + +output=$(./"$(basename ${FILE_MAP[web]})" generate reality-keypair) +private_key=$(echo "${output}" | awk '/PrivateKey:/ {print $2}') +public_key=$(echo "${output}" | awk '/PublicKey:/ {print $2}') +echo "${private_key}" > private_key.txt +echo "${public_key}" > public_key.txt + +openssl ecparam -genkey -name prime256v1 -out "private.key" +openssl req -new -x509 -days 3650 -key "private.key" -out "cert.pem" -subj "/CN=$USERNAME.serv00.net" + +nb=$(hostname | cut -d '.' -f 1 | tr -d 's') +if [ "$nb" == "14" ]; then +ytb='"jnn-pa.googleapis.com",' +fi + + cat > config.json << EOF +{ + "log": { + "disabled": true, + "level": "info", + "timestamp": true + }, + "inbounds": [ + { + "tag": "hysteria-in", + "type": "hysteria2", + "listen": "$IP", + "listen_port": $hy2_port, + "users": [ + { + "password": "$UUID" + } + ], + "masquerade": "https://www.bing.com", + "ignore_client_bandwidth":false, + "tls": { + "enabled": true, + "alpn": [ + "h3" + ], + "certificate_path": "cert.pem", + "key_path": "private.key" + } + }, + { + "tag": "vless-reality-vesion", + "type": "vless", + "listen": "::", + "listen_port": $vless_port, + "users": [ + { + "uuid": "$UUID", + "flow": "xtls-rprx-vision" + } + ], + "tls": { + "enabled": true, + "server_name": "$reym", + "reality": { + "enabled": true, + "handshake": { + "server": "$reym", + "server_port": 443 + }, + "private_key": "$private_key", + "short_id": [ + "" + ] + } + } + }, +{ + "tag": "vmess-ws-in", + "type": "vmess", + "listen": "::", + "listen_port": $vmess_port, + "users": [ + { + "uuid": "$UUID" + } + ], + "transport": { + "type": "ws", + "path": "$UUID-vm", + "early_data_header_name": "Sec-WebSocket-Protocol" + } + } + ], + "outbounds": [ + { + "type": "wireguard", + "tag": "wg", + "server": "162.159.192.200", + "server_port": 4500, + "local_address": [ + "172.16.0.2/32", + "2606:4700:110:8f77:1ca9:f086:846c:5f9e/128" + ], + "private_key": "wIxszdR2nMdA7a2Ul3XQcniSfSZqdqjPb6w6opvf5AU=", + "peer_public_key": "bmXOC+F1FxEMF9dyiK2H5/1SUtzH0JuVo51h2wPfgyo=", + "reserved": [ + 126, + 246, + 173 + ] + }, + { + "type": "direct", + "tag": "direct" + }, + { + "type": "block", + "tag": "block" + } + ], + "route": { + "rules": [ + { + "domain": [ + $ytb + "oh.my.god" + ], + "outbound": "wg" + } + ], + "final": "direct" + } +} +EOF + +if [ -e "$(basename ${FILE_MAP[web]})" ]; then + echo "$(basename ${FILE_MAP[web]})" > sb.txt + sbb=$(cat sb.txt) + nohup ./"$sbb" run -c config.json >/dev/null 2>&1 & + sleep 5 +if pgrep -x "$sbb" > /dev/null; then + green "$sbb 主进程已启动" +else +for ((i=1; i<=5; i++)); do + red "$sbb 主进程未启动, 重启中... (尝试次数: $i)" + pkill -x "$sbb" + nohup ./"$sbb" run -c config.json >/dev/null 2>&1 & + sleep 5 + if pgrep -x "$sbb" > /dev/null; then + purple "$sbb 主进程已成功重启" + break + fi + if [[ $i -eq 5 ]]; then + red "$sbb 主进程重启失败" + fi +done +fi +fi + +if [ -e "$(basename ${FILE_MAP[bot]})" ]; then + echo "$(basename ${FILE_MAP[bot]})" > ag.txt + agg=$(cat ag.txt) + rm -rf boot.log + if [[ $ARGO_AUTH =~ ^[A-Z0-9a-z=]{120,250}$ ]]; then + args="tunnel --edge-ip-version auto --no-autoupdate --protocol http2 run --token ${ARGO_AUTH}" + elif [[ $ARGO_AUTH =~ TunnelSecret ]]; then + args="tunnel --edge-ip-version auto --config tunnel.yml run" + else + args="tunnel --edge-ip-version auto --no-autoupdate --protocol http2 --logfile boot.log --loglevel info --url http://localhost:$vmess_port" + fi + nohup ./"$agg" $args >/dev/null 2>&1 & + sleep 10 +if pgrep -x "$agg" > /dev/null; then + green "$agg Arog进程已启动" +else + red "$agg Argo进程未启动, 重启中..." + pkill -x "$agg" + nohup ./"$agg" "${args}" >/dev/null 2>&1 & + sleep 5 + purple "$agg Argo进程已重启" +fi +fi +sleep 2 +if pgrep -x "$sbb" > /dev/null; then +green "主进程已启动成功" +else +red "主进程未启动,根据以下情况一一排查" +yellow "1、网页端权限是否开启" +yellow "2、端口是否设置错误(2个TCP、1个UDP)" +yellow "3、尝试更换网页端3个端口并重装" +yellow "4、选择5重置" +yellow "5、当前Serv00服务器炸了?等会再试" +red "6、以上都试了,哥直接躺平,交给进程保活,过会再来看" +sleep 6 +fi +} + +get_argodomain() { + if [[ -n $ARGO_AUTH ]]; then + echo "$ARGO_DOMAIN" > gdym.log + echo "$ARGO_DOMAIN" + else + local retry=0 + local max_retries=6 + local argodomain="" + while [[ $retry -lt $max_retries ]]; do + ((retry++)) + argodomain=$(grep -oE 'https://[[:alnum:]+\.-]+\.trycloudflare\.com' boot.log 2>/dev/null | sed 's@https://@@') + if [[ -n $argodomain ]]; then + break + fi + sleep 2 + done + if [ -z ${argodomain} ]; then + argodomain="Argo临时域名暂时获取失败,Argo节点暂不可用" + fi + echo "$argodomain" + fi +} + +get_links(){ +argodomain=$(get_argodomain) +echo -e "\e[1;32mArgo域名:\e[1;35m${argodomain}\e[0m\n" +green "安装进程保活" +curl -sSL https://raw.githubusercontent.com/yonggekkk/Cloudflare_vless_trojan/main/serv00keep.sh -o serv00keep.sh && chmod +x serv00keep.sh +sed -i '' -e "18s|''|'$UUID'|" serv00keep.sh +sed -i '' -e "21s|''|'$vless_port'|" serv00keep.sh +sed -i '' -e "22s|''|'$vmess_port'|" serv00keep.sh +sed -i '' -e "23s|''|'$hy2_port'|" serv00keep.sh +sed -i '' -e "24s|''|'$IP'|" serv00keep.sh +sed -i '' -e "25s|''|'$reym'|" serv00keep.sh +if [ ! -f boot.log ]; then +sed -i '' -e "19s|''|'${ARGO_DOMAIN}'|" serv00keep.sh +sed -i '' -e "20s|''|'${ARGO_AUTH}'|" serv00keep.sh +fi +if ! crontab -l 2>/dev/null | grep -q 'serv00keep'; then +if [ -f boot.log ] || grep -q "trycloudflare.com" boot.log 2>/dev/null; then +check_process="! ps aux | grep '[c]onfig' > /dev/null || ! ps aux | grep [l]ocalhost > /dev/null" +else +check_process="! ps aux | grep '[c]onfig' > /dev/null || ! ps aux | grep [t]oken > /dev/null" +fi +(crontab -l 2>/dev/null; echo "*/2 * * * * if $check_process; then /bin/bash ${WORKDIR}/serv00keep.sh; fi") | crontab - +fi +green "主进程+Argo进程保活安装完毕,默认每2分钟执行一次,运行 crontab -e 可自行修改保活执行间隔" && sleep 2 +ISP=$(curl -sL --max-time 5 https://speed.cloudflare.com/meta | awk -F\" '{print $26}' | sed -e 's/ /_/g' || echo "0") +get_name() { if [ "$HOSTNAME" = "s1.ct8.pl" ]; then SERVER="CT8"; else SERVER=$(echo "$HOSTNAME" | cut -d '.' -f 1); fi; echo "$SERVER"; } +NAME="$ISP-$(get_name)" +rm -rf jh.txt +vl_link="vless://$UUID@$IP:$vless_port?encryption=none&flow=xtls-rprx-vision&security=reality&sni=$reym&fp=chrome&pbk=$public_key&type=tcp&headerType=none#$NAME-reality" +echo "$vl_link" >> jh.txt +vmws_link="vmess://$(echo "{ \"v\": \"2\", \"ps\": \"$NAME-vmess-ws\", \"add\": \"$IP\", \"port\": \"$vmess_port\", \"id\": \"$UUID\", \"aid\": \"0\", \"scy\": \"auto\", \"net\": \"ws\", \"type\": \"none\", \"host\": \"\", \"path\": \"/$UUID-vm?ed=2048\", \"tls\": \"\", \"sni\": \"\", \"alpn\": \"\", \"fp\": \"\"}" | base64 -w0)" +echo "$vmws_link" >> jh.txt +vmatls_link="vmess://$(echo "{ \"v\": \"2\", \"ps\": \"$NAME-vmess-ws-tls-argo\", \"add\": \"icook.hk\", \"port\": \"8443\", \"id\": \"$UUID\", \"aid\": \"0\", \"scy\": \"auto\", \"net\": \"ws\", \"type\": \"none\", \"host\": \"$argodomain\", \"path\": \"/$UUID-vm?ed=2048\", \"tls\": \"tls\", \"sni\": \"$argodomain\", \"alpn\": \"\", \"fp\": \"\"}" | base64 -w0)" +echo "$vmatls_link" >> jh.txt +vma_link="vmess://$(echo "{ \"v\": \"2\", \"ps\": \"$NAME-vmess-ws-argo\", \"add\": \"icook.hk\", \"port\": \"8880\", \"id\": \"$UUID\", \"aid\": \"0\", \"scy\": \"auto\", \"net\": \"ws\", \"type\": \"none\", \"host\": \"$argodomain\", \"path\": \"/$UUID-vm?ed=2048\", \"tls\": \"\"}" | base64 -w0)" +echo "$vma_link" >> jh.txt +hy2_link="hysteria2://$UUID@$IP:$hy2_port?sni=www.bing.com&alpn=h3&insecure=1#$NAME-hy2" +echo "$hy2_link" >> jh.txt +url=$(cat jh.txt 2>/dev/null) +baseurl=$(echo -e "$url" | base64 -w 0) +echo +sleep 2 +cat > list.txt < sing_box.json < clash_meta.yaml <> $WORKDIR/ip.txt +sleep 1 +done +break +else +echo "$response" | while IFS='|' read -r ip status; do +if [[ $status == "Accessible" ]]; then +echo "$ip: 可用" >> $WORKDIR/ip.txt +else +echo "$ip: 被墙 (Argo与CDN回源节点、proxyip依旧有效)" >> $WORKDIR/ip.txt +fi +done +fi +done +snb=$(hostname | awk -F '.' '{print $1}') +green "Serv00服务器名称:$snb" +green "当前可选择的IP如下:" +cat $WORKDIR/ip.txt +echo +if [[ -e $WORKDIR/list.txt ]]; then +green "已安装sing-box" +ps aux | grep '[c]onfig' > /dev/null && green "主进程启动正常" || red "主进程未启动,请卸载后重装脚本" +if [ -f "$WORKDIR/boot.log" ] && grep -q "trycloudflare.com" "$WORKDIR/boot.log" 2>/dev/null && ps aux | grep [l]ocalhost > /dev/null; then +green "当前Argo临时域名:$(grep -oE 'https://[[:alnum:]+\.-]+\.trycloudflare\.com' $WORKDIR/boot.log 2>/dev/null | sed 's@https://@@')" +elif ps aux | grep [t]oken > /dev/null; then +green "当前Argo固定域名:$(cat $WORKDIR/gdym.log 2>/dev/null)" +fi +if ! crontab -l 2>/dev/null | grep -q 'serv00keep'; then +if [ -f "$WORKDIR/boot.log" ] || grep -q "trycloudflare.com" "$WORKDIR/boot.log" 2>/dev/null; then +check_process="! ps aux | grep '[c]onfig' > /dev/null || ! ps aux | grep [l]ocalhost > /dev/null" +else +check_process="! ps aux | grep '[c]onfig' > /dev/null || ! ps aux | grep [t]oken > /dev/null" +fi +(crontab -l 2>/dev/null; echo "*/2 * * * * if $check_process; then /bin/bash ${WORKDIR}/serv00keep.sh; fi") | crontab - +yellow "Cron保活丢失?已修复成功" +else +green "Cron保活运行正常" +fi +else +red "未安装sing-box,请选择 1 进行安装" +fi + echo "=========================================================" + reading "请输入选择【0-5】: " choice + echo "" + case "${choice}" in + 1) install_singbox ;; + 2) uninstall_singbox ;; + 3) showlist ;; + 4) showsbclash ;; + 5) kill_all_tasks ;; + 0) exit 0 ;; + *) red "无效的选项,请输入 0 到 5" ;; + esac +} +menu