diff --git a/节点类/hysteria.sh b/节点类/hysteria.sh new file mode 100644 index 0000000..ab891b9 --- /dev/null +++ b/节点类/hysteria.sh @@ -0,0 +1,594 @@ +#!/bin/bash + +export LANG=en_US.UTF-8 + +RED="\033[31m" +GREEN="\033[32m" +YELLOW="\033[33m" +PLAIN="\033[0m" + +red(){ + echo -e "\033[31m\033[01m$1\033[0m" +} + +green(){ + echo -e "\033[32m\033[01m$1\033[0m" +} + +yellow(){ + echo -e "\033[33m\033[01m$1\033[0m" +} + +# 判断系统及定义系统安装依赖方式 +REGEX=("debian" "ubuntu" "centos|red hat|kernel|oracle linux|alma|rocky" "'amazon linux'" "fedora") +RELEASE=("Debian" "Ubuntu" "CentOS" "CentOS" "Fedora") +PACKAGE_UPDATE=("apt-get update" "apt-get update" "yum -y update" "yum -y update" "yum -y update") +PACKAGE_INSTALL=("apt -y install" "apt -y install" "yum -y install" "yum -y install" "yum -y install") +PACKAGE_REMOVE=("apt -y remove" "apt -y remove" "yum -y remove" "yum -y remove" "yum -y remove") +PACKAGE_UNINSTALL=("apt -y autoremove" "apt -y autoremove" "yum -y autoremove" "yum -y autoremove" "yum -y autoremove") + +[[ $EUID -ne 0 ]] && red "注意: 请在root用户下运行脚本" && exit 1 + +CMD=("$(grep -i pretty_name /etc/os-release 2>/dev/null | cut -d \" -f2)" "$(hostnamectl 2>/dev/null | grep -i system | cut -d : -f2)" "$(lsb_release -sd 2>/dev/null)" "$(grep -i description /etc/lsb-release 2>/dev/null | cut -d \" -f2)" "$(grep . /etc/redhat-release 2>/dev/null)" "$(grep . /etc/issue 2>/dev/null | cut -d \\ -f1 | sed '/^[ ]*$/d')") + +for i in "${CMD[@]}"; do + SYS="$i" && [[ -n $SYS ]] && break +done + +for ((int = 0; int < ${#REGEX[@]}; int++)); do + [[ $(echo "$SYS" | tr '[:upper:]' '[:lower:]') =~ ${REGEX[int]} ]] && SYSTEM="${RELEASE[int]}" && [[ -n $SYSTEM ]] && break +done + +[[ -z $SYSTEM ]] && red "目前暂不支持你的VPS的操作系统!" && exit 1 + +if [[ -z $(type -P curl) ]]; then + if [[ ! $SYSTEM == "CentOS" ]]; then + ${PACKAGE_UPDATE[int]} + fi + ${PACKAGE_INSTALL[int]} curl +fi + +realip(){ + ip=$(curl -s4m8 ip.gs -k) || ip=$(curl -s6m8 ip.gs -k) +} + +inst_cert(){ + green "Hysteria 2 协议证书申请方式如下:" + echo "" + echo -e " ${GREEN}1.${PLAIN} 必应自签证书 ${YELLOW}(默认)${PLAIN}" + echo -e " ${GREEN}2.${PLAIN} Acme 脚本自动申请" + echo -e " ${GREEN}3.${PLAIN} 自定义证书路径" + echo "" + read -rp "请输入选项 [1-3]: " certInput + if [[ $certInput == 2 ]]; then + cert_path="/root/cert.crt" + key_path="/root/private.key" + + chmod -R 777 /root + + chmod +rw /root/cert.crt + chmod +rw /root/private.key + + if [[ -f /root/cert.crt && -f /root/private.key ]] && [[ -s /root/cert.crt && -s /root/private.key ]] && [[ -f /root/ca.log ]]; then + domain=$(cat /root/ca.log) + green "检测到原有域名:$domain 的证书,正在应用" + hy_domain=$domain + else + WARPv4Status=$(curl -s4m8 https://www.cloudflare.com/cdn-cgi/trace -k | grep warp | cut -d= -f2) + WARPv6Status=$(curl -s6m8 https://www.cloudflare.com/cdn-cgi/trace -k | grep warp | cut -d= -f2) + if [[ $WARPv4Status =~ on|plus ]] || [[ $WARPv6Status =~ on|plus ]]; then + wg-quick down wgcf >/dev/null 2>&1 + systemctl stop warp-go >/dev/null 2>&1 + realip + wg-quick up wgcf >/dev/null 2>&1 + systemctl start warp-go >/dev/null 2>&1 + else + realip + fi + + read -p "请输入需要申请证书的域名:" domain + [[ -z $domain ]] && red "未输入域名,无法执行操作!" && exit 1 + green "已输入的域名:$domain" && sleep 1 + domainIP=$(dig @8.8.8.8 +time=2 +short "$domain" 2>/dev/null) + if echo $domainIP | grep -q "network unreachable\|timed out" || [[ -z $domainIP ]]; then + domainIP=$(dig @2001:4860:4860::8888 +time=2 aaaa +short "$domain" 2>/dev/null) + fi + if echo $domainIP | grep -q "network unreachable\|timed out" || [[ -z $domainIP ]] ; then + red "未解析出 IP,请检查域名是否输入有误" + yellow "是否尝试强行匹配?" + green "1. 是,将使用强行匹配" + green "2. 否,退出脚本" + read -p "请输入选项 [1-2]:" ipChoice + if [[ $ipChoice == 1 ]]; then + yellow "将尝试强行匹配以申请域名证书" + else + red "将退出脚本" + exit 1 + fi + fi + if [[ $domainIP == $ip ]]; then + ${PACKAGE_INSTALL[int]} curl wget sudo socat openssl + if [[ $SYSTEM == "CentOS" ]]; then + ${PACKAGE_INSTALL[int]} cronie + systemctl start crond + systemctl enable crond + else + ${PACKAGE_INSTALL[int]} cron + systemctl start cron + systemctl enable cron + fi + curl https://get.acme.sh | sh -s email=$(date +%s%N | md5sum | cut -c 1-16)@gmail.com + source ~/.bashrc + bash ~/.acme.sh/acme.sh --upgrade --auto-upgrade + bash ~/.acme.sh/acme.sh --set-default-ca --server letsencrypt + if [[ -n $(echo $ip | grep ":") ]]; then + bash ~/.acme.sh/acme.sh --issue -d ${domain} --standalone -k ec-256 --listen-v6 --insecure + else + bash ~/.acme.sh/acme.sh --issue -d ${domain} --standalone -k ec-256 --insecure + fi + bash ~/.acme.sh/acme.sh --install-cert -d ${domain} --key-file /root/private.key --fullchain-file /root/cert.crt --ecc + if [[ -f /root/cert.crt && -f /root/private.key ]] && [[ -s /root/cert.crt && -s /root/private.key ]]; then + echo $domain > /root/ca.log + sed -i '/--cron/d' /etc/crontab >/dev/null 2>&1 + echo "0 0 * * * root bash /root/.acme.sh/acme.sh --cron -f >/dev/null 2>&1" >> /etc/crontab + green "证书申请成功! 脚本申请到的证书 (cert.crt) 和私钥 (private.key) 文件已保存到 /root 文件夹下" + yellow "证书crt文件路径如下: /root/cert.crt" + yellow "私钥key文件路径如下: /root/private.key" + hy_domain=$domain + fi + else + red "当前域名解析的IP与当前VPS使用的真实IP不匹配" + green "建议如下:" + yellow "1. 请确保CloudFlare小云朵为关闭状态(仅限DNS), 其他域名解析或CDN网站设置同理" + yellow "2. 请检查DNS解析设置的IP是否为VPS的真实IP" + yellow "3. 脚本可能跟不上时代, 建议截图发布到GitHub Issues、GitLab Issues、论坛或TG群询问" + exit 1 + fi + fi + elif [[ $certInput == 3 ]]; then + read -p "请输入公钥文件 crt 的路径:" cert_path + yellow "公钥文件 crt 的路径:$cert_path " + read -p "请输入密钥文件 key 的路径:" key_path + yellow "密钥文件 key 的路径:$key_path " + read -p "请输入证书的域名:" domain + yellow "证书域名:$domain" + hy_domain=$domain + + chmod +rw $cert_path + chmod +rw $key_path + else + green "将使用必应自签证书作为 Hysteria 2 的节点证书" + + cert_path="/etc/hysteria/cert.crt" + key_path="/etc/hysteria/private.key" + openssl ecparam -genkey -name prime256v1 -out /etc/hysteria/private.key + openssl req -new -x509 -days 36500 -key /etc/hysteria/private.key -out /etc/hysteria/cert.crt -subj "/CN=www.bing.com" + chmod 777 /etc/hysteria/cert.crt + chmod 777 /etc/hysteria/private.key + hy_domain="www.bing.com" + domain="www.bing.com" + fi +} + +inst_port(){ + iptables -t nat -F PREROUTING >/dev/null 2>&1 + + read -p "设置 Hysteria 2 端口 [1-65535](回车则随机分配端口):" port + [[ -z $port ]] && port=$(shuf -i 2000-65535 -n 1) + until [[ -z $(ss -tunlp | grep -w udp | awk '{print $5}' | sed 's/.*://g' | grep -w "$port") ]]; do + if [[ -n $(ss -tunlp | grep -w udp | awk '{print $5}' | sed 's/.*://g' | grep -w "$port") ]]; then + echo -e "${RED} $port ${PLAIN} 端口已经被其他程序占用,请更换端口重试!" + read -p "设置 Hysteria 2 端口 [1-65535](回车则随机分配端口):" port + [[ -z $port ]] && port=$(shuf -i 2000-65535 -n 1) + fi + done + + yellow "将在 Hysteria 2 节点使用的端口是:$port" + inst_jump +} + +inst_jump(){ + green "Hysteria 2 端口使用模式如下:" + echo "" + echo -e " ${GREEN}1.${PLAIN} 单端口 ${YELLOW}(默认)${PLAIN}" + echo -e " ${GREEN}2.${PLAIN} 端口跳跃" + echo "" + read -rp "请输入选项 [1-2]: " jumpInput + if [[ $jumpInput == 2 ]]; then + read -p "设置范围端口的起始端口 (建议10000-65535之间):" firstport + read -p "设置一个范围端口的末尾端口 (建议10000-65535之间,一定要比上面起始端口大):" endport + if [[ $firstport -ge $endport ]]; then + until [[ $firstport -le $endport ]]; do + if [[ $firstport -ge $endport ]]; then + red "你设置的起始端口小于末尾端口,请重新输入起始和末尾端口" + read -p "设置范围端口的起始端口 (建议10000-65535之间):" firstport + read -p "设置一个范围端口的末尾端口 (建议10000-65535之间,一定要比上面起始端口大):" endport + fi + done + fi + iptables -t nat -A PREROUTING -p udp --dport $firstport:$endport -j DNAT --to-destination :$port + ip6tables -t nat -A PREROUTING -p udp --dport $firstport:$endport -j DNAT --to-destination :$port + netfilter-persistent save >/dev/null 2>&1 + else + red "将继续使用单端口模式" + fi +} + +inst_pwd(){ + read -p "设置 Hysteria 2 密码(回车跳过为随机字符):" auth_pwd + [[ -z $auth_pwd ]] && auth_pwd=$(date +%s%N | md5sum | cut -c 1-8) + yellow "使用在 Hysteria 2 节点的密码为:$auth_pwd" +} + +inst_site(){ + read -rp "请输入 Hysteria 2 的伪装网站地址 (去除https://) [回车世嘉maimai日本网站]:" proxysite + [[ -z $proxysite ]] && proxysite="maimai.sega.jp" + yellow "使用在 Hysteria 2 节点的伪装网站为:$proxysite" +} + +insthysteria(){ + warpv6=$(curl -s6m8 https://www.cloudflare.com/cdn-cgi/trace -k | grep warp | cut -d= -f2) + warpv4=$(curl -s4m8 https://www.cloudflare.com/cdn-cgi/trace -k | grep warp | cut -d= -f2) + if [[ $warpv4 =~ on|plus || $warpv6 =~ on|plus ]]; then + wg-quick down wgcf >/dev/null 2>&1 + systemctl stop warp-go >/dev/null 2>&1 + realip + systemctl start warp-go >/dev/null 2>&1 + wg-quick up wgcf >/dev/null 2>&1 + else + realip + fi + + if [[ ! ${SYSTEM} == "CentOS" ]]; then + ${PACKAGE_UPDATE} + fi + ${PACKAGE_INSTALL} curl wget sudo qrencode procps iptables-persistent netfilter-persistent + + wget -N https://raw.githubusercontent.com/Misaka-blog/hysteria-install/main/hy2/install_server.sh + bash install_server.sh + rm -f install_server.sh + + if [[ -f "/usr/local/bin/hysteria" ]]; then + green "Hysteria 2 安装成功!" + else + red "Hysteria 2 安装失败!" + exit 1 + fi + + # 询问用户 Hysteria 配置 + inst_cert + inst_port + inst_pwd + inst_site + + # 设置 Hysteria 配置文件 + cat << EOF > /etc/hysteria/config.yaml +listen: :$port + +tls: + cert: $cert_path + key: $key_path + +quic: + initStreamReceiveWindow: 16777216 + maxStreamReceiveWindow: 16777216 + initConnReceiveWindow: 33554432 + maxConnReceiveWindow: 33554432 + +auth: + type: password + password: $auth_pwd + +masquerade: + type: proxy + proxy: + url: https://$proxysite + rewriteHost: true +EOF + + # 确定最终入站端口范围 + if [[ -n $firstport ]]; then + last_port="$port,$firstport-$endport" + else + last_port=$port + fi + + # 给 IPv6 地址加中括号 + if [[ -n $(echo $ip | grep ":") ]]; then + last_ip="[$ip]" + else + last_ip=$ip + fi + + mkdir /root/hy + cat << EOF > /root/hy/hy-client.yaml +server: $last_ip:$last_port + +auth: $auth_pwd + +tls: + sni: $hy_domain + insecure: true + +quic: + initStreamReceiveWindow: 16777216 + maxStreamReceiveWindow: 16777216 + initConnReceiveWindow: 33554432 + maxConnReceiveWindow: 33554432 + +fastOpen: true + +socks5: + listen: 127.0.0.1:5080 + +transport: + udp: + hopInterval: 30s +EOF + cat << EOF > /root/hy/hy-client.json +{ + "server": "$last_ip:$last_port", + "auth": "$auth_pwd", + "tls": { + "sni": "$hy_domain", + "insecure": true + }, + "quic": { + "initStreamReceiveWindow": 16777216, + "maxStreamReceiveWindow": 16777216, + "initConnReceiveWindow": 33554432, + "maxConnReceiveWindow": 33554432 + }, + "fastOpen": true, + "socks5": { + "listen": "127.0.0.1:5080" + }, + "transport": { + "udp": { + "hopInterval": "30s" + } + } +} +EOF + cat < /root/hy/clash-meta.yaml +mixed-port: 7890 +external-controller: 127.0.0.1:9090 +allow-lan: false +mode: rule +log-level: debug +ipv6: true +dns: + enable: true + listen: 0.0.0.0:53 + enhanced-mode: fake-ip + nameserver: + - 8.8.8.8 + - 1.1.1.1 + - 114.114.114.114 +proxies: + - name: Misaka-Hysteria2 + type: hysteria2 + server: $last_ip + port: $port + password: $auth_pwd + sni: $hy_domain + skip-cert-verify: true +proxy-groups: + - name: Proxy + type: select + proxies: + - Misaka-Hysteria2 + +rules: + - GEOIP,CN,DIRECT + - MATCH,Proxy +EOF + url="hysteria2://$auth_pwd@$last_ip:$last_port/?insecure=1&sni=$hy_domain#Misaka-Hysteria2" + echo $url > /root/hy/url.txt + nohopurl="hysteria2://$auth_pwd@$last_ip:$port/?insecure=1&sni=$hy_domain#Misaka-Hysteria2" + echo $nohopurl > /root/hy/url-nohop.txt + + systemctl daemon-reload + systemctl enable hysteria-server + systemctl start hysteria-server + if [[ -n $(systemctl status hysteria-server 2>/dev/null | grep -w active) && -f '/etc/hysteria/config.yaml' ]]; then + green "Hysteria 2 服务启动成功" + else + red "Hysteria 2 服务启动失败,请运行 systemctl status hysteria-server 查看服务状态并反馈,脚本退出" && exit 1 + fi + red "======================================================================================" + green "Hysteria 2 代理服务安装完成" + yellow "Hysteria 2 客户端 YAML 配置文件 hy-client.yaml 内容如下,并保存到 /root/hy/hy-client.yaml" + red "$(cat /root/hy/hy-client.yaml)" + yellow "Hysteria 2 客户端 JSON 配置文件 hy-client.json 内容如下,并保存到 /root/hy/hy-client.json" + red "$(cat /root/hy/hy-client.json)" + yellow "Clash Meta 客户端配置文件已保存到 /root/hy/clash-meta.yaml" + yellow "Hysteria 2 节点分享链接如下,并保存到 /root/hy/url.txt" + red "$(cat /root/hy/url.txt)" + yellow "Hysteria 2 节点单端口的分享链接如下,并保存到 /root/hy/url.txt" + red "$(cat /root/hy/url-nohop.txt)" +} + +unsthysteria(){ + systemctl stop hysteria-server.service >/dev/null 2>&1 + systemctl disable hysteria-server.service >/dev/null 2>&1 + rm -f /lib/systemd/system/hysteria-server.service /lib/systemd/system/hysteria-server@.service + rm -rf /usr/local/bin/hysteria /etc/hysteria /root/hy /root/hysteria.sh + iptables -t nat -F PREROUTING >/dev/null 2>&1 + netfilter-persistent save >/dev/null 2>&1 + + green "Hysteria 2 已彻底卸载完成!" +} + +starthysteria(){ + systemctl start hysteria-server + systemctl enable hysteria-server >/dev/null 2>&1 +} + +stophysteria(){ + systemctl stop hysteria-server + systemctl disable hysteria-server >/dev/null 2>&1 +} + +hysteriaswitch(){ + yellow "请选择你需要的操作:" + echo "" + echo -e " ${GREEN}1.${PLAIN} 启动 Hysteria 2" + echo -e " ${GREEN}2.${PLAIN} 关闭 Hysteria 2" + echo -e " ${GREEN}3.${PLAIN} 重启 Hysteria 2" + echo "" + read -rp "请输入选项 [0-3]: " switchInput + case $switchInput in + 1 ) starthysteria ;; + 2 ) stophysteria ;; + 3 ) stophysteria && starthysteria ;; + * ) exit 1 ;; + esac +} + +changeport(){ + oldport=$(cat /etc/hysteria/config.yaml 2>/dev/null | sed -n 1p | awk '{print $2}' | awk -F ":" '{print $2}') + + read -p "设置 Hysteria 2 端口[1-65535](回车则随机分配端口):" port + [[ -z $port ]] && port=$(shuf -i 2000-65535 -n 1) + + until [[ -z $(ss -tunlp | grep -w udp | awk '{print $5}' | sed 's/.*://g' | grep -w "$port") ]]; do + if [[ -n $(ss -tunlp | grep -w udp | awk '{print $5}' | sed 's/.*://g' | grep -w "$port") ]]; then + echo -e "${RED} $port ${PLAIN} 端口已经被其他程序占用,请更换端口重试!" + read -p "设置 Hysteria 2 端口 [1-65535](回车则随机分配端口):" port + [[ -z $port ]] && port=$(shuf -i 2000-65535 -n 1) + fi + done + + sed -i "1s#$oldport#$port#g" /etc/hysteria/config.yaml + sed -i "1s#$oldport#$port#g" /root/hy/hy-client.yaml + sed -i "2s#$oldport#$port#g" /root/hy/hy-client.json + + stophysteria && starthysteria + + green "Hysteria 2 端口已成功修改为:$port" + yellow "请手动更新客户端配置文件以使用节点" + showconf +} + +changepasswd(){ + oldpasswd=$(cat /etc/hysteria/config.yaml 2>/dev/null | sed -n 15p | awk '{print $2}') + + read -p "设置 Hysteria 2 密码(回车跳过为随机字符):" passwd + [[ -z $passwd ]] && passwd=$(date +%s%N | md5sum | cut -c 1-8) + + sed -i "1s#$oldpasswd#$passwd#g" /etc/hysteria/config.yaml + sed -i "1s#$oldpasswd#$passwd#g" /root/hy/hy-client.yaml + sed -i "3s#$oldpasswd#$passwd#g" /root/hy/hy-client.json + + stophysteria && starthysteria + + green "Hysteria 2 节点密码已成功修改为:$passwd" + yellow "请手动更新客户端配置文件以使用节点" + showconf +} + +change_cert(){ + old_cert=$(cat /etc/hysteria/config.yaml | grep cert | awk -F " " '{print $2}') + old_key=$(cat /etc/hysteria/config.yaml | grep key | awk -F " " '{print $2}') + old_hydomain=$(cat /root/hy/hy-client.yaml | grep sni | awk '{print $2}') + + inst_cert + + sed -i "s!$old_cert!$cert_path!g" /etc/hysteria/config.yaml + sed -i "s!$old_key!$key_path!g" /etc/hysteria/config.yaml + sed -i "6s/$old_hydomain/$hy_domain/g" /root/hy/hy-client.yaml + sed -i "5s/$old_hydomain/$hy_domain/g" /root/hy/hy-client.json + + stophysteria && starthysteria + + green "Hysteria 2 节点证书类型已成功修改" + yellow "请手动更新客户端配置文件以使用节点" + showconf +} + +changeproxysite(){ + oldproxysite=$(cat /etc/hysteria/config.yaml | grep url | awk -F " " '{print $2}' | awk -F "https://" '{print $2}') + + inst_site + + sed -i "s#$oldproxysite#$proxysite#g" /etc/caddy/Caddyfile + + stophysteria && starthysteria + + green "Hysteria 2 节点伪装网站已成功修改为:$proxysite" +} + +changeconf(){ + green "Hysteria 2 配置变更选择如下:" + echo -e " ${GREEN}1.${PLAIN} 修改端口" + echo -e " ${GREEN}2.${PLAIN} 修改密码" + echo -e " ${GREEN}3.${PLAIN} 修改证书类型" + echo -e " ${GREEN}4.${PLAIN} 修改伪装网站" + echo "" + read -p " 请选择操作 [1-4]:" confAnswer + case $confAnswer in + 1 ) changeport ;; + 2 ) changepasswd ;; + 3 ) change_cert ;; + 4 ) changeproxysite ;; + * ) exit 1 ;; + esac +} + +showconf(){ + yellow "Hysteria 2 客户端 YAML 配置文件 hy-client.yaml 内容如下,并保存到 /root/hy/hy-client.yaml" + red "$(cat /root/hy/hy-client.yaml)" + yellow "Hysteria 2 客户端 JSON 配置文件 hy-client.json 内容如下,并保存到 /root/hy/hy-client.json" + red "$(cat /root/hy/hy-client.json)" + yellow "Clash Meta 客户端配置文件已保存到 /root/hy/clash-meta.yaml" + yellow "Hysteria 2 节点分享链接如下,并保存到 /root/hy/url.txt" + red "$(cat /root/hy/url.txt)" + yellow "Hysteria 2 节点单端口的分享链接如下,并保存到 /root/hy/url.txt" + red "$(cat /root/hy/url-nohop.txt)" +} + +update_core(){ + wget -N https://raw.githubusercontent.com/Misaka-blog/hysteria-install/main/hy2/install_server.sh + bash install_server.sh + + rm -f install_server.sh +} + +menu() { + clear + echo "#############################################################" + echo -e "# ${RED}Hysteria 2 一键安装脚本${PLAIN} #" + echo -e "# ${GREEN}作者${PLAIN}: MisakaNo の 小破站 #" + echo -e "# ${GREEN}博客${PLAIN}: https://blog.misaka.cyou #" + echo -e "# ${GREEN}GitHub 项目${PLAIN}: https://github.com/Misaka-blog #" + echo -e "# ${GREEN}GitLab 项目${PLAIN}: https://gitlab.com/Misaka-blog #" + echo -e "# ${GREEN}Telegram 频道${PLAIN}: https://t.me/misakanocchannel #" + echo -e "# ${GREEN}Telegram 群组${PLAIN}: https://t.me/misakanoc #" + echo -e "# ${GREEN}YouTube 频道${PLAIN}: https://www.youtube.com/@misaka-blog #" + echo "#############################################################" + echo "" + echo -e " ${GREEN}1.${PLAIN} 安装 Hysteria 2" + echo -e " ${GREEN}2.${PLAIN} ${RED}卸载 Hysteria 2${PLAIN}" + echo " -------------" + echo -e " ${GREEN}3.${PLAIN} 关闭、开启、重启 Hysteria 2" + echo -e " ${GREEN}4.${PLAIN} 修改 Hysteria 2 配置" + echo -e " ${GREEN}5.${PLAIN} 显示 Hysteria 2 配置文件" + echo " -------------" + echo -e " ${GREEN}6.${PLAIN} 更新 Hysteria 2 内核" + echo " -------------" + echo -e " ${GREEN}0.${PLAIN} 退出脚本" + echo "" + read -rp "请输入选项 [0-5]: " menuInput + case $menuInput in + 1 ) insthysteria ;; + 2 ) unsthysteria ;; + 3 ) hysteriaswitch ;; + 4 ) changeconf ;; + 5 ) showconf ;; + 6 ) update_core ;; + * ) exit 1 ;; + esac +} + +menu diff --git a/节点类/mt_setup.sh b/节点类/mt_setup.sh new file mode 100644 index 0000000..cb53fba --- /dev/null +++ b/节点类/mt_setup.sh @@ -0,0 +1,417 @@ +#!/bin/bash +# MTProto一键安装脚本 +# Author: palm + +RED="\033[31m" # Error message +GREEN="\033[32m" # Success message +YELLOW="\033[33m" # Warning message +BLUE="\033[36m" # Info message +PLAIN='\033[0m' + +export MTG_CONFIG="${MTG_CONFIG:-$HOME/.config/mtg}" +export MTG_ENV="$MTG_CONFIG/env" +export MTG_SECRET="$MTG_CONFIG/secret" +export MTG_CONTAINER="${MTG_CONTAINER:-mtg}" +export MTG_IMAGENAME="${MTG_IMAGENAME:-nineseconds/mtg:1}" + +DOCKER_CMD="$(command -v docker)" +OSNAME=`hostnamectl | grep -i system | cut -d: -f2` + +IP=`curl -sL -4 ip.sb` + +colorEcho() { + echo -e "${1}${@:2}${PLAIN}" +} + +checkSystem() { + result=$(id | awk '{print $1}') + if [[ $result != "uid=0(root)" ]]; then + colorEcho $RED " 请以root身份执行该脚本" + exit 1 + fi + + res=`which yum` + if [[ "$?" != "0" ]]; then + res=`which apt` + if [ "$?" != "0" ]; then + colorEcho $RED " 不受支持的Linux系统" + exit 1 + fi + res=`hostnamectl | grep -i ubuntu` + if [[ "${res}" != "" ]]; then + OS="ubuntu" + else + OS="debian" + fi + PMT="apt" + CMD_INSTALL="apt install -y " + CMD_REMOVE="apt remove -y " + else + OS="centos" + PMT="yum" + CMD_INSTALL="yum install -y " + CMD_REMOVE="yum remove -y " + fi + res=`which systemctl` + if [[ "$?" != "0" ]]; then + colorEcho $RED " 系统版本过低,请升级到最新版本" + exit 1 + fi +} + +status() { + if [[ "$DOCKER_CMD" = "" ]]; then + echo 0 + return + elif [[ ! -f $MTG_ENV ]]; then + echo 1 + return + fi + port=`grep MTG_PORT $MTG_ENV|cut -d= -f2` + if [[ -z "$port" ]]; then + echo 2 + return + fi + res=`ss -ntlp| grep ${port} | grep docker` + if [[ -z "$res" ]]; then + echo 3 + else + echo 4 + fi +} + +statusText() { + res=`status` + case $res in + 3) + echo -e ${GREEN}已安装${PLAIN} ${RED}未运行${PLAIN} + ;; + 4) + echo -e ${GREEN}已安装${PLAIN} ${GREEN}正在运行${PLAIN} + ;; + *) + echo -e ${RED}未安装${PLAIN} + ;; + esac +} + +getData() { + read -p " 请输入MTProto端口[100-65535的一个数字]:" PORT + [[ -z "${PORT}" ]] && { + echo -e " ${RED}请输入MTProto端口!${PLAIN}" + exit 1 + } + if [[ "${PORT:0:1}" = "0" ]]; then + echo -e " ${RED}端口不能以0开头${PLAIN}" + exit 1 + fi + MTG_PORT=$PORT + mkdir -p $MTG_CONFIG + echo "MTG_IMAGENAME=$MTG_IMAGENAME" > "$MTG_ENV" + echo "MTG_PORT=$MTG_PORT" >> "$MTG_ENV" + echo "MTG_CONTAINER=$MTG_CONTAINER" >> "$MTG_ENV" +} + +installDocker() { + if [[ "$DOCKER_CMD" != "" ]]; then + systemctl enable docker + systemctl start docker + selinux + return + fi + + #$CMD_REMOVE docker docker-engine docker.io containerd runc + $PMT clean all + $CMD_INSTALL wget curl + if [[ $PMT = "apt" ]]; then + apt clean all + apt-get -y install \ + apt-transport-https \ + ca-certificates \ + curl \ + gnupg-agent \ + software-properties-common + curl -fsSL https://download.docker.com/linux/$OS/gpg | apt-key add - + add-apt-repository \ + "deb [arch=amd64] https://download.docker.com/linux/$OS \ + $(lsb_release -cs) \ + stable" + apt update + else + wget -O /etc/yum.repos.d/docker-ce.repo https://download.docker.com/linux/centos/docker-ce.repo + yum clean all + fi + $CMD_INSTALL docker-ce docker-ce-cli containerd.io + + DOCKER_CMD="$(command -v docker)" + if [[ "$DOCKER_CMD" = "" ]]; then + echo -e " ${RED}$OSNAME docker安装失败,请到https://tizi.blog反馈${PLAIN}" + exit 1 + fi + systemctl enable docker + systemctl start docker + + selinux +} + +pullImage() { + if [[ "$DOCKER_CMD" = "" ]]; then + echo -e " ${RED}MTProto未安装,请先安装!${PLAIN}" + exit 1 + fi + + set -a + source "$MTG_ENV" + set +a + + $DOCKER_CMD pull "$MTG_IMAGENAME" > /dev/null +} + +selinux() { + if [[ -s /etc/selinux/config ]] && grep 'SELINUX=enforcing' /etc/selinux/config; then + sed -i 's/SELINUX=enforcing/SELINUX=permissive/g' /etc/selinux/config + setenforce 0 + fi +} + +firewall() { + port=$1 + systemctl status firewalld > /dev/null 2>&1 + if [[ $? -eq 0 ]];then + firewall-cmd --permanent --add-port=$port/tcp + firewall-cmd --reload + else + nl=`iptables -nL | nl | grep FORWARD | awk '{print $1}'` + if [ "$nl" != "3" ]; then + iptables -I INPUT -p tcp --dport=$port -j ACCEPT + else + res=`ufw status | grep -i inactive` + if [ "$res" = "" ]; then + ufw allow $port/tcp + fi + fi + fi +} + +start() { + res=`status` + if [[ $res -lt 3 ]]; then + echo -e " ${RED}MTProto未安装,请先安装!${PLAIN}" + return + fi + + set -a + source "$MTG_ENV" + set +a + + if [[ ! -f "$MTG_SECRET" ]]; then + $DOCKER_CMD run \ + --rm \ + "$MTG_IMAGENAME" \ + generate-secret tls -c "$(openssl rand -hex 16).com" \ + > "$MTG_SECRET" + fi + + $DOCKER_CMD ps --filter "Name=$MTG_CONTAINER" -aq | xargs -r $DOCKER_CMD rm -fv > /dev/null + $DOCKER_CMD run \ + -d \ + --restart=unless-stopped \ + --name "$MTG_CONTAINER" \ + --ulimit nofile=51200:51200 \ + -p "$MTG_PORT:3128" \ + "$MTG_IMAGENAME" run "$(cat "$MTG_SECRET")" > /dev/null + + sleep 3 + res=`ss -ntlp| grep ${MTG_PORT} | grep docker` + if [[ "$res" = "" ]]; then + docker logs $MTG_CONTAINER | tail + echo -e " ${RED}$OSNAME 启动docker镜像失败,请到 https://tizi.blog 反馈${PLAIN}" + exit 1 + else + colorEcho $BLUE " MTProto启动成功!" + fi +} + +stop() { + res=`status` + if [[ $res -lt 3 ]]; then + echo -e " ${RED}MTProto未安装,请先安装!${PLAIN}" + return + fi + + set -a + source "$MTG_ENV" + set +a + + $DOCKER_CMD stop $MTG_CONTAINER >> /dev/null + colorEcho $BLUE " MTProto停止成功!" +} + +showInfo() { + res=`status` + if [[ $res -lt 3 ]]; then + echo -e " ${RED}MTProto未安装,请先安装!${PLAIN}" + return + fi + + SECRET=$(cat "$MTG_SECRET") + set -a + source "$MTG_ENV" + set +a + + echo + echo -e " ${RED}MTProto代理信息:${PLAIN}" + echo + echo -n -e " ${BLUE}当前状态:${PLAIN}" + statusText + echo -e " ${BLUE}IP:${PLAIN}${RED}$IP${PLAIN}" + echo -e " ${BLUE}端口:${PLAIN}${RED}$MTG_PORT${PLAIN}" + echo -e " ${BLUE}密钥:${PLAIN}${RED}$SECRET${PLAIN}" + echo "" + echo -e " 如需获取tg://proxy形式的链接,请打开telegrame关注${GREEN}@MTProxybot${PLAIN}生成" + echo "" +} + +install() { + getData + installDocker + pullImage + start + firewall $MTG_PORT + showInfo +} + +update() { + res=`status` + if [[ $res -lt 2 ]]; then + echo -e " ${RED}MTProto未安装,请先安装!${PLAIN}" + return + fi + + pullImage + stop + start + showInfo +} + +uninstall() { + echo "" + read -p " 确定卸载MTProto?[y/n]:" answer + if [[ "$answer" = "y" ]] || [[ "$answer" = "Y" ]]; then + stop + rm -rf $MTG_CONFIG + docker system prune -af + systemctl stop docker + systemctl disable docker + $CMD_REMOVE docker-ce docker-ce-cli containerd.io + colorEcho $GREEN " 卸载成功" + fi +} + +restart() { + res=`status` + if [[ $res -lt 3 ]]; then + echo -e " ${RED}MTProto未安装,请先安装!${PLAIN}" + return + fi + + stop + start +} + +reconfig() +{ + res=`status` + if [[ $res -lt 2 ]]; then + echo -e " ${RED}MTProto未安装,请先安装!${PLAIN}" + return + fi + + getData + stop + start + firewall $MTG_PORT + showInfo +} + +showLog() { + res=`status` + if [[ $res -lt 3 ]]; then + echo -e " ${RED}MTProto未安装,请先安装!${PLAIN}" + return + fi + + set -a + source "$MTG_ENV" + set +a + + $DOCKER_CMD logs $MTG_CONTAINER | tail +} + +menu() { + clear + echo "#############################################################" + echo -e "# ${RED}MTProto一键安装脚本${PLAIN} #" + echo "#############################################################" + echo "" + + echo -e " ${GREEN}1.${PLAIN} 安装MTProto代理" + echo -e " ${GREEN}2.${PLAIN} 更新MTProto代理" + echo -e " ${GREEN}3.${PLAIN} 卸载MTProto代理" + echo " -------------" + echo -e " ${GREEN}4.${PLAIN} 启动MTProto代理" + echo -e " ${GREEN}5.${PLAIN} 重启MTProto代理" + echo -e " ${GREEN}6.${PLAIN} 停止MTProto代理" + echo " -------------" + echo -e " ${GREEN}7.${PLAIN} 查看MTProto信息" + echo -e " ${GREEN}8.${PLAIN} 修改MTProto配置" + echo -e " ${GREEN}9.${PLAIN} 查看MTProto日志" + echo " -------------" + echo -e " ${GREEN}0.${PLAIN} 退出" + echo + echo -n " 当前状态:" + statusText + echo + + read -p " 请选择操作[0-9]:" answer + case $answer in + 0) + exit 0 + ;; + 1) + install + ;; + 2) + update + ;; + 3) + uninstall + ;; + 4) + start + ;; + 5) + restart + ;; + 6) + stop + ;; + 7) + showInfo + ;; + 8) + reconfig + ;; + 9) + showLog + ;; + *) + echo -e " ${RED}请选择正确的操作!${PLAIN}" + exit 1 + ;; + esac +} + +checkSystem + +menu diff --git a/节点类/ss-rust.sh b/节点类/ss-rust.sh new file mode 100644 index 0000000..5d6ee36 --- /dev/null +++ b/节点类/ss-rust.sh @@ -0,0 +1,1927 @@ +#!/usr/bin/env bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH + +#================================================= +# System Required: CentOS/Debian/Ubuntu +# Description: Shadowsocks Rust 管理脚本 +# Author: 翠花 +# WebSite: https://aapls.com +#================================================= + +# 当前脚本版本号 +sh_ver="1.5.4" + +# Shadowsocks Rust 相关路径 +SS_Folder="/etc/ss-rust" +SS_File="/usr/local/bin/ss-rust" +SS_Conf="/etc/ss-rust/config.json" +SS_Now_ver_File="/etc/ss-rust/ver.txt" + +# BBR 配置文件 +BBR_Local="/etc/sysctl.d/local.conf" + +# Shadow TLS 相关路径 +STLS_Folder="/etc/shadowtls" +STLS_File="/usr/local/bin/shadow-tls" +STLS_Conf="/etc/shadowtls/config.json" +STLS_Now_ver_File="/etc/shadowtls/ver.txt" + +Green_font_prefix="\033[32m" && Red_font_prefix="\033[31m" && Green_background_prefix="\033[42;37m" && Red_background_prefix="\033[41;37m" && Font_color_suffix="\033[0m" && Yellow_font_prefix="\033[0;33m" +Info="${Green_font_prefix}[信息]${Font_color_suffix}" +Error="${Red_font_prefix}[错误]${Font_color_suffix}" +Tip="${Yellow_font_prefix}[注意]${Font_color_suffix}" + +check_root(){ + if [[ $EUID != 0 ]]; then + echo -e "${Error} 当前非ROOT账号(或没有ROOT权限),无法继续操作,请更换ROOT账号或使用 ${Green_background_prefix}sudo su${Font_color_suffix} 命令获取临时ROOT权限(执行后可能会提示输入当前账号的密码)。" + exit 1 + fi +} + +check_sys(){ + if [[ -f /etc/redhat-release ]]; then + release="centos" + elif cat /etc/issue | grep -q -E -i "debian"; then + release="debian" + elif cat /etc/issue | grep -q -E -i "ubuntu"; then + release="ubuntu" + elif cat /etc/issue | grep -q -E -i "centos|red hat|redhat"; then + release="centos" + elif cat /proc/version | grep -q -E -i "debian"; then + release="debian" + elif cat /proc/version | grep -q -E -i "ubuntu"; then + release="ubuntu" + elif cat /proc/version | grep -q -E -i "centos|red hat|redhat"; then + release="centos" + fi +} + +sys_arch() { + uname=$(uname -m) + if [[ "$uname" == "i686" ]] || [[ "$uname" == "i386" ]]; then + arch="i686" + elif [[ "$uname" == *"armv7"* ]] || [[ "$uname" == "armv6l" ]]; then + arch="arm" + elif [[ "$uname" == *"armv8"* ]] || [[ "$uname" == "aarch64" ]]; then + arch="aarch64" + else + arch="x86_64" + fi +} + +#开启系统 TCP Fast Open +enable_systfo() { + kernel=$(uname -r | awk -F . '{print $1}') + if [ "$kernel" -ge 3 ]; then + echo 3 >/proc/sys/net/ipv4/tcp_fastopen + [[ ! -e $BBR_Local ]] && echo "fs.file-max = 51200 +net.core.rmem_max = 67108864 +net.core.wmem_max = 67108864 +net.core.rmem_default = 65536 +net.core.wmem_default = 65536 +net.core.netdev_max_backlog = 4096 +net.core.somaxconn = 4096 +net.ipv4.tcp_syncookies = 1 +net.ipv4.tcp_tw_reuse = 1 +net.ipv4.tcp_tw_recycle = 0 +net.ipv4.tcp_fin_timeout = 30 +net.ipv4.tcp_keepalive_time = 1200 +net.ipv4.ip_local_port_range = 10000 65000 +net.ipv4.tcp_max_syn_backlog = 4096 +net.ipv4.tcp_max_tw_buckets = 5000 +net.ipv4.tcp_fastopen = 3 +net.ipv4.tcp_rmem = 4096 87380 67108864 +net.ipv4.tcp_wmem = 4096 65536 67108864 +net.ipv4.tcp_mtu_probing = 1 +net.ipv4.tcp_ecn=1 +net.core.default_qdisc=fq +net.ipv4.tcp_congestion_control = bbr" >>/etc/sysctl.d/local.conf && sysctl --system >/dev/null 2>&1 + else + echo -e "$Error系统内核版本过低,无法支持 TCP Fast Open !" + fi +} + +check_installed_status(){ + [[ ! -e ${SS_File} ]] && echo -e "${Error} Shadowsocks Rust 没有安装,请检查!" && exit 1 +} + +check_status(){ + status=`systemctl status ss-rust | grep Active | awk '{print $3}' | cut -d "(" -f2 | cut -d ")" -f1` +} + +check_new_ver(){ + new_ver=$(wget -qO- https://api.github.com/repos/shadowsocks/shadowsocks-rust/releases| jq -r '[.[] | select(.prerelease == false) | select(.draft == false) | .tag_name] | .[0]') + [[ -z ${new_ver} ]] && echo -e "${Error} Shadowsocks Rust 最新版本获取失败!" && exit 1 + echo -e "${Info} 检测到 Shadowsocks Rust 最新版本为 [ ${new_ver} ]" +} + +check_ver_comparison(){ + now_ver=$(cat ${SS_Now_ver_File}) + if [[ "${now_ver}" != "${new_ver}" ]]; then + echo -e "${Info} 发现 Shadowsocks Rust 已有新版本 [ ${new_ver} ],旧版本 [ ${now_ver} ]" + read -e -p "是否更新 ? [Y/n]:" yn + [[ -z "${yn}" ]] && yn="y" + if [[ $yn == [Yy] ]]; then + check_status + # [[ "$status" == "running" ]] && systemctl stop ss-rust + \cp "${SS_Conf}" "/tmp/config.json" + # rm -rf ${SS_Folder} + download + mv -f "/tmp/config.json" "${SS_Conf}" + restart + fi + else + echo -e "${Info} 当前 Shadowsocks Rust 已是最新版本 [ ${new_ver} ] !" && exit 1 + fi +} + +# 官方源 +stable_download() { + echo -e "${Info} 默认开始下载官方源 Shadowsocks Rust ……" + wget --no-check-certificate -N "https://github.com/shadowsocks/shadowsocks-rust/releases/download/${new_ver}/shadowsocks-${new_ver}.${arch}-unknown-linux-gnu.tar.xz" + if [[ ! -e "shadowsocks-${new_ver}.${arch}-unknown-linux-gnu.tar.xz" ]]; then + echo -e "${Error} Shadowsocks Rust 官方源下载失败!" + return 1 && exit 1 + else + tar -xvf "shadowsocks-${new_ver}.${arch}-unknown-linux-gnu.tar.xz" + fi + if [[ ! -e "ssserver" ]]; then + echo -e "${Error} Shadowsocks Rust 解压失败!" + echo -e "${Error} Shadowsocks Rust 安装失败 !" + return 1 && exit 1 + else + rm -rf "shadowsocks-${new_ver}.${arch}-unknown-linux-gnu.tar.xz" + chmod +x ssserver + mv -f ssserver "${SS_File}" + rm sslocal ssmanager ssservice ssurl + echo "${new_ver}" > ${SS_Now_ver_File} + + echo -e "${Info} Shadowsocks Rust 主程序下载安装完毕!" + return 0 + fi +} + +# 备用源 +backup_download() { + echo -e "${Info} 试图请求 备份源(旧版本) Shadowsocks Rust ……" + wget --no-check-certificate -N "https://raw.githubusercontent.com/xOS/Others/master/shadowsocks-rust/v1.14.1/shadowsocks-v1.14.1.${arch}-unknown-linux-gnu.tar.xz" + if [[ ! -e "shadowsocks-v1.14.1.${arch}-unknown-linux-gnu.tar.xz" ]]; then + echo -e "${Error} Shadowsocks Rust 备份源(旧版本) 下载失败!" + return 1 && exit 1 + else + tar -xvf "shadowsocks-v1.14.1.${arch}-unknown-linux-gnu.tar.xz" + fi + if [[ ! -e "ssserver" ]]; then + echo -e "${Error} Shadowsocks Rust 备份源(旧版本) 解压失败 !" + echo -e "${Error} Shadowsocks Rust 备份源(旧版本) 安装失败 !" + return 1 && exit 1 + else + rm -rf "shadowsocks-v1.14.1.${arch}-unknown-linux-gnu.tar.xz" + chmod +x ssserver + mv -f ssserver "${SS_File}" + rm sslocal ssmanager ssservice ssurl + echo "v1.14.1" > ${SS_Now_ver_File} + echo -e "${Info} Shadowsocks Rust 备份源(旧版本) 主程序下载安装完毕!" + return 0 + fi +} + +download() { + if [[ ! -e "${SS_Folder}" ]]; then + mkdir "${SS_Folder}" + # else + # [[ -e "${SS_File}" ]] && rm -rf "${SS_File}" + fi + stable_download + if [[ $? != 0 ]]; then + backup_download + fi +} + +# Shadow TLS 官方源下载 +stable_download_stls() { + echo -e "${Info} 默认开始下载官方源 Shadow TLS ……" + wget --no-check-certificate -N "https://github.com/ihciah/shadow-tls/releases/download/${stls_new_ver}/shadow-tls-${arch}-unknown-linux-musl" + if [[ ! -e "shadow-tls-${arch}-unknown-linux-musl" ]]; then + echo -e "${Error} Shadow TLS 官方源下载失败!" + return 1 && exit 1 + else + chmod +x "shadow-tls-${arch}-unknown-linux-musl" + mv -f "shadow-tls-${arch}-unknown-linux-musl" "${STLS_File}" + echo "${stls_new_ver}" > ${STLS_Now_ver_File} + echo -e "${Info} Shadow TLS 主程序下载安装完毕!" + return 0 + fi +} + +download_stls() { + if [[ ! -e "${STLS_Folder}" ]]; then + mkdir "${STLS_Folder}" + fi + stable_download_stls +} + +# Shadow TLS 配置相关函数 +set_stls_port(){ + while true + do + echo -e "${Tip} Shadow TLS 端口需与防火墙端口一致!" + echo -e "请输入 Shadow TLS 端口 [1-65535]" + read -e -p "(默认:8443):" stls_port + [[ -z "${stls_port}" ]] && stls_port="8443" + echo $((${stls_port}+0)) &>/dev/null + if [[ $? -eq 0 ]]; then + if [[ ${stls_port} -ge 1 ]] && [[ ${stls_port} -le 65535 ]]; then + echo && echo "========================================" + echo -e "Shadow TLS 端口:${Red_background_prefix} ${stls_port} ${Font_color_suffix}" + echo "========================================" && echo + break + else + echo "输入错误, 请输入正确的端口。" + fi + else + echo "输入错误, 请输入正确的端口。" + fi + done +} + +set_stls_password(){ + echo "请输入 Shadow TLS 密码 [0-9][a-z][A-Z]" + read -e -p "(默认:随机生成):" stls_password + [[ -z "${stls_password}" ]] && stls_password=$(< /dev/urandom tr -dc 'a-zA-Z0-9' | head -c 16) + echo && echo "========================================" + echo -e "Shadow TLS 密码:${Red_background_prefix} ${stls_password} ${Font_color_suffix}" + echo "========================================" && echo +} + +set_stls_sni(){ + echo "请输入 Shadow TLS SNI 域名" + read -e -p "(默认:cloudflare.com):" stls_sni + [[ -z "${stls_sni}" ]] && stls_sni="cloudflare.com" + echo && echo "========================================" + echo -e "Shadow TLS SNI:${Red_background_prefix} ${stls_sni} ${Font_color_suffix}" + echo "========================================" && echo +} + +set_stls_fastopen(){ + echo -e "是否开启 Shadow TLS TCP Fast Open ? +======================================== +${Green_font_prefix} 1.${Font_color_suffix} 开启 ${Green_font_prefix} 2.${Font_color_suffix} 关闭 +========================================" + read -e -p "(默认:1.开启):" stls_fastopen_choice + [[ -z "${stls_fastopen_choice}" ]] && stls_fastopen_choice="1" + if [[ ${stls_fastopen_choice} == "1" ]]; then + stls_fastopen="true" + else + stls_fastopen="false" + fi + echo && echo "========================================" + echo -e "Shadow TLS FastOpen:${Red_background_prefix} ${stls_fastopen} ${Font_color_suffix}" + echo "========================================" && echo +} + +set_stls_strict(){ + echo -e "是否开启 Shadow TLS Strict 模式 ? +======================================== +${Green_font_prefix} 1.${Font_color_suffix} 开启 ${Green_font_prefix} 2.${Font_color_suffix} 关闭 +========================================" + read -e -p "(默认:1.开启):" stls_strict_choice + [[ -z "${stls_strict_choice}" ]] && stls_strict_choice="1" + if [[ ${stls_strict_choice} == "1" ]]; then + stls_strict="true" + else + stls_strict="false" + fi + echo && echo "========================================" + echo -e "Shadow TLS Strict:${Red_background_prefix} ${stls_strict} ${Font_color_suffix}" + echo "========================================" && echo +} + +set_stls_tls_wildcard_sni(){ + echo -e "请选择 Shadow TLS Wildcard SNI 模式: +======================================== +${Green_font_prefix} 1.${Font_color_suffix} authed (默认) +${Green_font_prefix} 2.${Font_color_suffix} off +${Green_font_prefix} 3.${Font_color_suffix} all +========================================" + read -e -p "(默认:1.authed):" stls_tls_wildcard_sni_choice + [[ -z "${stls_tls_wildcard_sni_choice}" ]] && stls_tls_wildcard_sni_choice="1" + case "${stls_tls_wildcard_sni_choice}" in + 1) stls_tls_wildcard_sni="authed" ;; + 2) stls_tls_wildcard_sni="off" ;; + 3) stls_tls_wildcard_sni="all" ;; + *) stls_tls_wildcard_sni="authed" ;; + esac + echo && echo "========================================" + echo -e "TLS Wildcard SNI:${Red_background_prefix} ${stls_tls_wildcard_sni} ${Font_color_suffix}" + echo "========================================" && echo +} + +set_stls_fallback(){ + echo "请输入 Shadow TLS 回退域名" + read -e -p "(默认:cloud.tencent.com:443):" stls_fallback + [[ -z "${stls_fallback}" ]] && stls_fallback="cloud.tencent.com:443" + echo && echo "========================================" + echo -e "Shadow TLS 回退域名:${Red_background_prefix} ${stls_fallback} ${Font_color_suffix}" + echo "========================================" && echo +} + +service(){ + echo " +[Unit] +Description= Shadowsocks Rust Service +After=network-online.target +Wants=network-online.target systemd-networkd-wait-online.service +[Service] +LimitNOFILE=32767 +Type=simple +User=root +Restart=on-failure +RestartSec=5s +DynamicUser=true +ExecStartPre=/bin/sh -c 'ulimit -n 51200' +ExecStart=${SS_File} -c ${SS_Conf} +[Install] +WantedBy=multi-user.target" > /etc/systemd/system/ss-rust.service +systemctl enable --now ss-rust + echo -e "${Info} Shadowsocks Rust 服务配置完成!" +} + +service_stls(){ + cat > /etc/systemd/system/shadowtls.service<<-EOF +[Unit] +Description=Shadow TLS Service +After=network-online.target +Wants=network-online.target systemd-networkd-wait-online.service + +[Service] +LimitNOFILE=32767 +Type=simple +User=root +Restart=on-failure +RestartSec=5s +Environment=MONOIO_FORCE_LEGACY_DRIVER=1 +ExecStartPre=/bin/sh -c 'ulimit -n 51200' +ExecStart=${STLS_File} config --config ${STLS_Conf} + +[Install] +WantedBy=multi-user.target +EOF + systemctl enable --now shadowtls + echo -e "${Info} Shadow TLS 服务配置完成!" +} + +installation_dependency(){ + if [[ ${release} == "centos" ]]; then + yum update + yum install jq gzip wget curl unzip xz openssl -y + else + apt-get update + apt-get install jq gzip wget curl unzip xz-utils openssl -y + fi + \cp -f /usr/share/zoneinfo/Asia/Shanghai /etc/localtime +} + +write_config(){ + cat > ${SS_Conf}<<-EOF +{ + "server": "::", + "server_port": ${port}, + "password": "${password}", + "method": "${cipher}", + "fast_open": ${tfo}, + "mode": "tcp_and_udp", + "user":"nobody", + "timeout":300, + "nameserver":"1.1.1.1" +} +EOF +} + +write_stls_config(){ + # 设置默认值 + [[ -z "${stls_fastopen}" ]] && stls_fastopen="true" + [[ -z "${stls_strict}" ]] && stls_strict="true" + [[ -z "${stls_tls_wildcard_sni}" ]] && stls_tls_wildcard_sni="authed" + [[ -z "${stls_fallback}" ]] && stls_fallback="cloud.tencent.com:443" + + # 构建 dispatch 配置 + if [[ -z "${stls_dispatch}" ]]; then + # 没有现有配置,使用默认配置 + # SNI 域名对应的目标地址也使用相同的域名:443 + stls_dispatch_config="\"${stls_sni}\": \"${stls_sni}:443\", + \"captive.apple.com\": \"captive.apple.com:443\"" + else + # 有现有配置,需要智能更新 SNI + if [[ ! -z "${stls_sni}" && -e ${STLS_Conf} ]]; then + # 查找需要更新的 SNI(排除 captive.apple.com) + old_sni=$(cat ${STLS_Conf}|jq -r '.server.tls_addr.dispatch | keys[] | select(. != "captive.apple.com")' 2>/dev/null | head -1) + if [[ ! -z "${old_sni}" && "${old_sni}" != "null" && "${old_sni}" != "${stls_sni}" ]]; then + # 需要替换旧的 SNI 为新的 SNI,同时更新键和值 + echo -e "${Info} 更新主要 SNI 从 ${old_sni} 到 ${stls_sni}" + + # 使用 jq 来精确替换键和值 + stls_dispatch_config=$(cat ${STLS_Conf} | jq -r --arg old_sni "${old_sni}" --arg new_sni "${stls_sni}" ' + .server.tls_addr.dispatch | + to_entries | + map(if .key == $old_sni then (.key = $new_sni | .value = ($new_sni + ":443")) else . end) | + map("\"\(.key)\": \"\(.value)\"") | + join(",\n ") + ' 2>/dev/null) + + # 如果 jq 处理失败,回退到字符串替换 + if [[ -z "${stls_dispatch_config}" || "${stls_dispatch_config}" == "null" ]]; then + echo -e "${Info} jq 处理失败,使用字符串替换" + # 先替换键,再替换对应的值 + stls_dispatch_config=$(echo "${stls_dispatch}" | sed "s/\"${old_sni}\": \"[^\"]*\"/\"${stls_sni}\": \"${stls_sni}:443\"/g") + fi + else + # 如果没有找到需要更新的 SNI,或者 SNI 已经是正确的,直接使用现有配置 + stls_dispatch_config="${stls_dispatch}" + fi + else + stls_dispatch_config="${stls_dispatch}" + fi + fi + + # 调试信息(可选,用于排查问题) + # echo -e "${Info} 调试信息:" + # echo -e " stls_sni: ${stls_sni}" + # echo -e " stls_dispatch_config: ${stls_dispatch_config}" + + cat > ${STLS_Conf}<<-EOF +{ + "disable_nodelay": false, + "fastopen": ${stls_fastopen}, + "v3": true, + "strict": ${stls_strict}, + "server": { + "listen": "0.0.0.0:${stls_port}", + "server_addr": "127.0.0.1:${port}", + "tls_addr": { + "wildcard_sni": "${stls_tls_wildcard_sni}", + "dispatch": { + ${stls_dispatch_config} + }, + "fallback": "${stls_fallback}" + }, + "password": "${stls_password}", + "wildcard_sni": "authed" + } +} +EOF + + # 验证配置文件是否正确生成 + if [[ -e ${STLS_Conf} ]]; then + # 检查配置文件是否为有效的 JSON + if ! jq . ${STLS_Conf} >/dev/null 2>&1; then + echo -e "${Error} Shadow TLS 配置文件格式错误!" + return 1 + fi + echo -e "${Info} Shadow TLS 配置文件写入成功" + + # 显示当前配置的 SNI + current_sni=$(cat ${STLS_Conf}|jq -r '.server.tls_addr.dispatch | keys[0]' 2>/dev/null) + if [[ ! -z "${current_sni}" && "${current_sni}" != "null" ]]; then + echo -e "${Info} 当前配置的 SNI: ${Green_font_prefix}${current_sni}${Font_color_suffix}" + fi + else + echo -e "${Error} Shadow TLS 配置文件写入失败!" + return 1 + fi +} + +read_config(){ + [[ ! -e ${SS_Conf} ]] && echo -e "${Error} Shadowsocks Rust 配置文件不存在!" && exit 1 + port=$(cat ${SS_Conf}|jq -r '.server_port') + password=$(cat ${SS_Conf}|jq -r '.password') + cipher=$(cat ${SS_Conf}|jq -r '.method') + tfo=$(cat ${SS_Conf}|jq -r '.fast_open') +} + +read_stls_config(){ + [[ ! -e ${STLS_Conf} ]] && echo -e "${Error} Shadow TLS 配置文件不存在!" && exit 1 + stls_port=$(cat ${STLS_Conf}|jq -r '.server.listen' | grep -oE '[0-9]+$') + stls_password=$(cat ${STLS_Conf}|jq -r '.server.password') + stls_sni=$(cat ${STLS_Conf}|jq -r '.server.tls_addr.dispatch | keys[0]') + stls_fastopen=$(cat ${STLS_Conf}|jq -r '.fastopen') + stls_strict=$(cat ${STLS_Conf}|jq -r '.strict') + stls_tls_wildcard_sni=$(cat ${STLS_Conf}|jq -r '.server.tls_addr.wildcard_sni') + stls_fallback=$(cat ${STLS_Conf}|jq -r '.server.tls_addr.fallback') + # 读取完整的 dispatch 配置 + stls_dispatch=$(cat ${STLS_Conf}|jq -r '.server.tls_addr.dispatch | to_entries | map("\"\(.key)\": \"\(.value)\"") | join(",\n ")') +} + +set_port(){ + while true + do + echo -e "${Tip} 本步骤不涉及系统防火墙端口操作,请手动放行相应端口!" + echo -e "请输入 Shadowsocks Rust 端口 [1-65535]" + read -e -p "(默认:2525):" port + [[ -z "${port}" ]] && port="2525" + echo $((${port}+0)) &>/dev/null + if [[ $? -eq 0 ]]; then + if [[ ${port} -ge 1 ]] && [[ ${port} -le 65535 ]]; then + echo && echo "========================================" + echo -e "端口:${Red_background_prefix} ${port} ${Font_color_suffix}" + echo "========================================" && echo + break + else + echo "输入错误, 请输入正确的端口。" + fi + else + echo "输入错误, 请输入正确的端口。" + fi + done +} + +set_tfo(){ + echo -e "是否开启 TCP Fast Open ? +======================================== +${Green_font_prefix} 1.${Font_color_suffix} 开启 ${Green_font_prefix} 2.${Font_color_suffix} 关闭 +========================================" + read -e -p "(默认:1.开启):" tfo + [[ -z "${tfo}" ]] && tfo="1" + if [[ ${tfo} == "1" ]]; then + tfo=true + enable_systfo + else + tfo=false + fi + echo && echo "==================================" + echo -e "TCP Fast Open 开启状态:${Red_background_prefix} ${tfo} ${Font_color_suffix}" + echo "==================================" && echo +} + +check_password_compatible(){ + local check_cipher="$1" + local check_password="$2" + local decoded_len="" + + [[ -z "${check_password}" ]] && return 1 + + case "${check_cipher}" in + "2022-blake3-aes-128-gcm") + decoded_len=$(printf '%s' "${check_password}" | base64 -d 2>/dev/null | wc -c | tr -d ' ') + [[ "${decoded_len}" == "16" ]] && return 0 || return 1 + ;; + "2022-blake3-aes-256-gcm"|"2022-blake3-chacha20-poly1305") + decoded_len=$(printf '%s' "${check_password}" | base64 -d 2>/dev/null | wc -c | tr -d ' ') + [[ "${decoded_len}" == "32" ]] && return 0 || return 1 + ;; + *) + return 0 + ;; + esac +} + +show_2022_password_rule(){ + echo -e "${Tip} 2022-blake3-aes-128-gcm 需要 Base64 解码后 16 字节密钥" + echo -e "${Tip} 2022-blake3-aes-256-gcm / 2022-blake3-chacha20-poly1305 需要 Base64 解码后 32 字节密钥" +} + +set_password(){ + while true + do + echo "请输入 Shadowsocks Rust 密码 [0-9][a-z][A-Z]" + if [[ ${cipher} == "2022-blake3-aes-128-gcm" || ${cipher} == "2022-blake3-aes-256-gcm" || ${cipher} == "2022-blake3-chacha20-poly1305" ]]; then + show_2022_password_rule + fi + read -e -p "(默认:随机生成):" password + # 当用户未输入密码时,执行默认生成逻辑 + if [[ -z "${password}" ]]; then + # 判断是否为2022系列加密 + if [[ ${cipher} == "2022-blake3-aes-256-gcm" || ${cipher} == "2022-blake3-chacha20-poly1305" ]]; then + # 2022系列必须使用指定长度的Base64密钥 + echo -e "${Tip} 为 ${cipher} 生成 32 字节 Base64 密钥..." + password=$(openssl rand -base64 32) + elif [[ ${cipher} == "2022-blake3-aes-128-gcm" ]]; then + # 2022系列必须使用指定长度的Base64密钥 + echo -e "${Tip} 为 ${cipher} 生成 16 字节 Base64 密钥..." + password=$(openssl rand -base64 16) + else + # 其他加密方式,生成一个普通的16位字母和数字的随机密码 + echo -e "${Tip} 为 ${cipher} 生成 16 位随机密码 (非Base64)..." + password=$(< /dev/urandom tr -dc 'a-zA-Z0-9' | head -c 16) + fi + fi + + if check_password_compatible "${cipher}" "${password}"; then + break + fi + + echo -e "${Error} 当前密码格式与加密方式 ${cipher} 不兼容!" + show_2022_password_rule + password="" + done + + echo && echo "========================================" + echo -e "密码:${Red_font_prefix} ${password} ${Font_color_suffix}" + echo "========================================" && echo +} + +set_cipher(){ + echo -e "请选择 Shadowsocks Rust 加密方式 +======================================== + ${Green_font_prefix} 1.${Font_color_suffix} aes-128-gcm ${Green_font_prefix}${Font_color_suffix} + ${Green_font_prefix} 2.${Font_color_suffix} aes-256-gcm ${Green_font_prefix}(推荐)${Font_color_suffix} + ${Green_font_prefix} 3.${Font_color_suffix} chacha20-ietf-poly1305 ${Green_font_prefix}${Font_color_suffix} + ${Green_font_prefix} 4.${Font_color_suffix} plain ${Red_font_prefix}(不推荐)${Font_color_suffix} + ${Green_font_prefix} 5.${Font_color_suffix} none ${Red_font_prefix}(不推荐)${Font_color_suffix} + ${Green_font_prefix} 6.${Font_color_suffix} table + ${Green_font_prefix} 7.${Font_color_suffix} aes-128-cfb + ${Green_font_prefix} 8.${Font_color_suffix} aes-256-cfb + ${Green_font_prefix} 9.${Font_color_suffix} aes-256-ctr + ${Green_font_prefix}10.${Font_color_suffix} camellia-256-cfb + ${Green_font_prefix}11.${Font_color_suffix} rc4-md5 + ${Green_font_prefix}12.${Font_color_suffix} chacha20-ietf +======================================== + ${Tip} AEAD 2022 加密(须v1.15.0及以上版本且密码须经过Base64加密) +======================================== + ${Green_font_prefix}13.${Font_color_suffix} 2022-blake3-aes-128-gcm ${Green_font_prefix}(推荐)${Font_color_suffix} + ${Green_font_prefix}14.${Font_color_suffix} 2022-blake3-aes-256-gcm ${Green_font_prefix}(默认/推荐)${Font_color_suffix} + ${Green_font_prefix}15.${Font_color_suffix} 2022-blake3-chacha20-poly1305 + ======================================== + ${Tip} 如需其它加密方式请手动修改配置文件 !" && echo + read -e -p "(默认: 14. 2022-blake3-aes-256-gcm):" cipher + [[ -z "${cipher}" ]] && cipher="14" + if [[ ${cipher} == "1" ]]; then + cipher="aes-128-gcm" + elif [[ ${cipher} == "2" ]]; then + cipher="aes-256-gcm" + elif [[ ${cipher} == "3" ]]; then + cipher="chacha20-ietf-poly1305" + elif [[ ${cipher} == "4" ]]; then + cipher="plain" + elif [[ ${cipher} == "5" ]]; then + cipher="none" + elif [[ ${cipher} == "6" ]]; then + cipher="table" + elif [[ ${cipher} == "7" ]]; then + cipher="aes-128-cfb" + elif [[ ${cipher} == "8" ]]; then + cipher="aes-256-cfb" + elif [[ ${cipher} == "9" ]]; then + cipher="aes-256-ctr" + elif [[ ${cipher} == "10" ]]; then + cipher="camellia-256-cfb" + elif [[ ${cipher} == "11" ]]; then + cipher="arc4-md5" + elif [[ ${cipher} == "12" ]]; then + cipher="chacha20-ietf" + elif [[ ${cipher} == "13" ]]; then + cipher="2022-blake3-aes-128-gcm" + elif [[ ${cipher} == "14" ]]; then + cipher="2022-blake3-aes-256-gcm" + elif [[ ${cipher} == "15" ]]; then + cipher="2022-blake3-chacha20-poly1305" + else + cipher="2022-blake3-aes-256-gcm" + fi + echo && echo "========================================" + echo -e "加密:${Red_background_prefix} ${cipher} ${Font_color_suffix}" + echo "========================================" && echo +} + +set_config(){ + check_installed_status + echo && echo -e "你要做什么? +======================================== + ${Green_font_prefix}1.${Font_color_suffix} 修改 端口配置 + ${Green_font_prefix}2.${Font_color_suffix} 修改 加密配置 + ${Green_font_prefix}3.${Font_color_suffix} 修改 密码配置 + ${Green_font_prefix}4.${Font_color_suffix} 修改 TFO 配置 +======================================== + ${Green_font_prefix}5.${Font_color_suffix} 修改 全部配置" && echo + read -e -p "(默认:取消):" modify + [[ -z "${modify}" ]] && echo "已取消..." && exit 1 + if [[ "${modify}" == "1" ]]; then + read_config + set_port + cipher=${cipher} + password=${password} + tfo=${tfo} + write_config + restart + elif [[ "${modify}" == "2" ]]; then + read_config + set_cipher + if ! check_password_compatible "${cipher}" "${password}"; then + echo -e "${Error} 当前密码与新加密方式 ${cipher} 不兼容,需先修改密码。" + show_2022_password_rule + echo -e "${Tip} 现在开始引导你修改密码..." + set_password + fi + port=${port} + password=${password} + tfo=${tfo} + write_config + restart + elif [[ "${modify}" == "3" ]]; then + read_config + cipher=${cipher} + set_password + port=${port} + tfo=${tfo} + write_config + restart + elif [[ "${modify}" == "4" ]]; then + read_config + set_tfo + cipher=${cipher} + port=${port} + password=${password} + write_config + restart + elif [[ "${modify}" == "5" ]]; then + read_config + set_port + set_cipher + if ! check_password_compatible "${cipher}" "${password}"; then + echo -e "${Error} 当前密码与新加密方式 ${cipher} 不兼容,需先修改密码。" + show_2022_password_rule + echo -e "${Tip} 现在开始引导你修改密码..." + set_password + else + echo -e "${Info} 当前密码与新加密方式兼容。" + read -e -p "是否修改密码?[y/N]:" change_password + [[ -z "${change_password}" ]] && change_password="n" + if [[ ${change_password} == [Yy] ]]; then + set_password + fi + fi + set_tfo + write_config + restart + else + echo -e "${Error} 请输入正确的数字(1-5)" && exit 1 + fi +} + +install(){ + [[ -e ${SS_File} ]] && echo -e "${Error} 检测到 Shadowsocks Rust 已安装!" && exit 1 + echo -e "${Info} 开始设置 配置..." + set_port + set_cipher + set_password + set_tfo + echo -e "${Info} 开始安装/配置 依赖..." + installation_dependency + echo -e "${Info} 开始下载/安装..." + check_new_ver + download + echo -e "${Info} 开始安装系统服务脚本..." + service + echo -e "${Info} 开始写入 配置文件..." + write_config + echo -e "${Info} 所有步骤 安装完毕,开始启动..." + start + echo -e "${Info} Shadowsocks Rust 安装完成!" + + # 询问是否继续安装 Shadow TLS + echo && echo -e "${Tip} 是否继续安装 Shadow TLS 流量伪装?" + read -e -p "(默认: N 不安装) [y/N]: " install_stls_choice + [[ -z "${install_stls_choice}" ]] && install_stls_choice="n" + + if [[ ${install_stls_choice} == [Yy] ]]; then + echo -e "${Info} 开始安装 Shadow TLS..." + install_stls_after_ss + else + echo -e "${Info} 跳过 Shadow TLS 安装,显示 Shadowsocks Rust 配置..." + view_ss_only + fi +} + +install_stls(){ + [[ -e ${STLS_File} ]] && echo -e "${Error} 检测到 Shadow TLS 已安装!" && exit 1 + + # 检查 Shadowsocks Rust 是否已安装 + if [[ ! -e ${SS_File} ]]; then + echo -e "${Error} 检测到 Shadowsocks Rust 尚未安装!" + echo -e "${Info} Shadow TLS 需要配合 Shadowsocks Rust 使用。" + echo -e "${Info} 建议先安装 Shadowsocks Rust,再安装 Shadow TLS。" + echo && read -e -p "是否现在安装 Shadowsocks Rust?[Y/n]:" install_ss_choice + [[ -z "${install_ss_choice}" ]] && install_ss_choice="Y" + if [[ ${install_ss_choice} == [Yy] ]]; then + echo -e "${Info} 开始安装 Shadowsocks Rust..." + install + echo -e "${Info} Shadowsocks Rust 安装完成,现在开始安装 Shadow TLS..." + else + echo -e "${Info} 已取消安装,返回上级菜单..." + shadowtls_menu + return + fi + fi + + echo -e "${Info} 开始设置 Shadow TLS 配置..." + read_config # 读取现有 SS 配置 + + # 使用交互式配置,每项都有默认值 + echo -e "${Info} 请配置 Shadow TLS 参数(可直接回车使用默认值):" + + set_stls_port + set_stls_password + set_stls_sni + set_stls_fastopen + set_stls_strict + set_stls_tls_wildcard_sni + set_stls_fallback + manage_stls_dispatch + + echo -e "${Info} 开始下载/安装 Shadow TLS..." + check_stls_new_ver + download_stls + echo -e "${Info} 开始安装 Shadow TLS 服务脚本..." + service_stls + echo -e "${Info} 开始写入 Shadow TLS 配置文件..." + write_stls_config + echo -e "${Info} Shadow TLS 安装完毕,开始启动..." + start_stls + echo -e "${Info} Shadow TLS 安装完成!显示完整配置信息..." + view_combined_config_with_return +} + +install_stls_after_ss(){ + echo -e "${Info} 开始设置 Shadow TLS 配置..." + read_config # 读取现有 SS 配置 + + # 使用交互式配置,每项都有默认值 + echo -e "${Info} 请配置 Shadow TLS 参数(可直接回车使用默认值):" + + set_stls_port + set_stls_password + set_stls_sni + set_stls_fastopen + set_stls_strict + set_stls_tls_wildcard_sni + set_stls_fallback + manage_stls_dispatch + + echo -e "${Info} 开始下载/安装 Shadow TLS..." + check_stls_new_ver + download_stls + echo -e "${Info} 开始安装 Shadow TLS 服务脚本..." + service_stls + echo -e "${Info} 开始写入 Shadow TLS 配置文件..." + write_stls_config + echo -e "${Info} Shadow TLS 安装完毕,开始启动..." + start_stls + echo -e "${Info} Shadow TLS 安装完成!显示完整配置信息..." + view_combined_config_with_return +} + +view(){ + check_installed_status + read_config + getipv4 + getipv6 + link_qr + clear && echo + echo -e "Shadowsocks Rust 配置:" + echo -e "————————————————————————————————————————" + [[ "${ipv4}" != "IPv4_Error" ]] && echo -e " 地址:${Green_font_prefix}${ipv4}${Font_color_suffix}" + [[ "${ipv6}" != "IPv6_Error" ]] && echo -e " 地址:${Green_font_prefix}${ipv6}${Font_color_suffix}" + echo -e " 端口:${Green_font_prefix}${port}${Font_color_suffix}" + echo -e " 密码:${Green_font_prefix}${password}${Font_color_suffix}" + echo -e " 加密:${Green_font_prefix}${cipher}${Font_color_suffix}" + echo -e " TFO :${Green_font_prefix}${tfo}${Font_color_suffix}" + echo -e "————————————————————————————————————————" + [[ ! -z "${link_ipv4}" ]] && echo -e "${link_ipv4}" + [[ ! -z "${link_ipv6}" ]] && echo -e "${link_ipv6}" + echo -e "—————————————————————————" + echo -e "${Info} Surge 配置:" + if [[ "${ipv4}" != "IPv4_Error" ]]; then + echo -e "$(uname -n) = ss, ${ipv4},${port}, encrypt-method=${cipher}, password=${password}, tfo=${tfo}, udp-relay=true, ecn=true" + else + echo -e "$(uname -n) = ss, ${ipv6},${port}, encrypt-method=${cipher}, password=${password}, tfo=${tfo}, udp-relay=true, ecn=true" + fi + echo -e "—————————————————————————" + echo && echo -n " 按回车键返回主菜单..." && read + start_menu +} + +view_ss_only(){ + check_installed_status + read_config + getipv4 + getipv6 + link_qr + clear && echo + echo -e "Shadowsocks Rust 配置:" + echo -e "————————————————————————————————————————" + [[ "${ipv4}" != "IPv4_Error" ]] && echo -e " 地址:${Green_font_prefix}${ipv4}${Font_color_suffix}" + [[ "${ipv6}" != "IPv6_Error" ]] && echo -e " 地址:${Green_font_prefix}${ipv6}${Font_color_suffix}" + echo -e " 端口:${Green_font_prefix}${port}${Font_color_suffix}" + echo -e " 密码:${Green_font_prefix}${password}${Font_color_suffix}" + echo -e " 加密:${Green_font_prefix}${cipher}${Font_color_suffix}" + echo -e " TFO :${Green_font_prefix}${tfo}${Font_color_suffix}" + echo -e "————————————————————————————————————————" + [[ ! -z "${link_ipv4}" ]] && echo -e "${link_ipv4}" + [[ ! -z "${link_ipv6}" ]] && echo -e "${link_ipv6}" + echo -e "—————————————————————————" + echo -e "${Info} Surge 配置:" + if [[ "${ipv4}" != "IPv4_Error" ]]; then + echo -e "$(uname -n) = ss, ${ipv4},${port}, encrypt-method=${cipher}, password=${password}, tfo=${tfo}, udp-relay=true, ecn=true" + else + echo -e "$(uname -n) = ss, ${ipv6},${port}, encrypt-method=${cipher}, password=${password}, tfo=${tfo}, udp-relay=true, ecn=true" + fi + echo -e "—————————————————————————" + echo && echo -n " 按回车键返回主菜单..." && read + start_menu +} + +view_combined_config(){ + local menu_source="$1" # 接收调用来源参数 + check_installed_status + read_config + getipv4 + getipv6 + link_qr + clear && echo + + echo -e "完整配置信息:" + echo -e "========================================" + + # 显示 Shadow TLS + SS 配置 + if [[ -e ${STLS_File} ]]; then + read_stls_config + echo -e "${Info} Shadow TLS + Shadowsocks Rust 配置:" + echo -e "————————————————————————————————————————" + [[ "${ipv4}" != "IPv4_Error" ]] && echo -e " 服务器:${Green_font_prefix}${ipv4}${Font_color_suffix}" + [[ "${ipv6}" != "IPv6_Error" ]] && echo -e " 服务器:${Green_font_prefix}${ipv6}${Font_color_suffix}" + echo -e " Shadow TLS 端口:${Green_font_prefix}${stls_port}${Font_color_suffix}" + echo -e " Shadow TLS 密码:${Green_font_prefix}${stls_password}${Font_color_suffix}" + echo -e " Shadow TLS SNI:${Green_font_prefix}${stls_sni}${Font_color_suffix}" + echo -e " SS 本地端口:${Green_font_prefix}${port}${Font_color_suffix}" + echo -e " SS 密码:${Green_font_prefix}${password}${Font_color_suffix}" + echo -e " SS 加密:${Green_font_prefix}${cipher}${Font_color_suffix}" + echo -e "————————————————————————————————————————" + echo -e "${Info} Shadow TLS + SS Surge 配置:" + if [[ "${ipv4}" != "IPv4_Error" ]]; then + echo -e "$(uname -n) = ss, ${ipv4}, ${stls_port}, encrypt-method=${cipher}, password=${password}, shadow-tls-password=${stls_password}, shadow-tls-sni=${stls_sni}, shadow-tls-version=3, tfo=${tfo}, udp-relay=true, ecn=true, udp-port=${port}" + else + echo -e "$(uname -n) = ss, ${ipv6}, ${stls_port}, encrypt-method=${cipher}, password=${password}, shadow-tls-password=${stls_password}, shadow-tls-sni=${stls_sni}, shadow-tls-version=3, tfo=${tfo}, udp-relay=true, ecn=true, udp-port=${port}" + fi + echo && echo -e "========================================" + fi + + # 显示纯 SS 配置 + echo -e "${Info} 原始 Shadowsocks Rust 配置:" + echo -e "————————————————————————————————————————" + [[ "${ipv4}" != "IPv4_Error" ]] && echo -e " 地址:${Green_font_prefix}${ipv4}${Font_color_suffix}" + [[ "${ipv6}" != "IPv6_Error" ]] && echo -e " 地址:${Green_font_prefix}${ipv6}${Font_color_suffix}" + echo -e " 端口:${Green_font_prefix}${port}${Font_color_suffix}" + echo -e " 密码:${Green_font_prefix}${password}${Font_color_suffix}" + echo -e " 加密:${Green_font_prefix}${cipher}${Font_color_suffix}" + echo -e "————————————————————————————————————————" + [[ ! -z "${link_ipv4}" ]] && echo -e "${link_ipv4}" + [[ ! -z "${link_ipv6}" ]] && echo -e "${link_ipv6}" + echo -e "—————————————————————————" + echo -e "${Info} 原始 SS Surge 配置:" + if [[ "${ipv4}" != "IPv4_Error" ]]; then + echo -e "$(uname -n) = ss, ${ipv4}, ${port}, encrypt-method=${cipher}, password=${password}, tfo=${tfo}, udp-relay=true, ecn=true" + fi + echo -e "========================================" + + # 根据调用来源返回到相应菜单 + if [[ "$menu_source" == "shadowtls" ]]; then + echo && echo -n " 按回车键返回 Shadow TLS 菜单..." && read + shadowtls_menu + else + echo && echo -n " 按回车键返回主菜单..." && read + start_menu + fi +} + +view_combined_config_with_return(){ + check_installed_status + read_config + getipv4 + getipv6 + link_qr + clear && echo + + echo -e "完整配置信息:" + echo -e "========================================" + + # 显示 Shadow TLS + SS 配置 + if [[ -e ${STLS_File} ]]; then + read_stls_config + echo -e "${Info} Shadow TLS + Shadowsocks Rust 配置:" + echo -e "————————————————————————————————————————" + [[ "${ipv4}" != "IPv4_Error" ]] && echo -e " 服务器:${Green_font_prefix}${ipv4}${Font_color_suffix}" + [[ "${ipv6}" != "IPv6_Error" ]] && echo -e " 服务器:${Green_font_prefix}${ipv6}${Font_color_suffix}" + echo -e " Shadow TLS 端口:${Green_font_prefix}${stls_port}${Font_color_suffix}" + echo -e " Shadow TLS 密码:${Green_font_prefix}${stls_password}${Font_color_suffix}" + echo -e " Shadow TLS SNI:${Green_font_prefix}${stls_sni}${Font_color_suffix}" + echo -e " SS 本地端口:${Green_font_prefix}${port}${Font_color_suffix}" + echo -e " SS 密码:${Green_font_prefix}${password}${Font_color_suffix}" + echo -e " SS 加密:${Green_font_prefix}${cipher}${Font_color_suffix}" + echo -e "————————————————————————————————————————" + echo -e "${Info} Shadow TLS + SS Surge 配置:" + if [[ "${ipv4}" != "IPv4_Error" ]]; then + echo -e "$(uname -n) = ss, ${ipv4}, ${stls_port}, encrypt-method=${cipher}, password=${password}, shadow-tls-password=${stls_password}, shadow-tls-sni=${stls_sni}, shadow-tls-version=3, tfo=${tfo}, ecn=true, udp-relay=true, udp-port=${port}" + else + echo -e "$(uname -n) = ss, ${ipv6}, ${stls_port}, encrypt-method=${cipher}, password=${password}, shadow-tls-password=${stls_password}, shadow-tls-sni=${stls_sni}, shadow-tls-version=3, tfo=${tfo}, ecn=true, udp-relay=true, udp-port=${port}" + fi + echo && echo -e "========================================" + fi + + # 显示纯 SS 配置 + echo -e "${Info} 原始 Shadowsocks Rust 配置:" + echo -e "————————————————————————————————————————" + [[ "${ipv4}" != "IPv4_Error" ]] && echo -e " 地址:${Green_font_prefix}${ipv4}${Font_color_suffix}" + [[ "${ipv6}" != "IPv6_Error" ]] && echo -e " 地址:${Green_font_prefix}${ipv6}${Font_color_suffix}" + echo -e " 端口:${Green_font_prefix}${port}${Font_color_suffix}" + echo -e " 密码:${Green_font_prefix}${password}${Font_color_suffix}" + echo -e " 加密:${Green_font_prefix}${cipher}${Font_color_suffix}" + echo -e "————————————————————————————————————————" + [[ ! -z "${link_ipv4}" ]] && echo -e "${link_ipv4}" + [[ ! -z "${link_ipv6}" ]] && echo -e "${link_ipv6}" + echo -e "—————————————————————————" + echo -e "${Info} SS Surge 配置:" + if [[ "${ipv4}" != "IPv4_Error" ]]; then + echo -e "$(uname -n) = ss, ${ipv4}, ${port}, encrypt-method=${cipher}, password=${password}, tfo=${tfo}, udp-relay=true, ecn=true" + fi + echo -e "========================================" + echo && echo -n " 按回车键继续..." && read +} + +# 综合 Shadow TLS 配置函数 +set_stls_config(){ + check_stls_installed_status + read_config + read_stls_config + + echo && echo -e "你要修改哪项 Shadow TLS 配置? +======================================== + ${Green_font_prefix}1.${Font_color_suffix} 修改 端口配置 + ${Green_font_prefix}2.${Font_color_suffix} 修改 密码配置 + ${Green_font_prefix}3.${Font_color_suffix} 修改 Shadow TLS SNI 域名 + ${Green_font_prefix}4.${Font_color_suffix} 修改 FastOpen 配置 + ${Green_font_prefix}5.${Font_color_suffix} 修改 Strict 模式配置 + ${Green_font_prefix}6.${Font_color_suffix} 修改 TLS Wildcard SNI 配置 + ${Green_font_prefix}7.${Font_color_suffix} 修改 回退域名配置 + ${Green_font_prefix}8.${Font_color_suffix} 管理 Dispatch 分发配置 +======================================== + ${Green_font_prefix}9.${Font_color_suffix} 修改 全部配置" && echo + read -e -p "(默认:取消):" modify + [[ -z "${modify}" ]] && echo "已取消..." && return + + case "${modify}" in + 1) + set_stls_port + write_stls_config + restart_stls + ;; + 2) + set_stls_password + write_stls_config + restart_stls + ;; + 3) + set_stls_sni + write_stls_config + restart_stls + ;; + 4) + set_stls_fastopen + write_stls_config + restart_stls + ;; + 5) + set_stls_strict + write_stls_config + restart_stls + ;; + 6) + set_stls_tls_wildcard_sni + write_stls_config + restart_stls + ;; + 7) + set_stls_fallback + write_stls_config + restart_stls + ;; + 8) + manage_stls_dispatch + if [[ ! -z "${stls_dispatch}" ]]; then + write_stls_config + restart_stls + fi + ;; + 9) + set_stls_port + set_stls_password + set_stls_sni + set_stls_fastopen + set_stls_strict + set_stls_tls_wildcard_sni + set_stls_fallback + manage_stls_dispatch + write_stls_config + restart_stls + ;; + *) + echo -e "${Error} 请输入正确的数字(1-9)" + set_stls_config + ;; + esac +} + +# Dispatch 管理的简化版本 +manage_stls_dispatch(){ + echo -e "${Info} 当前 dispatch 配置:" + if [[ -e ${STLS_Conf} ]]; then + echo -e "${Green_font_prefix}$(cat ${STLS_Conf}|jq -r '.server.tls_addr.dispatch | to_entries | map(" \(.key) -> \(.value)") | join("\n")')${Font_color_suffix}" + else + echo -e " ${stls_sni} -> 1.1.1.1:443" + echo -e " captive.apple.com -> captive.apple.com:443" + fi + + echo && echo -e "是否需要修改 dispatch 配置? +======================================== +${Green_font_prefix} 1.${Font_color_suffix} 使用默认配置(推荐) +${Green_font_prefix} 2.${Font_color_suffix} 自定义配置所有条目 +========================================" + read -e -p "(默认:1.使用默认):" dispatch_choice + [[ -z "${dispatch_choice}" ]] && dispatch_choice="1" + + if [[ "${dispatch_choice}" == "1" ]]; then + # 使用默认配置,确保使用当前设置的 SNI + echo -e "${Info} 使用默认 dispatch 配置,SNI: ${stls_sni}" + # 不设置 stls_dispatch,让 write_stls_config 使用默认逻辑 + stls_dispatch="" + elif [[ "${dispatch_choice}" == "2" ]]; then + echo -e "${Info} 重新配置 dispatch 条目" + echo "请输入 dispatch 配置 (每行格式: 域名:目标地址,回车结束):" + echo "示例: cloudflare.com:1.1.1.1:443" + + local dispatch_entries="" + local line_count=0 + while true; do + read -e -p "条目 $((line_count+1)) (直接回车结束):" dispatch_entry + if [[ -z "${dispatch_entry}" ]]; then + break + fi + + if [[ "${dispatch_entry}" =~ ^([^:]+):(.+)$ ]]; then + local sni="${BASH_REMATCH[1]}" + local target="${BASH_REMATCH[2]}" + + if [[ ${line_count} -gt 0 ]]; then + dispatch_entries="${dispatch_entries}, + " + fi + dispatch_entries="${dispatch_entries}\"${sni}\": \"${target}\"" + line_count=$((line_count+1)) + else + echo -e "${Error} 格式错误,请使用格式: 域名:目标地址" + fi + done + + if [[ ${line_count} -gt 0 ]]; then + stls_dispatch="${dispatch_entries}" + echo -e "${Info} 已配置 ${line_count} 个 dispatch 条目" + else + echo -e "${Info} 没有输入条目,使用默认 dispatch 配置" + # 不设置 stls_dispatch,让 write_stls_config 使用默认逻辑 + stls_dispatch="" + fi + fi +} + +# 增强版 Dispatch 管理(支持单条增删改) +manage_stls_dispatch_advanced(){ + echo -e "${Info} 当前 dispatch 配置:" + if [[ -e ${STLS_Conf} ]]; then + echo -e "${Green_font_prefix}$(cat ${STLS_Conf}|jq -r '.server.tls_addr.dispatch | to_entries | map(" \(.key) -> \(.value)") | join("\n")')${Font_color_suffix}" + else + echo -e " ${stls_sni} -> 1.1.1.1:443" + echo -e " captive.apple.com -> captive.apple.com:443" + fi + + echo && echo -e "请选择 dispatch 管理操作: +======================================== +${Green_font_prefix} 1.${Font_color_suffix} 保持当前配置 +${Green_font_prefix} 2.${Font_color_suffix} 添加新条目 +${Green_font_prefix} 3.${Font_color_suffix} 删除指定条目 +${Green_font_prefix} 4.${Font_color_suffix} 修改指定条目 +${Green_font_prefix} 5.${Font_color_suffix} 重新配置所有条目 +========================================" + read -e -p "(默认:1.保持当前):" dispatch_choice + [[ -z "${dispatch_choice}" ]] && dispatch_choice="1" + + case "${dispatch_choice}" in + 1) + echo -e "${Info} 保持当前配置" + ;; + 2) + add_dispatch_entry + ;; + 3) + delete_dispatch_entry + ;; + 4) + modify_dispatch_entry + ;; + 5) + reconfigure_all_dispatch + ;; + *) + echo -e "${Error} 输入错误,保持当前配置" + ;; + esac +} + +# 添加 dispatch 条目 +add_dispatch_entry(){ + echo -e "${Info} 添加新的 dispatch 条目" + read -e -p "请输入域名 (如: example.com):" new_sni + if [[ -z "${new_sni}" ]]; then + echo -e "${Error} 域名不能为空" + return + fi + + read -e -p "请输入目标地址 (如: 1.1.1.1:443):" new_target + if [[ -z "${new_target}" ]]; then + echo -e "${Error} 目标地址不能为空" + return + fi + + # 获取当前 dispatch 配置 + if [[ -e ${STLS_Conf} ]]; then + current_dispatch=$(cat ${STLS_Conf}|jq -r '.server.tls_addr.dispatch | to_entries | map("\"\(.key)\": \"\(.value)\"") | join(",\n ")') + stls_dispatch="${current_dispatch}, + \"${new_sni}\": \"${new_target}\"" + else + stls_dispatch="\"${stls_sni}\": \"1.1.1.1:443\", + \"captive.apple.com\": \"captive.apple.com:443\", + \"${new_sni}\": \"${new_target}\"" + fi + + echo -e "${Info} 已添加条目: ${new_sni} -> ${new_target}" +} + +# 删除 dispatch 条目 +delete_dispatch_entry(){ + if [[ ! -e ${STLS_Conf} ]]; then + echo -e "${Error} 配置文件不存在" + return + fi + + echo -e "${Info} 当前 dispatch 条目:" + # 显示带编号的列表 + local domains=($(cat ${STLS_Conf}|jq -r '.server.tls_addr.dispatch | keys[]')) + local targets=($(cat ${STLS_Conf}|jq -r '.server.tls_addr.dispatch | to_entries | map(.value) | .[]')) + + if [[ ${#domains[@]} -eq 0 ]]; then + echo -e "${Error} 没有可删除的条目" + return + fi + + for i in "${!domains[@]}"; do + echo -e " ${Green_font_prefix}$((i+1)).${Font_color_suffix} ${domains[i]} -> ${targets[i]}" + done + + read -e -p "请输入要删除的条目编号 (1-${#domains[@]}):" del_num + if [[ -z "${del_num}" ]] || [[ ${del_num} -lt 1 ]] || [[ ${del_num} -gt ${#domains[@]} ]]; then + echo -e "${Error} 输入的编号无效" + return + fi + + # 重新构建 dispatch 配置,排除删除的条目 + local del_domain="${domains[$((del_num-1))]}" + local new_entries="" + local count=0 + + for i in "${!domains[@]}"; do + if [[ "${domains[i]}" != "${del_domain}" ]]; then + if [[ ${count} -gt 0 ]]; then + new_entries="${new_entries}, + " + fi + new_entries="${new_entries}\"${domains[i]}\": \"${targets[i]}\"" + count=$((count+1)) + fi + done + + if [[ ${count} -eq 0 ]]; then + # 如果删除后没有条目,使用默认配置 + stls_dispatch="\"${stls_sni}\": \"1.1.1.1:443\", + \"captive.apple.com\": \"captive.apple.com:443\"" + else + stls_dispatch="${new_entries}" + fi + + echo -e "${Info} 已删除条目: ${del_domain}" +} + +# 修改 dispatch 条目 +modify_dispatch_entry(){ + if [[ ! -e ${STLS_Conf} ]]; then + echo -e "${Error} 配置文件不存在" + return + fi + + echo -e "${Info} 当前 dispatch 条目:" + # 显示带编号的列表 + local domains=($(cat ${STLS_Conf}|jq -r '.server.tls_addr.dispatch | keys[]')) + local targets=($(cat ${STLS_Conf}|jq -r '.server.tls_addr.dispatch | to_entries | map(.value) | .[]')) + + if [[ ${#domains[@]} -eq 0 ]]; then + echo -e "${Error} 没有可修改的条目" + return + fi + + for i in "${!domains[@]}"; do + echo -e " ${Green_font_prefix}$((i+1)).${Font_color_suffix} ${domains[i]} -> ${targets[i]}" + done + + read -e -p "请输入要修改的条目编号 (1-${#domains[@]}):" mod_num + if [[ -z "${mod_num}" ]] || [[ ${mod_num} -lt 1 ]] || [[ ${mod_num} -gt ${#domains[@]} ]]; then + echo -e "${Error} 输入的编号无效" + return + fi + + local mod_domain="${domains[$((mod_num-1))]}" + local mod_target="${targets[$((mod_num-1))]}" + + echo -e "${Info} 当前条目: ${mod_domain} -> ${mod_target}" + read -e -p "请输入新的域名 (直接回车保持不变):" new_domain + read -e -p "请输入新的目标地址 (直接回车保持不变):" new_target + + [[ -z "${new_domain}" ]] && new_domain="${mod_domain}" + [[ -z "${new_target}" ]] && new_target="${mod_target}" + + # 重新构建 dispatch 配置 + local new_entries="" + for i in "${!domains[@]}"; do + if [[ ${i} -gt 0 ]]; then + new_entries="${new_entries}, + " + fi + + if [[ ${i} -eq $((mod_num-1)) ]]; then + new_entries="${new_entries}\"${new_domain}\": \"${new_target}\"" + else + new_entries="${new_entries}\"${domains[i]}\": \"${targets[i]}\"" + fi + done + + stls_dispatch="${new_entries}" + echo -e "${Info} 已修改条目: ${mod_domain} -> ${mod_target} => ${new_domain} -> ${new_target}" +} + +# 重新配置所有 dispatch 条目 +reconfigure_all_dispatch(){ + echo -e "${Info} 重新配置所有 dispatch 条目" + echo "请输入 dispatch 配置 (每行格式: 域名:目标地址,回车结束):" + echo "示例: cloudflare.com:1.1.1.1:443" + + local dispatch_entries="" + local line_count=0 + while true; do + read -e -p "条目 $((line_count+1)) (直接回车结束):" dispatch_entry + if [[ -z "${dispatch_entry}" ]]; then + break + fi + + if [[ "${dispatch_entry}" =~ ^([^:]+):(.+)$ ]]; then + local sni="${BASH_REMATCH[1]}" + local target="${BASH_REMATCH[2]}" + + if [[ ${line_count} -gt 0 ]]; then + dispatch_entries="${dispatch_entries}, + " + fi + dispatch_entries="${dispatch_entries}\"${sni}\": \"${target}\"" + line_count=$((line_count+1)) + else + echo -e "${Error} 格式错误,请使用格式: 域名:目标地址" + fi + done + + if [[ ${line_count} -gt 0 ]]; then + stls_dispatch="${dispatch_entries}" + echo -e "${Info} 已配置 ${line_count} 个 dispatch 条目" + else + echo -e "${Info} 使用默认 dispatch 配置" + stls_dispatch="\"${stls_sni}\": \"1.1.1.1:443\", + \"captive.apple.com\": \"captive.apple.com:443\"" + fi +} + +start(){ + check_installed_status + check_status + if [[ "$status" == "running" ]]; then + echo -e "${Info} Shadowsocks Rust 已在运行!" + else + systemctl start ss-rust + check_status + if [[ "$status" == "running" ]]; then + echo -e "${Info} Shadowsocks Rust 启动成功!" + else + echo -e "${Error} Shadowsocks Rust 启动失败!" + exit 1 + fi + fi + sleep 3s +} + +start_stls(){ + check_stls_installed_status + check_stls_status + if [[ "$stls_status" == "running" ]]; then + echo -e "${Info} Shadow TLS 已在运行!" + else + systemctl start shadowtls + check_stls_status + if [[ "$stls_status" == "running" ]]; then + echo -e "${Info} Shadow TLS 启动成功!" + else + echo -e "${Error} Shadow TLS 启动失败!" + exit 1 + fi + fi + sleep 3s +} + +stop(){ + check_installed_status + check_status + [[ !"$status" == "running" ]] && echo -e "${Error} Shadowsocks Rust 没有运行,请检查!" && exit 1 + systemctl stop ss-rust + sleep 3s + start_menu +} + +stop_stls(){ + check_stls_installed_status + check_stls_status + [[ !"$stls_status" == "running" ]] && echo -e "${Error} Shadow TLS 没有运行,请检查!" && exit 1 + systemctl stop shadowtls + sleep 3s +} + +restart(){ + check_installed_status + systemctl restart ss-rust + echo -e "${Info} Shadowsocks Rust 重启完毕 !" + sleep 3s + start_menu +} + +restart_stls(){ + check_stls_installed_status + systemctl restart shadowtls + echo -e "${Info} Shadow TLS 重启完毕 !" + sleep 3s +} + +update(){ + check_installed_status + check_new_ver + check_ver_comparison + echo -e "${Info} Shadowsocks Rust 更新完毕!" + sleep 3s + start_menu +} + +update_stls(){ + check_stls_installed_status + check_stls_new_ver + check_stls_ver_comparison + echo -e "${Info} Shadow TLS 更新完毕!" + sleep 3s +} + +# 脚本更新函数 +update_sh(){ + echo -e "当前版本为 [ ${sh_ver} ],开始检测最新版本..." + sh_new_ver=$(wget --no-check-certificate -qO- "https://raw.githubusercontent.com/xOS/Shadowsocks-Rust/master/ss-rust.sh"|grep 'sh_ver="'|awk -F "=" '{print $NF}'|sed 's/\"//g'|head -1) + [[ -z ${sh_new_ver} ]] && echo -e "${Error} 检测最新版本失败 !" && start_menu + if [[ ${sh_new_ver} != ${sh_ver} ]]; then + echo -e "发现新版本[ ${sh_new_ver} ],是否更新?[Y/n]" + read -p "(默认:y):" yn + [[ -z "${yn}" ]] && yn="y" + if [[ ${yn} == [Yy] ]]; then + wget -O ss-rust.sh --no-check-certificate https://raw.githubusercontent.com/xOS/Shadowsocks-Rust/master/ss-rust.sh && chmod +x ss-rust.sh + echo -e "脚本已更新为最新版本[ ${sh_new_ver} ]!" + echo -e "3s后执行新脚本" + sleep 3s + bash ss-rust.sh + else + echo && echo " 已取消..." && echo + sleep 3s + start_menu + fi + else + echo -e "当前已是最新版本[ ${sh_new_ver} ] !" + sleep 3s + start_menu + fi + sleep 3s + bash ss-rust.sh +} + +uninstall(){ + check_installed_status + echo "确定要卸载 Shadowsocks Rust ? (y/N)" + echo + read -e -p "(默认:n):" unyn + [[ -z ${unyn} ]] && unyn="n" + if [[ ${unyn} == [Yy] ]]; then + check_status + [[ "$status" == "running" ]] && systemctl stop ss-rust + systemctl disable ss-rust + rm -rf "${SS_Folder}" + rm -rf "${SS_File}" + echo && echo "Shadowsocks Rust 卸载完成!" && echo + else + echo && echo "卸载已取消..." && echo + fi + sleep 3s + start_menu +} + +uninstall_stls(){ + check_stls_installed_status + echo "确定要卸载 Shadow TLS ? (y/N)" + echo + read -e -p "(默认:n):" unyn + [[ -z ${unyn} ]] && unyn="n" + if [[ ${unyn} == [Yy] ]]; then + check_stls_status + [[ "$stls_status" == "running" ]] && systemctl stop shadowtls + systemctl disable shadowtls + rm -rf "${STLS_Folder}" + rm -rf "${STLS_File}" + rm -f "/etc/systemd/system/shadowtls.service" + systemctl daemon-reload + echo && echo "Shadow TLS 卸载完成!" && echo + else + echo && echo "卸载已取消..." && echo + fi + sleep 3s +} + +getipv4(){ + ipv4=$(wget -qO- -4 -t1 -T2 ipinfo.io/ip) + if [[ -z "${ipv4}" ]]; then + ipv4=$(wget -qO- -4 -t1 -T2 api.ip.sb/ip) + if [[ -z "${ipv4}" ]]; then + ipv4=$(wget -qO- -4 -t1 -T2 members.3322.org/dyndns/getip) + if [[ -z "${ipv4}" ]]; then + ipv4="IPv4_Error" + fi + fi + fi +} + +getipv6(){ + ipv6=$(wget -qO- -6 -t1 -T2 ifconfig.co) + if [[ -z "${ipv6}" ]]; then + ipv6="IPv6_Error" + fi +} + +urlsafe_base64(){ + date=$(echo -n "$1"|base64|sed ':a;N;s/\n/ /g;ta'|sed 's/ //g;s/=//g;s/+/-/g;s/\//_/g') + echo -e "${date}" +} + +link_qr(){ + if [[ "${ipv4}" != "IPv4_Error" ]]; then + SSbase64=$(urlsafe_base64 "${cipher}:${password}@${ipv4}:${port}") + SSurl="ss://${SSbase64}" + SSQRcode="https://cli.im/api/qrcode/code?text=${SSurl}" + link_ipv4=" 链接 [IPv4]:${Red_font_prefix}${SSurl}${Font_color_suffix} \n 二维码[IPv4]:${Red_font_prefix}${SSQRcode}${Font_color_suffix}" + fi + if [[ "${ipv6}" != "IPv6_Error" ]]; then + SSbase64=$(urlsafe_base64 "${cipher}:${password}@${ipv6}:${port}") + SSurl="ss://${SSbase64}" + SSQRcode="https://cli.im/api/qrcode/code?text=${SSurl}" + link_ipv6=" 链接 [IPv6]:${Red_font_prefix}${SSurl}${Font_color_suffix} \n 二维码[IPv6]:${Red_font_prefix}${SSQRcode}${Font_color_suffix}" + fi +} + +before_start_menu(){ + echo && echo -n " 任意键继续..." && read + start_menu +} + +before_shadowtls_menu(){ + echo && echo -n " 任意键继续..." && read + shadowtls_menu +} + +# Shadow TLS 状态和版本检查函数 +check_stls_installed_status(){ + [[ ! -e ${STLS_File} ]] && echo -e "${Error} Shadow TLS 没有安装,请检查!" && exit 1 +} + +check_stls_status(){ + stls_status=`systemctl status shadowtls | grep Active | awk '{print $3}' | cut -d "(" -f2 | cut -d ")" -f1` +} + +check_stls_new_ver(){ + stls_new_ver=$(wget -qO- https://api.github.com/repos/ihciah/shadow-tls/releases| jq -r '[.[] | select(.prerelease == false) | select(.draft == false) | .tag_name] | .[0]') + [[ -z ${stls_new_ver} ]] && echo -e "${Error} Shadow TLS 最新版本获取失败!" && exit 1 + echo -e "${Info} 检测到 Shadow TLS 最新版本为 [ ${stls_new_ver} ]" +} + +check_stls_ver_comparison(){ + stls_now_ver=$(cat ${STLS_Now_ver_File}) + if [[ "${stls_now_ver}" != "${stls_new_ver}" ]]; then + echo -e "${Info} 发现 Shadow TLS 已有新版本 [ ${stls_new_ver} ],旧版本 [ ${stls_now_ver} ]" + read -e -p "是否更新 ? [Y/n]:" yn + [[ -z "${yn}" ]] && yn="y" + if [[ $yn == [Yy] ]]; then + check_stls_status + \cp "${STLS_Conf}" "/tmp/stls_config.json" + download_stls + mv -f "/tmp/stls_config.json" "${STLS_Conf}" + restart_stls + fi + else + echo -e "${Info} 当前 Shadow TLS 已是最新版本 [ ${stls_new_ver} ] !" && exit 1 + fi +} + +# Shadow TLS 专用菜单 +shadowtls_menu(){ + check_root + check_sys + sys_arch + + # 检查 Shadow TLS 安装状态 + if [[ -e ${STLS_File} ]]; then + check_stls_status + if [[ "$stls_status" == "running" ]]; then + stls_status_show="${Green_font_prefix}已安装${Font_color_suffix} 且 ${Green_font_prefix}运行中${Font_color_suffix}" + else + stls_status_show="${Green_font_prefix}已安装${Font_color_suffix} 但 ${Yellow_font_prefix}未运行${Font_color_suffix}" + fi + else + stls_status_show="${Red_font_prefix}未安装${Font_color_suffix}" + fi + + clear + echo -e "Shadow TLS 管理菜单 ${Red_font_prefix}[v${sh_ver}]${Font_color_suffix} + +==================状态================== + Shadow TLS : [${stls_status_show}] +======================================== + ${Green_font_prefix}0.${Font_color_suffix} 更新脚本 +==================菜单================== + ${Green_font_prefix}1.${Font_color_suffix} 安装 Shadow TLS + ${Green_font_prefix}2.${Font_color_suffix} 更新 Shadow TLS + ${Green_font_prefix}3.${Font_color_suffix} 卸载 Shadow TLS +———————————————————————————————————————— + ${Green_font_prefix}4.${Font_color_suffix} 启动 Shadow TLS + ${Green_font_prefix}5.${Font_color_suffix} 停止 Shadow TLS + ${Green_font_prefix}6.${Font_color_suffix} 重启 Shadow TLS +———————————————————————————————————————— + ${Green_font_prefix}7.${Font_color_suffix} 修改 Shadow TLS 配置 + ${Green_font_prefix}8.${Font_color_suffix} 查看 Shadow TLS 配置 + ${Green_font_prefix}9.${Font_color_suffix} 查看 Shadow TLS 状态 +———————————————————————————————————————— + ${Green_font_prefix}10.${Font_color_suffix} 查看完整配置信息 + ${Green_font_prefix}11.${Font_color_suffix} 返回主菜单 +———————————————————————————————————————— + ${Green_font_prefix}00.${Font_color_suffix} 退出脚本 +========================================" && echo + + read -e -p " 请输入数字 [0-11]:" stls_num + case "$stls_num" in + 1) + install_stls + shadowtls_menu + ;; + 2) + update_stls + shadowtls_menu + ;; + 3) + uninstall_stls + shadowtls_menu + ;; + 4) + start_stls + shadowtls_menu + ;; + 5) + stop_stls + shadowtls_menu + ;; + 6) + restart_stls + shadowtls_menu + ;; + 7) + set_stls_config + shadowtls_menu + ;; + 8) + view_stls_only + ;; + 9) + view_stls_status + ;; + 10) + view_combined_config shadowtls + ;; + 11) + start_menu + ;; + 0) + update_sh + ;; + 00) + exit 1 + ;; + *) + echo -e "${Error} 请输入正确数字 [0-11] (退出输入00)" + sleep 5s + shadowtls_menu + ;; + esac +} + +# 查看 Shadowsocks Rust 状态函数 +view_ss_status(){ + check_installed_status + + echo -e "${Info} 正在获取 Shadowsocks Rust 状态信息..." + echo + echo "==================================" + echo -e " Shadowsocks Rust 服务状态" + echo "==================================" + + systemctl status ss-rust + + echo "==================================" + echo + read -e -p "按回车键返回主菜单..." + start_menu +} + +# 查看 Shadow TLS 状态函数 +view_stls_status(){ + check_stls_installed_status + + echo -e "${Info} 正在获取 Shadow TLS 状态信息..." + echo + echo "==================================" + echo -e " Shadow TLS 服务状态" + echo "==================================" + + systemctl status shadowtls + + echo "==================================" + echo + read -e -p "按回车键返回 Shadow TLS 菜单..." + shadowtls_menu +} + +# 查看 Shadow TLS 配置函数 +view_stls_only(){ + check_stls_installed_status + echo -e "${Info} 正在获取 Shadow TLS 配置信息..." + echo + echo "==================================" + echo -e " Shadow TLS 配置信息" + echo "==================================" + + if [[ -f "$STLS_Conf" ]]; then + cat "$STLS_Conf" + else + echo -e "${Error} Shadow TLS 配置文件不存在!" + fi + + echo "==================================" + echo + read -e -p "按回车键返回 Shadow TLS 菜单..." + shadowtls_menu +} + +# 主菜单函数 +start_menu(){ + echo -e "${Info} 正在启动 Shadowsocks Rust 管理脚本..." + check_root + echo -e "${Info} 权限检查完成,正在检测系统..." + check_sys + echo -e "${Info} 系统检测完成,正在检测架构..." + sys_arch + echo -e "${Info} 架构检测完成,正在检查服务状态..." + + # 检查安装状态 + if [[ -e ${SS_File} ]]; then + check_status + if [[ "$status" == "running" ]]; then + ss_status_show="${Green_font_prefix}已安装${Font_color_suffix} 且 ${Green_font_prefix}运行中${Font_color_suffix}" + else + ss_status_show="${Green_font_prefix}已安装${Font_color_suffix} 但 ${Yellow_font_prefix}未运行${Font_color_suffix}" + fi + else + ss_status_show="${Red_font_prefix}未安装${Font_color_suffix}" + fi + + # 检查 Shadow TLS 安装状态 + if [[ -e ${STLS_File} ]]; then + check_stls_status + if [[ "$stls_status" == "running" ]]; then + stls_status_show="${Green_font_prefix}已安装${Font_color_suffix} 且 ${Green_font_prefix}运行中${Font_color_suffix}" + else + stls_status_show="${Green_font_prefix}已安装${Font_color_suffix} 但 ${Yellow_font_prefix}未运行${Font_color_suffix}" + fi + else + stls_status_show="${Red_font_prefix}未安装${Font_color_suffix}" + fi + + clear + echo -e "Shadowsocks Rust 管理脚本 ${Red_font_prefix}[v${sh_ver}]${Font_color_suffix} + +==================状态================== + Shadowsocks Rust : [${ss_status_show}] + Shadow TLS : [${stls_status_show}] +======================================== + ${Green_font_prefix}0.${Font_color_suffix} 更新脚本 +==================菜单================== + ${Green_font_prefix}1.${Font_color_suffix} 安装 Shadowsocks Rust + ${Green_font_prefix}2.${Font_color_suffix} 更新 Shadowsocks Rust + ${Green_font_prefix}3.${Font_color_suffix} 卸载 Shadowsocks Rust +———————————————————————————————————————— + ${Green_font_prefix}4.${Font_color_suffix} 启动 Shadowsocks Rust + ${Green_font_prefix}5.${Font_color_suffix} 停止 Shadowsocks Rust + ${Green_font_prefix}6.${Font_color_suffix} 重启 Shadowsocks Rust +———————————————————————————————————————— + ${Green_font_prefix}7.${Font_color_suffix} 配置 Shadowsocks Rust 相关 + ${Green_font_prefix}8.${Font_color_suffix} 查看 Shadowsocks Rust 配置 + ${Green_font_prefix}9.${Font_color_suffix} 查看 Shadowsocks Rust 状态 +======================================== + ${Green_font_prefix}10.${Font_color_suffix} 配置 Shadow TLS 相关 + ${Green_font_prefix}11.${Font_color_suffix} 查看完整配置信息 +———————————————————————————————————————— + ${Green_font_prefix}00.${Font_color_suffix} 退出脚本 +========================================" && echo + read -e -p " 请输入数字 [0-11]:" num + case "$num" in + 1) + install + ;; + 2) + update + start_menu + ;; + 3) + uninstall + start_menu + ;; + 4) + start + start_menu + ;; + 5) + stop + start_menu + ;; + 6) + restart + start_menu + ;; + 7) + set_config + ;; + 8) + view + ;; + 9) + view_ss_status + ;; + 10) + shadowtls_menu + ;; + 11) + view_combined_config + ;; + 0) + update_sh + ;; + 00) + exit 1 + ;; + *) + echo -e "${Error} 请输入正确数字 [0-11] (退出输入00)" + sleep 5s + start_menu + ;; + esac +} + +# 脚本执行入口 +start_menu