diff --git a/tcp.sh b/tcp.sh new file mode 100644 index 0000000..20be982 --- /dev/null +++ b/tcp.sh @@ -0,0 +1,2337 @@ +#!/usr/bin/env bash +PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin +export PATH +#================================================= +# System Required: CentOS 7/8,Debian/ubuntu,oraclelinux +# Description: BBR+BBRplus+Lotserver +# Version: 100.0.4.2 +# Author: 千影,cx9208,YLX +# 更新内容及反馈: https://blog.ylx.me/archives/783.html +#================================================= + +# RED='\033[0;31m' +# GREEN='\033[0;32m' +# YELLOW='\033[0;33m' +# SKYBLUE='\033[0;36m' +# PLAIN='\033[0m' + +sh_ver="100.0.4.2" +github="raw.githubusercontent.com/ylx2016/Linux-NetSpeed/master" + +imgurl="" +headurl="" +github_network=1 + +Green_font_prefix="\033[32m" +Red_font_prefix="\033[31m" +Font_color_suffix="\033[0m" +Info="${Green_font_prefix}[信息]${Font_color_suffix}" +Error="${Red_font_prefix}[错误]${Font_color_suffix}" +Tip="${Green_font_prefix}[注意]${Font_color_suffix}" + +if [ -f "/etc/sysctl.d/bbr.conf" ]; then + rm -rf /etc/sysctl.d/bbr.conf +fi + +# 检查当前用户是否为 root 用户 +if [ "$EUID" -ne 0 ]; then + echo "请使用 root 用户身份运行此脚本" + exit +fi + +#优化系统配置 +optimizing_system_old() { + if [ ! -f "/etc/sysctl.d/99-sysctl.conf" ]; then + touch /etc/sysctl.d/99-sysctl.conf + fi + sed -i '/net.ipv4.tcp_retries2/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_slow_start_after_idle/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_fastopen/d' /etc/sysctl.conf + sed -i '/fs.file-max/d' /etc/sysctl.conf + sed -i '/fs.inotify.max_user_instances/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_syncookies/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_fin_timeout/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_tw_reuse/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_max_syn_backlog/d' /etc/sysctl.conf + sed -i '/net.ipv4.ip_local_port_range/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_max_tw_buckets/d' /etc/sysctl.conf + sed -i '/net.ipv4.route.gc_timeout/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_synack_retries/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_syn_retries/d' /etc/sysctl.conf + sed -i '/net.core.somaxconn/d' /etc/sysctl.conf + sed -i '/net.core.netdev_max_backlog/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_timestamps/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_max_orphans/d' /etc/sysctl.conf + sed -i '/net.ipv4.ip_forward/d' /etc/sysctl.conf + + echo "net.ipv4.tcp_retries2 = 8 +net.ipv4.tcp_slow_start_after_idle = 0 +fs.file-max = 1000000 +fs.inotify.max_user_instances = 8192 +net.ipv4.tcp_syncookies = 1 +net.ipv4.tcp_fin_timeout = 30 +net.ipv4.tcp_tw_reuse = 1 +net.ipv4.ip_local_port_range = 1024 65000 +net.ipv4.tcp_max_syn_backlog = 16384 +net.ipv4.tcp_max_tw_buckets = 6000 +net.ipv4.route.gc_timeout = 100 +net.ipv4.tcp_syn_retries = 1 +net.ipv4.tcp_synack_retries = 1 +net.core.somaxconn = 32768 +net.core.netdev_max_backlog = 32768 +net.ipv4.tcp_timestamps = 0 +net.ipv4.tcp_max_orphans = 32768 +# forward ipv4 +#net.ipv4.ip_forward = 1" >>/etc/sysctl.d/99-sysctl.conf + sysctl -p + echo "* soft nofile 1000000 +* hard nofile 1000000" >/etc/security/limits.conf + echo "ulimit -SHn 1000000" >>/etc/profile + read -p "需要重启VPS后,才能生效系统优化配置,是否现在重启 ? [Y/n] :" yn + [ -z "${yn}" ] && yn="y" + if [[ $yn == [Yy] ]]; then + echo -e "${Info} VPS 重启中..." + reboot + fi +} + +optimizing_system_johnrosen1() { + if [ ! -f "/etc/sysctl.d/99-sysctl.conf" ]; then + touch /etc/sysctl.d/99-sysctl.conf + fi + sed -i '/net.ipv4.tcp_fack/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_early_retrans/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.neigh.default.unres_qlen/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_max_orphans/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.netfilter.nf_conntrack_buckets/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/kernel.pid_max/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/vm.nr_hugepages/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.optmem_max/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.all.route_localnet/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.ip_forward/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.all.forwarding/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.default.forwarding/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.all.forwarding/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.default.forwarding/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.lo.forwarding/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.all.disable_ipv6/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.default.disable_ipv6/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.lo.disable_ipv6/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.all.accept_ra/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.default.accept_ra/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.netdev_max_backlog/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.netdev_budget/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.netdev_budget_usecs/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/fs.file-max /d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.rmem_max/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.wmem_max/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.rmem_default/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.wmem_default/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.somaxconn/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.icmp_echo_ignore_all/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.icmp_echo_ignore_broadcasts/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.icmp_ignore_bogus_error_responses/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.all.accept_redirects/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.default.accept_redirects/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.all.secure_redirects/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.default.secure_redirects/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.all.send_redirects/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.default.send_redirects/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.default.rp_filter/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.all.rp_filter/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_keepalive_time/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_keepalive_intvl/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_keepalive_probes/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_synack_retries/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_syncookies/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_rfc1337/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_timestamps/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_tw_reuse/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_fin_timeout/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.ip_local_port_range/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_max_tw_buckets/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_fastopen/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_rmem/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_wmem/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.udp_rmem_min/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.udp_wmem_min/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_mtu_probing/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.all.arp_ignore /d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.default.arp_ignore/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.all.arp_announce/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.conf.default.arp_announce/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_autocorking/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_slow_start_after_idle/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_max_syn_backlog/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.default_qdisc/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_congestion_control/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_notsent_lowat/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_no_metrics_save/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_ecn/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_ecn_fallback/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_frto/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.all.accept_redirects/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.default.accept_redirects/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/vm.swappiness/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.ip_unprivileged_port_start/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/vm.overcommit_memory/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.neigh.default.gc_thresh3/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.neigh.default.gc_thresh2/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.neigh.default.gc_thresh1/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.neigh.default.gc_thresh3/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.neigh.default.gc_thresh2/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.neigh.default.gc_thresh1/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.netfilter.nf_conntrack_max/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.nf_conntrack_max/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.netfilter.nf_conntrack_tcp_timeout_fin_wait/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.netfilter.nf_conntrack_tcp_timeout_time_wait/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.netfilter.nf_conntrack_tcp_timeout_close_wait/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.netfilter.nf_conntrack_tcp_timeout_established/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/fs.inotify.max_user_instances/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/fs.inotify.max_user_watches/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_low_latency/d' /etc/sysctl.d/99-sysctl.conf + + cat >'/etc/sysctl.d/99-sysctl.conf' </sys/kernel/mm/transparent_hugepage/enabled + + cat >'/etc/systemd/system.conf' <'/etc/security/limits.conf' <>/etc/profile + + if grep -q "pam_limits.so" /etc/pam.d/common-session; then + : + else + sed -i '/required pam_limits.so/d' /etc/pam.d/common-session + echo "session required pam_limits.so" >>/etc/pam.d/common-session + fi + systemctl daemon-reload + echo -e "${Info}优化方案2应用结束,可能需要重启!" +} + +# 函数:生成并写入 Linux 内核网络优化配置 +# 参数(可选,按顺序): +# $1: 延迟 (ms,默认 100) +# $2: 本地带宽 (Mbps,默认 1000) +# $3: VPS 带宽 (Mbps,默认 1000) +# $4: VPS 内存 (MB,默认自动获取) +optimizing_system_radicalizate() { + # 设置默认值 + local latency_default=100 + local local_bw_default=1000 + local vps_bw_default=1000 + local vps_mem_default=$(awk '/MemTotal/ {print int($2/1024)}' /proc/meminfo) + + # 提示用户输入参数 + echo -e "请输入参数(用空格隔开):延迟(ms) 本地带宽(Mbps) VPS带宽(Mbps) VPS内存(MB)" + echo -e "可以输入单个或多个参数,未提供的参数将使用默认值" + echo -e "示例1(完整参数):180 500 500 1024" + echo -e "示例2(单个参数):200 # 结果:延迟=200ms, 本地带宽=1000Mbps, VPS带宽=1000Mbps, 内存=自动获取" + echo -e "默认参数:延迟=$latency_default ms, 本地带宽=$local_bw_default Mbps, VPS带宽=$vps_bw_default Mbps, 内存=自动获取($vps_mem_default MB)" + echo -e "将在 12 秒后使用默认参数,请输入参数并按回车提交(留空使用默认值):" + + # 设置 12 秒倒计时,静默等待输入 + local input + if ! read -t 12 -r input; then + echo -e "\n未输入参数或超时,使用默认参数..." + local latency=$latency_default + local local_bw=$local_bw_default + local vps_bw=$vps_bw_default + local vps_mem=$vps_mem_default + else + # 分割用户输入 + read -r latency local_bw vps_bw vps_mem <<<"$input" + # 使用默认值补齐未提供的参数 + latency=${latency:-$latency_default} + local_bw=${local_bw:-$local_bw_default} + vps_bw=${vps_bw:-$vps_bw_default} + vps_mem=${vps_mem:-$vps_mem_default} + fi + + # 显示最终提交的参数信息 + echo -e "最终提交参数:延迟=$latency ms, 本地带宽=$local_bw Mbps, VPS带宽=$vps_bw Mbps, 内存=$vps_mem MB" + sleep 1 # 延迟 1 秒 + + # 检查输入参数是否有效 + if ! [[ "$latency" =~ ^[0-9]+$ ]] || ! [[ "$local_bw" =~ ^[0-9]+$ ]] || + ! [[ "$vps_bw" =~ ^[0-9]+$ ]] || ! [[ "$vps_mem" =~ ^[0-9]+$ ]]; then + echo -e "Error: All parameters must be positive integers." + return 1 + fi + + # 检查是否有 root 权限 + if [ "$EUID" -ne 0 ]; then + echo -e "Error: This script requires root privileges to write to /etc/sysctl.d/99-sysctl.conf." + return 1 + fi + + # 计算带宽延迟积 (BDP, 字节):min(local_bw, vps_bw) * latency / 8 + local min_bw=$((local_bw < vps_bw ? local_bw : vps_bw)) + local bdp=$((min_bw * 1000000 * latency / 8 / 1000)) + + # 根据内存和 BDP 设置缓冲区大小 (上限不超过内存的 50%),使用整数运算 + local rmem_max=$((bdp * 2)) # 2 倍 BDP + local wmem_max=$((bdp * 3 / 2)) # 1.5 倍 BDP + local max_mem_bytes=$((vps_mem * 1024 * 1024 * 50 / 100)) + [[ $rmem_max -gt $max_mem_bytes ]] && rmem_max=$max_mem_bytes + [[ $wmem_max -gt $max_mem_bytes ]] && wmem_max=$max_mem_bytes + [[ $rmem_max -lt 1048576 ]] && rmem_max=1048576 # 最小 1MB + [[ $wmem_max -lt 1048576 ]] && wmem_max=1048576 # 最小 1MB + + # 根据带宽和内存设置队列长度 + local netdev_max_backlog=$((min_bw * 10)) + [[ $netdev_max_backlog -gt 10000 ]] && netdev_max_backlog=10000 + [[ $netdev_max_backlog -lt 1000 ]] && netdev_max_backlog=1000 + local somaxconn=$((vps_mem * 20)) + [[ $somaxconn -gt 16384 ]] && somaxconn=16384 + [[ $somaxconn -lt 512 ]] && somaxconn=512 + local tcp_max_syn_backlog=$((somaxconn * 4)) + [[ $tcp_max_syn_backlog -gt 65536 ]] && tcp_max_syn_backlog=65536 + + # 根据延迟设置初始拥塞窗口 + local tcp_init_cwnd=$((latency / 20 + 10)) + [[ $tcp_init_cwnd -gt 32 ]] && tcp_init_cwnd=32 + [[ $tcp_init_cwnd -lt 10 ]] && tcp_init_cwnd=10 + + # 根据内存设置最小空闲内存 (约 10-15% 内存) + local min_free_kbytes=$((vps_mem * 1024 * 12 / 100)) + [[ $min_free_kbytes -gt 524288 ]] && min_free_kbytes=524288 # 最大 512MB + [[ $min_free_kbytes -lt 65536 ]] && min_free_kbytes=65536 # 最小 64MB + + # 目标配置文件 + local config_file="/etc/sysctl.d/99-sysctl.conf" + local temp_file=$(mktemp) + + # 定义所有参数和值(包含测试中发现的额外参数) + declare -A sysctl_params=( + ["kernel.pid_max"]="65535" + ["kernel.panic"]="1" + ["kernel.sysrq"]="1" + ["kernel.core_pattern"]="core_%e" + ["kernel.printk"]="3 4 1 3" + ["kernel.numa_balancing"]="0" + ["kernel.sched_autogroup_enabled"]="0" + ["vm.swappiness"]="5" + ["vm.dirty_ratio"]="5" + ["vm.dirty_background_ratio"]="2" + ["vm.panic_on_oom"]="1" + ["vm.overcommit_memory"]="1" + ["vm.min_free_kbytes"]="$min_free_kbytes" + ["net.core.netdev_max_backlog"]="$netdev_max_backlog" + ["net.core.rmem_max"]="$rmem_max" + ["net.core.wmem_max"]="$wmem_max" + ["net.core.rmem_default"]="262144" + ["net.core.wmem_default"]="262144" + ["net.core.somaxconn"]="$somaxconn" + ["net.core.optmem_max"]="262144" + ["net.netfilter.nf_conntrack_max"]="262144" + ["net.nf_conntrack_max"]="262144" + ["net.ipv4.tcp_fastopen"]="3" + ["net.ipv4.tcp_timestamps"]="1" + ["net.ipv4.tcp_tw_reuse"]="1" + ["net.ipv4.tcp_fin_timeout"]="10" + ["net.ipv4.tcp_slow_start_after_idle"]="0" + ["net.ipv4.tcp_max_tw_buckets"]="32768" + ["net.ipv4.tcp_sack"]="1" + ["net.ipv4.tcp_fack"]="1" + ["net.ipv4.tcp_rmem"]="32768 262144 $rmem_max" + ["net.ipv4.tcp_wmem"]="32768 262144 $wmem_max" + ["net.ipv4.tcp_mtu_probing"]="1" + ["net.ipv4.tcp_notsent_lowat"]="16384" + ["net.ipv4.tcp_window_scaling"]="1" + ["net.ipv4.tcp_adv_win_scale"]="2" + ["net.ipv4.tcp_moderate_rcvbuf"]="1" + ["net.ipv4.tcp_no_metrics_save"]="1" + ["net.ipv4.tcp_init_cwnd"]="$tcp_init_cwnd" + ["net.ipv4.tcp_max_syn_backlog"]="$tcp_max_syn_backlog" + ["net.ipv4.tcp_max_orphans"]="32768" + ["net.ipv4.tcp_synack_retries"]="2" + ["net.ipv4.tcp_syn_retries"]="2" + ["net.ipv4.tcp_abort_on_overflow"]="0" + ["net.ipv4.tcp_stdurg"]="0" + ["net.ipv4.tcp_rfc1337"]="0" + ["net.ipv4.tcp_syncookies"]="1" + ["net.ipv4.tcp_low_latency"]="1" + ["net.ipv4.ip_local_port_range"]="1024 65535" + ["net.ipv4.ip_no_pmtu_disc"]="0" + ["net.ipv4.route.gc_timeout"]="100" + ["net.ipv4.neigh.default.gc_stale_time"]="120" + ["net.ipv4.neigh.default.gc_thresh3"]="4096" + ["net.ipv4.neigh.default.gc_thresh2"]="2048" + ["net.ipv4.neigh.default.gc_thresh1"]="512" + ["net.ipv4.icmp_echo_ignore_broadcasts"]="1" + ["net.ipv4.icmp_ignore_bogus_error_responses"]="1" + ["net.ipv4.conf.all.rp_filter"]="1" + ["net.ipv4.conf.default.rp_filter"]="1" + ["net.ipv4.conf.all.arp_announce"]="2" + ["net.ipv4.conf.default.arp_announce"]="2" + ["net.ipv4.conf.all.arp_ignore"]="1" + ["net.ipv4.conf.default.arp_ignore"]="1" + ) + + # 添加文件头部注释 + echo "# Generated sysctl configuration" >"$temp_file" + echo "# Latency: $latency ms" >>"$temp_file" + echo "# Local Bandwidth: $local_bw Mbps" >>"$temp_file" + echo "# VPS Bandwidth: $vps_bw Mbps" >>"$temp_file" + echo "# VPS Memory: $vps_mem MB" >>"$temp_file" + echo "" >>"$temp_file" + + # 如果配置文件不存在,直接写入所有参数 + if [ ! -f "$config_file" ]; then + for key in "${!sysctl_params[@]}"; do + echo "$key=${sysctl_params[$key]}" >>"$temp_file" + done + else + # 读取现有配置文件内容,去除多余空格 + cp "$config_file" "$temp_file.bak" + while IFS='=' read -r key value; do + key=$(echo "$key" | xargs) # 去除首尾空格 + value=$(echo "$value" | xargs) # 去除值中的多余空格 + if [ -n "$key" ] && [[ ! "$key" =~ ^# ]]; then + if [[ -n "${sysctl_params[$key]}" ]]; then + echo "$key=${sysctl_params[$key]}" >>"$temp_file.new" + unset sysctl_params["$key"] + else + echo "$key=$value" >>"$temp_file.new" + fi + fi + done <"$config_file" + + # 将新参数追加到临时文件 + for key in "${!sysctl_params[@]}"; do + echo "$key=${sysctl_params[$key]}" >>"$temp_file.new" + done + + # 合并并替换临时文件 + cat "$temp_file" >"$temp_file.final" + cat "$temp_file.new" >>"$temp_file.final" + mv "$temp_file.final" "$temp_file" + rm -f "$temp_file.new" "$temp_file.bak" + fi + + # 写入目标文件并应用配置 + mv "$temp_file" "$config_file" + chmod 644 "$config_file" + sysctl -p "$config_file" >/dev/null 2>&1 + sysctl --system >/dev/null 2>&1 + echo -e "${Info}激进方案应用完毕,有些设置可能需要重启生效!" +} + +#处理传进来的参数 直接优化 +err() { + echo "错误: $1" + exit 1 +} + +while [ $# -gt 0 ]; do + case $1 in + op) + optimizing_system_old # 调用函数 + exit + ;; + op2) + optimizing_system_johnrosen1 # 调用函数 + exit + ;; + op3) + optimizing_system_radicalizate # 调用函数 + exit + ;; + *) + err "未知选项: \"$1\"" + ;; + esac + shift # 移动到下一个参数 +done + +# 检查github网络 +check_github() { + # 检测域名的可访问性函数 + check_domain() { + local domain="$1" + if ! curl --max-time 5 --head --silent --fail "$domain" >/dev/null; then + echo -e "${Error}无法访问 $domain,请检查网络或者本地DNS 或者访问频率过快而受限" + github_network=0 + fi + } + + # 检测所有域名的可访问性 + check_domain "https://raw.githubusercontent.com" + check_domain "https://api.github.com" + check_domain "https://github.com" + + if [ "$github_network" -eq 0 ]; then + echo -e "${Error}github网络访问受限,将影响内核的安装以及脚本的检查更新,1秒后继续运行脚本" + sleep 1 + else + # 所有域名均可访问,打印成功提示 + echo -e "${Green_font_prefix}github可访问${Font_color_suffix},继续执行脚本..." + fi +} + +#检查连接 +checkurl() { + local url="$1" + local maxRetries=3 + local retryDelay=2 + + if [[ -z "$url" ]]; then + echo "错误:缺少URL参数!" + exit 1 + fi + + local retries=0 + local responseCode="" + + while [[ -z "$responseCode" && $retries -lt $maxRetries ]]; do + responseCode=$(curl --max-time 6 -s -L -m 10 --connect-timeout 5 -o /dev/null -w "%{http_code}" "$url") + + if [[ -z "$responseCode" ]]; then + ((retries++)) + sleep $retryDelay + fi + done + + if [[ -n "$responseCode" && ("$responseCode" == "200" || "$responseCode" =~ ^3[0-9]{2}$) ]]; then + echo "下载地址检查OK,继续!" + else + echo "下载地址检查出错,退出!" + exit 1 + fi +} + +#cn处理github加速 +check_cn() { + # 检查是否安装了jq命令,如果没有安装则进行安装 + if ! command -v jq >/dev/null 2>&1; then + if command -v yum >/dev/null 2>&1; then + sudo yum install epel-release -y + sudo yum install -y jq + elif command -v apt-get >/dev/null 2>&1; then + sudo apt-get update + sudo apt-get install -y jq + else + echo "无法安装jq命令。请手动安装jq后再试。" + exit 1 + fi + fi + + # 获取当前IP地址,设置超时为3秒 + current_ip=$(curl -s --max-time 3 https://api.ipify.org) + + # 使用ip-api.com查询IP所在国家,设置超时为3秒 + response=$(curl -s --max-time 3 "http://ip-api.com/json/$current_ip") + + # 检查国家是否为中国 + country=$(echo "$response" | jq -r '.countryCode') + if [[ "$country" == "CN" ]]; then + local suffixes=( + "https://gh.con.sh/" + "https://gh-proxy.com/" + "https://ghp.ci/" + "https://gh.m-l.cc/" + "https://down.npee.cn/?" + "https://mirror.ghproxy.com/" + "https://ghps.cc/" + "https://gh.api.99988866.xyz/" + "https://git.886.be/" + "https://hub.gitmirror.com/" + "https://pd.zwc365.com/" + "https://gh.ddlc.top/" + "https://slink.ltd/" + "https://github.moeyy.xyz/" + "https://ghproxy.crazypeace.workers.dev/" + "https://gh.h233.eu.org/" + ) + + # 循环遍历每个后缀并测试组合的链接 + for suffix in "${suffixes[@]}"; do + # 组合后缀和原始链接 + combined_url="$suffix$1" + + # 使用 curl -I 获取头部信息,提取状态码 + local response_code=$(curl --max-time 2 -sL -w "%{http_code}" -I "$combined_url" | head -n 1 | awk '{print $2}') + + # 检查响应码是否表示成功 (2xx) + if [[ $response_code -ge 200 && $response_code -lt 300 ]]; then + echo "$combined_url" + return 0 # 返回可用链接,结束函数 + fi + done + + # 如果没有找到有效链接,返回原始链接 + else + echo "$1" + return 1 + + fi +} + +#下载 +download_file() { + url="$1" + filename="$2" + + wget "$url" -O "$filename" + status=$? + + if [ $status -eq 0 ]; then + echo -e "\e[32m文件下载成功或已经是最新。\e[0m" + else + echo -e "\e[31m文件下载失败,退出状态码: $status\e[0m" + exit 1 + fi +} + +#檢查賦值 +check_empty() { + local var_value=$1 + + if [[ -z $var_value ]]; then + echo "$var_value 是空值,退出!" + exit 1 + fi +} + +#安装BBR内核 +installbbr() { + kernel_version="5.9.6" + bit=$(uname -m) + rm -rf bbr + mkdir bbr && cd bbr || exit + + if [[ "${OS_type}" == "CentOS" ]]; then + if [[ ${version} == "7" ]]; then + if [[ ${bit} == "x86_64" ]]; then + echo -e "如果下载地址出错,可能当前正在更新,超过半天还是出错请反馈,大陆自行解决污染问题" + #github_ver=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}' | awk -F '[_]' '{print $3}') + #github_tag=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep 'Centos_Kernel' | grep '_latest_bbr_' | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}') + #github_ver=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep 'headers' | awk -F '"' '{print $4}' | awk -F '[/]' '{print $9}' | awk -F '[-]' '{print $3}') + #check_empty $github_ver + #echo -e "获取的版本号为:${Green_font_prefix}${github_ver}${Font_color_suffix}" + #kernel_version=$github_ver + #detele_kernel_head + #headurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep 'headers' | awk -F '"' '{print $4}') + #imgurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep -v 'headers' | grep -v 'devel' | awk -F '"' '{print $4}') + #headurl=https://github.com/ylx2016/kernel/releases/download/$github_tag/kernel-headers-${github_ver}-1.x86_64.rpm + #imgurl=https://github.com/ylx2016/kernel/releases/download/$github_tag/kernel-${github_ver}-1.x86_64.rpm + + headurl=https://github.com/ylx2016/kernel/releases/download/Centos_Kernel_6.1.35_latest_bbr_2023.06.22-0855/kernel-headers-6.1.35-1.x86_64.rpm + imgurl=https://github.com/ylx2016/kernel/releases/download/Centos_Kernel_6.1.35_latest_bbr_2023.06.22-0855/kernel-6.1.35-1.x86_64.rpm + + check_empty $imgurl + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + download_file $headurl kernel-headers-c7.rpm + download_file $imgurl kernel-c7.rpm + yum install -y kernel-c7.rpm + yum install -y kernel-headers-c7.rpm + else + echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + fi + fi + + elif [[ "${OS_type}" == "Debian" ]]; then + if [[ ${bit} == "x86_64" ]]; then + echo -e "如果下载地址出错,可能当前正在更新,超过半天还是出错请反馈,大陆自行解决污染问题" + github_tag=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep 'Debian_Kernel' | grep '_latest_bbr_' | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}') + github_ver=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep 'headers' | awk -F '"' '{print $4}' | awk -F '[/]' '{print $9}' | awk -F '[-]' '{print $3}' | awk -F '[_]' '{print $1}') + check_empty $github_ver + echo -e "获取的版本号为:${Green_font_prefix}${github_ver}${Font_color_suffix}" + kernel_version=$github_ver + detele_kernel_head + headurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep 'headers' | awk -F '"' '{print $4}') + imgurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep -v 'headers' | grep -v 'devel' | awk -F '"' '{print $4}') + #headurl=https://github.com/ylx2016/kernel/releases/download/$github_tag/linux-headers-${github_ver}_${github_ver}-1_amd64.deb + #imgurl=https://github.com/ylx2016/kernel/releases/download/$github_tag/linux-image-${github_ver}_${github_ver}-1_amd64.deb + + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + download_file $headurl linux-headers-d10.deb + download_file $imgurl linux-image-d10.deb + dpkg -i linux-image-d10.deb + dpkg -i linux-headers-d10.deb + elif [[ ${bit} == "aarch64" ]]; then + echo -e "如果下载地址出错,可能当前正在更新,超过半天还是出错请反馈,大陆自行解决污染问题" + github_tag=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep 'Debian_Kernel' | grep '_arm64_' | grep '_bbr_' | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}') + github_ver=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep 'headers' | awk -F '"' '{print $4}' | awk -F '[/]' '{print $9}' | awk -F '[-]' '{print $3}' | awk -F '[_]' '{print $1}') + echo -e "获取的版本号为:${Green_font_prefix}${github_ver}${Font_color_suffix}" + kernel_version=$github_ver + detele_kernel_head + headurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep 'headers' | awk -F '"' '{print $4}') + imgurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep -v 'headers' | grep -v 'devel' | awk -F '"' '{print $4}') + #headurl=https://github.com/ylx2016/kernel/releases/download/$github_tag/linux-headers-${github_ver}_${github_ver}-1_amd64.deb + #imgurl=https://github.com/ylx2016/kernel/releases/download/$github_tag/linux-image-${github_ver}_${github_ver}-1_amd64.deb + + check_empty $imgurl + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + download_file $headurl linux-headers-d10.deb + download_file $imgurl linux-image-d10.deb + dpkg -i linux-image-d10.deb + dpkg -i linux-headers-d10.deb + else + echo -e "${Error} 不支持x86_64及arm64/aarch64以外的系统 !" && exit 1 + fi + fi + + cd .. && rm -rf bbr + + detele_kernel + BBR_grub + echo -e "${Tip} ${Red_font_prefix}请检查上面是否有内核信息,无内核千万别重启${Font_color_suffix}" + echo -e "${Tip} ${Red_font_prefix}rescue不是正常内核,要排除这个${Font_color_suffix}" + echo -e "${Tip} 重启VPS后,请重新运行脚本开启${Red_font_prefix}BBR${Font_color_suffix}" + check_kernel + stty erase '^H' && read -p "需要重启VPS后,才能开启BBR,是否现在重启 ? [Y/n] :" yn + [ -z "${yn}" ] && yn="y" + if [[ $yn == [Yy] ]]; then + echo -e "${Info} VPS 重启中..." + reboot + fi + #echo -e "${Tip} 内核安装完毕,请参考上面的信息检查是否安装成功及手动调整内核启动顺序" +} + +#安装BBRplus内核 4.14.129 +installbbrplus() { + kernel_version="4.14.160-bbrplus" + bit=$(uname -m) + rm -rf bbrplus + mkdir bbrplus && cd bbrplus || exit + if [[ "${OS_type}" == "CentOS" ]]; then + if [[ ${version} == "7" ]]; then + if [[ ${bit} == "x86_64" ]]; then + kernel_version="4.14.129_bbrplus" + detele_kernel_head + headurl=https://github.com/cx9208/Linux-NetSpeed/raw/master/bbrplus/centos/7/kernel-headers-4.14.129-bbrplus.rpm + imgurl=https://github.com/cx9208/Linux-NetSpeed/raw/master/bbrplus/centos/7/kernel-4.14.129-bbrplus.rpm + + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + download_file $headurl kernel-headers-c7.rpm + download_file $imgurl kernel-c7.rpm + yum install -y kernel-c7.rpm + yum install -y kernel-headers-c7.rpm + else + echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + fi + fi + + elif [[ "${OS_type}" == "Debian" ]]; then + if [[ ${bit} == "x86_64" ]]; then + kernel_version="4.14.129-bbrplus" + detele_kernel_head + headurl=https://github.com/cx9208/Linux-NetSpeed/raw/master/bbrplus/debian-ubuntu/x64/linux-headers-4.14.129-bbrplus.deb + imgurl=https://github.com/cx9208/Linux-NetSpeed/raw/master/bbrplus/debian-ubuntu/x64/linux-image-4.14.129-bbrplus.deb + + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + wget -O linux-headers.deb $headurl + wget -O linux-image.deb $imgurl + + dpkg -i linux-image.deb + dpkg -i linux-headers.deb + else + echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + fi + fi + + cd .. && rm -rf bbrplus + detele_kernel + BBR_grub + echo -e "${Tip} ${Red_font_prefix}请检查上面是否有内核信息,无内核千万别重启${Font_color_suffix}" + echo -e "${Tip} ${Red_font_prefix}rescue不是正常内核,要排除这个${Font_color_suffix}" + echo -e "${Tip} 重启VPS后,请重新运行脚本开启${Red_font_prefix}BBRplus${Font_color_suffix}" + check_kernel + stty erase '^H' && read -p "需要重启VPS后,才能开启BBRplus,是否现在重启 ? [Y/n] :" yn + [ -z "${yn}" ] && yn="y" + if [[ $yn == [Yy] ]]; then + echo -e "${Info} VPS 重启中..." + reboot + fi + #echo -e "${Tip} 内核安装完毕,请参考上面的信息检查是否安装成功及手动调整内核启动顺序" +} + +#安装Lotserver内核 +installlot() { + bit=$(uname -m) + if [[ ${bit} != "x86_64" ]]; then + echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + fi + if [[ ${bit} == "x86_64" ]]; then + bit='x64' + fi + if [[ ${bit} == "i386" ]]; then + bit='x32' + fi + if [[ "${OS_type}" == "CentOS" ]]; then + rpm --import http://${github}/lotserver/${release}/RPM-GPG-KEY-elrepo.org + yum remove -y kernel-firmware + yum install -y http://${github}/lotserver/${release}/${version}/${bit}/kernel-firmware-${kernel_version}.rpm + yum install -y http://${github}/lotserver/${release}/${version}/${bit}/kernel-${kernel_version}.rpm + yum remove -y kernel-headers + yum install -y http://${github}/lotserver/${release}/${version}/${bit}/kernel-headers-${kernel_version}.rpm + yum install -y http://${github}/lotserver/${release}/${version}/${bit}/kernel-devel-${kernel_version}.rpm + fi + + if [[ "${OS_type}" == "Debian" ]]; then + deb_issue="$(cat /etc/issue)" + deb_relese="$(echo $deb_issue | grep -io 'Ubuntu\|Debian' | sed -r 's/(.*)/\L\1/')" + os_ver="$(dpkg --print-architecture)" + [ -n "$os_ver" ] || exit 1 + if [ "$deb_relese" == 'ubuntu' ]; then + deb_ver="$(echo $deb_issue | grep -o '[0-9]*\.[0-9]*' | head -n1)" + if [ "$deb_ver" == "14.04" ]; then + kernel_version="3.16.0-77-generic" && item="3.16.0-77-generic" && ver='trusty' + elif [ "$deb_ver" == "16.04" ]; then + kernel_version="4.8.0-36-generic" && item="4.8.0-36-generic" && ver='xenial' + elif [ "$deb_ver" == "18.04" ]; then + kernel_version="4.15.0-30-generic" && item="4.15.0-30-generic" && ver='bionic' + else + exit 1 + fi + url='archive.ubuntu.com' + urls='security.ubuntu.com' + elif [ "$deb_relese" == 'debian' ]; then + deb_ver="$(echo $deb_issue | grep -o '[0-9]*' | head -n1)" + if [ "$deb_ver" == "7" ]; then + kernel_version="3.2.0-4-${os_ver}" && item="3.2.0-4-${os_ver}" && ver='wheezy' && url='archive.debian.org' && urls='archive.debian.org' + elif [ "$deb_ver" == "8" ]; then + kernel_version="3.16.0-4-${os_ver}" && item="3.16.0-4-${os_ver}" && ver='jessie' && url='archive.debian.org' && urls='archive.debian.org' + elif [ "$deb_ver" == "9" ]; then + kernel_version="4.9.0-4-${os_ver}" && item="4.9.0-4-${os_ver}" && ver='stretch' && url='archive.debian.org' && urls='archive.debian.org' + else + exit 1 + fi + fi + [ -n "$item" ] && [ -n "$urls" ] && [ -n "$url" ] && [ -n "$ver" ] || exit 1 + if [ "$deb_relese" == 'ubuntu' ]; then + echo "deb http://${url}/${deb_relese} ${ver} main restricted universe multiverse" >/etc/apt/sources.list + echo "deb http://${url}/${deb_relese} ${ver}-updates main restricted universe multiverse" >>/etc/apt/sources.list + echo "deb http://${url}/${deb_relese} ${ver}-backports main restricted universe multiverse" >>/etc/apt/sources.list + echo "deb http://${urls}/${deb_relese} ${ver}-security main restricted universe multiverse" >>/etc/apt/sources.list + + apt-get update || apt-get --allow-releaseinfo-change update + apt-get install --no-install-recommends -y linux-image-${item} + elif [ "$deb_relese" == 'debian' ]; then + echo "deb http://${url}/${deb_relese} ${ver} main" >/etc/apt/sources.list + echo "deb-src http://${url}/${deb_relese} ${ver} main" >>/etc/apt/sources.list + echo "deb http://${urls}/${deb_relese}-security ${ver}/updates main" >>/etc/apt/sources.list + echo "deb-src http://${urls}/${deb_relese}-security ${ver}/updates main" >>/etc/apt/sources.list + + if [ "$deb_ver" == "8" ]; then + dpkg -l | grep -q 'linux-base' || { + wget --no-check-certificate -qO '/tmp/linux-base_3.5_all.deb' 'http://snapshot.debian.org/archive/debian/20120304T220938Z/pool/main/l/linux-base/linux-base_3.5_all.deb' + dpkg -i '/tmp/linux-base_3.5_all.deb' + } + wget --no-check-certificate -qO '/tmp/linux-image-3.16.0-4-amd64_3.16.43-2+deb8u5_amd64.deb' 'http://snapshot.debian.org/archive/debian/20171008T163152Z/pool/main/l/linux/linux-image-3.16.0-4-amd64_3.16.43-2+deb8u5_amd64.deb' + dpkg -i '/tmp/linux-image-3.16.0-4-amd64_3.16.43-2+deb8u5_amd64.deb' + + if [ $? -ne 0 ]; then + exit 1 + fi + elif [ "$deb_ver" == "9" ]; then + dpkg -l | grep -q 'linux-base' || { + wget --no-check-certificate -qO '/tmp/linux-base_4.5_all.deb' 'http://snapshot.debian.org/archive/debian/20160917T042239Z/pool/main/l/linux-base/linux-base_4.5_all.deb' + dpkg -i '/tmp/linux-base_4.5_all.deb' + } + wget --no-check-certificate -qO '/tmp/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb' 'http://snapshot.debian.org/archive/debian/20171224T175424Z/pool/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb' + dpkg -i '/tmp/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb' + ##备选 + #https://sys.if.ci/download/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb + #http://mirror.cs.uchicago.edu/debian-security/pool/updates/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb + #https://debian.sipwise.com/debian-security/pool/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb + #http://srv24.dsidata.sk/security.debian.org/pool/updates/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb + #https://pubmirror.plutex.de/debian-security/pool/updates/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb + #https://packages.mendix.com/debian/pool/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3_amd64.deb + #http://snapshot.debian.org/archive/debian/20171224T175424Z/pool/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3+deb9u1_amd64.deb + #http://snapshot.debian.org/archive/debian/20171231T180144Z/pool/main/l/linux/linux-image-4.9.0-4-amd64_4.9.65-3_amd64.deb + if [ $? -ne 0 ]; then + exit 1 + fi + else + exit 1 + fi + fi + apt-get autoremove -y + [ -d '/var/lib/apt/lists' ] && find /var/lib/apt/lists -type f -delete + fi + + detele_kernel + BBR_grub + echo -e "${Tip} ${Red_font_prefix}请检查上面是否有内核信息,无内核千万别重启${Font_color_suffix}" + echo -e "${Tip} ${Red_font_prefix}rescue不是正常内核,要排除这个${Font_color_suffix}" + echo -e "${Tip} 重启VPS后,请重新运行脚本开启${Red_font_prefix}Lotserver${Font_color_suffix}" + check_kernel + stty erase '^H' && read -p "需要重启VPS后,才能开启Lotserver,是否现在重启 ? [Y/n] :" yn + [ -z "${yn}" ] && yn="y" + if [[ $yn == [Yy] ]]; then + echo -e "${Info} VPS 重启中..." + reboot + fi + #echo -e "${Tip} 内核安装完毕,请参考上面的信息检查是否安装成功及手动调整内核启动顺序" +} + +#安装xanmod内核 from xanmod.org +installxanmod() { + kernel_version="5.5.1-xanmod1" + bit=$(uname -m) + if [[ ${bit} != "x86_64" ]]; then + echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + fi + rm -rf xanmod + mkdir xanmod && cd xanmod || exit + if [[ "${OS_type}" == "CentOS" ]]; then + if [[ ${version} == "7" ]]; then + if [[ ${bit} == "x86_64" ]]; then + echo -e "如果下载地址出错,可能当前正在更新,超过半天还是出错请反馈,大陆自行解决污染问题" + #github_tag=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep 'Centos_Kernel' | grep '_lts_latest_' | grep 'xanmod' | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}') + #github_ver=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep 'headers' | awk -F '"' '{print $4}' | awk -F '[/]' '{print $9}' | awk -F '[-]' '{print $3}') + #echo -e "获取的版本号为:${Green_font_prefix}${github_ver}${Font_color_suffix}" + #kernel_version=$github_ver + #detele_kernel_head + #headurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep 'headers' | awk -F '"' '{print $4}') + #imgurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep -v 'headers' | grep -v 'devel' | awk -F '"' '{print $4}') + + headurl=https://github.com/ylx2016/kernel/releases/download/Centos_Kernel_5.15.95-xanmod1_lts_latest_2023.02.24-2159/kernel-headers-5.15.95_xanmod1-1.x86_64.rpm + imgurl=https://github.com/ylx2016/kernel/releases/download/Centos_Kernel_5.15.95-xanmod1_lts_latest_2023.02.24-2159/kernel-5.15.95_xanmod1-1.x86_64.rpm + + check_empty $imgurl + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + download_file $headurl kernel-headers-c7.rpm + download_file $imgurl kernel-c7.rpm + yum install -y kernel-c7.rpm + yum install -y kernel-headers-c7.rpm + else + echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + fi + elif [[ ${version} == "8" ]]; then + echo -e "如果下载地址出错,可能当前正在更新,超过半天还是出错请反馈,大陆自行解决污染问题" + #github_tag=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep 'Centos_Kernel' | grep '_lts_C8_latest_' | grep 'xanmod' | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}') + #github_ver=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep 'headers' | awk -F '"' '{print $4}' | awk -F '[/]' '{print $9}' | awk -F '[-]' '{print $3}') + #echo -e "获取的版本号为:${Green_font_prefix}${github_ver}${Font_color_suffix}" + #kernel_version=$github_ver + #detele_kernel_head + #headurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep 'headers' | awk -F '"' '{print $4}') + #imgurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep -v 'headers' | grep -v 'devel' | awk -F '"' '{print $4}') + + headurl=https://github.com/ylx2016/kernel/releases/download/Centos_Kernel_5.15.81-xanmod1_lts_C8_latest_2022.12.06-1614/kernel-headers-5.15.81_xanmod1-1.x86_64.rpm + imgurl=https://github.com/ylx2016/kernel/releases/download/Centos_Kernel_5.15.81-xanmod1_lts_C8_latest_2022.12.06-1614/kernel-5.15.81_xanmod1-1.x86_64.rpm + + check_empty $imgurl + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + wget -O kernel-headers-c8.rpm $headurl + wget -O kernel-c8.rpm $imgurl + yum install -y kernel-c8.rpm + yum install -y kernel-headers-c8.rpm + fi + + elif [[ "${OS_type}" == "Debian" ]]; then + + if [[ ${bit} == "x86_64" ]]; then + echo -e "如果下载地址出错,可能当前正在更新,超过半天还是出错请反馈,大陆自行解决污染问题" + #github_tag=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep 'Debian_Kernel' | grep '_lts_latest_' | grep 'xanmod' | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}') + #github_ver=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep 'headers' | awk -F '"' '{print $4}' | awk -F '[/]' '{print $9}' | awk -F '[-]' '{print $3}') + + #check_empty $github_ver + #echo -e "获取的xanmod lts版本号为:${github_ver}" + + #kernel_version=$github_ver + + #detele_kernel_head + #headurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep 'headers' | awk -F '"' '{print $4}') + #imgurl=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep -v 'headers' | grep -v 'devel' | awk -F '"' '{print $4}') + + headurl=https://github.com/ylx2016/kernel/releases/download/Debian_Kernel_5.15.95-xanmod1_lts_latest_2023.02.24-2210/linux-headers-5.15.95-xanmod1_5.15.95-xanmod1-1_amd64.deb + imgurl=https://github.com/ylx2016/kernel/releases/download/Debian_Kernel_5.15.95-xanmod1_lts_latest_2023.02.24-2210/linux-image-5.15.95-xanmod1_5.15.95-xanmod1-1_amd64.deb + + check_empty $imgurl + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + download_file $headurl linux-headers-d10.deb + download_file $imgurl linux-image-d10.deb + dpkg -i linux-image-d10.deb + dpkg -i linux-headers-d10.deb + else + echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + fi + fi + + cd .. && rm -rf xanmod + detele_kernel + BBR_grub + echo -e "${Tip} ${Red_font_prefix}请检查上面是否有内核信息,无内核千万别重启${Font_color_suffix}" + echo -e "${Tip} ${Red_font_prefix}rescue不是正常内核,要排除这个${Font_color_suffix}" + echo -e "${Tip} 重启VPS后,请重新运行脚本开启${Red_font_prefix}BBR${Font_color_suffix}" + check_kernel + stty erase '^H' && read -p "需要重启VPS后,才能开启BBR,是否现在重启 ? [Y/n] :" yn + [ -z "${yn}" ] && yn="y" + if [[ $yn == [Yy] ]]; then + echo -e "${Info} VPS 重启中..." + reboot + fi + #echo -e "${Tip} 内核安装完毕,请参考上面的信息检查是否安装成功及手动调整内核启动顺序" +} + +#安装bbr2内核 集成到xanmod内核了 +#安装bbrplus 新内核 +#2021.3.15 开始由https://github.com/UJX6N/bbrplus-5.19 替换bbrplusnew +#2021.4.12 地址更新为https://github.com/ylx2016/kernel/releases +#2021.9.2 再次改为https://github.com/UJX6N/bbrplus +#2022.9.6 改为https://github.com/UJX6N/bbrplus-5.19 +#2022.11.24 改为https://github.com/UJX6N/bbrplus-6.x_stable + +installbbrplusnew() { + github_ver_plus=$(curl -s https://api.github.com/repos/UJX6N/bbrplus-6.x_stable/releases | grep /bbrplus-6.x_stable/releases/tag/ | head -1 | awk -F "[/]" '{print $8}' | awk -F "[\"]" '{print $1}') + github_ver_plus_num=$(curl -s https://api.github.com/repos/UJX6N/bbrplus-6.x_stable/releases | grep /bbrplus-6.x_stable/releases/tag/ | head -1 | awk -F "[/]" '{print $8}' | awk -F "[\"]" '{print $1}' | awk -F "[-]" '{print $1}') + echo -e "获取的UJX6N的bbrplus-6.x_stable版本号为:${Green_font_prefix}${github_ver_plus}${Font_color_suffix}" + echo -e "如果下载地址出错,可能当前正在更新,超过半天还是出错请反馈,大陆自行解决污染问题" + echo -e "${Green_font_prefix}安装失败这边反馈,内核问题给UJX6N反馈${Font_color_suffix}" + # kernel_version=$github_ver_plus + + bit=$(uname -m) + #if [[ ${bit} != "x86_64" ]]; then + # echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + #fi + rm -rf bbrplusnew + mkdir bbrplusnew && cd bbrplusnew || exit + if [[ "${OS_type}" == "CentOS" ]]; then + if [[ ${version} == "7" ]]; then + if [[ ${bit} == "x86_64" ]]; then + #github_tag=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep 'Centos_Kernel' | grep '_latest_bbrplus_' | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}') + #github_ver=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep 'headers' | awk -F '"' '{print $4}' | awk -F '[/]' '{print $9}' | awk -F '[-]' '{print $3}' | awk -F '[_]' '{print $1}') + #echo -e "获取的版本号为:${Green_font_prefix}${github_ver}${Font_color_suffix}" + kernel_version=${github_ver_plus_num}-bbrplus + detele_kernel_head + headurl=$(curl -s 'https://api.github.com/repos/UJX6N/bbrplus-6.x_stable/releases' | grep ${github_ver_plus} | grep 'rpm' | grep 'headers' | grep 'el7' | awk -F '"' '{print $4}' | grep 'http') + imgurl=$(curl -s 'https://api.github.com/repos/UJX6N/bbrplus-6.x_stable/releases' | grep ${github_ver_plus} | grep 'rpm' | grep -v 'devel' | grep -v 'headers' | grep -v 'Source' | grep 'el7' | awk -F '"' '{print $4}' | grep 'http') + + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + wget -O kernel-c7.rpm $headurl + wget -O kernel-headers-c7.rpm $imgurl + yum install -y kernel-c7.rpm + yum install -y kernel-headers-c7.rpm + else + echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + fi + fi + if [[ ${version} == "8" ]]; then + if [[ ${bit} == "x86_64" ]]; then + #github_tag=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep 'Centos_Kernel' | grep '_latest_bbrplus_' | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}') + #github_ver=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'rpm' | grep 'headers' | awk -F '"' '{print $4}' | awk -F '[/]' '{print $9}' | awk -F '[-]' '{print $3}' | awk -F '[_]' '{print $1}') + #echo -e "获取的版本号为:${Green_font_prefix}${github_ver}${Font_color_suffix}" + kernel_version=${github_ver_plus_num}-bbrplus + detele_kernel_head + headurl=$(curl -s 'https://api.github.com/repos/UJX6N/bbrplus-6.x_stable/releases' | grep ${github_ver_plus} | grep 'rpm' | grep 'headers' | grep 'el8.x86_64' | grep 'https' | awk -F '"' '{print $4}' | grep 'http') + imgurl=$(curl -s 'https://api.github.com/repos/UJX6N/bbrplus-6.x_stable/releases' | grep ${github_ver_plus} | grep 'rpm' | grep -v 'devel' | grep -v 'headers' | grep -v 'Source' | grep 'el8.x86_64' | grep 'https' | awk -F '"' '{print $4}' | grep 'http') + + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + wget -O kernel-c8.rpm $headurl + wget -O kernel-headers-c8.rpm $imgurl + yum install -y kernel-c8.rpm + yum install -y kernel-headers-c8.rpm + else + echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + fi + fi + elif [[ "${OS_type}" == "Debian" ]]; then + if [[ ${bit} == "x86_64" ]]; then + #github_tag=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep 'Ubuntu_Kernel' | grep '_latest_bbrplus_' | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}') + #github_ver=$(curl -s 'http s://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep 'headers' | awk -F '"' '{print $4}' | awk -F '[/]' '{print $9}' | awk -F '[-]' '{print $3}' | awk -F '[_]' '{print $1}') + #echo -e "获取的版本号为:${Green_font_prefix}${github_ver}${Font_color_suffix}" + kernel_version=${github_ver_plus_num}-bbrplus + detele_kernel_head + headurl=$(curl -s 'https://api.github.com/repos/UJX6N/bbrplus-6.x_stable/releases' | grep ${github_ver_plus} | grep 'https' | grep 'amd64.deb' | grep 'headers' | awk -F '"' '{print $4}' | grep 'http') + imgurl=$(curl -s 'https://api.github.com/repos/UJX6N/bbrplus-6.x_stable/releases' | grep ${github_ver_plus} | grep 'https' | grep 'amd64.deb' | grep 'image' | awk -F '"' '{print $4}' | grep 'http') + + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + download_file $headurl linux-headers-d10.deb + download_file $imgurl linux-image-d10.deb + dpkg -i linux-image-d10.deb + dpkg -i linux-headers-d10.deb + elif [[ ${bit} == "aarch64" ]]; then + #github_tag=$(curl -s 'https://api.github.com/repos/ylx2016/kernel/releases' | grep 'Ubuntu_Kernel' | grep '_latest_bbrplus_' | head -n 1 | awk -F '"' '{print $4}' | awk -F '[/]' '{print $8}') + #github_ver=$(curl -s 'http s://api.github.com/repos/ylx2016/kernel/releases' | grep ${github_tag} | grep 'deb' | grep 'headers' | awk -F '"' '{print $4}' | awk -F '[/]' '{print $9}' | awk -F '[-]' '{print $3}' | awk -F '[_]' '{print $1}') + #echo -e "获取的版本号为:${Green_font_prefix}${github_ver}${Font_color_suffix}" + kernel_version=${github_ver_plus_num}-bbrplus + detele_kernel_head + headurl=$(curl -s 'https://api.github.com/repos/UJX6N/bbrplus-6.x_stable/releases' | grep ${github_ver_plus} | grep 'https' | grep 'arm64.deb' | grep 'headers' | awk -F '"' '{print $4}') + imgurl=$(curl -s 'https://api.github.com/repos/UJX6N/bbrplus-6.x_stable/releases' | grep ${github_ver_plus} | grep 'https' | grep 'arm64.deb' | grep 'image' | awk -F '"' '{print $4}') + + headurl=$(check_cn $headurl) + imgurl=$(check_cn $imgurl) + + download_file $headurl linux-headers-d10.deb + download_file $imgurl linux-image-d10.deb + dpkg -i linux-image-d10.deb + dpkg -i linux-headers-d10.deb + else + echo -e "${Error} 不支持x86_64及arm64/aarch64以外的系统 !" && exit 1 + fi + fi + + cd .. && rm -rf bbrplusnew + detele_kernel + BBR_grub + echo -e "${Tip} ${Red_font_prefix}请检查上面是否有内核信息,无内核千万别重启${Font_color_suffix}" + echo -e "${Tip} ${Red_font_prefix}rescue不是正常内核,要排除这个${Font_color_suffix}" + echo -e "${Tip} 重启VPS后,请重新运行脚本开启${Red_font_prefix}BBRplus${Font_color_suffix}" + check_kernel + stty erase '^H' && read -p "需要重启VPS后,才能开启BBRplus,是否现在重启 ? [Y/n] :" yn + [ -z "${yn}" ] && yn="y" + if [[ $yn == [Yy] ]]; then + echo -e "${Info} VPS 重启中..." + reboot + fi + #echo -e "${Tip} 内核安装完毕,请参考上面的信息检查是否安装成功及手动调整内核启动顺序" + +} + +#安装cloud内核 +installcloud() { + + # 检查当前系统发行版 + local DISTRO=$(grep -oP '(?<=^ID=).+' /etc/os-release | tr -d '"') + local ARCH=$(uname -m) + local VERSIONS=() + local VERSION_MAP_FILE="/tmp/version_map.txt" + + # 检查架构并设置 IMAGE_URL 和 IMAGE_PATTERN + local IMAGE_URL + local IMAGE_PATTERN + if [ "$ARCH" == "x86_64" ]; then + IMAGE_URL="https://deb.debian.org/debian/pool/main/l/linux-signed-amd64/" + IMAGE_PATTERN='linux-image-[^"]+cloud-amd64_[^"]+_amd64\.deb' + elif [ "$ARCH" == "aarch64" ]; then + IMAGE_URL="https://deb.debian.org/debian/pool/main/l/linux-signed-arm64/" + IMAGE_PATTERN='linux-image-[^"]+cloud-arm64_[^"]+_arm64\.deb' + else + echo "不支持的架构:$ARCH,仅支持 x86_64 和 aarch64" + exit 1 + fi + + echo "检测到架构 $ARCH,正在从官方源获取cloud内核版本..." + + # 获取 cloud 内核 .deb 文件列表 + local DEB_FILES_RAW=$(curl -s "$IMAGE_URL" | grep -oP "$IMAGE_PATTERN") + + # 清空临时映射文件 + >"$VERSION_MAP_FILE" + + # 提取 image 版本号并写入映射文件 + while IFS= read -r file; do + if [[ "$file" =~ linux-image-([0-9]+\.[0-9]+(\.[0-9]+)?(-[0-9]+)?) ]]; then + local ver="${BASH_REMATCH[1]}" + echo "$ver:$file" >>"$VERSION_MAP_FILE" + fi + done <<<"$DEB_FILES_RAW" + + # 读取排序并去重后的版本号 + mapfile -t VERSIONS < <(cut -d':' -f1 "$VERSION_MAP_FILE" | sort -V -u) + + # 确保有可用版本 + if [ ${#VERSIONS[@]} -eq 0 ]; then + echo "未找到可用的cloud内核版本,请检查网络或反馈。" + exit 1 + fi + + echo "检测到 $DISTRO 系统(架构 $ARCH),以下是从 Debian 签名cloud内核列表中获取的版本(按从小到大排序,已去重):" + for i in "${!VERSIONS[@]}"; do + echo " $i) [${VERSIONS[$i]}]" + done + + # 默认选择最新版本 + local DEFAULT_INDEX=$((${#VERSIONS[@]} - 1)) + echo "请选择要安装的cloud内核版本(10秒后默认选择最新版本回车加速 ${VERSIONS[$DEFAULT_INDEX]},输入'h'则使用apt安装非最新cloud及headers):" + read -t 10 -p "输入选项编号或'h': " CHOICE + + # 检查是否使用 apt 安装 cloud 及 headers + local USE_APT=false + if [[ "$CHOICE" =~ ^[hH]$ ]]; then + USE_APT=true + if [ "$DISTRO" != "debian" ]; then + echo "错误:使用 'h' 安装 headers 仅支持 Debian 系统,当前系统为 $DISTRO" + exit 1 + fi + CHOICE=$DEFAULT_INDEX + else + CHOICE=${CHOICE:-$DEFAULT_INDEX} + fi + + # 验证输入 + if [[ ! "$CHOICE" =~ ^[0-9]+$ ]] || [ "$CHOICE" -lt 0 ] || [ "$CHOICE" -ge "${#VERSIONS[@]}" ]; then + echo "无效选项,默认安装最新版本 ${VERSIONS[$DEFAULT_INDEX]}..." + CHOICE=$DEFAULT_INDEX + fi + + local SELECTED_VERSION="${VERSIONS[$CHOICE]}" + local IMAGE_DEB_FILE=$(grep "^$SELECTED_VERSION:" "$VERSION_MAP_FILE" | tail -n 1 | cut -d':' -f2) + + kernel_version=$SELECTED_VERSION + + # 如果选择 'h',使用 apt 安装 cloud 内核及 headers + if [ "$USE_APT" = true ]; then + echo "正在使用 apt 安装 linux-image-cloud-${ARCH} 及 headers..." + sudo apt update + if [ "$ARCH" == "x86_64" ]; then + sudo apt install -y "linux-image-cloud-amd64" "linux-headers-cloud-amd64" + elif [ "$ARCH" == "aarch64" ]; then + sudo apt install -y "linux-image-cloud-arm64" "linux-headers-cloud-arm64" + fi + else + # 下载并安装 image + echo "正在下载 $IMAGE_URL$IMAGE_DEB_FILE ..." + curl -O "$IMAGE_URL$IMAGE_DEB_FILE" + echo "正在安装 $IMAGE_DEB_FILE ..." + sudo dpkg -i "$IMAGE_DEB_FILE" + sudo apt-get install -f -y # 解决可能的依赖问题 + fi + + # 清理下载的文件 + rm -f "$IMAGE_DEB_FILE" "$VERSION_MAP_FILE" + + detele_kernel + BBR_grub + echo -e "${Tip} ${Red_font_prefix}请检查上面是否有内核信息,无内核千万别重启${Font_color_suffix}" + echo -e "${Tip} ${Red_font_prefix}rescue不是正常内核,要排除这个${Font_color_suffix}" + echo -e "${Tip} 重启VPS后,请重新运行脚本开启${Red_font_prefix}BBRplus${Font_color_suffix}" + check_kernel + stty erase '^H' && read -p "需要重启VPS后,才能开启BBRplus,是否现在重启 ? [Y/n] :" yn + [ -z "${yn}" ] && yn="y" + if [[ $yn == [Yy] ]]; then + echo -e "${Info} VPS 重启中..." + reboot + fi + +} + +#启用BBR+fq +startbbrfq() { + remove_bbr_lotserver + echo "net.core.default_qdisc=fq" >>/etc/sysctl.d/99-sysctl.conf + echo "net.ipv4.tcp_congestion_control=bbr" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}BBR+FQ修改成功,重启生效!" +} + +#启用BBR+fq_pie +startbbrfqpie() { + remove_bbr_lotserver + echo "net.core.default_qdisc=fq_pie" >>/etc/sysctl.d/99-sysctl.conf + echo "net.ipv4.tcp_congestion_control=bbr" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}BBR+FQ_PIE修改成功,重启生效!" +} + +#启用BBR+cake +startbbrcake() { + remove_bbr_lotserver + echo "net.core.default_qdisc=cake" >>/etc/sysctl.d/99-sysctl.conf + echo "net.ipv4.tcp_congestion_control=bbr" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}BBR+cake修改成功,重启生效!" +} + +#启用BBRplus +startbbrplus() { + remove_bbr_lotserver + echo "net.core.default_qdisc=fq" >>/etc/sysctl.d/99-sysctl.conf + echo "net.ipv4.tcp_congestion_control=bbrplus" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}BBRplus修改成功,重启生效!" +} + +#启用Lotserver +startlotserver() { + remove_bbr_lotserver + if [[ "${OS_type}" == "CentOS" ]]; then + yum install ethtool -y + else + apt-get update || apt-get --allow-releaseinfo-change update + apt-get install ethtool -y + fi + #bash <(wget -qO- https://git.io/lotServerInstall.sh) install + #echo | bash <(wget --no-check-certificate -qO- https://raw.githubusercontent.com/1265578519/lotServer/main/lotServerInstall.sh) install + echo | bash <(wget --no-check-certificate -qO- https://raw.githubusercontent.com/fei5seven/lotServer/master/lotServerInstall.sh) install + sed -i '/advinacc/d' /appex/etc/config + sed -i '/maxmode/d' /appex/etc/config + echo -e "advinacc=\"1\" +maxmode=\"1\"" >>/appex/etc/config + /appex/bin/lotServer.sh restart + start_menu +} + +#启用BBR2+FQ +startbbr2fq() { + remove_bbr_lotserver + echo "net.core.default_qdisc=fq" >>/etc/sysctl.d/99-sysctl.conf + echo "net.ipv4.tcp_congestion_control=bbr2" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}BBR2修改成功,重启生效!" +} + +#启用BBR2+FQ_PIE +startbbr2fqpie() { + remove_bbr_lotserver + echo "net.core.default_qdisc=fq_pie" >>/etc/sysctl.d/99-sysctl.conf + echo "net.ipv4.tcp_congestion_control=bbr2" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}BBR2修改成功,重启生效!" +} + +#启用BBR2+CAKE +startbbr2cake() { + remove_bbr_lotserver + echo "net.core.default_qdisc=cake" >>/etc/sysctl.d/99-sysctl.conf + echo "net.ipv4.tcp_congestion_control=bbr2" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}BBR2修改成功,重启生效!" +} + +#开启ecn +startecn() { + sed -i '/net.ipv4.tcp_ecn/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_ecn/d' /etc/sysctl.conf + + echo "net.ipv4.tcp_ecn=1" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}开启ecn结束!" +} + +#关闭ecn +closeecn() { + sed -i '/net.ipv4.tcp_ecn/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_ecn/d' /etc/sysctl.conf + + echo "net.ipv4.tcp_ecn=0" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}关闭ecn结束!" +} + +#卸载bbr+锐速 +remove_bbr_lotserver() { + sed -i '/net.ipv4.tcp_ecn/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.core.default_qdisc/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_congestion_control/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_ecn/d' /etc/sysctl.conf + sed -i '/net.core.default_qdisc/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_congestion_control/d' /etc/sysctl.conf + sysctl --system + + rm -rf bbrmod + + if [[ -e /appex/bin/lotServer.sh ]]; then + echo | bash <(wget -qO- https://raw.githubusercontent.com/fei5seven/lotServer/master/lotServerInstall.sh) uninstall + fi + clear + # echo -e "${Info}:清除bbr/lotserver加速完成。" + # sleep 1s +} + +#卸载全部加速 +remove_all() { + rm -rf /etc/sysctl.d/*.conf + #rm -rf /etc/sysctl.conf + #touch /etc/sysctl.conf + if [ ! -f "/etc/sysctl.conf" ]; then + touch /etc/sysctl.conf + else + cat /dev/null >/etc/sysctl.conf + fi + sysctl --system + sed -i '/DefaultTimeoutStartSec/d' /etc/systemd/system.conf + sed -i '/DefaultTimeoutStopSec/d' /etc/systemd/system.conf + sed -i '/DefaultRestartSec/d' /etc/systemd/system.conf + sed -i '/DefaultLimitCORE/d' /etc/systemd/system.conf + sed -i '/DefaultLimitNOFILE/d' /etc/systemd/system.conf + sed -i '/DefaultLimitNPROC/d' /etc/systemd/system.conf + + sed -i '/soft nofile/d' /etc/security/limits.conf + sed -i '/hard nofile/d' /etc/security/limits.conf + sed -i '/soft nproc/d' /etc/security/limits.conf + sed -i '/hard nproc/d' /etc/security/limits.conf + + sed -i '/ulimit -SHn/d' /etc/profile + sed -i '/ulimit -SHn/d' /etc/profile + sed -i '/required pam_limits.so/d' /etc/pam.d/common-session + + systemctl daemon-reload + + rm -rf bbrmod + sed -i '/net.ipv4.tcp_retries2/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_slow_start_after_idle/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_fastopen/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_ecn/d' /etc/sysctl.conf + sed -i '/net.core.default_qdisc/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_congestion_control/d' /etc/sysctl.conf + sed -i '/fs.file-max/d' /etc/sysctl.conf + sed -i '/net.core.rmem_max/d' /etc/sysctl.conf + sed -i '/net.core.wmem_max/d' /etc/sysctl.conf + sed -i '/net.core.rmem_default/d' /etc/sysctl.conf + sed -i '/net.core.wmem_default/d' /etc/sysctl.conf + sed -i '/net.core.netdev_max_backlog/d' /etc/sysctl.conf + sed -i '/net.core.somaxconn/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_syncookies/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_tw_reuse/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_tw_recycle/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_fin_timeout/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_keepalive_time/d' /etc/sysctl.conf + sed -i '/net.ipv4.ip_local_port_range/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_max_syn_backlog/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_max_tw_buckets/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_rmem/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_wmem/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_mtu_probing/d' /etc/sysctl.conf + sed -i '/net.ipv4.ip_forward/d' /etc/sysctl.conf + sed -i '/fs.inotify.max_user_instances/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_syncookies/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_fin_timeout/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_tw_reuse/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_max_syn_backlog/d' /etc/sysctl.conf + sed -i '/net.ipv4.ip_local_port_range/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_max_tw_buckets/d' /etc/sysctl.conf + sed -i '/net.ipv4.route.gc_timeout/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_synack_retries/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_syn_retries/d' /etc/sysctl.conf + sed -i '/net.core.somaxconn/d' /etc/sysctl.conf + sed -i '/net.core.netdev_max_backlog/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_timestamps/d' /etc/sysctl.conf + sed -i '/net.ipv4.tcp_max_orphans/d' /etc/sysctl.conf + if [[ -e /appex/bin/lotServer.sh ]]; then + bash <(wget -qO- https://raw.githubusercontent.com/fei5seven/lotServer/master/lotServerInstall.sh) uninstall + fi + clear + echo -e "${Info}:清除加速完成。" + sleep 1s +} + +optimizing_ddcc() { + sed -i '/net.ipv4.conf.all.rp_filter/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_syncookies/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv4.tcp_max_syn_backlog/d' /etc/sysctl.d/99-sysctl.conf + + echo "net.ipv4.conf.all.rp_filter = 1" >>/etc/sysctl.d/99-sysctl.conf + echo "net.ipv4.tcp_syncookies = 1" >>/etc/sysctl.d/99-sysctl.conf + echo "net.ipv4.tcp_max_syn_backlog = 1024" >>/etc/sysctl.d/99-sysctl.conf + sysctl -p + sysctl --system +} + +#更新脚本 +Update_Shell() { + local shell_file + shell_file="$(readlink -f "$0")" + local shell_url="https://raw.githubusercontent.com/ylx2016/Linux-NetSpeed/master/tcp.sh" + + # 下载最新版本的脚本 + wget -O "/tmp/tcp.sh" "$shell_url" &>/dev/null + + # 比较本地和远程脚本的 md5 值 + local md5_local + local md5_remote + md5_local="$(md5sum "$shell_file" | awk '{print $1}')" + md5_remote="$(md5sum /tmp/tcp.sh | awk '{print $1}')" + + if [ "$md5_local" != "$md5_remote" ]; then + # 替换本地脚本文件 + cp "/tmp/tcp.sh" "$shell_file" + chmod +x "$shell_file" + + echo "脚本已更新,请重新运行。" + exit 0 + else + echo "脚本是最新版本,无需更新。" + fi +} + +#切换到不卸载内核版本 +gototcpx() { + clear + #wget -O tcpx.sh "https://github.com/ylx2016/Linux-NetSpeed/raw/master/tcpx.sh" && chmod +x tcpx.sh && ./tcpx.sh + bash <(wget -qO- https://github.com/ylx2016/Linux-NetSpeed/raw/master/tcpx.sh) +} + +#切换到秋水逸冰BBR安装脚本 +gototeddysun_bbr() { + clear + #wget https://github.com/teddysun/across/raw/master/bbr.sh && chmod +x bbr.sh && ./bbr.sh + bash <(wget -qO- https://github.com/teddysun/across/raw/master/bbr.sh) +} + +#切换到一键DD安装系统脚本 新手勿入 +gotodd() { + clear + echo DD使用git.beta.gs的脚本,知悉 + sleep 1.5 + #wget -O NewReinstall.sh https://github.com/fcurrk/reinstall/raw/master/NewReinstall.sh && chmod a+x NewReinstall.sh && bash NewReinstall.sh + bash <(wget -qO- https://github.com/fcurrk/reinstall/raw/master/NewReinstall.sh) + #wget -qO ~/Network-Reinstall-System-Modify.sh 'https://github.com/ylx2016/reinstall/raw/master/Network-Reinstall-System-Modify.sh' && chmod a+x ~/Network-Reinstall-System-Modify.sh && bash ~/Network-Reinstall-System-Modify.sh -UI_Options +} + +#切换到检查当前IP质量/媒体解锁/邮箱通信脚本 +gotoipcheck() { + clear + sleep 1.5 + bash <(wget -qO- https://raw.githubusercontent.com/xykt/IPQuality/main/ip.sh) + #bash <(wget -qO- https://IP.Check.Place) +} + +#禁用IPv6 +closeipv6() { + clear + sed -i '/net.ipv6.conf.all.disable_ipv6/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.default.disable_ipv6/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.lo.disable_ipv6/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.all.disable_ipv6/d' /etc/sysctl.conf + sed -i '/net.ipv6.conf.default.disable_ipv6/d' /etc/sysctl.conf + sed -i '/net.ipv6.conf.lo.disable_ipv6/d' /etc/sysctl.conf + + echo "net.ipv6.conf.all.disable_ipv6 = 1 +net.ipv6.conf.default.disable_ipv6 = 1 +net.ipv6.conf.lo.disable_ipv6 = 1" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}禁用IPv6结束,可能需要重启!" +} + +#开启IPv6 +openipv6() { + clear + sed -i '/net.ipv6.conf.all.disable_ipv6/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.default.disable_ipv6/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.lo.disable_ipv6/d' /etc/sysctl.d/99-sysctl.conf + sed -i '/net.ipv6.conf.all.disable_ipv6/d' /etc/sysctl.conf + sed -i '/net.ipv6.conf.default.disable_ipv6/d' /etc/sysctl.conf + sed -i '/net.ipv6.conf.lo.disable_ipv6/d' /etc/sysctl.conf + sed -i '/net.ipv6.conf.all.accept_ra/d' /etc/sysctl.conf + sed -i '/net.ipv6.conf.default.accept_ra/d' /etc/sysctl.conf + + echo "net.ipv6.conf.all.disable_ipv6 = 0 +net.ipv6.conf.default.disable_ipv6 = 0 +net.ipv6.conf.lo.disable_ipv6 = 0 +net.ipv6.conf.all.accept_ra = 2 +net.ipv6.conf.default.accept_ra = 2" >>/etc/sysctl.d/99-sysctl.conf + sysctl --system + echo -e "${Info}开启IPv6结束,可能需要重启!" +} + +#开始菜单 +start_menu() { + clear + echo && echo -e " TCP加速 一键安装管理脚本 ${Red_font_prefix}[v${sh_ver}]${Font_color_suffix} from blog.ylx.me 母鸡慎用 + ${Green_font_prefix}0.${Font_color_suffix} 升级脚本 + ${Green_font_prefix}9.${Font_color_suffix} 切换到不卸载内核版本 ${Green_font_prefix}10.${Font_color_suffix} 切换到一键DD系统脚本 + ${Green_font_prefix}60.${Font_color_suffix} 切换到检查当前IP质量/媒体解锁/邮箱通信脚本 + ${Green_font_prefix}1.${Font_color_suffix} 安装 BBR原版内核 + ${Green_font_prefix}2.${Font_color_suffix} 安装 BBRplus版内核 ${Green_font_prefix}5.${Font_color_suffix} 安装 BBRplus新版内核 + ${Green_font_prefix}3.${Font_color_suffix} 安装 Lotserver(锐速)内核 + ${Green_font_prefix}8.${Font_color_suffix} 安装 官方cloud内核 (支持debian系列) + ${Green_font_prefix}11.${Font_color_suffix} 使用BBR+FQ加速 ${Green_font_prefix}12.${Font_color_suffix} 使用BBR+FQ_PIE加速 + ${Green_font_prefix}13.${Font_color_suffix} 使用BBR+CAKE加速 ${Green_font_prefix}14.${Font_color_suffix} 使用BBR2+FQ加速 + ${Green_font_prefix}15.${Font_color_suffix} 使用BBR2+FQ_PIE加速 ${Green_font_prefix}16.${Font_color_suffix} 使用BBR2+CAKE加速 + ${Green_font_prefix}17.${Font_color_suffix} 开启ECN ${Green_font_prefix}18.${Font_color_suffix} 关闭ECN + ${Green_font_prefix}19.${Font_color_suffix} 使用BBRplus+FQ版加速 + ${Green_font_prefix}20.${Font_color_suffix} 使用Lotserver(锐速)加速 + ${Green_font_prefix}21.${Font_color_suffix} 系统配置优化旧 ${Green_font_prefix}22.${Font_color_suffix} 系统配置优化新 + ${Green_font_prefix}27.${Font_color_suffix} 系统配置优化激进方案 + ${Green_font_prefix}23.${Font_color_suffix} 禁用IPv6 ${Green_font_prefix}24.${Font_color_suffix} 开启IPv6 + ${Green_font_prefix}25.${Font_color_suffix} 卸载全部加速 ${Green_font_prefix}99.${Font_color_suffix} 退出脚本 +————————————————————————————————————————————————————————————————" && + check_status + get_system_info + echo -e " 系统信息: ${Font_color_suffix}$opsy ${Green_font_prefix}$virtual${Font_color_suffix} $arch ${Green_font_prefix}$kern${Font_color_suffix} " + if [[ ${kernel_status} == "noinstall" ]]; then + echo -e " 当前状态: ${Green_font_prefix}未安装${Font_color_suffix} 加速内核 ${Red_font_prefix}请先安装内核${Font_color_suffix}" + else + echo -e " 当前状态: ${Green_font_prefix}已安装${Font_color_suffix} ${Red_font_prefix}${kernel_status}${Font_color_suffix} 加速内核 , ${Green_font_prefix}${run_status}${Font_color_suffix}" + + fi + echo -e " 当前拥塞控制算法为: ${Green_font_prefix}${net_congestion_control}${Font_color_suffix} 当前队列算法为: ${Green_font_prefix}${net_qdisc}${Font_color_suffix} " + + read -p " 请输入数字 :" num + case "$num" in + 0) + Update_Shell + ;; + 1) + check_sys_bbr + ;; + 2) + check_sys_bbrplus + ;; + 3) + check_sys_Lotsever + ;; + 5) + check_sys_bbrplusnew + ;; + 8) + check_sys_cloud + ;; + 9) + gototcpx + ;; + 10) + gotodd + ;; + 60) + gotoipcheck + ;; + 11) + startbbrfq + ;; + 12) + startbbrfqpie + ;; + 13) + startbbrcake + ;; + 14) + startbbr2fq + ;; + 15) + startbbr2fqpie + ;; + 16) + startbbr2cake + ;; + 17) + startecn + ;; + 18) + closeecn + ;; + 19) + startbbrplus + ;; + 20) + startlotserver + ;; + 21) + optimizing_system + ;; + 22) + optimizing_system_johnrosen1 + ;; + 23) + closeipv6 + ;; + 24) + openipv6 + ;; + 25) + remove_all + ;; + 26) + optimizing_ddcc + ;; + 27) + optimizing_system_radicalizate + ;; + 99) + exit 1 + ;; + *) + clear + echo -e "${Error}:请输入正确数字 [0-99]" + sleep 5s + start_menu + ;; + esac +} +#############内核管理组件############# + +#删除多余内核 +detele_kernel() { + if [[ "${OS_type}" == "CentOS" ]]; then + rpm_total=$(rpm -qa | grep kernel | grep -v "${kernel_version}" | grep -v "noarch" | wc -l) + if [ "${rpm_total}" ] >"1"; then + echo -e "检测到 ${rpm_total} 个其余内核,开始卸载..." + for ((integer = 1; integer <= ${rpm_total}; integer++)); do + rpm_del=$(rpm -qa | grep kernel | grep -v "${kernel_version}" | grep -v "noarch" | head -${integer}) + echo -e "开始卸载 ${rpm_del} 内核..." + rpm --nodeps -e ${rpm_del} + echo -e "卸载 ${rpm_del} 内核卸载完成,继续..." + done + echo --nodeps -e "内核卸载完毕,继续..." + else + echo -e " 检测到 内核 数量不正确,请检查 !" && exit 1 + fi + elif [[ "${OS_type}" == "Debian" ]]; then + deb_total=$(dpkg -l | grep linux-image | awk '{print $2}' | grep -v "${kernel_version}" | wc -l) + if [ "${deb_total}" ] >"1"; then + echo -e "检测到 ${deb_total} 个其余内核,开始卸载..." + for ((integer = 1; integer <= ${deb_total}; integer++)); do + deb_del=$(dpkg -l | grep linux-image | awk '{print $2}' | grep -v "${kernel_version}" | head -${integer}) + echo -e "开始卸载 ${deb_del} 内核..." + apt-get purge -y ${deb_del} + apt-get autoremove -y + echo -e "卸载 ${deb_del} 内核卸载完成,继续..." + done + echo -e "内核卸载完毕,继续..." + else + echo -e " 检测到 内核 数量不正确,请检查 !" && exit 1 + fi + fi +} + +detele_kernel_head() { + if [[ "${OS_type}" == "CentOS" ]]; then + rpm_total=$(rpm -qa | grep kernel-headers | grep -v "${kernel_version}" | grep -v "noarch" | wc -l) + if [ "${rpm_total}" ] >"1"; then + echo -e "检测到 ${rpm_total} 个其余head内核,开始卸载..." + for ((integer = 1; integer <= ${rpm_total}; integer++)); do + rpm_del=$(rpm -qa | grep kernel-headers | grep -v "${kernel_version}" | grep -v "noarch" | head -${integer}) + echo -e "开始卸载 ${rpm_del} headers内核..." + rpm --nodeps -e ${rpm_del} + echo -e "卸载 ${rpm_del} 内核卸载完成,继续..." + done + echo --nodeps -e "内核卸载完毕,继续..." + else + echo -e " 检测到 内核 数量不正确,请检查 !" && exit 1 + fi + elif [[ "${OS_type}" == "Debian" ]]; then + deb_total=$(dpkg -l | grep linux-headers | awk '{print $2}' | grep -v "${kernel_version}" | wc -l) + if [ "${deb_total}" ] >"1"; then + echo -e "检测到 ${deb_total} 个其余head内核,开始卸载..." + for ((integer = 1; integer <= ${deb_total}; integer++)); do + deb_del=$(dpkg -l | grep linux-headers | awk '{print $2}' | grep -v "${kernel_version}" | head -${integer}) + echo -e "开始卸载 ${deb_del} headers内核..." + apt-get purge -y ${deb_del} + apt-get autoremove -y + echo -e "卸载 ${deb_del} 内核卸载完成,继续..." + done + echo -e "内核卸载完毕,继续..." + else + echo -e " 检测到 内核 数量不正确,请检查 !" && exit 1 + fi + fi +} + +#更新引导 +BBR_grub() { + if [[ "${OS_type}" == "CentOS" ]]; then + if [[ ${version} == "6" ]]; then + if [ -f "/boot/grub/grub.conf" ]; then + sed -i 's/^default=.*/default=0/g' /boot/grub/grub.conf + elif [ -f "/boot/grub/grub.cfg" ]; then + grub-mkconfig -o /boot/grub/grub.cfg + grub-set-default 0 + elif [ -f "/boot/efi/EFI/centos/grub.cfg" ]; then + grub-mkconfig -o /boot/efi/EFI/centos/grub.cfg + grub-set-default 0 + elif [ -f "/boot/efi/EFI/redhat/grub.cfg" ]; then + grub-mkconfig -o /boot/efi/EFI/redhat/grub.cfg + grub-set-default 0 + else + echo -e "${Error} grub.conf/grub.cfg 找不到,请检查." + exit + fi + elif [[ ${version} == "7" ]]; then + if [ -f "/boot/grub2/grub.cfg" ]; then + grub2-mkconfig -o /boot/grub2/grub.cfg + grub2-set-default 0 + elif [ -f "/boot/efi/EFI/centos/grub.cfg" ]; then + grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg + grub2-set-default 0 + elif [ -f "/boot/efi/EFI/redhat/grub.cfg" ]; then + grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg + grub2-set-default 0 + else + echo -e "${Error} grub.cfg 找不到,请检查." + exit + fi + elif [[ ${version} == "8" ]]; then + if [ -f "/boot/grub2/grub.cfg" ]; then + grub2-mkconfig -o /boot/grub2/grub.cfg + grub2-set-default 0 + elif [ -f "/boot/efi/EFI/centos/grub.cfg" ]; then + grub2-mkconfig -o /boot/efi/EFI/centos/grub.cfg + grub2-set-default 0 + elif [ -f "/boot/efi/EFI/redhat/grub.cfg" ]; then + grub2-mkconfig -o /boot/efi/EFI/redhat/grub.cfg + grub2-set-default 0 + else + echo -e "${Error} grub.cfg 找不到,请检查." + exit + fi + grubby --info=ALL | awk -F= '$1=="kernel" {print i++ " : " $2}' + fi + elif [[ "${OS_type}" == "Debian" ]]; then + if _exists "update-grub"; then + update-grub + elif [ -f "/usr/sbin/update-grub" ]; then + /usr/sbin/update-grub + else + apt install grub2-common -y + update-grub + fi + #exit 1 + fi +} + +#简单的检查内核 +check_kernel() { + if [[ -z "$(find /boot -type f -name 'vmlinuz-*' ! -name 'vmlinuz-*rescue*')" ]]; then + echo -e "\033[0;31m警告: 未发现内核文件,请勿重启系统,不卸载内核版本选择30安装默认内核救急!\033[0m" + else + echo -e "\033[0;32m发现内核文件,看起来可以重启。\033[0m" + fi +} + +#############内核管理组件############# + +#############系统检测组件############# + +#检查系统 +check_sys() { + if [[ -f /etc/redhat-release ]]; then + release="centos" + elif grep -qi "debian" /etc/issue; then + release="debian" + elif grep -qi "ubuntu" /etc/issue; then + release="ubuntu" + elif grep -qi -E "centos|red hat|redhat" /etc/issue || grep -qi -E "centos|red hat|redhat" /proc/version; then + release="centos" + fi + + if [[ -f /etc/debian_version ]]; then + OS_type="Debian" + echo "检测为Debian通用系统,判断有误请反馈" + elif [[ -f /etc/redhat-release || -f /etc/centos-release || -f /etc/fedora-release ]]; then + OS_type="CentOS" + echo "检测为CentOS通用系统,判断有误请反馈" + else + echo "Unknown" + fi + + #from https://github.com/oooldking + + _exists() { + local cmd="$1" + if eval type type >/dev/null 2>&1; then + eval type "$cmd" >/dev/null 2>&1 + elif command >/dev/null 2>&1; then + command -v "$cmd" >/dev/null 2>&1 + else + which "$cmd" >/dev/null 2>&1 + fi + local rt=$? + return ${rt} + } + + get_opsy() { + if [ -f /etc/os-release ]; then + awk -F'[= "]' '/PRETTY_NAME/{print $3,$4,$5}' /etc/os-release + elif [ -f /etc/lsb-release ]; then + awk -F'[="]+' '/DESCRIPTION/{print $2}' /etc/lsb-release + elif [ -f /etc/system-release ]; then + cat /etc/system-release | awk '{print $1,$2}' + fi + } + + get_system_info() { + opsy=$(get_opsy) + arch=$(uname -m) + kern=$(uname -r) + virt_check + } + # from LemonBench + virt_check() { + if [ -f "/usr/bin/systemd-detect-virt" ]; then + Var_VirtType="$(/usr/bin/systemd-detect-virt)" + # 虚拟机检测 + if [ "${Var_VirtType}" = "qemu" ]; then + virtual="QEMU" + elif [ "${Var_VirtType}" = "kvm" ]; then + virtual="KVM" + elif [ "${Var_VirtType}" = "zvm" ]; then + virtual="S390 Z/VM" + elif [ "${Var_VirtType}" = "vmware" ]; then + virtual="VMware" + elif [ "${Var_VirtType}" = "microsoft" ]; then + virtual="Microsoft Hyper-V" + elif [ "${Var_VirtType}" = "xen" ]; then + virtual="Xen Hypervisor" + elif [ "${Var_VirtType}" = "bochs" ]; then + virtual="BOCHS" + elif [ "${Var_VirtType}" = "uml" ]; then + virtual="User-mode Linux" + elif [ "${Var_VirtType}" = "parallels" ]; then + virtual="Parallels" + elif [ "${Var_VirtType}" = "bhyve" ]; then + virtual="FreeBSD Hypervisor" + # 容器虚拟化检测 + elif [ "${Var_VirtType}" = "openvz" ]; then + virtual="OpenVZ" + elif [ "${Var_VirtType}" = "lxc" ]; then + virtual="LXC" + elif [ "${Var_VirtType}" = "lxc-libvirt" ]; then + virtual="LXC (libvirt)" + elif [ "${Var_VirtType}" = "systemd-nspawn" ]; then + virtual="Systemd nspawn" + elif [ "${Var_VirtType}" = "docker" ]; then + virtual="Docker" + elif [ "${Var_VirtType}" = "rkt" ]; then + virtual="RKT" + # 特殊处理 + elif [ -c "/dev/lxss" ]; then # 处理WSL虚拟化 + Var_VirtType="wsl" + virtual="Windows Subsystem for Linux (WSL)" + # 未匹配到任何结果, 或者非虚拟机 + elif [ "${Var_VirtType}" = "none" ]; then + Var_VirtType="dedicated" + virtual="None" + local Var_BIOSVendor + Var_BIOSVendor="$(dmidecode -s bios-vendor)" + if [ "${Var_BIOSVendor}" = "SeaBIOS" ]; then + Var_VirtType="Unknown" + virtual="Unknown with SeaBIOS BIOS" + else + Var_VirtType="dedicated" + virtual="Dedicated with ${Var_BIOSVendor} BIOS" + fi + fi + elif [ ! -f "/usr/sbin/virt-what" ]; then + Var_VirtType="Unknown" + virtual="[Error: virt-what not found !]" + elif [ -f "/.dockerenv" ]; then # 处理Docker虚拟化 + Var_VirtType="docker" + virtual="Docker" + elif [ -c "/dev/lxss" ]; then # 处理WSL虚拟化 + Var_VirtType="wsl" + virtual="Windows Subsystem for Linux (WSL)" + else # 正常判断流程 + Var_VirtType="$(virt-what | xargs)" + local Var_VirtTypeCount + Var_VirtTypeCount="$(echo $Var_VirtTypeCount | wc -l)" + if [ "${Var_VirtTypeCount}" -gt "1" ]; then # 处理嵌套虚拟化 + virtual="echo ${Var_VirtType}" + Var_VirtType="$(echo ${Var_VirtType} | head -n1)" # 使用检测到的第一种虚拟化继续做判断 + elif [ "${Var_VirtTypeCount}" -eq "1" ] && [ "${Var_VirtType}" != "" ]; then # 只有一种虚拟化 + virtual="${Var_VirtType}" + else + local Var_BIOSVendor + Var_BIOSVendor="$(dmidecode -s bios-vendor)" + if [ "${Var_BIOSVendor}" = "SeaBIOS" ]; then + Var_VirtType="Unknown" + virtual="Unknown with SeaBIOS BIOS" + else + Var_VirtType="dedicated" + virtual="Dedicated with ${Var_BIOSVendor} BIOS" + fi + fi + fi + } + + #检查依赖 + if [[ "${OS_type}" == "CentOS" ]]; then + # 检查是否安装了 ca-certificates 包,如果未安装则安装 + if ! rpm -q ca-certificates >/dev/null; then + echo '正在安装 ca-certificates 包...' + yum install ca-certificates -y + update-ca-trust force-enable + fi + echo 'CA证书检查OK' + + # 检查并安装 curl、wget 和 dmidecode 包 + for pkg in curl wget dmidecode redhat-lsb-core; do + if ! type $pkg >/dev/null 2>&1; then + echo "未安装 $pkg,正在安装..." + yum install $pkg -y + else + echo "$pkg 已安装。" + fi + done + + if [ -x "$(command -v lsb_release)" ]; then + echo "lsb_release 已安装" + else + echo "lsb_release 未安装,现在开始安装..." + yum install epel-release -y + yum install redhat-lsb-core -y + fi + + elif [[ "${OS_type}" == "Debian" ]]; then + # 检查是否安装了 ca-certificates 包,如果未安装则安装 + if ! dpkg-query -W ca-certificates >/dev/null; then + echo '正在安装 ca-certificates 包...' + apt-get update || apt-get --allow-releaseinfo-change update && apt-get install ca-certificates -y + update-ca-certificates + fi + echo 'CA证书检查OK' + + # 检查并安装 curl、wget 和 dmidecode 包 + for pkg in curl wget dmidecode; do + if ! type $pkg >/dev/null 2>&1; then + echo "未安装 $pkg,正在安装..." + apt-get update || apt-get --allow-releaseinfo-change update && apt-get install $pkg -y + else + echo "$pkg 已安装。" + fi + done + + if [ -x "$(command -v lsb_release)" ]; then + echo "lsb_release 已安装" + else + echo "lsb_release 未安装,现在开始安装..." + apt-get install lsb-release -y + fi + + else + echo "不支持的操作系统发行版:${release}" + exit 1 + fi +} + +#检查Linux版本 +check_version() { + if [[ -s /etc/redhat-release ]]; then + version=$(grep -oE "[0-9.]+" /etc/redhat-release | cut -d . -f 1) + else + version=$(grep -oE "[0-9.]+" /etc/issue | cut -d . -f 1) + fi + bit=$(uname -m) + check_github +} + +#检查安装bbr的系统要求 +check_sys_bbr() { + check_version + if [[ "${OS_type}" == "CentOS" ]]; then + if [[ ${version} == "7" ]]; then + installbbr + else + echo -e "${Error} BBR内核不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi + elif [[ "${OS_type}" == "Debian" ]]; then + apt-get --fix-broken install -y && apt-get autoremove -y + installbbr + else + echo -e "${Error} BBR内核不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi +} + +check_sys_bbrplus() { + check_version + if [[ "${OS_type}" == "CentOS" ]]; then + if [[ ${version} == "7" ]]; then + installbbrplus + else + echo -e "${Error} BBRplus内核不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi + elif [[ "${OS_type}" == "Debian" ]]; then + apt-get --fix-broken install -y && apt-get autoremove -y + installbbrplus + else + echo -e "${Error} BBRplus内核不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi +} + +check_sys_bbrplusnew() { + check_version + if [[ "${OS_type}" == "CentOS" ]]; then + #if [[ ${version} == "7" ]]; then + if [[ ${version} == "7" || ${version} == "8" ]]; then + installbbrplusnew + else + echo -e "${Error} BBRplusNew内核不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi + elif [[ "${OS_type}" == "Debian" ]]; then + apt-get --fix-broken install -y && apt-get autoremove -y + installbbrplusnew + else + echo -e "${Error} BBRplusNew内核不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi +} + +check_sys_xanmod() { + check_version + if [[ "${OS_type}" == "CentOS" ]]; then + if [[ ${version} == "7" || ${version} == "8" ]]; then + installxanmod + else + echo -e "${Error} xanmod内核不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi + elif [[ "${OS_type}" == "Debian" ]]; then + apt-get --fix-broken install -y && apt-get autoremove -y + installxanmod + else + echo -e "${Error} xanmod内核不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi +} + +check_sys_cloud() { + check_version + if [[ "${OS_type}" == "Debian" ]]; then + apt-get --fix-broken install -y && apt-get autoremove -y + installcloud + else + echo -e "${Error} cloud内核不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi +} + +#检查安装Lotsever的系统要求 +check_sys_Lotsever() { + check_version + bit=$(uname -m) + if [[ ${bit} != "x86_64" ]]; then + echo -e "${Error} 不支持x86_64以外的系统 !" && exit 1 + fi + if [[ "${OS_type}" == "CentOS" ]]; then + if [[ ${version} == "6" ]]; then + kernel_version="2.6.32-504" + installlot + elif [[ ${version} == "7" ]]; then + yum -y install net-tools + kernel_version="4.11.2-1" + installlot + else + echo -e "${Error} Lotsever不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi + elif [[ "${release}" == "debian" ]]; then + if [[ ${version} == "7" || ${version} == "8" ]]; then + if [[ ${bit} == "x86_64" ]]; then + kernel_version="3.16.0-4" + installlot + elif [[ ${bit} == "i386" ]]; then + kernel_version="3.2.0-4" + installlot + fi + elif [[ ${version} == "9" ]]; then + if [[ ${bit} == "x86_64" ]]; then + kernel_version="4.9.0-4" + installlot + fi + else + echo -e "${Error} Lotsever不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi + elif [[ "${release}" == "ubuntu" ]]; then + if [[ ${version} -ge "12" ]]; then + if [[ ${bit} == "x86_64" ]]; then + kernel_version="4.4.0-47" + installlot + elif [[ ${bit} == "i386" ]]; then + kernel_version="3.13.0-29" + installlot + fi + else + echo -e "${Error} Lotsever不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi + else + echo -e "${Error} Lotsever不支持当前系统 ${release} ${version} ${bit} !" && exit 1 + fi +} + +#检查系统当前状态 +check_status() { + kernel_version=$(uname -r | awk -F "-" '{print $1}') + kernel_version_full=$(uname -r) + net_congestion_control=$(cat /proc/sys/net/ipv4/tcp_congestion_control | awk '{print $1}') + net_qdisc=$(cat /proc/sys/net/core/default_qdisc | awk '{print $1}') + if [[ ${kernel_version_full} == *bbrplus* ]]; then + kernel_status="BBRplus" + elif [[ ${kernel_version_full} == *4.9.0-4* || ${kernel_version_full} == *4.15.0-30* || ${kernel_version_full} == *4.8.0-36* || ${kernel_version_full} == *3.16.0-77* || ${kernel_version_full} == *3.16.0-4* || ${kernel_version_full} == *3.2.0-4* || ${kernel_version_full} == *4.11.2-1* || ${kernel_version_full} == *2.6.32-504* || ${kernel_version_full} == *4.4.0-47* || ${kernel_version_full} == *3.13.0-29 || ${kernel_version_full} == *4.4.0-47* ]]; then + kernel_status="Lotserver" + elif [[ $(echo ${kernel_version} | awk -F'.' '{print $1}') == "4" ]] && [[ $(echo ${kernel_version} | awk -F'.' '{print $2}') -ge 9 ]] || [[ $(echo ${kernel_version} | awk -F'.' '{print $1}') == "5" ]] || [[ $(echo ${kernel_version} | awk -F'.' '{print $1}') == "6" ]]; then + kernel_status="BBR" + else + kernel_status="noinstall" + fi + + if [[ ${kernel_status} == "BBR" ]]; then + run_status=$(cat /proc/sys/net/ipv4/tcp_congestion_control | awk '{print $1}') + if [[ ${run_status} == "bbr" ]]; then + run_status=$(cat /proc/sys/net/ipv4/tcp_congestion_control | awk '{print $1}') + if [[ ${run_status} == "bbr" ]]; then + run_status="BBR启动成功" + else + run_status="BBR启动失败" + fi + elif [[ ${run_status} == "bbr2" ]]; then + run_status=$(cat /proc/sys/net/ipv4/tcp_congestion_control | awk '{print $1}') + if [[ ${run_status} == "bbr2" ]]; then + run_status="BBR2启动成功" + else + run_status="BBR2启动失败" + fi + elif [[ ${run_status} == "tsunami" ]]; then + run_status=$(lsmod | grep "tsunami" | awk '{print $1}') + if [[ ${run_status} == "tcp_tsunami" ]]; then + run_status="BBR魔改版启动成功" + else + run_status="BBR魔改版启动失败" + fi + elif [[ ${run_status} == "nanqinlang" ]]; then + run_status=$(lsmod | grep "nanqinlang" | awk '{print $1}') + if [[ ${run_status} == "tcp_nanqinlang" ]]; then + run_status="暴力BBR魔改版启动成功" + else + run_status="暴力BBR魔改版启动失败" + fi + else + run_status="未安装加速模块" + fi + + elif [[ ${kernel_status} == "Lotserver" ]]; then + if [[ -e /appex/bin/lotServer.sh ]]; then + run_status=$(bash /appex/bin/lotServer.sh status | grep "LotServer" | awk '{print $3}') + if [[ ${run_status} == "running!" ]]; then + run_status="启动成功" + else + run_status="启动失败" + fi + else + run_status="未安装加速模块" + fi + elif [[ ${kernel_status} == "BBRplus" ]]; then + run_status=$(cat /proc/sys/net/ipv4/tcp_congestion_control | awk '{print $1}') + if [[ ${run_status} == "bbrplus" ]]; then + run_status=$(cat /proc/sys/net/ipv4/tcp_congestion_control | awk '{print $1}') + if [[ ${run_status} == "bbrplus" ]]; then + run_status="BBRplus启动成功" + else + run_status="BBRplus启动失败" + fi + elif [[ ${run_status} == "bbr" ]]; then + run_status="BBR启动成功" + else + run_status="未安装加速模块" + fi + fi +} + +#############系统检测组件############# +check_sys +check_version +[[ "${OS_type}" == "Debian" ]] && [[ "${OS_type}" == "CentOS" ]] && echo -e "${Error} 本脚本不支持当前系统 ${release} !" && exit 1 +check_github +start_menu diff --git a/xray-core_sing-box.sh b/xray-core_sing-box.sh new file mode 100644 index 0000000..bf7c6a6 --- /dev/null +++ b/xray-core_sing-box.sh @@ -0,0 +1,9538 @@ +#!/usr/bin/env bash +# 检测区 +# ------------------------------------------------------------- +# 检查系统 +export LANG=en_US.UTF-8 + +echoContent() { + case $1 in + # 红色 + "red") + # shellcheck disable=SC2154 + ${echoType} "\033[31m${printN}$2 \033[0m" + ;; + # 天蓝色 + "skyBlue") + ${echoType} "\033[1;36m${printN}$2 \033[0m" + ;; + # 绿色 + "green") + ${echoType} "\033[32m${printN}$2 \033[0m" + ;; + # 白色 + "white") + ${echoType} "\033[37m${printN}$2 \033[0m" + ;; + "magenta") + ${echoType} "\033[31m${printN}$2 \033[0m" + ;; + # 黄色 + "yellow") + ${echoType} "\033[33m${printN}$2 \033[0m" + ;; + esac +} +# 检查SELinux状态 +checkCentosSELinux() { + if [[ -f "/etc/selinux/config" ]] && ! grep -q "SELINUX=disabled" <"/etc/selinux/config"; then + echoContent yellow "# 注意事项" + echoContent yellow "检测到SELinux已开启,请手动关闭,教程如下" + echoContent yellow "https://www.v2ray-agent.com/archives/1684115970026#centos7-%E5%85%B3%E9%97%ADselinux" + exit 0 + fi +} +checkSystem() { + if [[ -n $(find /etc -name "redhat-release") ]] || grep " + exit 1 + ;; + esac + fi + else + echoContent red " 无法识别此CPU架构,默认amd64、x86_64--->" + xrayCoreCPUVendor="Xray-linux-64" + # v2rayCoreCPUVendor="v2ray-linux-64" + fi +} + +# 初始化全局变量 +initVar() { + installType='yum -y install' + removeType='yum -y remove' + upgrade="yum -y update" + echoType='echo -e' + + # 核心支持的cpu版本 + xrayCoreCPUVendor="" + warpRegCoreCPUVendor="" + cpuVendor="" + + # 域名 + domain= + # 安装总进度 + totalProgress=1 + + # 1.xray-core安装 + # 2.v2ray-core 安装 + # 3.v2ray-core[xtls] 安装 + coreInstallType= + + # 核心安装path + # coreInstallPath= + + # v2ctl Path + ctlPath= + # 1.全部安装 + # 2.个性化安装 + # v2rayAgentInstallType= + + # 当前的个性化安装方式 01234 + currentInstallProtocolType= + + # 当前alpn的顺序 + currentAlpn= + + # 前置类型 + frontingType= + + # 选择的个性化安装方式 + selectCustomInstallType= + + # v2ray-core、xray-core配置文件的路径 + configPath= + + # xray-core reality状态 + realityStatus= + + # sing-box配置文件路径 + singBoxConfigPath= + + # sing-box端口 + + singBoxVLESSVisionPort= + singBoxVLESSRealityVisionPort= + singBoxVLESSRealityGRPCPort= + singBoxHysteria2Port= + singBoxTrojanPort= + singBoxTuicPort= + singBoxNaivePort= + singBoxVMessWSPort= + singBoxVLESSWSPort= + singBoxVMessHTTPUpgradePort= + + # nginx订阅端口 + subscribePort= + + subscribeType= + + # sing-box reality serverName publicKey + singBoxVLESSRealityGRPCServerName= + singBoxVLESSRealityVisionServerName= + singBoxVLESSRealityPublicKey= + + # xray-core reality serverName publicKey + xrayVLESSRealityServerName= + xrayVLESSRealityPort= + xrayVLESSRealityXHTTPServerName= + xrayVLESSRealityXHTTPort= + # xrayVLESSRealityPublicKey= + + # interfaceName= + # 端口跳跃 + portHoppingStart= + portHoppingEnd= + portHopping= + + hysteria2PortHoppingStart= + hysteria2PortHoppingEnd= + hysteria2PortHopping= + + # tuicPortHoppingStart= + # tuicPortHoppingEnd= + # tuicPortHopping= + + # tuic配置文件路径 + # tuicConfigPath= + tuicAlgorithm= + tuicPort= + + # 配置文件的path + currentPath= + + # 配置文件的host + currentHost= + + # 安装时选择的core类型 + selectCoreType= + + # 默认core版本 + # v2rayCoreVersion= + + # 随机路径 + customPath= + + # centos version + centosVersion= + + # UUID + currentUUID= + + # clients + currentClients= + + # previousClients + # previousClients= + + localIP= + + # 定时任务执行任务名称 RenewTLS-更新证书 UpdateGeo-更新geo文件 + cronName=$1 + + # tls安装失败后尝试的次数 + installTLSCount= + + # BTPanel状态 + # BTPanelStatus= + # 宝塔域名 + btDomain= + # nginx配置文件路径 + nginxConfigPath=/etc/nginx/conf.d/ + nginxStaticPath=/usr/share/nginx/html/ + + # 是否为预览版 + prereleaseStatus=false + + # ssl类型 + sslType= + # SSL CF API Token + cfAPIToken= + + # ssl邮箱 + sslEmail= + + # 检查天数 + sslRenewalDays=90 + + # dns ssl状态 + # dnsSSLStatus= + + # dns tls domain + dnsTLSDomain= + ipType= + + # 该域名是否通过dns安装通配符证书 + # installDNSACMEStatus= + + # 自定义端口 + customPort= + + # hysteria端口 + hysteriaPort= + + # hysteria协议 + # hysteriaProtocol= + + # hysteria延迟 + # hysteriaLag= + + # hysteria下行速度 + hysteria2ClientDownloadSpeed= + + # hysteria上行速度 + hysteria2ClientUploadSpeed= + + # Reality + realityPrivateKey= + realityServerName= + realityDestDomain= + + # 端口状态 + # isPortOpen= + # 通配符域名状态 + # wildcardDomainStatus= + # 通过nginx检查的端口 + # nginxIPort= + + # wget show progress + wgetShowProgressStatus= + + # warp + reservedWarpReg= + publicKeyWarpReg= + addressWarpReg= + secretKeyWarpReg= + + # 上次安装配置状态 + lastInstallationConfig= + +} + +# 读取tls证书详情 +readAcmeTLS() { + local readAcmeDomain= + if [[ -n "${currentHost}" ]]; then + readAcmeDomain="${currentHost}" + fi + + if [[ -n "${domain}" ]]; then + readAcmeDomain="${domain}" + fi + + dnsTLSDomain=$(echo "${readAcmeDomain}" | awk -F "." '{$1="";print $0}' | sed 's/^[[:space:]]*//' | sed 's/ /./g') + if [[ -d "$HOME/.acme.sh/*.${dnsTLSDomain}_ecc" && -f "$HOME/.acme.sh/*.${dnsTLSDomain}_ecc/*.${dnsTLSDomain}.key" && -f "$HOME/.acme.sh/*.${dnsTLSDomain}_ecc/*.${dnsTLSDomain}.cer" ]]; then + installedDNSAPIStatus=true + fi +} + +# 读取默认自定义端口 +readCustomPort() { + if [[ -n "${configPath}" && -z "${realityStatus}" && "${coreInstallType}" == "1" ]]; then + local port= + port=$(jq -r .inbounds[0].port "${configPath}${frontingType}.json") + if [[ "${port}" != "443" ]]; then + customPort=${port} + fi + fi +} + +# 读取nginx订阅端口 +readNginxSubscribe() { + subscribeType="https" + if [[ -f "${nginxConfigPath}subscribe.conf" ]]; then + if grep -q "sing-box" "${nginxConfigPath}subscribe.conf"; then + subscribePort=$(grep "listen" "${nginxConfigPath}subscribe.conf" | awk '{print $2}') + subscribeDomain=$(grep "server_name" "${nginxConfigPath}subscribe.conf" | awk '{print $2}') + subscribeDomain=${subscribeDomain//;/} + if [[ -n "${currentHost}" && "${subscribeDomain}" != "${currentHost}" ]]; then + subscribePort= + subscribeType= + else + if ! grep "listen" "${nginxConfigPath}subscribe.conf" | grep -q "ssl"; then + subscribeType="http" + fi + fi + + fi + fi +} + +# 检测安装方式 +readInstallType() { + coreInstallType= + configPath= + singBoxConfigPath= + + # 1.检测安装目录 + if [[ -d "/etc/v2ray-agent" ]]; then + if [[ -f "/etc/v2ray-agent/xray/xray" ]]; then + # 检测xray-core + if [[ -d "/etc/v2ray-agent/xray/conf" ]] && [[ -f "/etc/v2ray-agent/xray/conf/02_VLESS_TCP_inbounds.json" || -f "/etc/v2ray-agent/xray/conf/02_trojan_TCP_inbounds.json" || -f "/etc/v2ray-agent/xray/conf/07_VLESS_vision_reality_inbounds.json" ]]; then + # xray-core + configPath=/etc/v2ray-agent/xray/conf/ + ctlPath=/etc/v2ray-agent/xray/xray + coreInstallType=1 + if [[ -f "${configPath}07_VLESS_vision_reality_inbounds.json" ]]; then + realityStatus=1 + fi + if [[ -f "/etc/v2ray-agent/sing-box/sing-box" ]] && [[ -f "/etc/v2ray-agent/sing-box/conf/config/06_hysteria2_inbounds.json" || -f "/etc/v2ray-agent/sing-box/conf/config/09_tuic_inbounds.json" || -f "/etc/v2ray-agent/sing-box/conf/config/20_socks5_inbounds.json" ]]; then + singBoxConfigPath=/etc/v2ray-agent/sing-box/conf/config/ + fi + fi + elif [[ -f "/etc/v2ray-agent/sing-box/sing-box" && -f "/etc/v2ray-agent/sing-box/conf/config.json" ]]; then + # 检测sing-box + ctlPath=/etc/v2ray-agent/sing-box/sing-box + coreInstallType=2 + configPath=/etc/v2ray-agent/sing-box/conf/config/ + singBoxConfigPath=/etc/v2ray-agent/sing-box/conf/config/ + fi + fi +} + +# 读取协议类型 +readInstallProtocolType() { + currentInstallProtocolType= + frontingType= + + xrayVLESSRealityPort= + xrayVLESSRealityServerName= + + xrayVLESSRealityXHTTPort= + xrayVLESSRealityXHTTPServerName= + + # currentRealityXHTTPPrivateKey= + currentRealityXHTTPPublicKey= + + currentRealityPrivateKey= + currentRealityPublicKey= + + currentRealityMldsa65Seed= + currentRealityMldsa65Verify= + + singBoxVLESSVisionPort= + singBoxHysteria2Port= + singBoxTrojanPort= + + frontingTypeReality= + singBoxVLESSRealityVisionPort= + singBoxVLESSRealityVisionServerName= + singBoxVLESSRealityGRPCPort= + singBoxVLESSRealityGRPCServerName= + singBoxAnyTLSPort= + singBoxTuicPort= + singBoxNaivePort= + singBoxVMessWSPort= + singBoxSocks5Port= + + while read -r row; do + if echo "${row}" | grep -q VLESS_TCP_inbounds; then + currentInstallProtocolType="${currentInstallProtocolType}0," + frontingType=02_VLESS_TCP_inbounds + if [[ "${coreInstallType}" == "2" ]]; then + singBoxVLESSVisionPort=$(jq .inbounds[0].listen_port "${row}.json") + fi + fi + if echo "${row}" | grep -q VLESS_WS_inbounds; then + currentInstallProtocolType="${currentInstallProtocolType}1," + if [[ "${coreInstallType}" == "2" ]]; then + frontingType=03_VLESS_WS_inbounds + singBoxVLESSWSPort=$(jq .inbounds[0].listen_port "${row}.json") + fi + fi + if echo "${row}" | grep -q VLESS_XHTTP_inbounds; then + currentInstallProtocolType="${currentInstallProtocolType}12," + xrayVLESSRealityXHTTPort=$(jq -r .inbounds[0].port "${row}.json") + + xrayVLESSRealityXHTTPServerName=$(jq -r .inbounds[0].streamSettings.realitySettings.serverNames[0] "${row}.json") + + currentRealityXHTTPPublicKey=$(jq -r .inbounds[0].streamSettings.realitySettings.publicKey "${row}.json") + # currentRealityXHTTPPrivateKey=$(jq -r .inbounds[0].streamSettings.realitySettings.privateKey "${row}.json") + + # if [[ "${coreInstallType}" == "2" ]]; then + # frontingType=03_VLESS_WS_inbounds + # singBoxVLESSWSPort=$(jq .inbounds[0].listen_port "${row}.json") + # fi + fi + + if echo "${row}" | grep -q trojan_gRPC_inbounds; then + currentInstallProtocolType="${currentInstallProtocolType}2," + fi + if echo "${row}" | grep -q VMess_WS_inbounds; then + currentInstallProtocolType="${currentInstallProtocolType}3," + if [[ "${coreInstallType}" == "2" ]]; then + frontingType=05_VMess_WS_inbounds + singBoxVMessWSPort=$(jq .inbounds[0].listen_port "${row}.json") + fi + fi + if echo "${row}" | grep -q trojan_TCP_inbounds; then + currentInstallProtocolType="${currentInstallProtocolType}4," + if [[ "${coreInstallType}" == "2" ]]; then + frontingType=04_trojan_TCP_inbounds + singBoxTrojanPort=$(jq .inbounds[0].listen_port "${row}.json") + fi + fi + if echo "${row}" | grep -q VLESS_gRPC_inbounds; then + currentInstallProtocolType="${currentInstallProtocolType}5," + fi + if echo "${row}" | grep -q hysteria2_inbounds; then + currentInstallProtocolType="${currentInstallProtocolType}6," + if [[ "${coreInstallType}" == "2" ]]; then + frontingType=06_hysteria2_inbounds + singBoxHysteria2Port=$(jq .inbounds[0].listen_port "${row}.json") + fi + fi + if echo "${row}" | grep -q VLESS_vision_reality_inbounds; then + currentInstallProtocolType="${currentInstallProtocolType}7," + if [[ "${coreInstallType}" == "1" ]]; then + xrayVLESSRealityServerName=$(jq -r .inbounds[0].streamSettings.realitySettings.serverNames[0] "${row}.json") + realityServerName=${xrayVLESSRealityServerName} + xrayVLESSRealityPort=$(jq -r .inbounds[0].port "${row}.json") + + realityDomainPort=$(jq -r .inbounds[0].streamSettings.realitySettings.dest "${row}.json" | awk -F '[:]' '{print $2}') + + currentRealityPublicKey=$(jq -r .inbounds[0].streamSettings.realitySettings.publicKey "${row}.json") + currentRealityPrivateKey=$(jq -r .inbounds[0].streamSettings.realitySettings.privateKey "${row}.json") + + currentRealityMldsa65Seed=$(jq -r .inbounds[0].streamSettings.realitySettings.mldsa65Seed "${row}.json") + currentRealityMldsa65Verify=$(jq -r .inbounds[0].streamSettings.realitySettings.mldsa65Verify "${row}.json") + + frontingTypeReality=07_VLESS_vision_reality_inbounds + + elif [[ "${coreInstallType}" == "2" ]]; then + frontingTypeReality=07_VLESS_vision_reality_inbounds + singBoxVLESSRealityVisionPort=$(jq -r .inbounds[0].listen_port "${row}.json") + singBoxVLESSRealityVisionServerName=$(jq -r .inbounds[0].tls.server_name "${row}.json") + realityDomainPort=$(jq -r .inbounds[0].tls.reality.handshake.server_port "${row}.json") + + realityServerName=${singBoxVLESSRealityVisionServerName} + if [[ -f "${configPath}reality_key" ]]; then + singBoxVLESSRealityPublicKey=$(grep "publicKey" <"${configPath}reality_key" | awk -F "[:]" '{print $2}') + + currentRealityPrivateKey=$(jq -r .inbounds[0].tls.reality.private_key "${row}.json") + currentRealityPublicKey=$(grep "publicKey" <"${configPath}reality_key" | awk -F "[:]" '{print $2}') + fi + fi + fi + if echo "${row}" | grep -q VLESS_vision_gRPC_inbounds; then + currentInstallProtocolType="${currentInstallProtocolType}8," + if [[ "${coreInstallType}" == "2" ]]; then + frontingTypeReality=08_VLESS_vision_gRPC_inbounds + singBoxVLESSRealityGRPCPort=$(jq -r .inbounds[0].listen_port "${row}.json") + singBoxVLESSRealityGRPCServerName=$(jq -r .inbounds[0].tls.server_name "${row}.json") + if [[ -f "${configPath}reality_key" ]]; then + singBoxVLESSRealityPublicKey=$(grep "publicKey" <"${configPath}reality_key" | awk -F "[:]" '{print $2}') + fi + fi + fi + if echo "${row}" | grep -q tuic_inbounds; then + currentInstallProtocolType="${currentInstallProtocolType}9," + if [[ "${coreInstallType}" == "2" ]]; then + frontingType=09_tuic_inbounds + singBoxTuicPort=$(jq .inbounds[0].listen_port "${row}.json") + fi + fi + if echo "${row}" | grep -q naive_inbounds; then + currentInstallProtocolType="${currentInstallProtocolType}10," + if [[ "${coreInstallType}" == "2" ]]; then + frontingType=10_naive_inbounds + singBoxNaivePort=$(jq .inbounds[0].listen_port "${row}.json") + fi + fi + if echo "${row}" | grep -q anytls_inbounds; then + currentInstallProtocolType="${currentInstallProtocolType}13," + if [[ "${coreInstallType}" == "2" ]]; then + frontingType=13_anytls_inbounds + singBoxAnyTLSPort=$(jq .inbounds[0].listen_port "${row}.json") + fi + fi + if echo "${row}" | grep -q VMess_HTTPUpgrade_inbounds; then + currentInstallProtocolType="${currentInstallProtocolType}11," + if [[ "${coreInstallType}" == "2" ]]; then + frontingType=11_VMess_HTTPUpgrade_inbounds + singBoxVMessHTTPUpgradePort=$(grep 'listen' <${nginxConfigPath}sing_box_VMess_HTTPUpgrade.conf | awk '{print $2}') + fi + fi + if echo "${row}" | grep -q socks5_inbounds; then + currentInstallProtocolType="${currentInstallProtocolType}20," + singBoxSocks5Port=$(jq .inbounds[0].listen_port "${row}.json") + fi + + done < <(find ${configPath} -name "*inbounds.json" | sort | awk -F "[.]" '{print $1}') + + if [[ "${coreInstallType}" == "1" && -n "${singBoxConfigPath}" ]]; then + if [[ -f "${singBoxConfigPath}06_hysteria2_inbounds.json" ]]; then + currentInstallProtocolType="${currentInstallProtocolType}6," + singBoxHysteria2Port=$(jq .inbounds[0].listen_port "${singBoxConfigPath}06_hysteria2_inbounds.json") + fi + if [[ -f "${singBoxConfigPath}09_tuic_inbounds.json" ]]; then + currentInstallProtocolType="${currentInstallProtocolType}9," + singBoxTuicPort=$(jq .inbounds[0].listen_port "${singBoxConfigPath}09_tuic_inbounds.json") + fi + fi + if [[ "${currentInstallProtocolType:0:1}" != "," ]]; then + currentInstallProtocolType=",${currentInstallProtocolType}" + fi +} + +# 检查是否安装宝塔 +checkBTPanel() { + if [[ -n $(pgrep -f "BT-Panel") ]]; then + # 读取域名 + if [[ -d '/www/server/panel/vhost/cert/' && -n $(find /www/server/panel/vhost/cert/*/fullchain.pem) ]]; then + if [[ -z "${currentHost}" ]]; then + echoContent skyBlue "\n读取宝塔配置\n" + + find /www/server/panel/vhost/cert/*/fullchain.pem | awk -F "[/]" '{print $7}' | awk '{print NR""":"$0}' + + read -r -p "请输入编号选择:" selectBTDomain + else + selectBTDomain=$(find /www/server/panel/vhost/cert/*/fullchain.pem | awk -F "[/]" '{print $7}' | awk '{print NR""":"$0}' | grep "${currentHost}" | cut -d ":" -f 1) + fi + + if [[ -n "${selectBTDomain}" ]]; then + btDomain=$(find /www/server/panel/vhost/cert/*/fullchain.pem | awk -F "[/]" '{print $7}' | awk '{print NR""":"$0}' | grep -e "^${selectBTDomain}:" | cut -d ":" -f 2) + + if [[ -z "${btDomain}" ]]; then + echoContent red " ---> 选择错误,请重新选择" + checkBTPanel + else + domain=${btDomain} + if [[ ! -f "/etc/v2ray-agent/tls/${btDomain}.crt" && ! -f "/etc/v2ray-agent/tls/${btDomain}.key" ]]; then + ln -s "/www/server/panel/vhost/cert/${btDomain}/fullchain.pem" "/etc/v2ray-agent/tls/${btDomain}.crt" + ln -s "/www/server/panel/vhost/cert/${btDomain}/privkey.pem" "/etc/v2ray-agent/tls/${btDomain}.key" + fi + + nginxStaticPath="/www/wwwroot/${btDomain}/html/" + + mkdir -p "/www/wwwroot/${btDomain}/html/" + + if [[ -f "/www/wwwroot/${btDomain}/.user.ini" ]]; then + chattr -i "/www/wwwroot/${btDomain}/.user.ini" + fi + nginxConfigPath="/www/server/panel/vhost/nginx/" + fi + else + echoContent red " ---> 选择错误,请重新选择" + checkBTPanel + fi + fi + fi +} +check1Panel() { + if [[ -n $(pgrep -f "1panel") ]]; then + # 读取域名 + if [[ -d '/opt/1panel/apps/openresty/openresty/www/sites/' && -n $(find /opt/1panel/apps/openresty/openresty/www/sites/*/ssl/fullchain.pem) ]]; then + if [[ -z "${currentHost}" ]]; then + echoContent skyBlue "\n读取1Panel配置\n" + + find /opt/1panel/apps/openresty/openresty/www/sites/*/ssl/fullchain.pem | awk -F "[/]" '{print $9}' | awk '{print NR""":"$0}' + + read -r -p "请输入编号选择:" selectBTDomain + else + selectBTDomain=$(find /opt/1panel/apps/openresty/openresty/www/sites/*/ssl/fullchain.pem | awk -F "[/]" '{print $9}' | awk '{print NR""":"$0}' | grep "${currentHost}" | cut -d ":" -f 1) + fi + + if [[ -n "${selectBTDomain}" ]]; then + btDomain=$(find /opt/1panel/apps/openresty/openresty/www/sites/*/ssl/fullchain.pem | awk -F "[/]" '{print $9}' | awk '{print NR""":"$0}' | grep "${selectBTDomain}:" | cut -d ":" -f 2) + + if [[ -z "${btDomain}" ]]; then + echoContent red " ---> 选择错误,请重新选择" + check1Panel + else + domain=${btDomain} + if [[ ! -f "/etc/v2ray-agent/tls/${btDomain}.crt" && ! -f "/etc/v2ray-agent/tls/${btDomain}.key" ]]; then + ln -s "/opt/1panel/apps/openresty/openresty/www/sites/${btDomain}/ssl/fullchain.pem" "/etc/v2ray-agent/tls/${btDomain}.crt" + ln -s "/opt/1panel/apps/openresty/openresty/www/sites/${btDomain}/ssl/privkey.pem" "/etc/v2ray-agent/tls/${btDomain}.key" + fi + + nginxStaticPath="/opt/1panel/apps/openresty/openresty/www/sites/${btDomain}/index/" + fi + else + echoContent red " ---> 选择错误,请重新选择" + check1Panel + fi + fi + fi +} +# 读取当前alpn的顺序 +readInstallAlpn() { + if [[ -n "${currentInstallProtocolType}" && -z "${realityStatus}" ]]; then + local alpn + alpn=$(jq -r .inbounds[0].streamSettings.tlsSettings.alpn[0] ${configPath}${frontingType}.json) + if [[ -n ${alpn} ]]; then + currentAlpn=${alpn} + fi + fi +} + +# 检查防火墙 +allowPort() { + local type=$2 + if [[ -z "${type}" ]]; then + type=tcp + fi + # 如果防火墙启动状态则添加相应的开放端口 + if dpkg -l | grep -q "^[[:space:]]*ii[[:space:]]\+ufw"; then + if ufw status | grep -q "Status: active"; then + if ! ufw status | grep -q "$1/${type}"; then + sudo ufw allow "$1/${type}" + checkUFWAllowPort "$1" + fi + fi + elif systemctl status firewalld 2>/dev/null | grep -q "active (running)"; then + local updateFirewalldStatus= + if ! firewall-cmd --list-ports --permanent | grep -qw "$1/${type}"; then + updateFirewalldStatus=true + local firewallPort=$1 + if echo "${firewallPort}" | grep -q ":"; then + firewallPort=$(echo "${firewallPort}" | awk -F ":" '{print $1"-"$2}') + fi + firewall-cmd --zone=public --add-port="${firewallPort}/${type}" --permanent + checkFirewalldAllowPort "${firewallPort}" + fi + + if echo "${updateFirewalldStatus}" | grep -q "true"; then + firewall-cmd --reload + fi + elif rc-update show 2>/dev/null | grep -q ufw; then + if ufw status | grep -q "Status: active"; then + if ! ufw status | grep -q "$1/${type}"; then + sudo ufw allow "$1/${type}" + checkUFWAllowPort "$1" + fi + fi + elif dpkg -l | grep -q "^[[:space:]]*ii[[:space:]]\+netfilter-persistent" && systemctl status netfilter-persistent 2>/dev/null | grep -q "active (exited)"; then + local updateFirewalldStatus= + if ! iptables -L | grep -q "$1/${type}(mack-a)"; then + updateFirewalldStatus=true + iptables -I INPUT -p ${type} --dport "$1" -m comment --comment "allow $1/${type}(mack-a)" -j ACCEPT + fi + + if echo "${updateFirewalldStatus}" | grep -q "true"; then + netfilter-persistent save + fi + fi +} +# 获取公网IP +getPublicIP() { + local type=4 + if [[ -n "$1" ]]; then + type=$1 + fi + if [[ -n "${currentHost}" && -z "$1" ]] && [[ "${singBoxVLESSRealityVisionServerName}" == "${currentHost}" || "${singBoxVLESSRealityGRPCServerName}" == "${currentHost}" || "${xrayVLESSRealityServerName}" == "${currentHost}" ]]; then + echo "${currentHost}" + else + local currentIP= + currentIP=$(curl -s "-${type}" http://www.cloudflare.com/cdn-cgi/trace | grep "ip" | awk -F "[=]" '{print $2}') + if [[ -z "${currentIP}" && -z "$1" ]]; then + currentIP=$(curl -s "-6" http://www.cloudflare.com/cdn-cgi/trace | grep "ip" | awk -F "[=]" '{print $2}') + fi + echo "${currentIP}" + fi + +} + +# 输出ufw端口开放状态 +checkUFWAllowPort() { + if ufw status | grep -q "$1"; then + echoContent green " ---> $1端口开放成功" + else + echoContent red " ---> $1端口开放失败" + exit 0 + fi +} + +# 输出firewall-cmd端口开放状态 +checkFirewalldAllowPort() { + if firewall-cmd --list-ports --permanent | grep -q "$1"; then + echoContent green " ---> $1端口开放成功" + else + echoContent red " ---> $1端口开放失败" + exit 0 + fi +} + +# 读取Tuic配置 +readSingBoxConfig() { + tuicPort= + hysteriaPort= + if [[ -n "${singBoxConfigPath}" ]]; then + + if [[ -f "${singBoxConfigPath}09_tuic_inbounds.json" ]]; then + tuicPort=$(jq -r '.inbounds[0].listen_port' "${singBoxConfigPath}09_tuic_inbounds.json") + tuicAlgorithm=$(jq -r '.inbounds[0].congestion_control' "${singBoxConfigPath}09_tuic_inbounds.json") + fi + if [[ -f "${singBoxConfigPath}06_hysteria2_inbounds.json" ]]; then + hysteriaPort=$(jq -r '.inbounds[0].listen_port' "${singBoxConfigPath}06_hysteria2_inbounds.json") + hysteria2ClientUploadSpeed=$(jq -r '.inbounds[0].down_mbps' "${singBoxConfigPath}06_hysteria2_inbounds.json") + hysteria2ClientDownloadSpeed=$(jq -r '.inbounds[0].up_mbps' "${singBoxConfigPath}06_hysteria2_inbounds.json") + fi + fi +} + +# 读取上次安装的配置 +readLastInstallationConfig() { + if [[ -n "${configPath}" ]]; then + read -r -p "读取到上次安装的配置,是否使用 ?[y/n]:" lastInstallationConfigStatus + if [[ "${lastInstallationConfigStatus}" == "y" ]]; then + lastInstallationConfig=true + fi + fi +} +# 卸载 sing-box +unInstallSingBox() { + local type=$1 + if [[ -n "${singBoxConfigPath}" ]]; then + if grep -q 'tuic' 删除sing-box tuic配置成功" + fi + + if grep -q 'hysteria2' 删除sing-box hysteria2配置成功" + fi + rm "${singBoxConfigPath}config.json" + fi + + readInstallType + + if [[ -n "${singBoxConfigPath}" ]]; then + echoContent yellow " ---> 检测到有其他配置,保留sing-box核心" + handleSingBox stop + handleSingBox start + else + handleSingBox stop + rm /etc/systemd/system/sing-box.service + rm -rf /etc/v2ray-agent/sing-box/* + echoContent green " ---> sing-box 卸载完成" + fi +} + +# 检查文件目录以及path路径 +readConfigHostPathUUID() { + currentPath= + currentDefaultPort= + currentUUID= + currentClients= + currentHost= + currentPort= + currentCDNAddress= + singBoxVMessWSPath= + singBoxVLESSWSPath= + singBoxVMessHTTPUpgradePath= + + if [[ "${coreInstallType}" == "1" ]]; then + + # 安装 + if [[ -n "${frontingType}" ]]; then + currentHost=$(jq -r .inbounds[0].streamSettings.tlsSettings.certificates[0].certificateFile ${configPath}${frontingType}.json | awk -F '[t][l][s][/]' '{print $2}' | awk -F '[.][c][r][t]' '{print $1}') + + currentPort=$(jq .inbounds[0].port ${configPath}${frontingType}.json) + + local defaultPortFile= + defaultPortFile=$(find ${configPath}* | grep "default") + + if [[ -n "${defaultPortFile}" ]]; then + currentDefaultPort=$(echo "${defaultPortFile}" | awk -F [_] '{print $4}') + else + currentDefaultPort=$(jq -r .inbounds[0].port ${configPath}${frontingType}.json) + fi + currentUUID=$(jq -r .inbounds[0].settings.clients[0].id ${configPath}${frontingType}.json) + currentClients=$(jq -r .inbounds[0].settings.clients ${configPath}${frontingType}.json) + fi + + # reality + if echo ${currentInstallProtocolType} | grep -q ",7,"; then + + currentClients=$(jq -r .inbounds[0].settings.clients ${configPath}07_VLESS_vision_reality_inbounds.json) + currentUUID=$(jq -r .inbounds[0].settings.clients[0].id ${configPath}07_VLESS_vision_reality_inbounds.json) + xrayVLESSRealityVisionPort=$(jq -r .inbounds[0].port ${configPath}07_VLESS_vision_reality_inbounds.json) + if [[ "${currentPort}" == "${xrayVLESSRealityVisionPort}" ]]; then + xrayVLESSRealityVisionPort="${currentDefaultPort}" + fi + fi + elif [[ "${coreInstallType}" == "2" ]]; then + if [[ -n "${frontingType}" ]]; then + currentHost=$(jq -r .inbounds[0].tls.server_name ${configPath}${frontingType}.json) + if echo ${currentInstallProtocolType} | grep -q ",11," && [[ "${currentHost}" == "null" ]]; then + currentHost=$(grep 'server_name' <${nginxConfigPath}sing_box_VMess_HTTPUpgrade.conf | awk '{print $2}') + currentHost=${currentHost//;/} + fi + currentUUID=$(jq -r .inbounds[0].users[0].uuid ${configPath}${frontingType}.json) + currentClients=$(jq -r .inbounds[0].users ${configPath}${frontingType}.json) + else + currentUUID=$(jq -r .inbounds[0].users[0].uuid ${configPath}${frontingTypeReality}.json) + currentClients=$(jq -r .inbounds[0].users ${configPath}${frontingTypeReality}.json) + fi + fi + + # 读取path + if [[ -n "${configPath}" && -n "${frontingType}" ]]; then + if [[ "${coreInstallType}" == "1" ]]; then + local fallback + fallback=$(jq -r -c '.inbounds[0].settings.fallbacks[]|select(.path)' ${configPath}${frontingType}.json | head -1) + + local path + path=$(echo "${fallback}" | jq -r .path | awk -F "[/]" '{print $2}') + + if [[ $(echo "${fallback}" | jq -r .dest) == 31297 ]]; then + currentPath=$(echo "${path}" | awk -F "[w][s]" '{print $1}') + elif [[ $(echo "${fallback}" | jq -r .dest) == 31299 ]]; then + currentPath=$(echo "${path}" | awk -F "[v][w][s]" '{print $1}') + fi + + # 尝试读取alpn h2 Path + if [[ -z "${currentPath}" ]]; then + dest=$(jq -r -c '.inbounds[0].settings.fallbacks[]|select(.alpn)|.dest' ${configPath}${frontingType}.json | head -1) + if [[ "${dest}" == "31302" || "${dest}" == "31304" ]]; then + checkBTPanel + check1Panel + if grep -q "trojangrpc {" <${nginxConfigPath}alone.conf; then + currentPath=$(grep "trojangrpc {" <${nginxConfigPath}alone.conf | awk -F "[/]" '{print $2}' | awk -F "[t][r][o][j][a][n]" '{print $1}') + elif grep -q "grpc {" <${nginxConfigPath}alone.conf; then + currentPath=$(grep "grpc {" <${nginxConfigPath}alone.conf | head -1 | awk -F "[/]" '{print $2}' | awk -F "[g][r][p][c]" '{print $1}') + fi + fi + fi + if [[ -z "${currentPath}" && -f "${configPath}12_VLESS_XHTTP_inbounds.json" ]]; then + currentPath=$(jq -r .inbounds[0].streamSettings.xhttpSettings.path "${configPath}12_VLESS_XHTTP_inbounds.json" | awk -F "[x][H][T][T][P]" '{print $1}' | awk -F "[/]" '{print $2}') + fi + elif [[ "${coreInstallType}" == "2" && -f "${singBoxConfigPath}05_VMess_WS_inbounds.json" ]]; then + singBoxVMessWSPath=$(jq -r .inbounds[0].transport.path "${singBoxConfigPath}05_VMess_WS_inbounds.json") + currentPath=$(jq -r .inbounds[0].transport.path "${singBoxConfigPath}05_VMess_WS_inbounds.json" | awk -F "[/]" '{print $2}') + fi + if [[ "${coreInstallType}" == "2" && -f "${singBoxConfigPath}03_VLESS_WS_inbounds.json" ]]; then + singBoxVLESSWSPath=$(jq -r .inbounds[0].transport.path "${singBoxConfigPath}03_VLESS_WS_inbounds.json") + currentPath=$(jq -r .inbounds[0].transport.path "${singBoxConfigPath}03_VLESS_WS_inbounds.json" | awk -F "[/]" '{print $2}') + currentPath=${currentPath::-2} + fi + if [[ "${coreInstallType}" == "2" && -f "${singBoxConfigPath}11_VMess_HTTPUpgrade_inbounds.json" ]]; then + singBoxVMessHTTPUpgradePath=$(jq -r .inbounds[0].transport.path "${singBoxConfigPath}11_VMess_HTTPUpgrade_inbounds.json") + currentPath=$(jq -r .inbounds[0].transport.path "${singBoxConfigPath}11_VMess_HTTPUpgrade_inbounds.json" | awk -F "[/]" '{print $2}') + # currentPath=${currentPath::-2} + fi + fi + if [[ -f "/etc/v2ray-agent/cdn" ]] && [[ -n "$(head -1 /etc/v2ray-agent/cdn)" ]]; then + currentCDNAddress=$(head -1 /etc/v2ray-agent/cdn) + else + currentCDNAddress="${currentHost}" + fi +} + +# 状态展示 +showInstallStatus() { + if [[ -n "${coreInstallType}" ]]; then + if [[ "${coreInstallType}" == 1 ]]; then + if [[ -n $(pgrep -f "xray/xray") ]]; then + echoContent yellow "\n核心: Xray-core[运行中]" + else + echoContent yellow "\n核心: Xray-core[未运行]" + fi + + elif [[ "${coreInstallType}" == 2 ]]; then + if [[ -n $(pgrep -f "sing-box/sing-box") ]]; then + echoContent yellow "\n核心: sing-box[运行中]" + else + echoContent yellow "\n核心: sing-box[未运行]" + fi + fi + # 读取协议类型 + readInstallProtocolType + + if [[ -n ${currentInstallProtocolType} ]]; then + echoContent yellow "已安装协议: \c" + fi + if echo ${currentInstallProtocolType} | grep -q ",0,"; then + echoContent yellow "VLESS+TCP[TLS_Vision] \c" + fi + + if echo ${currentInstallProtocolType} | grep -q ",1,"; then + echoContent yellow "VLESS+WS[TLS] \c" + fi + + if echo ${currentInstallProtocolType} | grep -q ",2,"; then + echoContent yellow "Trojan+gRPC[TLS] \c" + fi + + if echo ${currentInstallProtocolType} | grep -q ",3,"; then + echoContent yellow "VMess+WS[TLS] \c" + fi + + if echo ${currentInstallProtocolType} | grep -q ",4,"; then + echoContent yellow "Trojan+TCP[TLS] \c" + fi + + if echo ${currentInstallProtocolType} | grep -q ",5,"; then + echoContent yellow "VLESS+gRPC[TLS] \c" + fi + if echo ${currentInstallProtocolType} | grep -q ",6,"; then + echoContent yellow "Hysteria2 \c" + fi + if echo ${currentInstallProtocolType} | grep -q ",7,"; then + echoContent yellow "VLESS+Reality+Vision \c" + fi + if echo ${currentInstallProtocolType} | grep -q ",8,"; then + echoContent yellow "VLESS+Reality+gRPC \c" + fi + if echo ${currentInstallProtocolType} | grep -q ",9,"; then + echoContent yellow "Tuic \c" + fi + if echo ${currentInstallProtocolType} | grep -q ",10,"; then + echoContent yellow "Naive \c" + fi + if echo ${currentInstallProtocolType} | grep -q ",11,"; then + echoContent yellow "VMess+TLS+HTTPUpgrade \c" + fi + if echo ${currentInstallProtocolType} | grep -q ",12,"; then + echoContent yellow "VLESS+XHTTP \c" + fi + if echo ${currentInstallProtocolType} | grep -q ",13,"; then + echoContent yellow "AnyTLS \c" + fi + fi +} + +# 清理旧残留 +cleanUp() { + if [[ "$1" == "xrayDel" ]]; then + handleXray stop + rm -rf /etc/v2ray-agent/xray/* + elif [[ "$1" == "singBoxDel" ]]; then + handleSingBox stop + rm -rf /etc/v2ray-agent/sing-box/conf/config.json >/dev/null 2>&1 + rm -rf /etc/v2ray-agent/sing-box/conf/config/* >/dev/null 2>&1 + fi +} +initVar "$1" +checkSystem +checkCPUVendor + +readInstallType +readInstallProtocolType +readConfigHostPathUUID +readCustomPort +readSingBoxConfig +# ------------------------------------------------------------- + +# 初始化安装目录 +mkdirTools() { + mkdir -p /etc/v2ray-agent/tls + mkdir -p /etc/v2ray-agent/subscribe_local/default + mkdir -p /etc/v2ray-agent/subscribe_local/clashMeta + + mkdir -p /etc/v2ray-agent/subscribe_remote/default + mkdir -p /etc/v2ray-agent/subscribe_remote/clashMeta + + mkdir -p /etc/v2ray-agent/subscribe/default + mkdir -p /etc/v2ray-agent/subscribe/clashMetaProfiles + mkdir -p /etc/v2ray-agent/subscribe/clashMeta + + mkdir -p /etc/v2ray-agent/subscribe/sing-box + mkdir -p /etc/v2ray-agent/subscribe/sing-box_profiles + mkdir -p /etc/v2ray-agent/subscribe_local/sing-box + + mkdir -p /etc/v2ray-agent/xray/conf + mkdir -p /etc/v2ray-agent/xray/reality_scan + mkdir -p /etc/v2ray-agent/xray/tmp + mkdir -p /etc/systemd/system/ + mkdir -p /tmp/v2ray-agent-tls/ + + mkdir -p /etc/v2ray-agent/warp + + mkdir -p /etc/v2ray-agent/sing-box/conf/config + + mkdir -p /usr/share/nginx/html/ +} + +# 安装工具包 +installTools() { + echoContent skyBlue "\n进度 $1/${totalProgress} : 安装工具" + # 修复ubuntu个别系统问题 + if [[ "${release}" == "ubuntu" ]]; then + dpkg --configure -a + fi + + if [[ -n $(pgrep -f "apt") ]]; then + pgrep -f apt | xargs kill -9 + fi + + echoContent green " ---> 检查、安装更新【新机器会很慢,如长时间无反应,请手动停止后重新执行】" + + ${upgrade} >/etc/v2ray-agent/install.log 2>&1 + if grep <"/etc/v2ray-agent/install.log" -q "changed"; then + ${updateReleaseInfoChange} >/dev/null 2>&1 + fi + + if [[ "${release}" == "centos" ]]; then + rm -rf /var/run/yum.pid + ${installType} epel-release >/dev/null 2>&1 + fi + + if ! find /usr/bin /usr/sbin | grep -q -w sudo; then + echoContent green " ---> 安装sudo" + ${installType} sudo >/dev/null 2>&1 + fi + + if ! find /usr/bin /usr/sbin | grep -q -w wget; then + echoContent green " ---> 安装wget" + ${installType} wget >/dev/null 2>&1 + fi + + if ! find /usr/bin /usr/sbin | grep -q -w netfilter-persistent; then + if [[ "${release}" != "centos" ]]; then + echoContent green " ---> 安装iptables" + echo "iptables-persistent iptables-persistent/autosave_v4 boolean true" | sudo debconf-set-selections + echo "iptables-persistent iptables-persistent/autosave_v6 boolean true" | sudo debconf-set-selections + ${installType} iptables-persistent >/dev/null 2>&1 + fi + fi + + if ! find /usr/bin /usr/sbin | grep -q -w curl; then + echoContent green " ---> 安装curl" + ${installType} curl >/dev/null 2>&1 + fi + + if ! find /usr/bin /usr/sbin | grep -q -w unzip; then + echoContent green " ---> 安装unzip" + ${installType} unzip >/dev/null 2>&1 + fi + + if ! find /usr/bin /usr/sbin | grep -q -w socat; then + echoContent green " ---> 安装socat" + ${installType} socat >/dev/null 2>&1 + fi + + if ! find /usr/bin /usr/sbin | grep -q -w tar; then + echoContent green " ---> 安装tar" + ${installType} tar >/dev/null 2>&1 + fi + + if ! find /usr/bin /usr/sbin | grep -q -w cron; then + echoContent green " ---> 安装crontabs" + if [[ "${release}" == "ubuntu" ]] || [[ "${release}" == "debian" ]]; then + ${installType} cron >/dev/null 2>&1 + else + ${installType} crontabs >/dev/null 2>&1 + fi + fi + if ! find /usr/bin /usr/sbin | grep -q -w jq; then + echoContent green " ---> 安装jq" + ${installType} jq >/dev/null 2>&1 + fi + + if ! find /usr/bin /usr/sbin | grep -q -w binutils; then + echoContent green " ---> 安装binutils" + ${installType} binutils >/dev/null 2>&1 + fi + + if ! find /usr/bin /usr/sbin | grep -q -w openssl; then + echoContent green " ---> 安装openssl" + ${installType} openssl >/dev/null 2>&1 + fi + + if ! find /usr/bin /usr/sbin | grep -q -w ping6; then + echoContent green " ---> 安装ping6" + ${installType} inetutils-ping >/dev/null 2>&1 + fi + + if ! find /usr/bin /usr/sbin | grep -q -w qrencode; then + echoContent green " ---> 安装qrencode" + ${installType} qrencode >/dev/null 2>&1 + fi + + if ! find /usr/bin /usr/sbin | grep -q -w lsb-release; then + echoContent green " ---> 安装lsb-release" + ${installType} lsb-release >/dev/null 2>&1 + fi + + if ! find /usr/bin /usr/sbin | grep -q -w lsof; then + echoContent green " ---> 安装lsof" + ${installType} lsof >/dev/null 2>&1 + fi + + if ! find /usr/bin /usr/sbin | grep -q -w dig; then + echoContent green " ---> 安装dig" + if echo "${installType}" | grep -qw "apt"; then + ${installType} dnsutils >/dev/null 2>&1 + elif echo "${installType}" | grep -qw "yum"; then + ${installType} bind-utils >/dev/null 2>&1 + elif echo "${installType}" | grep -qw "apk"; then + ${installType} bind-tools >/dev/null 2>&1 + fi + fi + + # 检测nginx版本,并提供是否卸载的选项 + if echo "${selectCustomInstallType}" | grep -qwE ",7,|,8,|,7,8,"; then + echoContent green " ---> 检测到无需依赖Nginx的服务,跳过安装" + else + if ! find /usr/bin /usr/sbin | grep -q -w nginx; then + echoContent green " ---> 安装nginx" + installNginxTools + else + nginxVersion=$(nginx -v 2>&1) + nginxVersion=$(echo "${nginxVersion}" | awk -F "[n][g][i][n][x][/]" '{print $2}' | awk -F "[.]" '{print $2}') + if [[ ${nginxVersion} -lt 14 ]]; then + read -r -p "读取到当前的Nginx版本不支持gRPC,会导致安装失败,是否卸载Nginx后重新安装 ?[y/n]:" unInstallNginxStatus + if [[ "${unInstallNginxStatus}" == "y" ]]; then + ${removeType} nginx >/dev/null 2>&1 + echoContent yellow " ---> nginx卸载完成" + echoContent green " ---> 安装nginx" + installNginxTools >/dev/null 2>&1 + else + exit 0 + fi + fi + fi + fi + + if ! find /usr/bin /usr/sbin | grep -q -w semanage; then + echoContent green " ---> 安装semanage" + ${installType} bash-completion >/dev/null 2>&1 + + if [[ "${centosVersion}" == "7" ]]; then + policyCoreUtils="policycoreutils-python.x86_64" + elif [[ "${centosVersion}" == "8" ]]; then + policyCoreUtils="policycoreutils-python-utils-2.9-9.el8.noarch" + fi + + if [[ -n "${policyCoreUtils}" ]]; then + ${installType} ${policyCoreUtils} >/dev/null 2>&1 + fi + if [[ -n $(which semanage) ]]; then + semanage port -a -t http_port_t -p tcp 31300 + + fi + fi + if [[ "${selectCustomInstallType}" == "7" ]]; then + echoContent green " ---> 检测到无需依赖证书的服务,跳过安装" + else + if [[ ! -d "$HOME/.acme.sh" ]] || [[ -d "$HOME/.acme.sh" && -z $(find "$HOME/.acme.sh/acme.sh") ]]; then + echoContent green " ---> 安装acme.sh" + curl -s https://get.acme.sh | sh >/etc/v2ray-agent/tls/acme.log 2>&1 + + if [[ ! -d "$HOME/.acme.sh" ]] || [[ -z $(find "$HOME/.acme.sh/acme.sh") ]]; then + echoContent red " acme安装失败--->" + tail -n 100 /etc/v2ray-agent/tls/acme.log + echoContent yellow "错误排查:" + echoContent red " 1.获取Github文件失败,请等待Github恢复后尝试,恢复进度可查看 [https://www.githubstatus.com/]" + echoContent red " 2.acme.sh脚本出现bug,可查看[https://github.com/acmesh-official/acme.sh] issues" + echoContent red " 3.如纯IPv6机器,请设置NAT64,可执行下方命令,如果添加下方命令还是不可用,请尝试更换其他NAT64" + echoContent skyBlue " sed -i \"1i\\\nameserver 2a00:1098:2b::1\\\nnameserver 2a00:1098:2c::1\\\nnameserver 2a01:4f8:c2c:123f::1\\\nnameserver 2a01:4f9:c010:3f02::1\" /etc/resolv.conf" + exit 0 + fi + fi + fi + +} +# 开机启动 +bootStartup() { + local serviceName=$1 + if [[ "${release}" == "alpine" ]]; then + rc-update add "${serviceName}" default + else + systemctl daemon-reload + systemctl enable "${serviceName}" + fi +} +# 安装Nginx +installNginxTools() { + + if [[ "${release}" == "debian" ]]; then + sudo apt install gnupg2 ca-certificates lsb-release -y >/dev/null 2>&1 + echo "deb http://nginx.org/packages/mainline/debian $(lsb_release -cs) nginx" | sudo tee /etc/apt/sources.list.d/nginx.list >/dev/null 2>&1 + echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" | sudo tee /etc/apt/preferences.d/99nginx >/dev/null 2>&1 + curl -o /tmp/nginx_signing.key https://nginx.org/keys/nginx_signing.key >/dev/null 2>&1 + # gpg --dry-run --quiet --import --import-options import-show /tmp/nginx_signing.key + sudo mv /tmp/nginx_signing.key /etc/apt/trusted.gpg.d/nginx_signing.asc + sudo apt update >/dev/null 2>&1 + + elif [[ "${release}" == "ubuntu" ]]; then + sudo apt install gnupg2 ca-certificates lsb-release -y >/dev/null 2>&1 + echo "deb http://nginx.org/packages/mainline/ubuntu $(lsb_release -cs) nginx" | sudo tee /etc/apt/sources.list.d/nginx.list >/dev/null 2>&1 + echo -e "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" | sudo tee /etc/apt/preferences.d/99nginx >/dev/null 2>&1 + curl -o /tmp/nginx_signing.key https://nginx.org/keys/nginx_signing.key >/dev/null 2>&1 + # gpg --dry-run --quiet --import --import-options import-show /tmp/nginx_signing.key + sudo mv /tmp/nginx_signing.key /etc/apt/trusted.gpg.d/nginx_signing.asc + sudo apt update >/dev/null 2>&1 + + elif [[ "${release}" == "centos" ]]; then + ${installType} yum-utils >/dev/null 2>&1 + cat </etc/yum.repos.d/nginx.repo +[nginx-stable] +name=nginx stable repo +baseurl=http://nginx.org/packages/centos/\$releasever/\$basearch/ +gpgcheck=1 +enabled=1 +gpgkey=https://nginx.org/keys/nginx_signing.key +module_hotfixes=true + +[nginx-mainline] +name=nginx mainline repo +baseurl=http://nginx.org/packages/mainline/centos/\$releasever/\$basearch/ +gpgcheck=1 +enabled=0 +gpgkey=https://nginx.org/keys/nginx_signing.key +module_hotfixes=true +EOF + sudo yum-config-manager --enable nginx-mainline >/dev/null 2>&1 + elif [[ "${release}" == "alpine" ]]; then + rm "${nginxConfigPath}default.conf" + fi + ${installType} nginx >/dev/null 2>&1 + bootStartup nginx +} + +# 安装warp +installWarp() { + if [[ "${cpuVendor}" == "arm" ]]; then + echoContent red " ---> 官方WARP客户端不支持ARM架构" + exit 0 + fi + + ${installType} gnupg2 -y >/dev/null 2>&1 + if [[ "${release}" == "debian" ]]; then + curl -s https://pkg.cloudflareclient.com/pubkey.gpg | sudo apt-key add - >/dev/null 2>&1 + echo "deb http://pkg.cloudflareclient.com/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflare-client.list >/dev/null 2>&1 + sudo apt update >/dev/null 2>&1 + + elif [[ "${release}" == "ubuntu" ]]; then + curl -s https://pkg.cloudflareclient.com/pubkey.gpg | sudo apt-key add - >/dev/null 2>&1 + echo "deb http://pkg.cloudflareclient.com/ focal main" | sudo tee /etc/apt/sources.list.d/cloudflare-client.list >/dev/null 2>&1 + sudo apt update >/dev/null 2>&1 + + elif [[ "${release}" == "centos" ]]; then + ${installType} yum-utils >/dev/null 2>&1 + sudo rpm -ivh "http://pkg.cloudflareclient.com/cloudflare-release-el${centosVersion}.rpm" >/dev/null 2>&1 + fi + + echoContent green " ---> 安装WARP" + ${installType} cloudflare-warp >/dev/null 2>&1 + if [[ -z $(which warp-cli) ]]; then + echoContent red " ---> 安装WARP失败" + exit 0 + fi + systemctl enable warp-svc + warp-cli --accept-tos register + warp-cli --accept-tos set-mode proxy + warp-cli --accept-tos set-proxy-port 31303 + warp-cli --accept-tos connect + warp-cli --accept-tos enable-always-on + + local warpStatus= + warpStatus=$(curl -s --socks5 127.0.0.1:31303 https://www.cloudflare.com/cdn-cgi/trace | grep "warp" | cut -d "=" -f 2) + + if [[ "${warpStatus}" == "on" ]]; then + echoContent green " ---> WARP启动成功" + fi +} + +# 通过dns检查域名的IP +checkDNSIP() { + local domain=$1 + local dnsIP= + ipType=4 + dnsIP=$(dig @1.1.1.1 +time=2 +short "${domain}" | grep -E "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$") + if [[ -z "${dnsIP}" ]]; then + dnsIP=$(dig @8.8.8.8 +time=2 +short "${domain}" | grep -E "^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$") + fi + if echo "${dnsIP}" | grep -q "timed out" || [[ -z "${dnsIP}" ]]; then + echo + echoContent red " ---> 无法通过DNS获取域名 IPv4 地址" + echoContent green " ---> 尝试检查域名 IPv6 地址" + dnsIP=$(dig @2606:4700:4700::1111 +time=2 aaaa +short "${domain}") + ipType=6 + if echo "${dnsIP}" | grep -q "network unreachable" || [[ -z "${dnsIP}" ]]; then + echoContent red " ---> 无法通过DNS获取域名IPv6地址,退出安装" + exit 0 + fi + fi + local publicIP= + + publicIP=$(getPublicIP "${ipType}") + if [[ "${publicIP}" != "${dnsIP}" ]]; then + echoContent red " ---> 域名解析IP与当前服务器IP不一致\n" + echoContent yellow " ---> 请检查域名解析是否生效以及正确" + echoContent green " ---> 当前VPS IP:${publicIP}" + echoContent green " ---> DNS解析 IP:${dnsIP}" + exit 0 + else + echoContent green " ---> 域名IP校验通过" + fi +} +# 检查端口实际开放状态 +checkPortOpen() { + handleSingBox stop >/dev/null 2>&1 + handleXray stop >/dev/null 2>&1 + + local port=$1 + local domain=$2 + local checkPortOpenResult= + allowPort "${port}" + + if [[ -z "${btDomain}" ]]; then + + handleNginx stop + # 初始化nginx配置 + touch ${nginxConfigPath}checkPortOpen.conf + local listenIPv6PortConfig= + + if [[ -n $(curl -s -6 -m 4 http://www.cloudflare.com/cdn-cgi/trace | grep "ip" | cut -d "=" -f 2) ]]; then + listenIPv6PortConfig="listen [::]:${port};" + fi + cat <${nginxConfigPath}checkPortOpen.conf +server { + listen ${port}; + ${listenIPv6PortConfig} + server_name ${domain}; + location /checkPort { + return 200 'fjkvymb6len'; + } + location /ip { + proxy_set_header Host \$host; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header REMOTE-HOST \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + default_type text/plain; + return 200 \$proxy_add_x_forwarded_for; + } +} +EOF + handleNginx start + # 检查域名+端口的开放 + checkPortOpenResult=$(curl -s -m 10 "http://${domain}:${port}/checkPort") + localIP=$(curl -s -m 10 "http://${domain}:${port}/ip") + rm "${nginxConfigPath}checkPortOpen.conf" + handleNginx stop + if [[ "${checkPortOpenResult}" == "fjkvymb6len" ]]; then + echoContent green " ---> 检测到${port}端口已开放" + else + echoContent green " ---> 未检测到${port}端口开放,退出安装" + if echo "${checkPortOpenResult}" | grep -q "cloudflare"; then + echoContent yellow " ---> 请关闭云朵后等待三分钟重新尝试" + else + if [[ -z "${checkPortOpenResult}" ]]; then + echoContent red " ---> 请检查是否有网页防火墙,比如Oracle等云服务商" + echoContent red " ---> 检查是否自己安装过nginx并且有配置冲突,可以尝试DD纯净系统后重新尝试" + else + echoContent red " ---> 错误日志:${checkPortOpenResult},请将此错误日志通过issues提交反馈" + fi + fi + exit 0 + fi + checkIP "${localIP}" + fi +} + +# 初始化Nginx申请证书配置 +initTLSNginxConfig() { + handleNginx stop + echoContent skyBlue "\n进度 $1/${totalProgress} : 初始化Nginx申请证书配置" + if [[ -n "${currentHost}" && -z "${lastInstallationConfig}" ]]; then + echo + read -r -p "读取到上次安装记录,是否使用上次安装时的域名 ?[y/n]:" historyDomainStatus + if [[ "${historyDomainStatus}" == "y" ]]; then + domain=${currentHost} + echoContent yellow "\n ---> 域名: ${domain}" + else + echo + echoContent yellow "请输入要配置的域名 例: www.v2ray-agent.com --->" + read -r -p "域名:" domain + fi + elif [[ -n "${currentHost}" && -n "${lastInstallationConfig}" ]]; then + domain=${currentHost} + else + echo + echoContent yellow "请输入要配置的域名 例: www.v2ray-agent.com --->" + read -r -p "域名:" domain + fi + + if [[ -z ${domain} ]]; then + echoContent red " 域名不可为空--->" + initTLSNginxConfig 3 + else + dnsTLSDomain=$(echo "${domain}" | awk -F "." '{$1="";print $0}' | sed 's/^[[:space:]]*//' | sed 's/ /./g') + if [[ "${selectCoreType}" == "1" ]]; then + customPortFunction + fi + # 修改配置 + handleNginx stop + fi +} + +# 删除nginx默认的配置 +removeNginxDefaultConf() { + if [[ -f ${nginxConfigPath}default.conf ]]; then + if [[ "$(grep -c "server_name" <${nginxConfigPath}default.conf)" == "1" ]] && [[ "$(grep -c "server_name localhost;" <${nginxConfigPath}default.conf)" == "1" ]]; then + echoContent green " ---> 删除Nginx默认配置" + rm -rf ${nginxConfigPath}default.conf >/dev/null 2>&1 + fi + fi +} +# 修改nginx重定向配置 +updateRedirectNginxConf() { + local redirectDomain= + redirectDomain=${domain}:${port} + + local nginxH2Conf= + nginxH2Conf="listen 127.0.0.1:31302 http2 so_keepalive=on proxy_protocol;" + nginxVersion=$(nginx -v 2>&1) + + if echo "${nginxVersion}" | grep -q "1.25" && [[ $(echo "${nginxVersion}" | awk -F "[.]" '{print $3}') -gt 0 ]] || [[ $(echo "${nginxVersion}" | awk -F "[.]" '{print $2}') -gt 25 ]]; then + nginxH2Conf="listen 127.0.0.1:31302 so_keepalive=on proxy_protocol;http2 on;" + fi + + cat <${nginxConfigPath}alone.conf + server { + listen 127.0.0.1:31300; + server_name _; + return 403; + } +EOF + + if echo "${selectCustomInstallType}" | grep -qE ",2,|,5," || [[ -z "${selectCustomInstallType}" ]]; then + + cat <>${nginxConfigPath}alone.conf +server { + ${nginxH2Conf} + server_name ${domain}; + root ${nginxStaticPath}; + + set_real_ip_from 127.0.0.1; + real_ip_header proxy_protocol; + + client_header_timeout 1071906480m; + keepalive_timeout 1071906480m; + + location /${currentPath}grpc { + if (\$content_type !~ "application/grpc") { + return 404; + } + client_max_body_size 0; + grpc_set_header X-Real-IP \$proxy_add_x_forwarded_for; + client_body_timeout 1071906480m; + grpc_read_timeout 1071906480m; + grpc_pass grpc://127.0.0.1:31301; + } + + location /${currentPath}trojangrpc { + if (\$content_type !~ "application/grpc") { + return 404; + } + client_max_body_size 0; + grpc_set_header X-Real-IP \$proxy_add_x_forwarded_for; + client_body_timeout 1071906480m; + grpc_read_timeout 1071906480m; + grpc_pass grpc://127.0.0.1:31304; + } + location / { + } +} +EOF + elif echo "${selectCustomInstallType}" | grep -q ",5," || [[ -z "${selectCustomInstallType}" ]]; then + cat <>${nginxConfigPath}alone.conf +server { + ${nginxH2Conf} + + set_real_ip_from 127.0.0.1; + real_ip_header proxy_protocol; + + server_name ${domain}; + root ${nginxStaticPath}; + + location /${currentPath}grpc { + client_max_body_size 0; + keepalive_requests 4294967296; + client_body_timeout 1071906480m; + send_timeout 1071906480m; + lingering_close always; + grpc_read_timeout 1071906480m; + grpc_send_timeout 1071906480m; + grpc_pass grpc://127.0.0.1:31301; + } + location / { + } +} +EOF + + elif echo "${selectCustomInstallType}" | grep -q ",2," || [[ -z "${selectCustomInstallType}" ]]; then + cat <>${nginxConfigPath}alone.conf +server { + ${nginxH2Conf} + + set_real_ip_from 127.0.0.1; + real_ip_header proxy_protocol; + + server_name ${domain}; + root ${nginxStaticPath}; + + location /${currentPath}trojangrpc { + client_max_body_size 0; + # keepalive_time 1071906480m; + keepalive_requests 4294967296; + client_body_timeout 1071906480m; + send_timeout 1071906480m; + lingering_close always; + grpc_read_timeout 1071906480m; + grpc_send_timeout 1071906480m; + grpc_pass grpc://127.0.0.1:31301; + } + location / { + } +} +EOF + else + + cat <>${nginxConfigPath}alone.conf +server { + ${nginxH2Conf} + + set_real_ip_from 127.0.0.1; + real_ip_header proxy_protocol; + + server_name ${domain}; + root ${nginxStaticPath}; + + location / { + } +} +EOF + fi + + cat <>${nginxConfigPath}alone.conf +server { + listen 127.0.0.1:31300 proxy_protocol; + server_name ${domain}; + + set_real_ip_from 127.0.0.1; + real_ip_header proxy_protocol; + + root ${nginxStaticPath}; + location / { + } +} +EOF + handleNginx stop +} +# singbox Nginx config +singBoxNginxConfig() { + local type=$1 + local port=$2 + + local nginxH2Conf= + nginxH2Conf="listen ${port} http2 so_keepalive=on ssl;" + nginxVersion=$(nginx -v 2>&1) + + local singBoxNginxSSL= + singBoxNginxSSL="ssl_certificate /etc/v2ray-agent/tls/${domain}.crt;ssl_certificate_key /etc/v2ray-agent/tls/${domain}.key;" + + if echo "${nginxVersion}" | grep -q "1.25" && [[ $(echo "${nginxVersion}" | awk -F "[.]" '{print $3}') -gt 0 ]] || [[ $(echo "${nginxVersion}" | awk -F "[.]" '{print $2}') -gt 25 ]]; then + nginxH2Conf="listen ${port} so_keepalive=on ssl;http2 on;" + fi + + if echo "${selectCustomInstallType}" | grep -q ",11," || [[ "$1" == "all" ]]; then + cat <>${nginxConfigPath}sing_box_VMess_HTTPUpgrade.conf +server { + ${nginxH2Conf} + + server_name ${domain}; + root ${nginxStaticPath}; + ${singBoxNginxSSL} + + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers TLS13_AES_128_GCM_SHA256:TLS13_AES_256_GCM_SHA384:TLS13_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305; + ssl_prefer_server_ciphers on; + + resolver 1.1.1.1 valid=60s; + resolver_timeout 2s; + client_max_body_size 100m; + + location /${currentPath} { + if (\$http_upgrade != "websocket") { + return 444; + } + + proxy_pass http://127.0.0.1:31306; + proxy_http_version 1.1; + proxy_set_header Upgrade \$http_upgrade; + proxy_set_header Connection "upgrade"; + proxy_set_header X-Real-IP \$remote_addr; + proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; + proxy_set_header Host \$host; + proxy_redirect off; + } +} +EOF + fi +} + +# 检查ip +checkIP() { + echoContent skyBlue "\n ---> 检查域名ip中" + local localIP=$1 + + if [[ -z ${localIP} ]] || ! echo "${localIP}" | sed '1{s/[^(]*(//;s/).*//;q}' | grep -q '\.' && ! echo "${localIP}" | sed '1{s/[^(]*(//;s/).*//;q}' | grep -q ':'; then + echoContent red "\n ---> 未检测到当前域名的ip" + echoContent skyBlue " ---> 请依次进行下列检查" + echoContent yellow " ---> 1.检查域名是否书写正确" + echoContent yellow " ---> 2.检查域名dns解析是否正确" + echoContent yellow " ---> 3.如解析正确,请等待dns生效,预计三分钟内生效" + echoContent yellow " ---> 4.如报Nginx启动问题,请手动启动nginx查看错误,如自己无法处理请提issues" + echo + echoContent skyBlue " ---> 如以上设置都正确,请重新安装纯净系统后再次尝试" + + if [[ -n ${localIP} ]]; then + echoContent yellow " ---> 检测返回值异常,建议手动卸载nginx后重新执行脚本" + echoContent red " ---> 异常结果:${localIP}" + fi + exit 0 + else + if echo "${localIP}" | awk -F "[,]" '{print $2}' | grep -q "." || echo "${localIP}" | awk -F "[,]" '{print $2}' | grep -q ":"; then + echoContent red "\n ---> 检测到多个ip,请确认是否关闭cloudflare的云朵" + echoContent yellow " ---> 关闭云朵后等待三分钟后重试" + echoContent yellow " ---> 检测到的ip如下:[${localIP}]" + exit 0 + fi + echoContent green " ---> 检查当前域名IP正确" + fi +} +# 自定义email +customSSLEmail() { + if echo "$1" | grep -q "validate email"; then + read -r -p "是否重新输入邮箱地址[y/n]:" sslEmailStatus + if [[ "${sslEmailStatus}" == "y" ]]; then + sed '/ACCOUNT_EMAIL/d' /root/.acme.sh/account.conf >/root/.acme.sh/account.conf_tmp && mv /root/.acme.sh/account.conf_tmp /root/.acme.sh/account.conf + else + exit 0 + fi + fi + + if [[ -d "/root/.acme.sh" && -f "/root/.acme.sh/account.conf" ]]; then + if ! grep -q "ACCOUNT_EMAIL" <"/root/.acme.sh/account.conf" && ! echo "${sslType}" | grep -q "letsencrypt"; then + read -r -p "请输入邮箱地址:" sslEmail + if echo "${sslEmail}" | grep -q "@"; then + echo "ACCOUNT_EMAIL='${sslEmail}'" >>/root/.acme.sh/account.conf + echoContent green " ---> 添加完毕" + else + echoContent yellow "请重新输入正确的邮箱格式[例: username@example.com]" + customSSLEmail + fi + fi + fi + +} +# DNS API申请证书 +switchDNSAPI() { + read -r -p "是否使用DNS API申请证书[支持NAT]?[y/n]:" dnsAPIStatus + if [[ "${dnsAPIStatus}" == "y" ]]; then + echoContent red "\n==============================================================" + echoContent yellow "1.cloudflare[默认]" + echoContent yellow "2.aliyun" + echoContent red "==============================================================" + read -r -p "请选择[回车]使用默认:" selectDNSAPIType + case ${selectDNSAPIType} in + 1) + dnsAPIType="cloudflare" + ;; + 2) + dnsAPIType="aliyun" + ;; + *) + dnsAPIType="cloudflare" + ;; + esac + initDNSAPIConfig "${dnsAPIType}" + fi +} +# 初始化dns配置 +initDNSAPIConfig() { + if [[ "$1" == "cloudflare" ]]; then + echoContent yellow "\n CF_Token参考配置教程:https://www.v2ray-agent.com/archives/1701160377972\n" + read -r -p "请输入API Token:" cfAPIToken + if [[ -z "${cfAPIToken}" ]]; then + echoContent red " ---> 输入为空,请重新输入" + initDNSAPIConfig "$1" + else + echo + if ! echo "${dnsTLSDomain}" | grep -q "\." || [[ -z $(echo "${dnsTLSDomain}" | awk -F "[.]" '{print $1}') ]]; then + echoContent green " ---> 不支持此域名申请通配符证书,建议使用此格式[xx.xx.xx]" + exit 0 + fi + read -r -p "是否使用*.${dnsTLSDomain}进行API申请通配符证书?[y/n]:" dnsAPIStatus + fi + elif [[ "$1" == "aliyun" ]]; then + read -r -p "请输入Ali Key:" aliKey + read -r -p "请输入Ali Secret:" aliSecret + if [[ -z "${aliKey}" || -z "${aliSecret}" ]]; then + echoContent red " ---> 输入为空,请重新输入" + initDNSAPIConfig "$1" + else + echo + if ! echo "${dnsTLSDomain}" | grep -q "\." || [[ -z $(echo "${dnsTLSDomain}" | awk -F "[.]" '{print $1}') ]]; then + echoContent green " ---> 不支持此域名申请通配符证书,建议使用此格式[xx.xx.xx]" + exit 0 + fi + read -r -p "是否使用*.${dnsTLSDomain}进行API申请通配符证书?[y/n]:" dnsAPIStatus + fi + fi +} +# 选择ssl安装类型 +switchSSLType() { + if [[ -z "${sslType}" ]]; then + echoContent red "\n==============================================================" + echoContent yellow "1.letsencrypt[默认]" + echoContent yellow "2.zerossl" + echoContent yellow "3.buypass[不支持DNS申请]" + echoContent red "==============================================================" + read -r -p "请选择[回车]使用默认:" selectSSLType + case ${selectSSLType} in + 1) + sslType="letsencrypt" + ;; + 2) + sslType="zerossl" + ;; + 3) + sslType="buypass" + ;; + *) + sslType="letsencrypt" + ;; + esac + if [[ -n "${dnsAPIType}" && "${sslType}" == "buypass" ]]; then + echoContent red " ---> buypass不支持API申请证书" + exit 0 + fi + echo "${sslType}" >/etc/v2ray-agent/tls/ssl_type + fi +} + +# 选择acme安装证书方式 +selectAcmeInstallSSL() { + # local sslIPv6= + # local currentIPType= + if [[ "${ipType}" == "6" ]]; then + sslIPv6="--listen-v6" + fi + # currentIPType=$(curl -s "-${ipType}" http://www.cloudflare.com/cdn-cgi/trace | grep "ip" | cut -d "=" -f 2) + + # if [[ -z "${currentIPType}" ]]; then + # currentIPType=$(curl -s -6 http://www.cloudflare.com/cdn-cgi/trace | grep "ip" | cut -d "=" -f 2) + # if [[ -n "${currentIPType}" ]]; then + # sslIPv6="--listen-v6" + # fi + # fi + + acmeInstallSSL + + readAcmeTLS +} + +# 安装SSL证书 +acmeInstallSSL() { + local dnsAPIDomain="${tlsDomain}" + if [[ "${dnsAPIStatus}" == "y" ]]; then + dnsAPIDomain="*.${dnsTLSDomain}" + fi + + if [[ "${dnsAPIType}" == "cloudflare" ]]; then + echoContent green " ---> DNS API 生成证书中" + sudo CF_Token="${cfAPIToken}" "$HOME/.acme.sh/acme.sh" --issue -d "${dnsAPIDomain}" -d "${dnsTLSDomain}" --dns dns_cf -k ec-256 --server "${sslType}" ${sslIPv6} 2>&1 | tee -a /etc/v2ray-agent/tls/acme.log >/dev/null + elif [[ "${dnsAPIType}" == "aliyun" ]]; then + echoContent green " ---> DNS API 生成证书中" + sudo Ali_Key="${aliKey}" Ali_Secret="${aliSecret}" "$HOME/.acme.sh/acme.sh" --issue -d "${dnsAPIDomain}" -d "${dnsTLSDomain}" --dns dns_ali -k ec-256 --server "${sslType}" ${sslIPv6} 2>&1 | tee -a /etc/v2ray-agent/tls/acme.log >/dev/null + else + echoContent green " ---> 生成证书中" + sudo "$HOME/.acme.sh/acme.sh" --issue -d "${tlsDomain}" --standalone -k ec-256 --server "${sslType}" ${sslIPv6} 2>&1 | tee -a /etc/v2ray-agent/tls/acme.log >/dev/null + fi +} +# 自定义端口 +customPortFunction() { + local historyCustomPortStatus= + if [[ -n "${customPort}" || -n "${currentPort}" ]]; then + echo + if [[ -z "${lastInstallationConfig}" ]]; then + read -r -p "读取到上次安装时的端口,是否使用上次安装时的端口?[y/n]:" historyCustomPortStatus + if [[ "${historyCustomPortStatus}" == "y" ]]; then + port=${currentPort} + echoContent yellow "\n ---> 端口: ${port}" + fi + elif [[ -n "${lastInstallationConfig}" ]]; then + port=${currentPort} + fi + fi + if [[ -z "${currentPort}" ]] || [[ "${historyCustomPortStatus}" == "n" ]]; then + echo + + if [[ -n "${btDomain}" ]]; then + echoContent yellow "请输入端口[不可与BT Panel/1Panel端口相同,回车随机]" + read -r -p "端口:" port + if [[ -z "${port}" ]]; then + port=$((RANDOM % 20001 + 10000)) + fi + else + echo + echoContent yellow "请输入端口[默认: 443],可自定义端口[回车使用默认]" + read -r -p "端口:" port + if [[ -z "${port}" ]]; then + port=443 + fi + if [[ "${port}" == "${xrayVLESSRealityPort}" ]]; then + handleXray stop + fi + fi + + if [[ -n "${port}" ]]; then + if ((port >= 1 && port <= 65535)); then + allowPort "${port}" + echoContent yellow "\n ---> 端口: ${port}" + if [[ -z "${btDomain}" ]]; then + checkDNSIP "${domain}" + removeNginxDefaultConf + checkPortOpen "${port}" "${domain}" + fi + else + echoContent red " ---> 端口输入错误" + exit 0 + fi + else + echoContent red " ---> 端口不可为空" + exit 0 + fi + fi +} + +# 检测端口是否占用 +checkPort() { + if [[ -n "$1" ]] && lsof -i "tcp:$1" | grep -q LISTEN; then + echoContent red "\n ---> $1端口被占用,请手动关闭后安装\n" + lsof -i "tcp:$1" | grep LISTEN + exit 0 + fi +} + +# 安装TLS +installTLS() { + echoContent skyBlue "\n进度 $1/${totalProgress} : 申请TLS证书\n" + readAcmeTLS + local tlsDomain=${domain} + + # 安装tls + if [[ -f "/etc/v2ray-agent/tls/${tlsDomain}.crt" && -f "/etc/v2ray-agent/tls/${tlsDomain}.key" && -n $(cat "/etc/v2ray-agent/tls/${tlsDomain}.crt") ]] || [[ -d "$HOME/.acme.sh/${tlsDomain}_ecc" && -f "$HOME/.acme.sh/${tlsDomain}_ecc/${tlsDomain}.key" && -f "$HOME/.acme.sh/${tlsDomain}_ecc/${tlsDomain}.cer" ]] || [[ "${installedDNSAPIStatus}" == "true" ]]; then + echoContent green " ---> 检测到证书" + renewalTLS + + if [[ -z $(find /etc/v2ray-agent/tls/ -name "${tlsDomain}.crt") ]] || [[ -z $(find /etc/v2ray-agent/tls/ -name "${tlsDomain}.key") ]] || [[ -z $(cat "/etc/v2ray-agent/tls/${tlsDomain}.crt") ]]; then + if [[ "${installedDNSAPIStatus}" == "true" ]]; then + sudo "$HOME/.acme.sh/acme.sh" --installcert -d "*.${dnsTLSDomain}" --fullchainpath "/etc/v2ray-agent/tls/${tlsDomain}.crt" --keypath "/etc/v2ray-agent/tls/${tlsDomain}.key" --ecc >/dev/null + else + sudo "$HOME/.acme.sh/acme.sh" --installcert -d "${tlsDomain}" --fullchainpath "/etc/v2ray-agent/tls/${tlsDomain}.crt" --keypath "/etc/v2ray-agent/tls/${tlsDomain}.key" --ecc >/dev/null + fi + + else + if [[ -d "$HOME/.acme.sh/${tlsDomain}_ecc" && -f "$HOME/.acme.sh/${tlsDomain}_ecc/${tlsDomain}.key" && -f "$HOME/.acme.sh/${tlsDomain}_ecc/${tlsDomain}.cer" ]] || [[ "${installedDNSAPIStatus}" == "true" ]]; then + if [[ -z "${lastInstallationConfig}" ]]; then + echoContent yellow " ---> 如未过期或者自定义证书请选择[n]\n" + read -r -p "是否重新安装?[y/n]:" reInstallStatus + if [[ "${reInstallStatus}" == "y" ]]; then + rm -rf /etc/v2ray-agent/tls/* + installTLS "$1" + fi + fi + fi + fi + + elif [[ -d "$HOME/.acme.sh" ]] && [[ ! -f "$HOME/.acme.sh/${tlsDomain}_ecc/${tlsDomain}.cer" || ! -f "$HOME/.acme.sh/${tlsDomain}_ecc/${tlsDomain}.key" ]]; then + switchDNSAPI + if [[ -z "${dnsAPIType}" ]]; then + echoContent yellow "\n ---> 不采用API申请证书" + echoContent green " ---> 安装TLS证书,需要依赖80端口" + allowPort 80 + fi + + switchSSLType + customSSLEmail + selectAcmeInstallSSL + + if [[ "${installedDNSAPIStatus}" == "true" ]]; then + sudo "$HOME/.acme.sh/acme.sh" --installcert -d "*.${dnsTLSDomain}" --fullchainpath "/etc/v2ray-agent/tls/${tlsDomain}.crt" --keypath "/etc/v2ray-agent/tls/${tlsDomain}.key" --ecc >/dev/null + else + sudo "$HOME/.acme.sh/acme.sh" --installcert -d "${tlsDomain}" --fullchainpath "/etc/v2ray-agent/tls/${tlsDomain}.crt" --keypath "/etc/v2ray-agent/tls/${tlsDomain}.key" --ecc >/dev/null + fi + + if [[ ! -f "/etc/v2ray-agent/tls/${tlsDomain}.crt" || ! -f "/etc/v2ray-agent/tls/${tlsDomain}.key" ]] || [[ -z $(cat "/etc/v2ray-agent/tls/${tlsDomain}.key") || -z $(cat "/etc/v2ray-agent/tls/${tlsDomain}.crt") ]]; then + tail -n 10 /etc/v2ray-agent/tls/acme.log + if [[ ${installTLSCount} == "1" ]]; then + echoContent red " ---> TLS安装失败,请检查acme日志" + exit 0 + fi + + installTLSCount=1 + echo + + if tail -n 10 /etc/v2ray-agent/tls/acme.log | grep -q "Could not validate email address as valid"; then + echoContent red " ---> 邮箱无法通过SSL厂商验证,请重新输入" + echo + customSSLEmail "validate email" + installTLS "$1" + else + installTLS "$1" + fi + fi + + echoContent green " ---> TLS生成成功" + else + echoContent yellow " ---> 未安装acme.sh" + exit 0 + fi +} + +# 初始化随机字符串 +initRandomPath() { + local chars="abcdefghijklmnopqrtuxyz" + local initCustomPath= + for i in {1..4}; do + echo "${i}" >/dev/null + initCustomPath+="${chars:RANDOM%${#chars}:1}" + done + customPath=${initCustomPath} +} + +# 自定义/随机路径 +randomPathFunction() { + if [[ -n $1 ]]; then + echoContent skyBlue "\n进度 $1/${totalProgress} : 生成随机路径" + else + echoContent skyBlue "生成随机路径" + fi + + if [[ -n "${currentPath}" && -z "${lastInstallationConfig}" ]]; then + echo + read -r -p "读取到上次安装记录,是否使用上次安装时的path路径 ?[y/n]:" historyPathStatus + echo + elif [[ -n "${currentPath}" && -n "${lastInstallationConfig}" ]]; then + historyPathStatus="y" + fi + + if [[ "${historyPathStatus}" == "y" ]]; then + customPath=${currentPath} + echoContent green " ---> 使用成功\n" + else + echoContent yellow "请输入自定义路径[例: alone],不需要斜杠,[回车]随机路径" + read -r -p '路径:' customPath + if [[ -z "${customPath}" ]]; then + initRandomPath + currentPath=${customPath} + else + if [[ "${customPath: -2}" == "ws" ]]; then + echo + echoContent red " ---> 自定义path结尾不可用ws结尾,否则无法区分分流路径" + randomPathFunction "$1" + else + currentPath=${customPath} + fi + fi + fi + echoContent yellow "\n path:${currentPath}" + echoContent skyBlue "\n----------------------------" +} +# 随机数 +randomNum() { + if [[ "${release}" == "alpine" ]]; then + local ranNum= + ranNum="$(shuf -i "$1"-"$2" -n 1)" + echo "${ranNum}" + else + echo $((RANDOM % $2 + $1)) + fi +} +# Nginx伪装博客 +nginxBlog() { + if [[ -n "$1" ]]; then + echoContent skyBlue "\n进度 $1/${totalProgress} : 添加伪装站点" + else + echoContent yellow "\n开始添加伪装站点" + fi + + if [[ -d "${nginxStaticPath}" && -f "${nginxStaticPath}/check" ]]; then + echo + if [[ -z "${lastInstallationConfig}" ]]; then + read -r -p "检测到安装伪装站点,是否需要重新安装[y/n]:" nginxBlogInstallStatus + else + nginxBlogInstallStatus="n" + fi + + if [[ "${nginxBlogInstallStatus}" == "y" ]]; then + rm -rf "${nginxStaticPath}*" + # randomNum=$((RANDOM % 6 + 1)) + randomNum=$(randomNum 1 9) + if [[ "${release}" == "alpine" ]]; then + wget -q -P "${nginxStaticPath}" "https://raw.githubusercontent.com/mack-a/v2ray-agent/master/fodder/blog/unable/html${randomNum}.zip" + else + wget -q "${wgetShowProgressStatus}" -P "${nginxStaticPath}" "https://raw.githubusercontent.com/mack-a/v2ray-agent/master/fodder/blog/unable/html${randomNum}.zip" + fi + + unzip -o "${nginxStaticPath}html${randomNum}.zip" -d "${nginxStaticPath}" >/dev/null + rm -f "${nginxStaticPath}html${randomNum}.zip*" + echoContent green " ---> 添加伪装站点成功" + fi + else + randomNum=$(randomNum 1 9) + # randomNum=$((RANDOM % 6 + 1)) + rm -rf "${nginxStaticPath}*" + + if [[ "${release}" == "alpine" ]]; then + wget -q -P "${nginxStaticPath}" "https://raw.githubusercontent.com/mack-a/v2ray-agent/master/fodder/blog/unable/html${randomNum}.zip" + else + wget -q "${wgetShowProgressStatus}" -P "${nginxStaticPath}" "https://raw.githubusercontent.com/mack-a/v2ray-agent/master/fodder/blog/unable/html${randomNum}.zip" + fi + + unzip -o "${nginxStaticPath}html${randomNum}.zip" -d "${nginxStaticPath}" >/dev/null + rm -f "${nginxStaticPath}html${randomNum}.zip*" + echoContent green " ---> 添加伪装站点成功" + fi + +} + +# 修改http_port_t端口 +updateSELinuxHTTPPortT() { + + $(find /usr/bin /usr/sbin | grep -w journalctl) -xe >/etc/v2ray-agent/nginx_error.log 2>&1 + + if find /usr/bin /usr/sbin | grep -q -w semanage && find /usr/bin /usr/sbin | grep -q -w getenforce && grep -E "31300|31302" 检查SELinux端口是否开放" + if ! $(find /usr/bin /usr/sbin | grep -w semanage) port -l | grep http_port | grep -q 31300; then + $(find /usr/bin /usr/sbin | grep -w semanage) port -a -t http_port_t -p tcp 31300 + echoContent green " ---> http_port_t 31300 端口开放成功" + fi + + if ! $(find /usr/bin /usr/sbin | grep -w semanage) port -l | grep http_port | grep -q 31302; then + $(find /usr/bin /usr/sbin | grep -w semanage) port -a -t http_port_t -p tcp 31302 + echoContent green " ---> http_port_t 31302 端口开放成功" + fi + handleNginx start + + else + exit 0 + fi +} + +# 操作Nginx +handleNginx() { + + if ! echo "${selectCustomInstallType}" | grep -qwE ",7,|,8,|,7,8," && [[ -z $(pgrep -f "nginx") ]] && [[ "$1" == "start" ]]; then + if [[ "${release}" == "alpine" ]]; then + rc-service nginx start 2>/etc/v2ray-agent/nginx_error.log + else + systemctl start nginx 2>/etc/v2ray-agent/nginx_error.log + fi + + sleep 0.5 + + if [[ -z $(pgrep -f "nginx") ]]; then + echoContent red " ---> Nginx启动失败" + echoContent red " ---> 请将下方日志反馈给开发者" + nginx + if grep -q "journalctl -xe" Nginx启动成功" + fi + + elif [[ -n $(pgrep -f "nginx") ]] && [[ "$1" == "stop" ]]; then + + if [[ "${release}" == "alpine" ]]; then + rc-service nginx stop + else + systemctl stop nginx + fi + sleep 0.5 + + if [[ -z ${btDomain} && -n $(pgrep -f "nginx") ]]; then + pgrep -f "nginx" | xargs kill -9 + fi + echoContent green " ---> Nginx关闭成功" + fi +} + +# 定时任务更新tls证书 +installCronTLS() { + if [[ -z "${btDomain}" ]]; then + echoContent skyBlue "\n进度 $1/${totalProgress} : 添加定时维护证书" + crontab -l >/etc/v2ray-agent/backup_crontab.cron + local historyCrontab + historyCrontab=$(sed '/v2ray-agent/d;/acme.sh/d' /etc/v2ray-agent/backup_crontab.cron) + echo "${historyCrontab}" >/etc/v2ray-agent/backup_crontab.cron + echo "30 1 * * * /bin/bash /etc/v2ray-agent/install.sh RenewTLS >> /etc/v2ray-agent/crontab_tls.log 2>&1" >>/etc/v2ray-agent/backup_crontab.cron + crontab /etc/v2ray-agent/backup_crontab.cron + echoContent green "\n ---> 添加定时维护证书成功" + fi +} +# 定时任务更新geo文件 +installCronUpdateGeo() { + if [[ "${coreInstallType}" == "1" ]]; then + if crontab -l | grep -q "UpdateGeo"; then + echoContent red "\n ---> 已添加自动更新定时任务,请不要重复添加" + exit 0 + fi + echoContent skyBlue "\n进度 1/1 : 添加定时更新geo文件" + crontab -l >/etc/v2ray-agent/backup_crontab.cron + echo "35 1 * * * /bin/bash /etc/v2ray-agent/install.sh UpdateGeo >> /etc/v2ray-agent/crontab_tls.log 2>&1" >>/etc/v2ray-agent/backup_crontab.cron + crontab /etc/v2ray-agent/backup_crontab.cron + echoContent green "\n ---> 添加定时更新geo文件成功" + fi +} + +# 更新证书 +renewalTLS() { + + if [[ -n $1 ]]; then + echoContent skyBlue "\n进度 $1/1 : 更新证书" + fi + readAcmeTLS + local domain=${currentHost} + if [[ -z "${currentHost}" && -n "${tlsDomain}" ]]; then + domain=${tlsDomain} + fi + + if [[ -f "/etc/v2ray-agent/tls/ssl_type" ]]; then + if grep -q "buypass" <"/etc/v2ray-agent/tls/ssl_type"; then + sslRenewalDays=180 + fi + fi + if [[ -d "$HOME/.acme.sh/${domain}_ecc" && -f "$HOME/.acme.sh/${domain}_ecc/${domain}.key" && -f "$HOME/.acme.sh/${domain}_ecc/${domain}.cer" ]] || [[ "${installedDNSAPIStatus}" == "true" ]]; then + modifyTime= + + if [[ "${installedDNSAPIStatus}" == "true" ]]; then + modifyTime=$(stat --format=%z "$HOME/.acme.sh/*.${dnsTLSDomain}_ecc/*.${dnsTLSDomain}.cer") + else + modifyTime=$(stat --format=%z "$HOME/.acme.sh/${domain}_ecc/${domain}.cer") + fi + + modifyTime=$(date +%s -d "${modifyTime}") + currentTime=$(date +%s) + ((stampDiff = currentTime - modifyTime)) + ((days = stampDiff / 86400)) + ((remainingDays = sslRenewalDays - days)) + + tlsStatus=${remainingDays} + if [[ ${remainingDays} -le 0 ]]; then + tlsStatus="已过期" + fi + + echoContent skyBlue " ---> 证书检查日期:$(date "+%F %H:%M:%S")" + echoContent skyBlue " ---> 证书生成日期:$(date -d @"${modifyTime}" +"%F %H:%M:%S")" + echoContent skyBlue " ---> 证书生成天数:${days}" + echoContent skyBlue " ---> 证书剩余天数:"${tlsStatus} + echoContent skyBlue " ---> 证书过期前最后一天自动更新,如更新失败请手动更新" + + if [[ ${remainingDays} -le 1 ]]; then + echoContent yellow " ---> 重新生成证书" + handleNginx stop + + if [[ "${coreInstallType}" == "1" ]]; then + handleXray stop + elif [[ "${coreInstallType}" == "2" ]]; then + handleV2Ray stop + fi + + sudo "$HOME/.acme.sh/acme.sh" --cron --home "$HOME/.acme.sh" + sudo "$HOME/.acme.sh/acme.sh" --installcert -d "${domain}" --fullchainpath /etc/v2ray-agent/tls/"${domain}.crt" --keypath /etc/v2ray-agent/tls/"${domain}.key" --ecc + reloadCore + handleNginx start + else + echoContent green " ---> 证书有效" + fi + elif [[ -f "/etc/v2ray-agent/tls/${tlsDomain}.crt" && -f "/etc/v2ray-agent/tls/${tlsDomain}.key" && -n $(cat "/etc/v2ray-agent/tls/${tlsDomain}.crt") ]]; then + echoContent yellow " ---> 检测到使用自定义证书,无法执行renew操作。" + else + echoContent red " ---> 未安装" + fi +} + +# 安装 sing-box +installSingBox() { + readInstallType + echoContent skyBlue "\n进度 $1/${totalProgress} : 安装sing-box" + + if [[ ! -f "/etc/v2ray-agent/sing-box/sing-box" ]]; then + + version=$(curl -s "https://api.github.com/repos/SagerNet/sing-box/releases?per_page=20" | jq -r ".[]|select (.prerelease==${prereleaseStatus})|.tag_name" | head -1) + + echoContent green " ---> sing-box版本:${version}" + + if [[ "${release}" == "alpine" ]]; then + wget -c -q -P /etc/v2ray-agent/sing-box/ "https://github.com/SagerNet/sing-box/releases/download/${version}/sing-box-${version/v/}${singBoxCoreCPUVendor}.tar.gz" + else + wget -c -q "${wgetShowProgressStatus}" -P /etc/v2ray-agent/sing-box/ "https://github.com/SagerNet/sing-box/releases/download/${version}/sing-box-${version/v/}${singBoxCoreCPUVendor}.tar.gz" + fi + + if [[ ! -f "/etc/v2ray-agent/sing-box/sing-box-${version/v/}${singBoxCoreCPUVendor}.tar.gz" ]]; then + read -r -p "核心下载失败,请重新尝试安装,是否重新尝试?[y/n]" downloadStatus + if [[ "${downloadStatus}" == "y" ]]; then + installSingBox "$1" + fi + else + + tar zxvf "/etc/v2ray-agent/sing-box/sing-box-${version/v/}${singBoxCoreCPUVendor}.tar.gz" -C "/etc/v2ray-agent/sing-box/" >/dev/null 2>&1 + + mv "/etc/v2ray-agent/sing-box/sing-box-${version/v/}${singBoxCoreCPUVendor}/sing-box" /etc/v2ray-agent/sing-box/sing-box + rm -rf /etc/v2ray-agent/sing-box/sing-box-* + chmod 655 /etc/v2ray-agent/sing-box/sing-box + fi + else + echoContent green " ---> sing-box版本:v$(/etc/v2ray-agent/sing-box/sing-box version | grep "sing-box version" | awk '{print $3}')" + if [[ -z "${lastInstallationConfig}" ]]; then + read -r -p "是否更新、升级?[y/n]:" reInstallSingBoxStatus + if [[ "${reInstallSingBoxStatus}" == "y" ]]; then + rm -f /etc/v2ray-agent/sing-box/sing-box + installSingBox "$1" + fi + fi + fi + +} + +# 检查wget showProgress +checkWgetShowProgress() { + if [[ "${release}" != "alpine" ]]; then + if find /usr/bin /usr/sbin | grep -q "/wget" && wget --help | grep -q show-progress; then + wgetShowProgressStatus="--show-progress" + fi + fi +} +# 安装xray +installXray() { + readInstallType + local prereleaseStatus=false + if [[ "$2" == "true" ]]; then + prereleaseStatus=true + fi + + echoContent skyBlue "\n进度 $1/${totalProgress} : 安装Xray" + + if [[ ! -f "/etc/v2ray-agent/xray/xray" ]]; then + + version=$(curl -s "https://api.github.com/repos/XTLS/Xray-core/releases?per_page=5" | jq -r ".[]|select (.prerelease==${prereleaseStatus})|.tag_name" | head -1) + echoContent green " ---> Xray-core版本:${version}" + if [[ "${release}" == "alpine" ]]; then + wget -c -q -P /etc/v2ray-agent/xray/ "https://github.com/XTLS/Xray-core/releases/download/${version}/${xrayCoreCPUVendor}.zip" + else + wget -c -q "${wgetShowProgressStatus}" -P /etc/v2ray-agent/xray/ "https://github.com/XTLS/Xray-core/releases/download/${version}/${xrayCoreCPUVendor}.zip" + fi + + if [[ ! -f "/etc/v2ray-agent/xray/${xrayCoreCPUVendor}.zip" ]]; then + read -r -p "核心下载失败,请重新尝试安装,是否重新尝试?[y/n]" downloadStatus + if [[ "${downloadStatus}" == "y" ]]; then + installXray "$1" + fi + else + unzip -o "/etc/v2ray-agent/xray/${xrayCoreCPUVendor}.zip" -d /etc/v2ray-agent/xray >/dev/null + rm -rf "/etc/v2ray-agent/xray/${xrayCoreCPUVendor}.zip" + + version=$(curl -s https://api.github.com/repos/Loyalsoldier/v2ray-rules-dat/releases?per_page=1 | jq -r '.[]|.tag_name') + echoContent skyBlue "------------------------Version-------------------------------" + echo "version:${version}" + rm /etc/v2ray-agent/xray/geo* >/dev/null 2>&1 + + if [[ "${release}" == "alpine" ]]; then + wget -c -q -P /etc/v2ray-agent/xray/ "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/download/${version}/geosite.dat" + wget -c -q -P /etc/v2ray-agent/xray/ "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/download/${version}/geoip.dat" + else + wget -c -q "${wgetShowProgressStatus}" -P /etc/v2ray-agent/xray/ "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/download/${version}/geosite.dat" + wget -c -q "${wgetShowProgressStatus}" -P /etc/v2ray-agent/xray/ "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/download/${version}/geoip.dat" + fi + + chmod 655 /etc/v2ray-agent/xray/xray + fi + else + if [[ -z "${lastInstallationConfig}" ]]; then + echoContent green " ---> Xray-core版本:$(/etc/v2ray-agent/xray/xray --version | awk '{print $2}' | head -1)" + read -r -p "是否更新、升级?[y/n]:" reInstallXrayStatus + if [[ "${reInstallXrayStatus}" == "y" ]]; then + rm -f /etc/v2ray-agent/xray/xray + installXray "$1" "$2" + fi + fi + fi +} + +# xray版本管理 +xrayVersionManageMenu() { + echoContent skyBlue "\n进度 $1/${totalProgress} : Xray版本管理" + if [[ "${coreInstallType}" != "1" ]]; then + echoContent red " ---> 没有检测到安装目录,请执行脚本安装内容" + exit 0 + fi + echoContent red "\n==============================================================" + echoContent yellow "1.升级Xray-core" + echoContent yellow "2.升级Xray-core 预览版" + echoContent yellow "3.回退Xray-core" + echoContent yellow "4.关闭Xray-core" + echoContent yellow "5.打开Xray-core" + echoContent yellow "6.重启Xray-core" + echoContent yellow "7.更新geosite、geoip" + echoContent yellow "8.设置自动更新geo文件[每天凌晨更新]" + echoContent yellow "9.查看日志" + echoContent red "==============================================================" + read -r -p "请选择:" selectXrayType + if [[ "${selectXrayType}" == "1" ]]; then + prereleaseStatus=false + updateXray + elif [[ "${selectXrayType}" == "2" ]]; then + prereleaseStatus=true + updateXray + elif [[ "${selectXrayType}" == "3" ]]; then + echoContent yellow "\n1.只可以回退最近的五个版本" + echoContent yellow "2.不保证回退后一定可以正常使用" + echoContent yellow "3.如果回退的版本不支持当前的config,则会无法连接,谨慎操作" + echoContent skyBlue "------------------------Version-------------------------------" + curl -s "https://api.github.com/repos/XTLS/Xray-core/releases?per_page=5" | jq -r ".[]|select (.prerelease==false)|.tag_name" | awk '{print ""NR""":"$0}' + echoContent skyBlue "--------------------------------------------------------------" + read -r -p "请输入要回退的版本:" selectXrayVersionType + version=$(curl -s "https://api.github.com/repos/XTLS/Xray-core/releases?per_page=5" | jq -r ".[]|select (.prerelease==false)|.tag_name" | awk '{print ""NR""":"$0}' | grep "${selectXrayVersionType}:" | awk -F "[:]" '{print $2}') + if [[ -n "${version}" ]]; then + updateXray "${version}" + else + echoContent red "\n ---> 输入有误,请重新输入" + xrayVersionManageMenu 1 + fi + elif [[ "${selectXrayType}" == "4" ]]; then + handleXray stop + elif [[ "${selectXrayType}" == "5" ]]; then + handleXray start + elif [[ "${selectXrayType}" == "6" ]]; then + reloadCore + elif [[ "${selectXrayType}" == "7" ]]; then + updateGeoSite + elif [[ "${selectXrayType}" == "8" ]]; then + installCronUpdateGeo + elif [[ "${selectXrayType}" == "9" ]]; then + checkLog 1 + fi +} + +# 更新 geosite +updateGeoSite() { + echoContent yellow "\n来源 https://github.com/Loyalsoldier/v2ray-rules-dat" + + version=$(curl -s https://api.github.com/repos/Loyalsoldier/v2ray-rules-dat/releases?per_page=1 | jq -r '.[]|.tag_name') + echoContent skyBlue "------------------------Version-------------------------------" + echo "version:${version}" + rm ${configPath}../geo* >/dev/null + + if [[ "${release}" == "alpine" ]]; then + wget -c -q -P ${configPath}../ "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/download/${version}/geosite.dat" + wget -c -q -P ${configPath}../ "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/download/${version}/geoip.dat" + else + wget -c -q "${wgetShowProgressStatus}" -P ${configPath}../ "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/download/${version}/geosite.dat" + wget -c -q "${wgetShowProgressStatus}" -P ${configPath}../ "https://github.com/Loyalsoldier/v2ray-rules-dat/releases/download/${version}/geoip.dat" + fi + + reloadCore + echoContent green " ---> 更新完毕" + +} + +# 更新Xray +updateXray() { + readInstallType + + if [[ -z "${coreInstallType}" || "${coreInstallType}" != "1" ]]; then + if [[ -n "$1" ]]; then + version=$1 + else + version=$(curl -s "https://api.github.com/repos/XTLS/Xray-core/releases?per_page=5" | jq -r ".[]|select (.prerelease==${prereleaseStatus})|.tag_name" | head -1) + fi + + echoContent green " ---> Xray-core版本:${version}" + + if [[ "${release}" == "alpine" ]]; then + wget -c -q -P /etc/v2ray-agent/xray/ "https://github.com/XTLS/Xray-core/releases/download/${version}/${xrayCoreCPUVendor}.zip" + else + wget -c -q "${wgetShowProgressStatus}" -P /etc/v2ray-agent/xray/ "https://github.com/XTLS/Xray-core/releases/download/${version}/${xrayCoreCPUVendor}.zip" + fi + + unzip -o "/etc/v2ray-agent/xray/${xrayCoreCPUVendor}.zip" -d /etc/v2ray-agent/xray >/dev/null + rm -rf "/etc/v2ray-agent/xray/${xrayCoreCPUVendor}.zip" + chmod 655 /etc/v2ray-agent/xray/xray + handleXray stop + handleXray start + else + echoContent green " ---> 当前Xray-core版本:$(/etc/v2ray-agent/xray/xray --version | awk '{print $2}' | head -1)" + + if [[ -n "$1" ]]; then + version=$1 + else + version=$(curl -s "https://api.github.com/repos/XTLS/Xray-core/releases?per_page=10" | jq -r ".[]|select (.prerelease==${prereleaseStatus})|.tag_name" | head -1) + fi + + if [[ -n "$1" ]]; then + read -r -p "回退版本为${version},是否继续?[y/n]:" rollbackXrayStatus + if [[ "${rollbackXrayStatus}" == "y" ]]; then + echoContent green " ---> 当前Xray-core版本:$(/etc/v2ray-agent/xray/xray --version | awk '{print $2}' | head -1)" + + handleXray stop + rm -f /etc/v2ray-agent/xray/xray + updateXray "${version}" + else + echoContent green " ---> 放弃回退版本" + fi + elif [[ "${version}" == "v$(/etc/v2ray-agent/xray/xray --version | awk '{print $2}' | head -1)" ]]; then + read -r -p "当前版本与最新版相同,是否重新安装?[y/n]:" reInstallXrayStatus + if [[ "${reInstallXrayStatus}" == "y" ]]; then + handleXray stop + rm -f /etc/v2ray-agent/xray/xray + updateXray + else + echoContent green " ---> 放弃重新安装" + fi + else + read -r -p "最新版本为:${version},是否更新?[y/n]:" installXrayStatus + if [[ "${installXrayStatus}" == "y" ]]; then + rm /etc/v2ray-agent/xray/xray + updateXray + else + echoContent green " ---> 放弃更新" + fi + + fi + fi +} + +# 验证整个服务是否可用 +checkGFWStatue() { + readInstallType + echoContent skyBlue "\n进度 $1/${totalProgress} : 验证服务启动状态" + if [[ "${coreInstallType}" == "1" ]] && [[ -n $(pgrep -f "xray/xray") ]]; then + echoContent green " ---> 服务启动成功" + elif [[ "${coreInstallType}" == "2" ]] && [[ -n $(pgrep -f "sing-box/sing-box") ]]; then + echoContent green " ---> 服务启动成功" + else + echoContent red " ---> 服务启动失败,请检查终端是否有日志打印" + exit 0 + fi +} + +# 安装alpine开机启动 +installAlpineStartup() { + local serviceName=$1 + if [[ "${serviceName}" == "sing-box" ]]; then + cat <"/etc/init.d/${serviceName}" +#!/sbin/openrc-run + +description="sing-box service" +command="/etc/v2ray-agent/sing-box/sing-box" +command_args="run -c /etc/v2ray-agent/sing-box/conf/config.json" +command_background=true +pidfile="/var/run/sing-box.pid" +EOF + elif [[ "${serviceName}" == "xray" ]]; then + cat <"/etc/init.d/${serviceName}" +#!/sbin/openrc-run + +description="xray service" +command="/etc/v2ray-agent/xray/xray" +command_args="run -confdir /etc/v2ray-agent/xray/conf" +command_background=true +pidfile="/var/run/xray.pid" +EOF + fi + + chmod +x "/etc/init.d/${serviceName}" +} + +# sing-box开机自启 +installSingBoxService() { + echoContent skyBlue "\n进度 $1/${totalProgress} : 配置sing-box开机自启" + execStart='/etc/v2ray-agent/sing-box/sing-box run -c /etc/v2ray-agent/sing-box/conf/config.json' + + if [[ -n $(find /bin /usr/bin -name "systemctl") && "${release}" != "alpine" ]]; then + rm -rf /etc/systemd/system/sing-box.service + touch /etc/systemd/system/sing-box.service + cat </etc/systemd/system/sing-box.service +[Unit] +Description=Sing-Box Service +Documentation=https://sing-box.sagernet.org +After=network.target nss-lookup.target + +[Service] +User=root +WorkingDirectory=/root +CapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH +AmbientCapabilities=CAP_NET_ADMIN CAP_NET_BIND_SERVICE CAP_SYS_PTRACE CAP_DAC_READ_SEARCH +ExecStart=${execStart} +ExecReload=/bin/kill -HUP $MAINPID +Restart=on-failure +RestartSec=10 +LimitNPROC=infinity +LimitNOFILE=infinity + +[Install] +WantedBy=multi-user.target +EOF + bootStartup "sing-box.service" + elif [[ "${release}" == "alpine" ]]; then + installAlpineStartup "sing-box" + bootStartup "sing-box" + fi + + echoContent green " ---> 配置sing-box开机启动完毕" +} + +# Xray开机自启 +installXrayService() { + echoContent skyBlue "\n进度 $1/${totalProgress} : 配置Xray开机自启" + execStart='/etc/v2ray-agent/xray/xray run -confdir /etc/v2ray-agent/xray/conf' + if [[ -n $(find /bin /usr/bin -name "systemctl") ]]; then + rm -rf /etc/systemd/system/xray.service + touch /etc/systemd/system/xray.service + cat </etc/systemd/system/xray.service +[Unit] +Description=Xray Service +Documentation=https://github.com/xtls +After=network.target nss-lookup.target +[Service] +User=root +ExecStart=${execStart} +Restart=on-failure +RestartPreventExitStatus=23 +LimitNPROC=infinity +LimitNOFILE=infinity +[Install] +WantedBy=multi-user.target +EOF + bootStartup "xray.service" + echoContent green " ---> 配置Xray开机自启成功" + elif [[ "${release}" == "alpine" ]]; then + installAlpineStartup "xray" + bootStartup "xray" + fi +} + +# 操作Hysteria +handleHysteria() { + # shellcheck disable=SC2010 + if find /bin /usr/bin | grep -q systemctl && ls /etc/systemd/system/ | grep -q hysteria.service; then + if [[ -z $(pgrep -f "hysteria/hysteria") ]] && [[ "$1" == "start" ]]; then + systemctl start hysteria.service + elif [[ -n $(pgrep -f "hysteria/hysteria") ]] && [[ "$1" == "stop" ]]; then + systemctl stop hysteria.service + fi + fi + sleep 0.8 + + if [[ "$1" == "start" ]]; then + if [[ -n $(pgrep -f "hysteria/hysteria") ]]; then + echoContent green " ---> Hysteria启动成功" + else + echoContent red "Hysteria启动失败" + echoContent red "请手动执行【/etc/v2ray-agent/hysteria/hysteria --log-level debug -c /etc/v2ray-agent/hysteria/conf/config.json server】,查看错误日志" + exit 0 + fi + elif [[ "$1" == "stop" ]]; then + if [[ -z $(pgrep -f "hysteria/hysteria") ]]; then + echoContent green " ---> Hysteria关闭成功" + else + echoContent red "Hysteria关闭失败" + echoContent red "请手动执行【ps -ef|grep -v grep|grep hysteria|awk '{print \$2}'|xargs kill -9】" + exit 0 + fi + fi +} + +# 操作sing-box +handleSingBox() { + if [[ -f "/etc/systemd/system/sing-box.service" ]]; then + if [[ -z $(pgrep -f "sing-box") ]] && [[ "$1" == "start" ]]; then + singBoxMergeConfig + systemctl start sing-box.service + elif [[ -n $(pgrep -f "sing-box") ]] && [[ "$1" == "stop" ]]; then + systemctl stop sing-box.service + fi + elif [[ -f "/etc/init.d/sing-box" ]]; then + if [[ -z $(pgrep -f "sing-box") ]] && [[ "$1" == "start" ]]; then + singBoxMergeConfig + rc-service sing-box start + elif [[ -n $(pgrep -f "sing-box") ]] && [[ "$1" == "stop" ]]; then + rc-service sing-box stop + fi + fi + sleep 1 + + if [[ "$1" == "start" ]]; then + if [[ -n $(pgrep -f "sing-box") ]]; then + echoContent green " ---> sing-box启动成功" + else + echoContent red "sing-box启动失败" + echoContent yellow "请手动执行【 /etc/v2ray-agent/sing-box/sing-box merge config.json -C /etc/v2ray-agent/sing-box/conf/config/ -D /etc/v2ray-agent/sing-box/conf/ 】,查看错误日志" + echo + echoContent yellow "如上面命令没有错误,请手动执行【 /etc/v2ray-agent/sing-box/sing-box run -c /etc/v2ray-agent/sing-box/conf/config.json 】,查看错误日志" + exit 0 + fi + elif [[ "$1" == "stop" ]]; then + if [[ -z $(pgrep -f "sing-box") ]]; then + echoContent green " ---> sing-box关闭成功" + else + echoContent red " ---> sing-box关闭失败" + echoContent red "请手动执行【ps -ef|grep -v grep|grep sing-box|awk '{print \$2}'|xargs kill -9】" + exit 0 + fi + fi +} + +# 操作xray +handleXray() { + if [[ -n $(find /bin /usr/bin -name "systemctl") ]] && [[ -n $(find /etc/systemd/system/ -name "xray.service") ]]; then + if [[ -z $(pgrep -f "xray/xray") ]] && [[ "$1" == "start" ]]; then + systemctl start xray.service + elif [[ -n $(pgrep -f "xray/xray") ]] && [[ "$1" == "stop" ]]; then + systemctl stop xray.service + fi + elif [[ -f "/etc/init.d/xray" ]]; then + if [[ -z $(pgrep -f "xray/xray") ]] && [[ "$1" == "start" ]]; then + rc-service xray start + elif [[ -n $(pgrep -f "xray/xray") ]] && [[ "$1" == "stop" ]]; then + rc-service xray stop + fi + fi + + sleep 0.8 + + if [[ "$1" == "start" ]]; then + if [[ -n $(pgrep -f "xray/xray") ]]; then + echoContent green " ---> Xray启动成功" + else + echoContent red "Xray启动失败" + echoContent red "请手动执行以下的命令后【/etc/v2ray-agent/xray/xray -confdir /etc/v2ray-agent/xray/conf】将错误日志进行反馈" + exit 0 + fi + elif [[ "$1" == "stop" ]]; then + if [[ -z $(pgrep -f "xray/xray") ]]; then + echoContent green " ---> Xray关闭成功" + else + echoContent red "xray关闭失败" + echoContent red "请手动执行【ps -ef|grep -v grep|grep xray|awk '{print \$2}'|xargs kill -9】" + exit 0 + fi + fi +} + +# 读取Xray用户数据并初始化 +initXrayClients() { + local type=",$1," + local newUUID=$2 + local newEmail=$3 + if [[ -n "${newUUID}" ]]; then + local newUser= + newUser="{\"id\":\"${uuid}\",\"flow\":\"xtls-rprx-vision\",\"email\":\"${newEmail}-VLESS_TCP/TLS_Vision\"}" + currentClients=$(echo "${currentClients}" | jq -r ". +=[${newUser}]") + fi + local users= + users=[] + while read -r user; do + uuid=$(echo "${user}" | jq -r .id//.uuid) + email=$(echo "${user}" | jq -r .email//.name | awk -F "[-]" '{print $1}') + currentUser= + if echo "${type}" | grep -q "0"; then + currentUser="{\"id\":\"${uuid}\",\"flow\":\"xtls-rprx-vision\",\"email\":\"${email}-VLESS_TCP/TLS_Vision\"}" + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + + # VLESS WS + if echo "${type}" | grep -q ",1,"; then + currentUser="{\"id\":\"${uuid}\",\"email\":\"${email}-VLESS_WS\"}" + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + # VLESS XHTTP + if echo "${type}" | grep -q ",12,"; then + currentUser="{\"id\":\"${uuid}\",\"email\":\"${email}-VLESS_XHTTP\"}" + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + # trojan grpc + if echo "${type}" | grep -q ",2,"; then + currentUser="{\"password\":\"${uuid}\",\"email\":\"${email}-Trojan_gRPC\"}" + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + # VMess WS + if echo "${type}" | grep -q ",3,"; then + currentUser="{\"id\":\"${uuid}\",\"email\":\"${email}-VMess_WS\",\"alterId\": 0}" + + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + + # trojan tcp + if echo "${type}" | grep -q ",4,"; then + currentUser="{\"password\":\"${uuid}\",\"email\":\"${email}-trojan_tcp\"}" + + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + + # vless grpc + if echo "${type}" | grep -q ",5,"; then + currentUser="{\"id\":\"${uuid}\",\"email\":\"${email}-vless_grpc\"}" + + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + + # hysteria + if echo "${type}" | grep -q ",6,"; then + currentUser="{\"password\":\"${uuid}\",\"name\":\"${email}-singbox_hysteria2\"}" + + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + + # vless reality vision + if echo "${type}" | grep -q ",7,"; then + currentUser="{\"id\":\"${uuid}\",\"email\":\"${email}-vless_reality_vision\",\"flow\":\"xtls-rprx-vision\"}" + + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + + # vless reality grpc + if echo "${type}" | grep -q ",8,"; then + currentUser="{\"id\":\"${uuid}\",\"email\":\"${email}-vless_reality_grpc\",\"flow\":\"\"}" + + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + # tuic + if echo "${type}" | grep -q ",9,"; then + currentUser="{\"uuid\":\"${uuid}\",\"password\":\"${uuid}\",\"name\":\"${email}-singbox_tuic\"}" + + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + + done < <(echo "${currentClients}" | jq -c '.[]') + echo "${users}" +} +# 读取singbox用户数据并初始化 +initSingBoxClients() { + local type=",$1," + local newUUID=$2 + local newName=$3 + + if [[ -n "${newUUID}" ]]; then + local newUser= + newUser="{\"uuid\":\"${newUUID}\",\"flow\":\"xtls-rprx-vision\",\"name\":\"${newName}-VLESS_TCP/TLS_Vision\"}" + currentClients=$(echo "${currentClients}" | jq -r ". +=[${newUser}]") + fi + local users= + users=[] + while read -r user; do + uuid=$(echo "${user}" | jq -r .uuid//.id//.password) + name=$(echo "${user}" | jq -r .name//.email//.username | awk -F "[-]" '{print $1}') + currentUser= + # VLESS Vision + if echo "${type}" | grep -q ",0,"; then + currentUser="{\"uuid\":\"${uuid}\",\"flow\":\"xtls-rprx-vision\",\"name\":\"${name}-VLESS_TCP/TLS_Vision\"}" + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + # VLESS WS + if echo "${type}" | grep -q ",1,"; then + currentUser="{\"uuid\":\"${uuid}\",\"name\":\"${name}-VLESS_WS\"}" + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + # VMess ws + if echo "${type}" | grep -q ",3,"; then + currentUser="{\"uuid\":\"${uuid}\",\"name\":\"${name}-VMess_WS\",\"alterId\": 0}" + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + + # trojan + if echo "${type}" | grep -q ",4,"; then + currentUser="{\"password\":\"${uuid}\",\"name\":\"${name}-Trojan_TCP\"}" + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + + # VLESS Reality Vision + if echo "${type}" | grep -q ",7,"; then + currentUser="{\"uuid\":\"${uuid}\",\"flow\":\"xtls-rprx-vision\",\"name\":\"${name}-VLESS_Reality_Vision\"}" + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + # VLESS Reality gRPC + if echo "${type}" | grep -q ",8,"; then + currentUser="{\"uuid\":\"${uuid}\",\"name\":\"${name}-VLESS_Reality_gPRC\"}" + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + + # hysteria2 + if echo "${type}" | grep -q ",6,"; then + currentUser="{\"password\":\"${uuid}\",\"name\":\"${name}-singbox_hysteria2\"}" + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + + # tuic + if echo "${type}" | grep -q ",9,"; then + currentUser="{\"uuid\":\"${uuid}\",\"password\":\"${uuid}\",\"name\":\"${name}-singbox_tuic\"}" + + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + + # naive + if echo "${type}" | grep -q ",10,"; then + currentUser="{\"password\":\"${uuid}\",\"username\":\"${name}-singbox_naive\"}" + + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + # VMess HTTPUpgrade + if echo "${type}" | grep -q ",11,"; then + currentUser="{\"uuid\":\"${uuid}\",\"name\":\"${name}-VMess_HTTPUpgrade\",\"alterId\": 0}" + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + # anytls + if echo "${type}" | grep -q ",13,"; then + currentUser="{\"password\":\"${uuid}\",\"name\":\"${name}-anytls\"}" + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + + if echo "${type}" | grep -q ",20,"; then + currentUser="{\"username\":\"${uuid}\",\"password\":\"${uuid}\"}" + + users=$(echo "${users}" | jq -r ". +=[${currentUser}]") + fi + + done < <(echo "${currentClients}" | jq -c '.[]') + echo "${users}" +} + +# 初始化hysteria端口 +initHysteriaPort() { + readSingBoxConfig + if [[ -n "${hysteriaPort}" ]]; then + read -r -p "读取到上次安装时的端口,是否使用上次安装时的端口?[y/n]:" historyHysteriaPortStatus + if [[ "${historyHysteriaPortStatus}" == "y" ]]; then + echoContent yellow "\n ---> 端口: ${hysteriaPort}" + else + hysteriaPort= + fi + fi + + if [[ -z "${hysteriaPort}" ]]; then + echoContent yellow "请输入Hysteria端口[回车随机10000-30000],不可与其他服务重复" + read -r -p "端口:" hysteriaPort + if [[ -z "${hysteriaPort}" ]]; then + hysteriaPort=$((RANDOM % 20001 + 10000)) + fi + fi + if [[ -z ${hysteriaPort} ]]; then + echoContent red " ---> 端口不可为空" + initHysteriaPort "$2" + elif ((hysteriaPort < 1 || hysteriaPort > 65535)); then + echoContent red " ---> 端口不合法" + initHysteriaPort "$2" + fi + allowPort "${hysteriaPort}" + allowPort "${hysteriaPort}" "udp" +} + +# 初始化hysteria网络信息 +initHysteria2Network() { + + echoContent yellow "请输入本地带宽峰值的下行速度(默认:100,单位:Mbps)" + read -r -p "下行速度:" hysteria2ClientDownloadSpeed + if [[ -z "${hysteria2ClientDownloadSpeed}" ]]; then + hysteria2ClientDownloadSpeed=100 + echoContent yellow "\n ---> 下行速度: ${hysteria2ClientDownloadSpeed}\n" + fi + + echoContent yellow "请输入本地带宽峰值的上行速度(默认:50,单位:Mbps)" + read -r -p "上行速度:" hysteria2ClientUploadSpeed + if [[ -z "${hysteria2ClientUploadSpeed}" ]]; then + hysteria2ClientUploadSpeed=50 + echoContent yellow "\n ---> 上行速度: ${hysteria2ClientUploadSpeed}\n" + fi +} + +# firewalld设置端口跳跃 +addFirewalldPortHopping() { + + local start=$1 + local end=$2 + local targetPort=$3 + for port in $(seq "$start" "$end"); do + sudo firewall-cmd --permanent --add-forward-port=port="${port}":proto=udp:toport="${targetPort}" + done + sudo firewall-cmd --reload +} + +# 端口跳跃 +addPortHopping() { + local type=$1 + local targetPort=$2 + if [[ -n "${portHoppingStart}" || -n "${portHoppingEnd}" ]]; then + echoContent red " ---> 已添加不可重复添加,可删除后重新添加" + exit 0 + fi + if [[ "${release}" == "centos" ]]; then + if ! systemctl status firewalld 2>/dev/null | grep -q "active (running)"; then + echoContent red " ---> 未启动firewalld防火墙,无法设置端口跳跃。" + exit 0 + fi + fi + + echoContent skyBlue "\n进度 1/1 : 端口跳跃" + echoContent red "\n==============================================================" + echoContent yellow "# 注意事项\n" + echoContent yellow "仅支持Hysteria2、Tuic" + echoContent yellow "端口跳跃的起始位置为30000" + echoContent yellow "端口跳跃的结束位置为40000" + echoContent yellow "可以在30000-40000范围中选一段" + echoContent yellow "建议1000个左右" + echoContent yellow "注意不要和其他的端口跳跃设置范围一样,设置相同会覆盖。" + + echoContent yellow "请输入端口跳跃的范围,例如[30000-31000]" + + read -r -p "范围:" portHoppingRange + if [[ -z "${portHoppingRange}" ]]; then + echoContent red " ---> 范围不可为空" + addPortHopping "${type}" "${targetPort}" + elif echo "${portHoppingRange}" | grep -q "-"; then + + local portStart= + local portEnd= + portStart=$(echo "${portHoppingRange}" | awk -F '-' '{print $1}') + portEnd=$(echo "${portHoppingRange}" | awk -F '-' '{print $2}') + + if [[ -z "${portStart}" || -z "${portEnd}" ]]; then + echoContent red " ---> 范围不合法" + addPortHopping "${type}" "${targetPort}" + elif ((portStart < 30000 || portStart > 40000 || portEnd < 30000 || portEnd > 40000 || portEnd < portStart)); then + echoContent red " ---> 范围不合法" + addPortHopping "${type}" "${targetPort}" + else + echoContent green "\n端口范围: ${portHoppingRange}\n" + if [[ "${release}" == "centos" ]]; then + sudo firewall-cmd --permanent --add-masquerade + sudo firewall-cmd --reload + addFirewalldPortHopping "${portStart}" "${portEnd}" "${targetPort}" + if ! sudo firewall-cmd --list-forward-ports | grep -q "toport=${targetPort}"; then + echoContent red " ---> 端口跳跃添加失败" + exit 0 + fi + else + iptables -t nat -A PREROUTING -p udp --dport "${portStart}:${portEnd}" -m comment --comment "mack-a_${type}_portHopping" -j DNAT --to-destination ":${targetPort}" + sudo netfilter-persistent save + if ! iptables-save | grep -q "mack-a_${type}_portHopping"; then + echoContent red " ---> 端口跳跃添加失败" + exit 0 + fi + fi + allowPort "${portStart}:${portEnd}" udp + echoContent green " ---> 端口跳跃添加成功" + fi + fi +} + +# 读取端口跳跃的配置 +readPortHopping() { + local type=$1 + local targetPort=$2 + local portHoppingStart= + local portHoppingEnd= + + if [[ "${release}" == "centos" ]]; then + portHoppingStart=$(sudo firewall-cmd --list-forward-ports | grep "toport=${targetPort}" | head -1 | cut -d ":" -f 1 | cut -d "=" -f 2) + portHoppingEnd=$(sudo firewall-cmd --list-forward-ports | grep "toport=${targetPort}" | tail -n 1 | cut -d ":" -f 1 | cut -d "=" -f 2) + else + if iptables-save | grep -q "mack-a_${type}_portHopping"; then + local portHopping= + portHopping=$(iptables-save | grep "mack-a_${type}_portHopping" | cut -d " " -f 8) + + portHoppingStart=$(echo "${portHopping}" | cut -d ":" -f 1) + portHoppingEnd=$(echo "${portHopping}" | cut -d ":" -f 2) + fi + fi + if [[ "${type}" == "hysteria2" ]]; then + hysteria2PortHoppingStart="${portHoppingStart}" + hysteria2PortHoppingEnd=${portHoppingEnd} + hysteria2PortHopping="${portHoppingStart}-${portHoppingEnd}" + elif [[ "${type}" == "tuic" ]]; then + tuicPortHoppingStart="${portHoppingStart}" + tuicPortHoppingEnd="${portHoppingEnd}" + # tuicPortHopping="${portHoppingStart}-${portHoppingEnd}" + fi +} +# 删除端口跳跃iptables规则 +deletePortHoppingRules() { + local type=$1 + local start=$2 + local end=$3 + local targetPort=$4 + + if [[ "${release}" == "centos" ]]; then + for port in $(seq "${start}" "${end}"); do + sudo firewall-cmd --permanent --remove-forward-port=port="${port}":proto=udp:toport="${targetPort}" + done + sudo firewall-cmd --reload + else + iptables -t nat -L PREROUTING --line-numbers | grep "mack-a_${type}_portHopping" | awk '{print $1}' | while read -r line; do + iptables -t nat -D PREROUTING 1 + sudo netfilter-persistent save + done + fi +} + +# 端口跳跃菜单 +portHoppingMenu() { + local type=$1 + # 判断iptables是否存在 + if ! find /usr/bin /usr/sbin | grep -q -w iptables; then + echoContent red " ---> 无法识别iptables工具,无法使用端口跳跃,退出安装" + exit 0 + fi + + local targetPort= + local portHoppingStart= + local portHoppingEnd= + + if [[ "${type}" == "hysteria2" ]]; then + readPortHopping "${type}" "${singBoxHysteria2Port}" + targetPort=${singBoxHysteria2Port} + portHoppingStart=${hysteria2PortHoppingStart} + portHoppingEnd=${hysteria2PortHoppingEnd} + elif [[ "${type}" == "tuic" ]]; then + readPortHopping "${type}" "${singBoxTuicPort}" + targetPort=${singBoxTuicPort} + portHoppingStart=${tuicPortHoppingStart} + portHoppingEnd=${tuicPortHoppingEnd} + fi + + echoContent skyBlue "\n进度 1/1 : 端口跳跃" + echoContent red "\n==============================================================" + echoContent yellow "1.添加端口跳跃" + echoContent yellow "2.删除端口跳跃" + echoContent yellow "3.查看端口跳跃" + read -r -p "请选择:" selectPortHoppingStatus + if [[ "${selectPortHoppingStatus}" == "1" ]]; then + addPortHopping "${type}" "${targetPort}" + elif [[ "${selectPortHoppingStatus}" == "2" ]]; then + deletePortHoppingRules "${type}" "${portHoppingStart}" "${portHoppingEnd}" "${targetPort}" + echoContent green " ---> 删除成功" + elif [[ "${selectPortHoppingStatus}" == "3" ]]; then + if [[ -n "${portHoppingStart}" && -n "${portHoppingEnd}" ]]; then + echoContent green " ---> 当前端口跳跃范围为: ${portHoppingStart}-${portHoppingEnd}" + else + echoContent yellow " ---> 未设置端口跳跃" + fi + else + portHoppingMenu + fi +} + +# 初始化tuic端口 +initTuicPort() { + readSingBoxConfig + if [[ -n "${tuicPort}" ]]; then + read -r -p "读取到上次安装时的端口,是否使用上次安装时的端口?[y/n]:" historyTuicPortStatus + if [[ "${historyTuicPortStatus}" == "y" ]]; then + echoContent yellow "\n ---> 端口: ${tuicPort}" + else + tuicPort= + fi + fi + + if [[ -z "${tuicPort}" ]]; then + echoContent yellow "请输入Tuic端口[回车随机10000-30000],不可与其他服务重复" + read -r -p "端口:" tuicPort + if [[ -z "${tuicPort}" ]]; then + tuicPort=$((RANDOM % 20001 + 10000)) + fi + fi + if [[ -z ${tuicPort} ]]; then + echoContent red " ---> 端口不可为空" + initTuicPort "$2" + elif ((tuicPort < 1 || tuicPort > 65535)); then + echoContent red " ---> 端口不合法" + initTuicPort "$2" + fi + echoContent green "\n ---> 端口: ${tuicPort}" + allowPort "${tuicPort}" + allowPort "${tuicPort}" "udp" +} + +# 初始化tuic的协议 +initTuicProtocol() { + if [[ -n "${tuicAlgorithm}" && -z "${lastInstallationConfig}" ]]; then + read -r -p "读取到上次使用的算法,是否使用 ?[y/n]:" historyTuicAlgorithm + if [[ "${historyTuicAlgorithm}" != "y" ]]; then + tuicAlgorithm= + else + echoContent yellow "\n ---> 算法: ${tuicAlgorithm}\n" + fi + elif [[ -n "${tuicAlgorithm}" && -n "${lastInstallationConfig}" ]]; then + echoContent yellow "\n ---> 算法: ${tuicAlgorithm}\n" + fi + + if [[ -z "${tuicAlgorithm}" ]]; then + + echoContent skyBlue "\n请选择算法类型" + echoContent red "==============================================================" + echoContent yellow "1.bbr(默认)" + echoContent yellow "2.cubic" + echoContent yellow "3.new_reno" + echoContent red "==============================================================" + read -r -p "请选择:" selectTuicAlgorithm + case ${selectTuicAlgorithm} in + 1) + tuicAlgorithm="bbr" + ;; + 2) + tuicAlgorithm="cubic" + ;; + 3) + tuicAlgorithm="new_reno" + ;; + *) + tuicAlgorithm="bbr" + ;; + esac + echoContent yellow "\n ---> 算法: ${tuicAlgorithm}\n" + fi +} + +# 初始化tuic配置 +#initTuicConfig() { +# echoContent skyBlue "\n进度 $1/${totalProgress} : 初始化Tuic配置" +# +# initTuicPort +# initTuicProtocol +# cat </etc/v2ray-agent/tuic/conf/config.json +#{ +# "server": "[::]:${tuicPort}", +# "users": $(initXrayClients 9), +# "certificate": "/etc/v2ray-agent/tls/${currentHost}.crt", +# "private_key": "/etc/v2ray-agent/tls/${currentHost}.key", +# "congestion_control":"${tuicAlgorithm}", +# "alpn": ["h3"], +# "log_level": "warn" +#} +#EOF +#} + +# 初始化singbox route配置 +initSingBoxRouteConfig() { + downloadSingBoxGeositeDB + local outboundTag=$1 + if [[ ! -f "${singBoxConfigPath}${outboundTag}_route.json" ]]; then + cat <"${singBoxConfigPath}${outboundTag}_route.json" +{ + "route": { + "geosite": { + "path": "${singBoxConfigPath}geosite.db" + }, + "rules": [ + { + "domain": [ + ], + "geosite": [ + ], + "outbound": "${outboundTag}" + } + ] + } +} +EOF + fi +} +# 下载sing-box geosite db +downloadSingBoxGeositeDB() { + if [[ ! -f "${singBoxConfigPath}geosite.db" ]]; then + if [[ "${release}" == "alpine" ]]; then + wget -q -P "${singBoxConfigPath}" https://github.com/Johnshall/sing-geosite/releases/latest/download/geosite.db + else + wget -q "${wgetShowProgressStatus}" -P "${singBoxConfigPath}" https://github.com/Johnshall/sing-geosite/releases/latest/download/geosite.db + fi + + fi +} + +# 添加sing-box路由规则 +addSingBoxRouteRule() { + local outboundTag=$1 + # 域名列表 + local domainList=$2 + # 路由文件名称 + local routingName=$3 + # 读取上次安装内容 + if [[ -f "${singBoxConfigPath}${routingName}.json" ]]; then + read -r -p "读取到上次的配置,是否保留 ?[y/n]:" historyRouteStatus + if [[ "${historyRouteStatus}" == "y" ]]; then + domainList="${domainList},$(jq -rc .route.rules[0].rule_set[] "${singBoxConfigPath}${routingName}.json" | awk -F "[_]" '{print $1}' | paste -sd ',')" + domainList="${domainList},$(jq -rc .route.rules[0].domain_regex[] "${singBoxConfigPath}${routingName}.json" | awk -F "[*]" '{print $2}' | paste -sd ',' | sed 's/\\//g')" + fi + fi + local rules= + rules=$(initSingBoxRules "${domainList}" "${routingName}") + # domain精确匹配规则 + local domainRules= + domainRules=$(echo "${rules}" | jq .domainRules) + + # ruleSet规则集 + local ruleSet= + ruleSet=$(echo "${rules}" | jq .ruleSet) + + # ruleSet规则tag + local ruleSetTag=[] + if [[ "$(echo "${ruleSet}" | jq '.|length')" != "0" ]]; then + ruleSetTag=$(echo "${ruleSet}" | jq '.|map(.tag)') + fi + if [[ -n "${singBoxConfigPath}" ]]; then + + cat <"${singBoxConfigPath}${routingName}.json" +{ + "route": { + "rules": [ + { + "rule_set":${ruleSetTag}, + "domain_regex":${domainRules}, + "outbound": "${outboundTag}" + } + ], + "rule_set":${ruleSet} + } +} +EOF + jq 'if .route.rule_set == [] then del(.route.rule_set) else . end' "${singBoxConfigPath}${routingName}.json" >"${singBoxConfigPath}${routingName}_tmp.json" && mv "${singBoxConfigPath}${routingName}_tmp.json" "${singBoxConfigPath}${routingName}.json" + fi + +} + +# 移除sing-box route rule +removeSingBoxRouteRule() { + local outboundTag=$1 + local delRules + if [[ -f "${singBoxConfigPath}${outboundTag}_route.json" ]]; then + delRules=$(jq -r 'del(.route.rules[]|select(.outbound=="'"${outboundTag}"'"))' "${singBoxConfigPath}${outboundTag}_route.json") + echo "${delRules}" >"${singBoxConfigPath}${outboundTag}_route.json" + fi +} + +# 添加sing-box出站 +addSingBoxOutbound() { + local tag=$1 + local type="ipv4" + local detour=$2 + if echo "${tag}" | grep -q "IPv6"; then + type=ipv6 + fi + if [[ -n "${detour}" ]]; then + cat <"${singBoxConfigPath}${tag}.json" +{ + "outbounds": [ + { + "type": "direct", + "tag": "${tag}", + "detour": "${detour}", + "domain_strategy": "${type}_only" + } + ] +} +EOF + elif echo "${tag}" | grep -q "direct"; then + + cat <"${singBoxConfigPath}${tag}.json" +{ + "outbounds": [ + { + "type": "direct", + "tag": "${tag}" + } + ] +} +EOF + elif echo "${tag}" | grep -q "block"; then + + cat <"${singBoxConfigPath}${tag}.json" +{ + "outbounds": [ + { + "type": "block", + "tag": "${tag}" + } + ] +} +EOF + else + cat <"${singBoxConfigPath}${tag}.json" +{ + "outbounds": [ + { + "type": "direct", + "tag": "${tag}", + "domain_strategy": "${type}_only" + } + ] +} +EOF + fi +} + +# 添加Xray-core 出站 +addXrayOutbound() { + local tag=$1 + local domainStrategy= + + if echo "${tag}" | grep -q "IPv4"; then + domainStrategy="ForceIPv4" + elif echo "${tag}" | grep -q "IPv6"; then + domainStrategy="ForceIPv6" + fi + + if [[ -n "${domainStrategy}" ]]; then + cat <"/etc/v2ray-agent/xray/conf/${tag}.json" +{ + "outbounds":[ + { + "protocol":"freedom", + "settings":{ + "domainStrategy":"${domainStrategy}" + }, + "tag":"${tag}" + } + ] +} +EOF + fi + # direct + if echo "${tag}" | grep -q "direct"; then + cat <"/etc/v2ray-agent/xray/conf/${tag}.json" +{ + "outbounds":[ + { + "protocol":"freedom", + "settings": { + "domainStrategy":"UseIP" + }, + "tag":"${tag}" + } + ] +} +EOF + fi + # blackhole + if echo "${tag}" | grep -q "blackhole"; then + cat <"/etc/v2ray-agent/xray/conf/${tag}.json" +{ + "outbounds":[ + { + "protocol":"blackhole", + "tag":"${tag}" + } + ] +} +EOF + fi + # socks5 outbound + if echo "${tag}" | grep -q "socks5"; then + cat <"/etc/v2ray-agent/xray/conf/${tag}.json" +{ + "outbounds": [ + { + "protocol": "socks", + "tag": "${tag}", + "settings": { + "servers": [ + { + "address": "${socks5RoutingOutboundIP}", + "port": ${socks5RoutingOutboundPort}, + "users": [ + { + "user": "${socks5RoutingOutboundUserName}", + "pass": "${socks5RoutingOutboundPassword}" + } + ] + } + ] + } + } + ] +} +EOF + fi + if echo "${tag}" | grep -q "wireguard_out_IPv4"; then + cat <"/etc/v2ray-agent/xray/conf/${tag}.json" +{ + "outbounds": [ + { + "protocol": "wireguard", + "settings": { + "secretKey": "${secretKeyWarpReg}", + "address": [ + "${address}" + ], + "peers": [ + { + "publicKey": "${publicKeyWarpReg}", + "allowedIPs": [ + "0.0.0.0/0", + "::/0" + ], + "endpoint": "162.159.192.1:2408" + } + ], + "reserved": ${reservedWarpReg}, + "mtu": 1280 + }, + "tag": "${tag}" + } + ] +} +EOF + fi + if echo "${tag}" | grep -q "wireguard_out_IPv6"; then + cat <"/etc/v2ray-agent/xray/conf/${tag}.json" +{ + "outbounds": [ + { + "protocol": "wireguard", + "settings": { + "secretKey": "${secretKeyWarpReg}", + "address": [ + "${address}" + ], + "peers": [ + { + "publicKey": "${publicKeyWarpReg}", + "allowedIPs": [ + "0.0.0.0/0", + "::/0" + ], + "endpoint": "162.159.192.1:2408" + } + ], + "reserved": ${reservedWarpReg}, + "mtu": 1280 + }, + "tag": "${tag}" + } + ] +} +EOF + fi + if echo "${tag}" | grep -q "vmess-out"; then + cat <"/etc/v2ray-agent/xray/conf/${tag}.json" +{ + "outbounds": [ + { + "tag": "${tag}", + "protocol": "vmess", + "streamSettings": { + "network": "ws", + "security": "tls", + "tlsSettings": { + "allowInsecure": false + }, + "wsSettings": { + "path": "${setVMessWSTLSPath}" + } + }, + "mux": { + "enabled": true, + "concurrency": 8 + }, + "settings": { + "vnext": [ + { + "address": "${setVMessWSTLSAddress}", + "port": "${setVMessWSTLSPort}", + "users": [ + { + "id": "${setVMessWSTLSUUID}", + "security": "auto", + "alterId": 0 + } + ] + } + ] + } + } + ] +} +EOF + fi +} + +# 删除 Xray-core出站 +removeXrayOutbound() { + local tag=$1 + if [[ -f "/etc/v2ray-agent/xray/conf/${tag}.json" ]]; then + rm "/etc/v2ray-agent/xray/conf/${tag}.json" >/dev/null 2>&1 + fi +} +# 移除sing-box配置 +removeSingBoxConfig() { + + local tag=$1 + if [[ -f "${singBoxConfigPath}${tag}.json" ]]; then + rm "${singBoxConfigPath}${tag}.json" + fi +} + +# 初始化wireguard出站信息 +addSingBoxWireGuardEndpoints() { + local type=$1 + + readConfigWarpReg + + cat <"${singBoxConfigPath}wireguard_endpoints_${type}.json" +{ + "endpoints": [ + { + "type": "wireguard", + "tag": "wireguard_endpoints_${type}", + "address": [ + "${address}" + ], + "private_key": "${secretKeyWarpReg}", + "peers": [ + { + "address": "162.159.192.1", + "port": 2408, + "public_key": "${publicKeyWarpReg}", + "reserved":${reservedWarpReg}, + "allowed_ips": ["0.0.0.0/0","::/0"] + } + ] + } + ] +} +EOF +} + +# 初始化 sing-box Hysteria2 配置 +initSingBoxHysteria2Config() { + echoContent skyBlue "\n进度 $1/${totalProgress} : 初始化Hysteria2配置" + + initHysteriaPort + initHysteria2Network + + cat </etc/v2ray-agent/sing-box/conf/config/hysteria2.json +{ + "inbounds": [ + { + "type": "hysteria2", + "listen": "::", + "listen_port": ${hysteriaPort}, + "users": $(initXrayClients 6), + "up_mbps":${hysteria2ClientDownloadSpeed}, + "down_mbps":${hysteria2ClientUploadSpeed}, + "tls": { + "enabled": true, + "server_name":"${currentHost}", + "alpn": [ + "h3" + ], + "certificate_path": "/etc/v2ray-agent/tls/${currentHost}.crt", + "key_path": "/etc/v2ray-agent/tls/${currentHost}.key" + } + } + ] +} +EOF +} + +# sing-box Tuic安装 +singBoxTuicInstall() { + if ! echo "${currentInstallProtocolType}" | grep -qE ",0,|,1,|,2,|,3,|,4,|,5,|,6,|,9,|,10,"; then + echoContent red "\n ---> 由于需要依赖证书,如安装Tuic,请先安装带有TLS标识协议" + exit 0 + fi + + totalProgress=5 + installSingBox 1 + selectCustomInstallType=",9," + initSingBoxConfig custom 2 true + installSingBoxService 3 + reloadCore + showAccounts 4 +} + +# sing-box hy2安装 +singBoxHysteria2Install() { + if ! echo "${currentInstallProtocolType}" | grep -qE ",0,|,1,|,2,|,3,|,4,|,5,|,6,|,9,|,10,"; then + echoContent red "\n ---> 由于需要依赖证书,如安装Hysteria2,请先安装带有TLS标识协议" + exit 0 + fi + + totalProgress=5 + installSingBox 1 + selectCustomInstallType=",6," + initSingBoxConfig custom 2 true + installSingBoxService 3 + reloadCore + showAccounts 4 +} + +# 合并config +singBoxMergeConfig() { + rm /etc/v2ray-agent/sing-box/conf/config.json >/dev/null 2>&1 + /etc/v2ray-agent/sing-box/sing-box merge config.json -C /etc/v2ray-agent/sing-box/conf/config/ -D /etc/v2ray-agent/sing-box/conf/ >/dev/null 2>&1 +} + +# 初始化Xray Trojan XTLS 配置文件 +#initXrayFrontingConfig() { +# echoContent red " ---> Trojan暂不支持 xtls-rprx-vision" +# if [[ -z "${configPath}" ]]; then +# echoContent red " ---> 未安装,请使用脚本安装" +# menu +# exit 0 +# fi +# if [[ "${coreInstallType}" != "1" ]]; then +# echoContent red " ---> 未安装可用类型" +# fi +# local xtlsType= +# if echo ${currentInstallProtocolType} | grep -q trojan; then +# xtlsType=VLESS +# else +# xtlsType=Trojan +# fi +# +# echoContent skyBlue "\n功能 1/${totalProgress} : 前置切换为${xtlsType}" +# echoContent red "\n==============================================================" +# echoContent yellow "# 注意事项\n" +# echoContent yellow "会将前置替换为${xtlsType}" +# echoContent yellow "如果前置是Trojan,查看账号时则会出现两个Trojan协议的节点,有一个不可用xtls" +# echoContent yellow "再次执行可切换至上一次的前置\n" +# +# echoContent yellow "1.切换至${xtlsType}" +# echoContent red "==============================================================" +# read -r -p "请选择:" selectType +# if [[ "${selectType}" == "1" ]]; then +# +# if [[ "${xtlsType}" == "Trojan" ]]; then +# +# local VLESSConfig +# VLESSConfig=$(cat ${configPath}${frontingType}.json) +# VLESSConfig=${VLESSConfig//"id"/"password"} +# VLESSConfig=${VLESSConfig//VLESSTCP/TrojanTCPXTLS} +# VLESSConfig=${VLESSConfig//VLESS/Trojan} +# VLESSConfig=${VLESSConfig//"vless"/"trojan"} +# VLESSConfig=${VLESSConfig//"id"/"password"} +# +# echo "${VLESSConfig}" | jq . >${configPath}02_trojan_TCP_inbounds.json +# rm ${configPath}${frontingType}.json +# elif [[ "${xtlsType}" == "VLESS" ]]; then +# +# local VLESSConfig +# VLESSConfig=$(cat ${configPath}02_trojan_TCP_inbounds.json) +# VLESSConfig=${VLESSConfig//"password"/"id"} +# VLESSConfig=${VLESSConfig//TrojanTCPXTLS/VLESSTCP} +# VLESSConfig=${VLESSConfig//Trojan/VLESS} +# VLESSConfig=${VLESSConfig//"trojan"/"vless"} +# VLESSConfig=${VLESSConfig//"password"/"id"} +# +# echo "${VLESSConfig}" | jq . >${configPath}02_VLESS_TCP_inbounds.json +# rm ${configPath}02_trojan_TCP_inbounds.json +# fi +# reloadCore +# fi +# +# exit 0 +#} + +# 初始化sing-box端口 +initSingBoxPort() { + local port=$1 + if [[ -n "${port}" && -z "${lastInstallationConfig}" ]]; then + read -r -p "读取到上次使用的端口,是否使用 ?[y/n]:" historyPort + if [[ "${historyPort}" != "y" ]]; then + port= + else + echo "${port}" + fi + elif [[ -n "${port}" && -n "${lastInstallationConfig}" ]]; then + echo "${port}" + fi + if [[ -z "${port}" ]]; then + read -r -p '请输入自定义端口[需合法],端口不可重复,[回车]随机端口:' port + if [[ -z "${port}" ]]; then + port=$((RANDOM % 50001 + 10000)) + fi + if ((port >= 1 && port <= 65535)); then + allowPort "${port}" + allowPort "${port}" "udp" + echo "${port}" + else + echoContent red " ---> 端口输入错误" + exit 0 + fi + fi +} + +# 初始化Xray 配置文件 +initXrayConfig() { + echoContent skyBlue "\n进度 $2/${totalProgress} : 初始化Xray配置" + echo + local uuid= + local addClientsStatus= + if [[ -n "${currentUUID}" && -z "${lastInstallationConfig}" ]]; then + read -r -p "读取到上次用户配置,是否使用上次安装的配置 ?[y/n]:" historyUUIDStatus + if [[ "${historyUUIDStatus}" == "y" ]]; then + addClientsStatus=true + echoContent green "\n ---> 使用成功" + fi + elif [[ -n "${currentUUID}" && -n "${lastInstallationConfig}" ]]; then + addClientsStatus=true + fi + + if [[ -z "${addClientsStatus}" ]]; then + echoContent yellow "请输入自定义UUID[需合法],[回车]随机UUID" + read -r -p 'UUID:' customUUID + + if [[ -n ${customUUID} ]]; then + uuid=${customUUID} + else + uuid=$(/etc/v2ray-agent/xray/xray uuid) + fi + + echoContent yellow "\n请输入自定义用户名[需合法],[回车]随机用户名" + read -r -p '用户名:' customEmail + if [[ -z ${customEmail} ]]; then + customEmail="$(echo "${uuid}" | cut -d "-" -f 1)-VLESS_TCP/TLS_Vision" + fi + fi + + if [[ -z "${addClientsStatus}" && -z "${uuid}" ]]; then + addClientsStatus= + echoContent red "\n ---> uuid读取错误,随机生成" + uuid=$(/etc/v2ray-agent/xray/xray uuid) + fi + + if [[ -n "${uuid}" ]]; then + currentClients='[{"id":"'${uuid}'","add":"'${add}'","flow":"xtls-rprx-vision","email":"'${customEmail}'"}]' + echoContent green "\n ${customEmail}:${uuid}" + echo + fi + + # log + if [[ ! -f "/etc/v2ray-agent/xray/conf/00_log.json" ]]; then + + cat </etc/v2ray-agent/xray/conf/00_log.json +{ + "log": { + "error": "/etc/v2ray-agent/xray/error.log", + "loglevel": "warning", + "dnsLog": false + } +} +EOF + fi + + if [[ ! -f "/etc/v2ray-agent/xray/conf/12_policy.json" ]]; then + + cat </etc/v2ray-agent/xray/conf/12_policy.json +{ + "policy": { + "levels": { + "0": { + "handshake": $((1 + RANDOM % 4)), + "connIdle": $((250 + RANDOM % 51)) + } + } + } +} +EOF + fi + + addXrayOutbound "z_direct_outbound" + # dns + if [[ ! -f "/etc/v2ray-agent/xray/conf/11_dns.json" ]]; then + cat </etc/v2ray-agent/xray/conf/11_dns.json +{ + "dns": { + "servers": [ + "localhost" + ] + } +} +EOF + fi + # routing + cat </etc/v2ray-agent/xray/conf/09_routing.json +{ + "routing": { + "rules": [ + { + "type": "field", + "domain": [ + "domain:gstatic.com", + "domain:googleapis.com", + "domain:googleapis.cn" + ], + "outboundTag": "z_direct_outbound" + } + ] + } +} +EOF + # VLESS_TCP_TLS_Vision + # 回落nginx + local fallbacksList='{"dest":31300,"xver":1},{"alpn":"h2","dest":31302,"xver":1}' + + # trojan + if echo "${selectCustomInstallType}" | grep -q ",4," || [[ "$1" == "all" ]]; then + fallbacksList='{"dest":31296,"xver":1},{"alpn":"h2","dest":31302,"xver":1}' + cat </etc/v2ray-agent/xray/conf/04_trojan_TCP_inbounds.json +{ +"inbounds":[ + { + "port": 31296, + "listen": "127.0.0.1", + "protocol": "trojan", + "tag":"trojanTCP", + "settings": { + "clients": $(initXrayClients 4), + "fallbacks":[ + { + "dest":"31300", + "xver":1 + } + ] + }, + "streamSettings": { + "network": "tcp", + "security": "none", + "tcpSettings": { + "acceptProxyProtocol": true + } + } + } + ] +} +EOF + elif [[ -z "$3" ]]; then + rm /etc/v2ray-agent/xray/conf/04_trojan_TCP_inbounds.json >/dev/null 2>&1 + fi + + # VLESS_WS_TLS + if echo "${selectCustomInstallType}" | grep -q ",1," || [[ "$1" == "all" ]]; then + fallbacksList=${fallbacksList}',{"path":"/'${customPath}'ws","dest":31297,"xver":1}' + cat </etc/v2ray-agent/xray/conf/03_VLESS_WS_inbounds.json +{ +"inbounds":[ + { + "port": 31297, + "listen": "127.0.0.1", + "protocol": "vless", + "tag":"VLESSWS", + "settings": { + "clients": $(initXrayClients 1), + "decryption": "none" + }, + "streamSettings": { + "network": "ws", + "security": "none", + "wsSettings": { + "acceptProxyProtocol": true, + "path": "/${customPath}ws" + } + } + } +] +} +EOF + elif [[ -z "$3" ]]; then + rm /etc/v2ray-agent/xray/conf/03_VLESS_WS_inbounds.json >/dev/null 2>&1 + fi + # VLESS_XHTTP_TLS + if echo "${selectCustomInstallType}" | grep -q ",12," || [[ "$1" == "all" ]]; then + initXrayXHTTPort + initRealityClientServersName + initRealityKey + initRealityMldsa65 + cat </etc/v2ray-agent/xray/conf/12_VLESS_XHTTP_inbounds.json +{ +"inbounds":[ + { + "port": ${xHTTPort}, + "listen": "0.0.0.0", + "protocol": "vless", + "tag":"VLESSRealityXHTTP", + "settings": { + "clients": $(initXrayClients 12), + "decryption": "none" + }, + "streamSettings": { + "network": "xhttp", + "security": "reality", + "realitySettings": { + "show": false, + "dest": "${realityServerName}:${realityDomainPort}", + "xver": 0, + "serverNames": [ + "${realityServerName}" + ], + "privateKey": "${realityPrivateKey}", + "publicKey": "${realityPublicKey}", + "maxTimeDiff": 70000, + "shortIds": [ + "", + "6ba85179e30d4fc2" + ] + }, + "xhttpSettings": { + "host": "${realityServerName}", + "path": "/${customPath}xHTTP", + "mode": "auto" + } + } + } +] +} +EOF + elif [[ -z "$3" ]]; then + rm /etc/v2ray-agent/xray/conf/12_VLESS_XHTTP_inbounds.json >/dev/null 2>&1 + fi + # trojan_grpc + # if echo "${selectCustomInstallType}" | grep -q ",2," || [[ "$1" == "all" ]]; then + # if ! echo "${selectCustomInstallType}" | grep -q ",5," && [[ -n ${selectCustomInstallType} ]]; then + # fallbacksList=${fallbacksList//31302/31304} + # fi + # cat </etc/v2ray-agent/xray/conf/04_trojan_gRPC_inbounds.json + #{ + # "inbounds": [ + # { + # "port": 31304, + # "listen": "127.0.0.1", + # "protocol": "trojan", + # "tag": "trojangRPCTCP", + # "settings": { + # "clients": $(initXrayClients 2), + # "fallbacks": [ + # { + # "dest": "31300" + # } + # ] + # }, + # "streamSettings": { + # "network": "grpc", + # "grpcSettings": { + # "serviceName": "${customPath}trojangrpc" + # } + # } + # } + # ] + #} + #EOF + # elif [[ -z "$3" ]]; then + # rm /etc/v2ray-agent/xray/conf/04_trojan_gRPC_inbounds.json >/dev/null 2>&1 + # fi + + # VMess_WS + if echo "${selectCustomInstallType}" | grep -q ",3," || [[ "$1" == "all" ]]; then + fallbacksList=${fallbacksList}',{"path":"/'${customPath}'vws","dest":31299,"xver":1}' + cat </etc/v2ray-agent/xray/conf/05_VMess_WS_inbounds.json +{ +"inbounds":[ +{ + "listen": "127.0.0.1", + "port": 31299, + "protocol": "vmess", + "tag":"VMessWS", + "settings": { + "clients": $(initXrayClients 3) + }, + "streamSettings": { + "network": "ws", + "security": "none", + "wsSettings": { + "acceptProxyProtocol": true, + "path": "/${customPath}vws" + } + } +} +] +} +EOF + elif [[ -z "$3" ]]; then + rm /etc/v2ray-agent/xray/conf/05_VMess_WS_inbounds.json >/dev/null 2>&1 + fi + # VLESS_gRPC + if echo "${selectCustomInstallType}" | grep -q ",5," || [[ "$1" == "all" ]]; then + cat </etc/v2ray-agent/xray/conf/06_VLESS_gRPC_inbounds.json +{ + "inbounds":[ + { + "port": 31301, + "listen": "127.0.0.1", + "protocol": "vless", + "tag":"VLESSGRPC", + "settings": { + "clients": $(initXrayClients 5), + "decryption": "none" + }, + "streamSettings": { + "network": "grpc", + "grpcSettings": { + "serviceName": "${customPath}grpc" + } + } + } + ] +} +EOF + elif [[ -z "$3" ]]; then + rm /etc/v2ray-agent/xray/conf/06_VLESS_gRPC_inbounds.json >/dev/null 2>&1 + fi + + # VLESS Vision + if echo "${selectCustomInstallType}" | grep -q ",0," || [[ "$1" == "all" ]]; then + + cat </etc/v2ray-agent/xray/conf/02_VLESS_TCP_inbounds.json +{ + "inbounds":[ + { + "port": ${port}, + "protocol": "vless", + "tag":"VLESSTCP", + "settings": { + "clients":$(initXrayClients 0), + "decryption": "none", + "fallbacks": [ + ${fallbacksList} + ] + }, + "add": "${add}", + "streamSettings": { + "network": "tcp", + "security": "tls", + "tlsSettings": { + "rejectUnknownSni": true, + "minVersion": "1.2", + "certificates": [ + { + "certificateFile": "/etc/v2ray-agent/tls/${domain}.crt", + "keyFile": "/etc/v2ray-agent/tls/${domain}.key", + "ocspStapling": 3600 + } + ] + } + } + } + ] +} +EOF + elif [[ -z "$3" ]]; then + rm /etc/v2ray-agent/xray/conf/02_VLESS_TCP_inbounds.json >/dev/null 2>&1 + fi + + # VLESS_TCP/reality + if echo "${selectCustomInstallType}" | grep -q ",7," || [[ "$1" == "all" ]]; then + echoContent skyBlue "\n===================== 配置VLESS+Reality =====================\n" + + initXrayRealityPort + initRealityClientServersName + initRealityKey + initRealityMldsa65 + + cat </etc/v2ray-agent/xray/conf/07_VLESS_vision_reality_inbounds.json +{ + "inbounds": [ + { + "port": ${realityPort}, + "protocol": "vless", + "tag": "VLESSReality", + "settings": { + "clients": $(initXrayClients 7), + "decryption": "none", + "fallbacks":[ + { + "dest": "31305", + "xver": 1 + } + ] + }, + "streamSettings": { + "network": "tcp", + "security": "reality", + "realitySettings": { + "show": false, + "dest": "${realityServerName}:${realityDomainPort}", + "xver": 0, + "serverNames": [ + "${realityServerName}" + ], + "privateKey": "${realityPrivateKey}", + "publicKey": "${realityPublicKey}", + "mldsa65Seed": "${realityMldsa65Seed}", + "mldsa65Verify": "${realityMldsa65Verify}", + "maxTimeDiff": 70000, + "shortIds": [ + "", + "6ba85179e30d4fc2" + ] + } + } + } + ] +} +EOF + + cat </etc/v2ray-agent/xray/conf/08_VLESS_vision_gRPC_inbounds.json +{ + "inbounds": [ + { + "port": 31305, + "listen": "127.0.0.1", + "protocol": "vless", + "tag": "VLESSRealityGRPC", + "settings": { + "clients": $(initXrayClients 8), + "decryption": "none" + }, + "streamSettings": { + "network": "grpc", + "grpcSettings": { + "serviceName": "grpc", + "multiMode": true + }, + "sockopt": { + "acceptProxyProtocol": true + } + } + } + ] +} +EOF + + elif [[ -z "$3" ]]; then + rm /etc/v2ray-agent/xray/conf/07_VLESS_vision_reality_inbounds.json >/dev/null 2>&1 + rm /etc/v2ray-agent/xray/conf/08_VLESS_vision_gRPC_inbounds.json >/dev/null 2>&1 + fi + installSniffing + if [[ -z "$3" ]]; then + removeXrayOutbound wireguard_out_IPv4_route + removeXrayOutbound wireguard_out_IPv6_route + removeXrayOutbound wireguard_outbound + removeXrayOutbound IPv4_out + removeXrayOutbound IPv6_out + removeXrayOutbound socks5_outbound + removeXrayOutbound blackhole_out + removeXrayOutbound wireguard_out_IPv6 + removeXrayOutbound wireguard_out_IPv4 + addXrayOutbound z_direct_outbound + fi +} + +# 初始化TCP Brutal +initTCPBrutal() { + echoContent skyBlue "\n进度 $2/${totalProgress} : 初始化TCP_Brutal配置" + read -r -p "是否使用TCP_Brutal?[y/n]:" tcpBrutalStatus + if [[ "${tcpBrutalStatus}" == "y" ]]; then + read -r -p "请输入本地带宽峰值的下行速度(默认:100,单位:Mbps):" tcpBrutalClientDownloadSpeed + if [[ -z "${tcpBrutalClientDownloadSpeed}" ]]; then + tcpBrutalClientDownloadSpeed=100 + fi + + read -r -p "请输入本地带宽峰值的上行速度(默认:50,单位:Mbps):" tcpBrutalClientUploadSpeed + if [[ -z "${tcpBrutalClientUploadSpeed}" ]]; then + tcpBrutalClientUploadSpeed=50 + fi + fi +} +# 初始化sing-box配置文件 +initSingBoxConfig() { + echoContent skyBlue "\n进度 $2/${totalProgress} : 初始化sing-box配置" + + echo + local uuid= + local addClientsStatus= + local sslDomain= + if [[ -n "${domain}" ]]; then + sslDomain="${domain}" + elif [[ -n "${currentHost}" ]]; then + sslDomain="${currentHost}" + fi + if [[ -n "${currentUUID}" && -z "${lastInstallationConfig}" ]]; then + read -r -p "读取到上次用户配置,是否使用上次安装的配置 ?[y/n]:" historyUUIDStatus + if [[ "${historyUUIDStatus}" == "y" ]]; then + addClientsStatus=true + echoContent green "\n ---> 使用成功" + fi + elif [[ -n "${currentUUID}" && -n "${lastInstallationConfig}" ]]; then + addClientsStatus=true + fi + + if [[ -z "${addClientsStatus}" ]]; then + echoContent yellow "请输入自定义UUID[需合法],[回车]随机UUID" + read -r -p 'UUID:' customUUID + + if [[ -n ${customUUID} ]]; then + uuid=${customUUID} + else + uuid=$(/etc/v2ray-agent/sing-box/sing-box generate uuid) + fi + + echoContent yellow "\n请输入自定义用户名[需合法],[回车]随机用户名" + read -r -p '用户名:' customEmail + if [[ -z ${customEmail} ]]; then + customEmail="$(echo "${uuid}" | cut -d "-" -f 1)-VLESS_TCP/TLS_Vision" + fi + fi + + if [[ -z "${addClientsStatus}" && -z "${uuid}" ]]; then + addClientsStatus= + echoContent red "\n ---> uuid读取错误,随机生成" + uuid=$(/etc/v2ray-agent/sing-box/sing-box generate uuid) + fi + + if [[ -n "${uuid}" ]]; then + currentClients='[{"uuid":"'${uuid}'","flow":"xtls-rprx-vision","name":"'${customEmail}'"}]' + echoContent yellow "\n ${customEmail}:${uuid}" + fi + + # VLESS Vision + if echo "${selectCustomInstallType}" | grep -q ",0," || [[ "$1" == "all" ]]; then + echoContent yellow "\n===================== 配置VLESS+Vision =====================\n" + echoContent skyBlue "\n开始配置VLESS+Vision协议端口" + echo + mapfile -t result < <(initSingBoxPort "${singBoxVLESSVisionPort}") + echoContent green "\n ---> VLESS_Vision端口:${result[-1]}" + + checkDNSIP "${domain}" + removeNginxDefaultConf + handleSingBox stop + + checkPortOpen "${result[-1]}" "${domain}" + cat </etc/v2ray-agent/sing-box/conf/config/02_VLESS_TCP_inbounds.json +{ + "inbounds":[ + { + "type": "vless", + "listen":"::", + "listen_port":${result[-1]}, + "tag":"VLESSTCP", + "users":$(initSingBoxClients 0), + "tls":{ + "server_name": "${sslDomain}", + "enabled": true, + "certificate_path": "/etc/v2ray-agent/tls/${sslDomain}.crt", + "key_path": "/etc/v2ray-agent/tls/${sslDomain}.key" + } + } + ] +} +EOF + elif [[ -z "$3" ]]; then + rm /etc/v2ray-agent/sing-box/conf/config/02_VLESS_TCP_inbounds.json >/dev/null 2>&1 + fi + + if echo "${selectCustomInstallType}" | grep -q ",1," || [[ "$1" == "all" ]]; then + echoContent yellow "\n===================== 配置VLESS+WS =====================\n" + echoContent skyBlue "\n开始配置VLESS+WS协议端口" + echo + mapfile -t result < <(initSingBoxPort "${singBoxVLESSWSPort}") + echoContent green "\n ---> VLESS_WS端口:${result[-1]}" + + checkDNSIP "${domain}" + removeNginxDefaultConf + handleSingBox stop + randomPathFunction + checkPortOpen "${result[-1]}" "${domain}" + cat </etc/v2ray-agent/sing-box/conf/config/03_VLESS_WS_inbounds.json +{ + "inbounds":[ + { + "type": "vless", + "listen":"::", + "listen_port":${result[-1]}, + "tag":"VLESSWS", + "users":$(initSingBoxClients 1), + "tls":{ + "server_name": "${sslDomain}", + "enabled": true, + "certificate_path": "/etc/v2ray-agent/tls/${sslDomain}.crt", + "key_path": "/etc/v2ray-agent/tls/${sslDomain}.key" + }, + "transport": { + "type": "ws", + "path": "/${currentPath}ws", + "max_early_data": 2048, + "early_data_header_name": "Sec-WebSocket-Protocol" + } + } + ] +} +EOF + elif [[ -z "$3" ]]; then + rm /etc/v2ray-agent/sing-box/conf/config/03_VLESS_WS_inbounds.json >/dev/null 2>&1 + fi + + if echo "${selectCustomInstallType}" | grep -q ",3," || [[ "$1" == "all" ]]; then + echoContent yellow "\n===================== 配置VMess+ws =====================\n" + echoContent skyBlue "\n开始配置VMess+ws协议端口" + echo + mapfile -t result < <(initSingBoxPort "${singBoxVMessWSPort}") + echoContent green "\n ---> VMess_ws端口:${result[-1]}" + + checkDNSIP "${domain}" + removeNginxDefaultConf + handleSingBox stop + randomPathFunction + checkPortOpen "${result[-1]}" "${domain}" + cat </etc/v2ray-agent/sing-box/conf/config/05_VMess_WS_inbounds.json +{ + "inbounds":[ + { + "type": "vmess", + "listen":"::", + "listen_port":${result[-1]}, + "tag":"VMessWS", + "users":$(initSingBoxClients 3), + "tls":{ + "server_name": "${sslDomain}", + "enabled": true, + "certificate_path": "/etc/v2ray-agent/tls/${sslDomain}.crt", + "key_path": "/etc/v2ray-agent/tls/${sslDomain}.key" + }, + "transport": { + "type": "ws", + "path": "/${currentPath}", + "max_early_data": 2048, + "early_data_header_name": "Sec-WebSocket-Protocol" + } + } + ] +} +EOF + elif [[ -z "$3" ]]; then + rm /etc/v2ray-agent/sing-box/conf/config/05_VMess_WS_inbounds.json >/dev/null 2>&1 + fi + + # VLESS_Reality_Vision + if echo "${selectCustomInstallType}" | grep -q ",7," || [[ "$1" == "all" ]]; then + echoContent yellow "\n================= 配置VLESS+Reality+Vision =================\n" + initRealityClientServersName + initRealityKey + echoContent skyBlue "\n开始配置VLESS+Reality+Vision协议端口" + echo + mapfile -t result < <(initSingBoxPort "${singBoxVLESSRealityVisionPort}") + echoContent green "\n ---> VLESS_Reality_Vision端口:${result[-1]}" + cat </etc/v2ray-agent/sing-box/conf/config/07_VLESS_vision_reality_inbounds.json +{ + "inbounds": [ + { + "type": "vless", + "listen":"::", + "listen_port":${result[-1]}, + "tag": "VLESSReality", + "users":$(initSingBoxClients 7), + "tls": { + "enabled": true, + "server_name": "${realityServerName}", + "reality": { + "enabled": true, + "handshake":{ + "server": "${realityServerName}", + "server_port":${realityDomainPort} + }, + "private_key": "${realityPrivateKey}", + "short_id": [ + "", + "6ba85179e30d4fc2" + ] + } + } + } + ] +} +EOF + elif [[ -z "$3" ]]; then + rm /etc/v2ray-agent/sing-box/conf/config/07_VLESS_vision_reality_inbounds.json >/dev/null 2>&1 + fi + + if echo "${selectCustomInstallType}" | grep -q ",8," || [[ "$1" == "all" ]]; then + echoContent yellow "\n================== 配置VLESS+Reality+gRPC ==================\n" + initRealityClientServersName + initRealityKey + echoContent skyBlue "\n开始配置VLESS+Reality+gRPC协议端口" + echo + mapfile -t result < <(initSingBoxPort "${singBoxVLESSRealityGRPCPort}") + echoContent green "\n ---> VLESS_Reality_gPRC端口:${result[-1]}" + cat </etc/v2ray-agent/sing-box/conf/config/08_VLESS_vision_gRPC_inbounds.json +{ + "inbounds": [ + { + "type": "vless", + "listen":"::", + "listen_port":${result[-1]}, + "users":$(initSingBoxClients 8), + "tag": "VLESSRealityGRPC", + "tls": { + "enabled": true, + "server_name": "${realityServerName}", + "reality": { + "enabled": true, + "handshake":{ + "server":"${realityServerName}", + "server_port":${realityDomainPort} + }, + "private_key": "${realityPrivateKey}", + "short_id": [ + "", + "6ba85179e30d4fc2" + ] + } + }, + "transport": { + "type": "grpc", + "service_name": "grpc" + } + } + ] +} +EOF + elif [[ -z "$3" ]]; then + rm /etc/v2ray-agent/sing-box/conf/config/08_VLESS_vision_gRPC_inbounds.json >/dev/null 2>&1 + fi + + if echo "${selectCustomInstallType}" | grep -q ",6," || [[ "$1" == "all" ]]; then + echoContent yellow "\n================== 配置 Hysteria2 ==================\n" + echoContent skyBlue "\n开始配置Hysteria2协议端口" + echo + mapfile -t result < <(initSingBoxPort "${singBoxHysteria2Port}") + echoContent green "\n ---> Hysteria2端口:${result[-1]}" + initHysteria2Network + cat </etc/v2ray-agent/sing-box/conf/config/06_hysteria2_inbounds.json +{ + "inbounds": [ + { + "type": "hysteria2", + "listen": "::", + "listen_port": ${result[-1]}, + "users": $(initSingBoxClients 6), + "up_mbps":${hysteria2ClientDownloadSpeed}, + "down_mbps":${hysteria2ClientUploadSpeed}, + "tls": { + "enabled": true, + "server_name":"${sslDomain}", + "alpn": [ + "h3" + ], + "certificate_path": "/etc/v2ray-agent/tls/${sslDomain}.crt", + "key_path": "/etc/v2ray-agent/tls/${sslDomain}.key" + } + } + ] +} +EOF + elif [[ -z "$3" ]]; then + rm /etc/v2ray-agent/sing-box/conf/config/06_hysteria2_inbounds.json >/dev/null 2>&1 + fi + + if echo "${selectCustomInstallType}" | grep -q ",4," || [[ "$1" == "all" ]]; then + echoContent yellow "\n================== 配置 Trojan ==================\n" + echoContent skyBlue "\n开始配置Trojan协议端口" + echo + mapfile -t result < <(initSingBoxPort "${singBoxTrojanPort}") + echoContent green "\n ---> Trojan端口:${result[-1]}" + cat </etc/v2ray-agent/sing-box/conf/config/04_trojan_TCP_inbounds.json +{ + "inbounds": [ + { + "type": "trojan", + "listen": "::", + "listen_port": ${result[-1]}, + "users": $(initSingBoxClients 4), + "tls": { + "enabled": true, + "server_name":"${sslDomain}", + "certificate_path": "/etc/v2ray-agent/tls/${sslDomain}.crt", + "key_path": "/etc/v2ray-agent/tls/${sslDomain}.key" + } + } + ] +} +EOF + elif [[ -z "$3" ]]; then + rm /etc/v2ray-agent/sing-box/conf/config/04_trojan_TCP_inbounds.json >/dev/null 2>&1 + fi + + if echo "${selectCustomInstallType}" | grep -q ",9," || [[ "$1" == "all" ]]; then + echoContent yellow "\n==================== 配置 Tuic =====================\n" + echoContent skyBlue "\n开始配置Tuic协议端口" + echo + mapfile -t result < <(initSingBoxPort "${singBoxTuicPort}") + echoContent green "\n ---> Tuic端口:${result[-1]}" + initTuicProtocol + cat </etc/v2ray-agent/sing-box/conf/config/09_tuic_inbounds.json +{ + "inbounds": [ + { + "type": "tuic", + "listen": "::", + "tag": "singbox-tuic-in", + "listen_port": ${result[-1]}, + "users": $(initSingBoxClients 9), + "congestion_control": "${tuicAlgorithm}", + "tls": { + "enabled": true, + "server_name":"${sslDomain}", + "alpn": [ + "h3" + ], + "certificate_path": "/etc/v2ray-agent/tls/${sslDomain}.crt", + "key_path": "/etc/v2ray-agent/tls/${sslDomain}.key" + } + } + ] +} +EOF + elif [[ -z "$3" ]]; then + rm /etc/v2ray-agent/sing-box/conf/config/09_tuic_inbounds.json >/dev/null 2>&1 + fi + + if echo "${selectCustomInstallType}" | grep -q ",10," || [[ "$1" == "all" ]]; then + echoContent yellow "\n==================== 配置 Naive =====================\n" + echoContent skyBlue "\n开始配置Naive协议端口" + echo + mapfile -t result < <(initSingBoxPort "${singBoxNaivePort}") + echoContent green "\n ---> Naive端口:${result[-1]}" + cat </etc/v2ray-agent/sing-box/conf/config/10_naive_inbounds.json +{ + "inbounds": [ + { + "type": "naive", + "listen": "::", + "tag": "singbox-naive-in", + "listen_port": ${result[-1]}, + "users": $(initSingBoxClients 10), + "tls": { + "enabled": true, + "server_name":"${sslDomain}", + "certificate_path": "/etc/v2ray-agent/tls/${sslDomain}.crt", + "key_path": "/etc/v2ray-agent/tls/${sslDomain}.key" + } + } + ] +} +EOF + elif [[ -z "$3" ]]; then + rm /etc/v2ray-agent/sing-box/conf/config/10_naive_inbounds.json >/dev/null 2>&1 + fi + if echo "${selectCustomInstallType}" | grep -q ",11," || [[ "$1" == "all" ]]; then + echoContent yellow "\n===================== 配置VMess+HTTPUpgrade =====================\n" + echoContent skyBlue "\n开始配置VMess+HTTPUpgrade协议端口" + echo + mapfile -t result < <(initSingBoxPort "${singBoxVMessHTTPUpgradePort}") + echoContent green "\n ---> VMess_HTTPUpgrade端口:${result[-1]}" + + checkDNSIP "${domain}" + removeNginxDefaultConf + handleSingBox stop + randomPathFunction + rm -rf "${nginxConfigPath}sing_box_VMess_HTTPUpgrade.conf" >/dev/null 2>&1 + checkPortOpen "${result[-1]}" "${domain}" + singBoxNginxConfig "$1" "${result[-1]}" + bootStartup nginx + cat </etc/v2ray-agent/sing-box/conf/config/11_VMess_HTTPUpgrade_inbounds.json +{ + "inbounds":[ + { + "type": "vmess", + "listen":"127.0.0.1", + "listen_port":31306, + "tag":"VMessHTTPUpgrade", + "users":$(initSingBoxClients 11), + "transport": { + "type": "httpupgrade", + "path": "/${currentPath}" + } + } + ] +} +EOF + elif [[ -z "$3" ]]; then + rm /etc/v2ray-agent/sing-box/conf/config/11_VMess_HTTPUpgrade_inbounds.json >/dev/null 2>&1 + fi + + if echo "${selectCustomInstallType}" | grep -q ",13," || [[ "$1" == "all" ]]; then + echoContent yellow "\n================== 配置 AnyTLS ==================\n" + echoContent skyBlue "\n开始配置AnyTLS协议端口" + echo + mapfile -t result < <(initSingBoxPort "${singBoxAnyTLSPort}") + echoContent green "\n ---> AnyTLS端口:${result[-1]}" + cat </etc/v2ray-agent/sing-box/conf/config/13_anytls_inbounds.json +{ + "inbounds": [ + { + "type": "anytls", + "listen": "::", + "tag":"anytls", + "listen_port": ${result[-1]}, + "users": $(initSingBoxClients 13), + "tls": { + "enabled": true, + "server_name":"${sslDomain}", + "certificate_path": "/etc/v2ray-agent/tls/${sslDomain}.crt", + "key_path": "/etc/v2ray-agent/tls/${sslDomain}.key" + } + } + ] +} +EOF + elif [[ -z "$3" ]]; then + rm /etc/v2ray-agent/sing-box/conf/config/13_anytls_inbounds.json >/dev/null 2>&1 + fi + + if [[ -z "$3" ]]; then + removeSingBoxConfig wireguard_endpoints_IPv4_route + removeSingBoxConfig wireguard_endpoints_IPv6_route + removeSingBoxConfig wireguard_endpoints_IPv4 + removeSingBoxConfig wireguard_endpoints_IPv6 + + removeSingBoxConfig IPv4_out + removeSingBoxConfig IPv6_out + removeSingBoxConfig IPv6_route + removeSingBoxConfig block + removeSingBoxConfig cn_block_outbound + removeSingBoxConfig cn_block_route + removeSingBoxConfig 01_direct_outbound + removeSingBoxConfig block_domain_outbound + removeSingBoxConfig dns + fi +} +# 初始化 sing-box订阅配置 +initSubscribeLocalConfig() { + rm -rf /etc/v2ray-agent/subscribe_local/sing-box/* +} +# 通用 +defaultBase64Code() { + local type=$1 + local port=$2 + local email=$3 + local id=$4 + local add=$5 + local path=$6 + local user= + user=$(echo "${email}" | awk -F "[-]" '{print $1}') + if [[ ! -f "/etc/v2ray-agent/subscribe_local/sing-box/${user}" ]]; then + echo [] >"/etc/v2ray-agent/subscribe_local/sing-box/${user}" + fi + local singBoxSubscribeLocalConfig= + if [[ "${type}" == "vlesstcp" ]]; then + + echoContent yellow " ---> 通用格式(VLESS+TCP+TLS_Vision)" + echoContent green " vless://${id}@${currentHost}:${port}?encryption=none&security=tls&fp=chrome&type=tcp&host=${currentHost}&headerType=none&sni=${currentHost}&flow=xtls-rprx-vision#${email}\n" + + echoContent yellow " ---> 格式化明文(VLESS+TCP+TLS_Vision)" + echoContent green "协议类型:VLESS,地址:${currentHost},端口:${port},用户ID:${id},安全:tls,client-fingerprint: chrome,传输方式:tcp,flow:xtls-rprx-vision,账户名:${email}\n" + cat <>"/etc/v2ray-agent/subscribe_local/default/${user}" +vless://${id}@${currentHost}:${port}?encryption=none&security=tls&type=tcp&host=${currentHost}&fp=chrome&headerType=none&sni=${currentHost}&flow=xtls-rprx-vision#${email} +EOF + cat <>"/etc/v2ray-agent/subscribe_local/clashMeta/${user}" + - name: "${email}" + type: vless + server: ${currentHost} + port: ${port} + uuid: ${id} + network: tcp + tls: true + udp: true + flow: xtls-rprx-vision + client-fingerprint: chrome +EOF + singBoxSubscribeLocalConfig=$(jq -r ". += [{\"tag\":\"${email}\",\"type\":\"vless\",\"server\":\"${currentHost}\",\"server_port\":${port},\"uuid\":\"${id}\",\"flow\":\"xtls-rprx-vision\",\"tls\":{\"enabled\":true,\"server_name\":\"${currentHost}\",\"utls\":{\"enabled\":true,\"fingerprint\":\"chrome\"}},\"packet_encoding\":\"xudp\"}]" "/etc/v2ray-agent/subscribe_local/sing-box/${user}") + echo "${singBoxSubscribeLocalConfig}" | jq . >"/etc/v2ray-agent/subscribe_local/sing-box/${user}" + + echoContent yellow " ---> 二维码 VLESS(VLESS+TCP+TLS_Vision)" + echoContent green " https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=vless%3A%2F%2F${id}%40${currentHost}%3A${port}%3Fencryption%3Dnone%26fp%3Dchrome%26security%3Dtls%26type%3Dtcp%26${currentHost}%3D${currentHost}%26headerType%3Dnone%26sni%3D${currentHost}%26flow%3Dxtls-rprx-vision%23${email}\n" + + elif [[ "${type}" == "vmessws" ]]; then + qrCodeBase64Default=$(echo -n "{\"port\":${port},\"ps\":\"${email}\",\"tls\":\"tls\",\"id\":\"${id}\",\"aid\":0,\"v\":2,\"host\":\"${currentHost}\",\"type\":\"none\",\"path\":\"${path}\",\"net\":\"ws\",\"add\":\"${add}\",\"allowInsecure\":0,\"method\":\"none\",\"peer\":\"${currentHost}\",\"sni\":\"${currentHost}\"}" | base64 -w 0) + qrCodeBase64Default="${qrCodeBase64Default// /}" + + echoContent yellow " ---> 通用json(VMess+WS+TLS)" + echoContent green " {\"port\":${port},\"ps\":\"${email}\",\"tls\":\"tls\",\"id\":\"${id}\",\"aid\":0,\"v\":2,\"host\":\"${currentHost}\",\"type\":\"none\",\"path\":\"${path}\",\"net\":\"ws\",\"add\":\"${add}\",\"allowInsecure\":0,\"method\":\"none\",\"peer\":\"${currentHost}\",\"sni\":\"${currentHost}\"}\n" + echoContent yellow " ---> 通用vmess(VMess+WS+TLS)链接" + echoContent green " vmess://${qrCodeBase64Default}\n" + echoContent yellow " ---> 二维码 vmess(VMess+WS+TLS)" + + cat <>"/etc/v2ray-agent/subscribe_local/default/${user}" +vmess://${qrCodeBase64Default} +EOF + cat <>"/etc/v2ray-agent/subscribe_local/clashMeta/${user}" + - name: "${email}" + type: vmess + server: ${add} + port: ${port} + uuid: ${id} + alterId: 0 + cipher: none + udp: true + tls: true + client-fingerprint: chrome + servername: ${currentHost} + network: ws + ws-opts: + path: ${path} + headers: + Host: ${currentHost} +EOF + singBoxSubscribeLocalConfig=$(jq -r ". += [{\"tag\":\"${email}\",\"type\":\"vmess\",\"server\":\"${add}\",\"server_port\":${port},\"uuid\":\"${id}\",\"alter_id\":0,\"tls\":{\"enabled\":true,\"server_name\":\"${currentHost}\",\"utls\":{\"enabled\":true,\"fingerprint\":\"chrome\"}},\"packet_encoding\":\"packetaddr\",\"transport\":{\"type\":\"ws\",\"path\":\"${path}\",\"max_early_data\":2048,\"early_data_header_name\":\"Sec-WebSocket-Protocol\"}}]" "/etc/v2ray-agent/subscribe_local/sing-box/${user}") + + echo "${singBoxSubscribeLocalConfig}" | jq . >"/etc/v2ray-agent/subscribe_local/sing-box/${user}" + + echoContent green " https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=vmess://${qrCodeBase64Default}\n" + + elif [[ "${type}" == "vlessws" ]]; then + + echoContent yellow " ---> 通用格式(VLESS+WS+TLS)" + echoContent green " vless://${id}@${add}:${port}?encryption=none&security=tls&type=ws&host=${currentHost}&sni=${currentHost}&fp=chrome&path=${path}#${email}\n" + + echoContent yellow " ---> 格式化明文(VLESS+WS+TLS)" + echoContent green " 协议类型:VLESS,地址:${add},伪装域名/SNI:${currentHost},端口:${port},client-fingerprint: chrome,用户ID:${id},安全:tls,传输方式:ws,路径:${path},账户名:${email}\n" + + cat <>"/etc/v2ray-agent/subscribe_local/default/${user}" +vless://${id}@${add}:${port}?encryption=none&security=tls&type=ws&host=${currentHost}&sni=${currentHost}&fp=chrome&path=${path}#${email} +EOF + cat <>"/etc/v2ray-agent/subscribe_local/clashMeta/${user}" + - name: "${email}" + type: vless + server: ${add} + port: ${port} + uuid: ${id} + udp: true + tls: true + network: ws + client-fingerprint: chrome + servername: ${currentHost} + ws-opts: + path: ${path} + headers: + Host: ${currentHost} +EOF + + singBoxSubscribeLocalConfig=$(jq -r ". += [{\"tag\":\"${email}\",\"type\":\"vless\",\"server\":\"${add}\",\"server_port\":${port},\"uuid\":\"${id}\",\"tls\":{\"enabled\":true,\"server_name\":\"${currentHost}\",\"utls\":{\"enabled\":true,\"fingerprint\":\"chrome\"}},\"multiplex\":{\"enabled\":false,\"protocol\":\"smux\",\"max_streams\":32},\"packet_encoding\":\"xudp\",\"transport\":{\"type\":\"ws\",\"path\":\"${path}\",\"headers\":{\"Host\":\"${currentHost}\"}}}]" "/etc/v2ray-agent/subscribe_local/sing-box/${user}") + echo "${singBoxSubscribeLocalConfig}" | jq . >"/etc/v2ray-agent/subscribe_local/sing-box/${user}" + + echoContent yellow " ---> 二维码 VLESS(VLESS+WS+TLS)" + echoContent green " https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=vless%3A%2F%2F${id}%40${add}%3A${port}%3Fencryption%3Dnone%26security%3Dtls%26type%3Dws%26host%3D${currentHost}%26fp%3Dchrome%26sni%3D${currentHost}%26path%3D${path}%23${email}" + + elif [[ "${type}" == "vlessXHTTP" ]]; then + + echoContent yellow " ---> 通用格式(VLESS+reality+XHTTP)" + echoContent green " vless://${id}@$(getPublicIP):${port}?encryption=none&security=reality&type=xhttp&sni=${xrayVLESSRealityXHTTPServerName}&host=${xrayVLESSRealityXHTTPServerName}&fp=chrome&path=${path}&pbk=${currentRealityXHTTPPublicKey}&sid=6ba85179e30d4fc2#${email}\n" + + echoContent yellow " ---> 格式化明文(VLESS+reality+XHTTP)" + echoContent green "协议类型:VLESS reality,地址:$(getPublicIP),publicKey:${currentRealityXHTTPPublicKey},shortId: 6ba85179e30d4fc2,serverNames:${xrayVLESSRealityXHTTPServerName},端口:${port},路径:${path},SNI:${xrayVLESSRealityXHTTPServerName},伪装域名:${xrayVLESSRealityXHTTPServerName},用户ID:${id},传输方式:xhttp,账户名:${email}\n" + cat <>"/etc/v2ray-agent/subscribe_local/default/${user}" +vless://${id}@$(getPublicIP):${port}?encryption=none&security=reality&type=xhttp&sni=${xrayVLESSRealityXHTTPServerName}&fp=chrome&path=${path}&pbk=${currentRealityXHTTPPublicKey}&sid=6ba85179e30d4fc2#${email} +EOF + echoContent yellow " ---> 二维码 VLESS(VLESS+reality+XHTTP)" + echoContent green " https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=vless%3A%2F%2F${id}%40$(getPublicIP)%3A${port}%3Fencryption%3Dnone%26security%3Dreality%26type%3Dtcp%26sni%3D${xrayVLESSRealityXHTTPServerName}%26fp%3Dchrome%26path%3D${path}%26host%3D${xrayVLESSRealityXHTTPServerName}%26pbk%3D${currentRealityXHTTPPublicKey}%26sid%3D6ba85179e30d4fc2%23${email}\n" + + elif + [[ "${type}" == "vlessgrpc" ]] + then + + echoContent yellow " ---> 通用格式(VLESS+gRPC+TLS)" + echoContent green " vless://${id}@${add}:${port}?encryption=none&security=tls&type=grpc&host=${currentHost}&path=${currentPath}grpc&fp=chrome&serviceName=${currentPath}grpc&alpn=h2&sni=${currentHost}#${email}\n" + + echoContent yellow " ---> 格式化明文(VLESS+gRPC+TLS)" + echoContent green " 协议类型:VLESS,地址:${add},伪装域名/SNI:${currentHost},端口:${port},用户ID:${id},安全:tls,传输方式:gRPC,alpn:h2,client-fingerprint: chrome,serviceName:${currentPath}grpc,账户名:${email}\n" + + cat <>"/etc/v2ray-agent/subscribe_local/default/${user}" +vless://${id}@${add}:${port}?encryption=none&security=tls&type=grpc&host=${currentHost}&path=${currentPath}grpc&serviceName=${currentPath}grpc&fp=chrome&alpn=h2&sni=${currentHost}#${email} +EOF + cat <>"/etc/v2ray-agent/subscribe_local/clashMeta/${user}" + - name: "${email}" + type: vless + server: ${add} + port: ${port} + uuid: ${id} + udp: true + tls: true + network: grpc + client-fingerprint: chrome + servername: ${currentHost} + grpc-opts: + grpc-service-name: ${currentPath}grpc +EOF + + singBoxSubscribeLocalConfig=$(jq -r ". += [{\"tag\":\"${email}\",\"type\": \"vless\",\"server\": \"${add}\",\"server_port\": ${port},\"uuid\": \"${id}\",\"tls\": { \"enabled\": true, \"server_name\": \"${currentHost}\", \"utls\": { \"enabled\": true, \"fingerprint\": \"chrome\" }},\"packet_encoding\": \"xudp\",\"transport\": { \"type\": \"grpc\", \"service_name\": \"${currentPath}grpc\"}}]" "/etc/v2ray-agent/subscribe_local/sing-box/${user}") + echo "${singBoxSubscribeLocalConfig}" | jq . >"/etc/v2ray-agent/subscribe_local/sing-box/${user}" + + echoContent yellow " ---> 二维码 VLESS(VLESS+gRPC+TLS)" + echoContent green " https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=vless%3A%2F%2F${id}%40${add}%3A${port}%3Fencryption%3Dnone%26security%3Dtls%26type%3Dgrpc%26host%3D${currentHost}%26serviceName%3D${currentPath}grpc%26fp%3Dchrome%26path%3D${currentPath}grpc%26sni%3D${currentHost}%26alpn%3Dh2%23${email}" + + elif [[ "${type}" == "trojan" ]]; then + # URLEncode + echoContent yellow " ---> Trojan(TLS)" + echoContent green " trojan://${id}@${currentHost}:${port}?peer=${currentHost}&fp=chrome&sni=${currentHost}&alpn=http/1.1#${currentHost}_Trojan\n" + + cat <>"/etc/v2ray-agent/subscribe_local/default/${user}" +trojan://${id}@${currentHost}:${port}?peer=${currentHost}&fp=chrome&sni=${currentHost}&alpn=http/1.1#${email}_Trojan +EOF + + cat <>"/etc/v2ray-agent/subscribe_local/clashMeta/${user}" + - name: "${email}" + type: trojan + server: ${currentHost} + port: ${port} + password: ${id} + client-fingerprint: chrome + udp: true + sni: ${currentHost} +EOF + singBoxSubscribeLocalConfig=$(jq -r ". += [{\"tag\":\"${email}\",\"type\":\"trojan\",\"server\":\"${currentHost}\",\"server_port\":${port},\"password\":\"${id}\",\"tls\":{\"alpn\":[\"http/1.1\"],\"enabled\":true,\"server_name\":\"${currentHost}\",\"utls\":{\"enabled\":true,\"fingerprint\":\"chrome\"}}}]" "/etc/v2ray-agent/subscribe_local/sing-box/${user}") + echo "${singBoxSubscribeLocalConfig}" | jq . >"/etc/v2ray-agent/subscribe_local/sing-box/${user}" + + echoContent yellow " ---> 二维码 Trojan(TLS)" + echoContent green " https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=trojan%3a%2f%2f${id}%40${currentHost}%3a${port}%3fpeer%3d${currentHost}%26fp%3Dchrome%26sni%3d${currentHost}%26alpn%3Dhttp/1.1%23${email}\n" + + elif [[ "${type}" == "trojangrpc" ]]; then + # URLEncode + + echoContent yellow " ---> Trojan gRPC(TLS)" + echoContent green " trojan://${id}@${add}:${port}?encryption=none&peer=${currentHost}&fp=chrome&security=tls&type=grpc&sni=${currentHost}&alpn=h2&path=${currentPath}trojangrpc&serviceName=${currentPath}trojangrpc#${email}\n" + cat <>"/etc/v2ray-agent/subscribe_local/default/${user}" +trojan://${id}@${add}:${port}?encryption=none&peer=${currentHost}&security=tls&type=grpc&fp=chrome&sni=${currentHost}&alpn=h2&path=${currentPath}trojangrpc&serviceName=${currentPath}trojangrpc#${email} +EOF + cat <>"/etc/v2ray-agent/subscribe_local/clashMeta/${user}" + - name: "${email}" + server: ${add} + port: ${port} + type: trojan + password: ${id} + network: grpc + sni: ${currentHost} + udp: true + grpc-opts: + grpc-service-name: ${currentPath}trojangrpc +EOF + + singBoxSubscribeLocalConfig=$(jq -r ". += [{\"tag\":\"${email}\",\"type\":\"trojan\",\"server\":\"${add}\",\"server_port\":${port},\"password\":\"${id}\",\"tls\":{\"enabled\":true,\"server_name\":\"${currentHost}\",\"insecure\":true,\"utls\":{\"enabled\":true,\"fingerprint\":\"chrome\"}},\"transport\":{\"type\":\"grpc\",\"service_name\":\"${currentPath}trojangrpc\",\"idle_timeout\":\"15s\",\"ping_timeout\":\"15s\",\"permit_without_stream\":false},\"multiplex\":{\"enabled\":false,\"protocol\":\"smux\",\"max_streams\":32}}]" "/etc/v2ray-agent/subscribe_local/sing-box/${user}") + echo "${singBoxSubscribeLocalConfig}" | jq . >"/etc/v2ray-agent/subscribe_local/sing-box/${user}" + + echoContent yellow " ---> 二维码 Trojan gRPC(TLS)" + echoContent green " https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=trojan%3a%2f%2f${id}%40${add}%3a${port}%3Fencryption%3Dnone%26fp%3Dchrome%26security%3Dtls%26peer%3d${currentHost}%26type%3Dgrpc%26sni%3d${currentHost}%26path%3D${currentPath}trojangrpc%26alpn%3Dh2%26serviceName%3D${currentPath}trojangrpc%23${email}\n" + + elif [[ "${type}" == "hysteria" ]]; then + echoContent yellow " ---> Hysteria(TLS)" + local clashMetaPortContent="port: ${port}" + local multiPort= + local multiPortEncode + if echo "${port}" | grep -q "-"; then + clashMetaPortContent="ports: ${port}" + multiPort="mport=${port}&" + multiPortEncode="mport%3D${port}%26" + fi + + echoContent green " hysteria2://${id}@${currentHost}:${singBoxHysteria2Port}?${multiPort}peer=${currentHost}&insecure=0&sni=${currentHost}&alpn=h3#${email}\n" + cat <>"/etc/v2ray-agent/subscribe_local/default/${user}" +hysteria2://${id}@${currentHost}:${singBoxHysteria2Port}?${multiPort}peer=${currentHost}&insecure=0&sni=${currentHost}&alpn=h3#${email} +EOF + echoContent yellow " ---> v2rayN(hysteria+TLS)" + echo "{\"server\": \"${currentHost}:${port}\",\"socks5\": { \"listen\": \"127.0.0.1:7798\", \"timeout\": 300},\"auth\":\"${id}\",\"tls\":{\"sni\":\"${currentHost}\"}}" | jq + + cat <>"/etc/v2ray-agent/subscribe_local/clashMeta/${user}" + - name: "${email}" + type: hysteria2 + server: ${currentHost} + ${clashMetaPortContent} + password: ${id} + alpn: + - h3 + sni: ${currentHost} + up: "${hysteria2ClientUploadSpeed} Mbps" + down: "${hysteria2ClientDownloadSpeed} Mbps" +EOF + + singBoxSubscribeLocalConfig=$(jq -r ". += [{\"tag\":\"${email}\",\"type\":\"hysteria2\",\"server\":\"${currentHost}\",\"server_port\":${singBoxHysteria2Port},\"up_mbps\":${hysteria2ClientUploadSpeed},\"down_mbps\":${hysteria2ClientDownloadSpeed},\"password\":\"${id}\",\"tls\":{\"enabled\":true,\"server_name\":\"${currentHost}\",\"alpn\":[\"h3\"]}}]" "/etc/v2ray-agent/subscribe_local/sing-box/${user}") + echo "${singBoxSubscribeLocalConfig}" | jq . >"/etc/v2ray-agent/subscribe_local/sing-box/${user}" + + echoContent yellow " ---> 二维码 Hysteria2(TLS)" + echoContent green " https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=hysteria2%3A%2F%2F${id}%40${currentHost}%3A${singBoxHysteria2Port}%3F${multiPortEncode}peer%3D${currentHost}%26insecure%3D0%26sni%3D${currentHost}%26alpn%3Dh3%23${email}\n" + + elif [[ "${type}" == "vlessReality" ]]; then + local realityServerName=${xrayVLESSRealityServerName} + local publicKey=${currentRealityPublicKey} + local realityMldsa65Verify=${currentRealityMldsa65Verify} + + if [[ "${coreInstallType}" == "2" ]]; then + realityServerName=${singBoxVLESSRealityVisionServerName} + publicKey=${singBoxVLESSRealityPublicKey} + fi + echoContent yellow " ---> 通用格式(VLESS+reality+uTLS+Vision)" + echoContent green " vless://${id}@$(getPublicIP):${port}?encryption=none&security=reality&pqv=${realityMldsa65Verify}&type=tcp&sni=${realityServerName}&fp=chrome&pbk=${publicKey}&sid=6ba85179e30d4fc2&flow=xtls-rprx-vision#${email}\n" + + echoContent yellow " ---> 格式化明文(VLESS+reality+uTLS+Vision)" + echoContent green "协议类型:VLESS reality,地址:$(getPublicIP),publicKey:${publicKey},shortId: 6ba85179e30d4fc2,pqv=${realityMldsa65Verify},serverNames:${realityServerName},端口:${port},用户ID:${id},传输方式:tcp,账户名:${email}\n" + cat <>"/etc/v2ray-agent/subscribe_local/default/${user}" +vless://${id}@$(getPublicIP):${port}?encryption=none&security=reality&pqv=${realityMldsa65Verify}&type=tcp&sni=${realityServerName}&fp=chrome&pbk=${publicKey}&sid=6ba85179e30d4fc2&flow=xtls-rprx-vision#${email} +EOF + cat <>"/etc/v2ray-agent/subscribe_local/clashMeta/${user}" + - name: "${email}" + type: vless + server: $(getPublicIP) + port: ${port} + uuid: ${id} + network: tcp + tls: true + udp: true + flow: xtls-rprx-vision + servername: ${realityServerName} + reality-opts: + public-key: ${publicKey} + short-id: 6ba85179e30d4fc2 + client-fingerprint: chrome +EOF + + singBoxSubscribeLocalConfig=$(jq -r ". += [{\"tag\":\"${email}\",\"type\":\"vless\",\"server\":\"$(getPublicIP)\",\"server_port\":${port},\"uuid\":\"${id}\",\"flow\":\"xtls-rprx-vision\",\"tls\":{\"enabled\":true,\"server_name\":\"${realityServerName}\",\"utls\":{\"enabled\":true,\"fingerprint\":\"chrome\"},\"reality\":{\"enabled\":true,\"public_key\":\"${publicKey}\",\"short_id\":\"6ba85179e30d4fc2\"}},\"packet_encoding\":\"xudp\"}]" "/etc/v2ray-agent/subscribe_local/sing-box/${user}") + echo "${singBoxSubscribeLocalConfig}" | jq . >"/etc/v2ray-agent/subscribe_local/sing-box/${user}" + + echoContent yellow " ---> 二维码 VLESS(VLESS+reality+uTLS+Vision)" + echoContent green " https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=vless%3A%2F%2F${id}%40$(getPublicIP)%3A${port}%3Fencryption%3Dnone%26security%3Dreality%26type%3Dtcp%26sni%3D${realityServerName}%26fp%3Dchrome%26pbk%3D${publicKey}%26sid%3D6ba85179e30d4fc2%26flow%3Dxtls-rprx-vision%23${email}\n" + + elif [[ "${type}" == "vlessRealityGRPC" ]]; then + local realityServerName=${xrayVLESSRealityServerName} + local publicKey=${currentRealityPublicKey} + local realityMldsa65Verify=${currentRealityMldsa65Verify} + + if [[ "${coreInstallType}" == "2" ]]; then + realityServerName=${singBoxVLESSRealityGRPCServerName} + publicKey=${singBoxVLESSRealityPublicKey} + fi + + echoContent yellow " ---> 通用格式(VLESS+reality+uTLS+gRPC)" + # pqv=${realityMldsa65Verify}& + echoContent green " vless://${id}@$(getPublicIP):${port}?encryption=none&security=reality&type=grpc&sni=${realityServerName}&fp=chrome&pbk=${publicKey}&sid=6ba85179e30d4fc2&path=grpc&serviceName=grpc#${email}\n" + + echoContent yellow " ---> 格式化明文(VLESS+reality+uTLS+gRPC)" + # pqv=${realityMldsa65Verify}, + echoContent green "协议类型:VLESS reality,serviceName:grpc,地址:$(getPublicIP),publicKey:${publicKey},shortId: 6ba85179e30d4fc2,serverNames:${realityServerName},端口:${port},用户ID:${id},传输方式:gRPC,client-fingerprint:chrome,账户名:${email}\n" + cat <>"/etc/v2ray-agent/subscribe_local/default/${user}" +vless://${id}@$(getPublicIP):${port}?encryption=none&security=reality&pqv=${realityMldsa65Verify}&type=grpc&sni=${realityServerName}&fp=chrome&pbk=${publicKey}&sid=6ba85179e30d4fc2&path=grpc&serviceName=grpc#${email} +EOF + cat <>"/etc/v2ray-agent/subscribe_local/clashMeta/${user}" + - name: "${email}" + type: vless + server: $(getPublicIP) + port: ${port} + uuid: ${id} + network: grpc + tls: true + udp: true + servername: ${realityServerName} + reality-opts: + public-key: ${publicKey} + short-id: 6ba85179e30d4fc2 + grpc-opts: + grpc-service-name: "grpc" + client-fingerprint: chrome +EOF + + singBoxSubscribeLocalConfig=$(jq -r ". += [{\"tag\":\"${email}\",\"type\":\"vless\",\"server\":\"$(getPublicIP)\",\"server_port\":${port},\"uuid\":\"${id}\",\"tls\":{\"enabled\":true,\"server_name\":\"${realityServerName}\",\"utls\":{\"enabled\":true,\"fingerprint\":\"chrome\"},\"reality\":{\"enabled\":true,\"public_key\":\"${publicKey}\",\"short_id\":\"6ba85179e30d4fc2\"}},\"packet_encoding\":\"xudp\",\"transport\":{\"type\":\"grpc\",\"service_name\":\"grpc\"}}]" "/etc/v2ray-agent/subscribe_local/sing-box/${user}") + echo "${singBoxSubscribeLocalConfig}" | jq . >"/etc/v2ray-agent/subscribe_local/sing-box/${user}" + + echoContent yellow " ---> 二维码 VLESS(VLESS+reality+uTLS+gRPC)" + echoContent green " https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=vless%3A%2F%2F${id}%40$(getPublicIP)%3A${port}%3Fencryption%3Dnone%26security%3Dreality%26type%3Dgrpc%26sni%3D${realityServerName}%26fp%3Dchrome%26pbk%3D${publicKey}%26sid%3D6ba85179e30d4fc2%26path%3Dgrpc%26serviceName%3Dgrpc%23${email}\n" + elif [[ "${type}" == "tuic" ]]; then + local tuicUUID= + tuicUUID=$(echo "${id}" | awk -F "[_]" '{print $1}') + + local tuicPassword= + tuicPassword=$(echo "${id}" | awk -F "[_]" '{print $2}') + + if [[ -z "${email}" ]]; then + echoContent red " ---> 读取配置失败,请重新安装" + exit 0 + fi + + echoContent yellow " ---> 格式化明文(Tuic+TLS)" + echoContent green " 协议类型:Tuic,地址:${currentHost},端口:${port},uuid:${tuicUUID},password:${tuicPassword},congestion-controller:${tuicAlgorithm},alpn: h3,账户名:${email}\n" + + cat <>"/etc/v2ray-agent/subscribe_local/default/${user}" +tuic://${tuicUUID}:${tuicPassword}@${currentHost}:${port}?congestion_control=${tuicAlgorithm}&alpn=h3&sni=${currentHost}&udp_relay_mode=quic&allow_insecure=0#${email} +EOF + echoContent yellow " ---> v2rayN(Tuic+TLS)" + echo "{\"relay\": {\"server\": \"${currentHost}:${port}\",\"uuid\": \"${tuicUUID}\",\"password\": \"${tuicPassword}\",\"ip\": \"${currentHost}\",\"congestion_control\": \"${tuicAlgorithm}\",\"alpn\": [\"h3\"]},\"local\": {\"server\": \"127.0.0.1:7798\"},\"log_level\": \"warn\"}" | jq + + cat <>"/etc/v2ray-agent/subscribe_local/clashMeta/${user}" + - name: "${email}" + server: ${currentHost} + type: tuic + port: ${port} + uuid: ${tuicUUID} + password: ${tuicPassword} + alpn: + - h3 + congestion-controller: ${tuicAlgorithm} + disable-sni: true + reduce-rtt: true + sni: ${email} +EOF + + singBoxSubscribeLocalConfig=$(jq -r ". += [{\"tag\":\"${email}\",\"type\": \"tuic\",\"server\": \"${currentHost}\",\"server_port\": ${port},\"uuid\": \"${tuicUUID}\",\"password\": \"${tuicPassword}\",\"congestion_control\": \"${tuicAlgorithm}\",\"tls\": {\"enabled\": true,\"server_name\": \"${currentHost}\",\"alpn\": [\"h3\"]}}]" "/etc/v2ray-agent/subscribe_local/sing-box/${user}") + echo "${singBoxSubscribeLocalConfig}" | jq . >"/etc/v2ray-agent/subscribe_local/sing-box/${user}" + + echoContent yellow "\n ---> 二维码 Tuic" + echoContent green " https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=tuic%3A%2F%2F${tuicUUID}%3A${tuicPassword}%40${currentHost}%3A${tuicPort}%3Fcongestion_control%3D${tuicAlgorithm}%26alpn%3Dh3%26sni%3D${currentHost}%26udp_relay_mode%3Dquic%26allow_insecure%3D0%23${email}\n" + elif [[ "${type}" == "naive" ]]; then + echoContent yellow " ---> Naive(TLS)" + + echoContent green " naive+https://${email}:${id}@${currentHost}:${port}?padding=true#${email}\n" + cat <>"/etc/v2ray-agent/subscribe_local/default/${user}" +naive+https://${email}:${id}@${currentHost}:${port}?padding=true#${email} +EOF + echoContent yellow " ---> 二维码 Naive(TLS)" + echoContent green " https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=naive%2Bhttps%3A%2F%2F${email}%3A${id}%40${currentHost}%3A${port}%3Fpadding%3Dtrue%23${email}\n" + elif [[ "${type}" == "vmessHTTPUpgrade" ]]; then + qrCodeBase64Default=$(echo -n "{\"port\":${port},\"ps\":\"${email}\",\"tls\":\"tls\",\"id\":\"${id}\",\"aid\":0,\"v\":2,\"host\":\"${currentHost}\",\"type\":\"none\",\"path\":\"${path}\",\"net\":\"httpupgrade\",\"add\":\"${add}\",\"allowInsecure\":0,\"method\":\"none\",\"peer\":\"${currentHost}\",\"sni\":\"${currentHost}\"}" | base64 -w 0) + qrCodeBase64Default="${qrCodeBase64Default// /}" + + echoContent yellow " ---> 通用json(VMess+HTTPUpgrade+TLS)" + echoContent green " {\"port\":${port},\"ps\":\"${email}\",\"tls\":\"tls\",\"id\":\"${id}\",\"aid\":0,\"v\":2,\"host\":\"${currentHost}\",\"type\":\"none\",\"path\":\"${path}\",\"net\":\"httpupgrade\",\"add\":\"${add}\",\"allowInsecure\":0,\"method\":\"none\",\"peer\":\"${currentHost}\",\"sni\":\"${currentHost}\"}\n" + echoContent yellow " ---> 通用vmess(VMess+HTTPUpgrade+TLS)链接" + echoContent green " vmess://${qrCodeBase64Default}\n" + echoContent yellow " ---> 二维码 vmess(VMess+HTTPUpgrade+TLS)" + + cat <>"/etc/v2ray-agent/subscribe_local/default/${user}" + vmess://${qrCodeBase64Default} +EOF + cat <>"/etc/v2ray-agent/subscribe_local/clashMeta/${user}" + - name: "${email}" + type: vmess + server: ${add} + port: ${port} + uuid: ${id} + alterId: 0 + cipher: auto + udp: true + tls: true + client-fingerprint: chrome + servername: ${currentHost} + network: ws + ws-opts: + path: ${path} + headers: + Host: ${currentHost} + v2ray-http-upgrade: true +EOF + singBoxSubscribeLocalConfig=$(jq -r ". += [{\"tag\":\"${email}\",\"type\":\"vmess\",\"server\":\"${add}\",\"server_port\":${port},\"uuid\":\"${id}\",\"security\":\"auto\",\"alter_id\":0,\"tls\":{\"enabled\":true,\"server_name\":\"${currentHost}\",\"utls\":{\"enabled\":true,\"fingerprint\":\"chrome\"}},\"packet_encoding\":\"packetaddr\",\"transport\":{\"type\":\"httpupgrade\",\"path\":\"${path}\"}}]" "/etc/v2ray-agent/subscribe_local/sing-box/${user}") + + echo "${singBoxSubscribeLocalConfig}" | jq . >"/etc/v2ray-agent/subscribe_local/sing-box/${user}" + + echoContent green " https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=vmess://${qrCodeBase64Default}\n" + + elif [[ "${type}" == "anytls" ]]; then + echoContent yellow " ---> AnyTLS" + + echoContent yellow " ---> 格式化明文(AnyTLS)" + echoContent green "协议类型:anytls,地址:${currentHost},端口:${singBoxAnyTLSPort},用户ID:${id},传输方式:tcp,账户名:${email}\n" + + echoContent green " anytls://${id}@${currentHost}:${singBoxAnyTLSPort}?peer=${currentHost}&insecure=0&sni=${currentHost}#${email}\n" + cat <>"/etc/v2ray-agent/subscribe_local/default/${user}" +anytls://${id}@${currentHost}:${singBoxAnyTLSPort}?peer=${currentHost}&insecure=0&sni=${currentHost}#${email} +EOF + cat <>"/etc/v2ray-agent/subscribe_local/clashMeta/${user}" + - name: "${email}" + type: anytls + port: ${singBoxAnyTLSPort} + server: ${currentHost} + password: ${id} + client-fingerprint: chrome + udp: true + sni: ${currentHost} + alpn: + - h2 + - http/1.1 +EOF + + singBoxSubscribeLocalConfig=$(jq -r ". += [{\"tag\":\"${email}\",\"type\":\"anytls\",\"server\":\"${currentHost}\",\"server_port\":${singBoxAnyTLSPort},\"password\":\"${id}\",\"tls\":{\"enabled\":true,\"server_name\":\"${currentHost}\"}}]" "/etc/v2ray-agent/subscribe_local/sing-box/${user}") + echo "${singBoxSubscribeLocalConfig}" | jq . >"/etc/v2ray-agent/subscribe_local/sing-box/${user}" + + echoContent yellow " ---> 二维码 AnyTLS" + echoContent green " https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=anytls%3A%2F%2F${id}%40${currentHost}%3A${singBoxAnyTLSPort}%3Fpeer%3D${currentHost}%26insecure%3D0%26sni%3D${currentHost}%23${email}\n" + fi + +} + +# 账号 +showAccounts() { + readInstallType + readInstallProtocolType + readConfigHostPathUUID + readSingBoxConfig + + echo + echoContent skyBlue "\n进度 $1/${totalProgress} : 账号" + + initSubscribeLocalConfig + # VLESS TCP + if echo ${currentInstallProtocolType} | grep -q ",0,"; then + + echoContent skyBlue "============================= VLESS TCP TLS_Vision [推荐] ==============================\n" + jq .inbounds[0].settings.clients//.inbounds[0].users ${configPath}02_VLESS_TCP_inbounds.json | jq -c '.[]' | while read -r user; do + local email= + email=$(echo "${user}" | jq -r .email//.name) + + echoContent skyBlue "\n ---> 账号:${email}" + echo + defaultBase64Code vlesstcp "${currentDefaultPort}${singBoxVLESSVisionPort}" "${email}" "$(echo "${user}" | jq -r .id//.uuid)" + done + fi + + # VLESS WS + if echo ${currentInstallProtocolType} | grep -q ",1,"; then + echoContent skyBlue "\n================================ VLESS WS TLS [仅CDN推荐] ================================\n" + + jq .inbounds[0].settings.clients//.inbounds[0].users ${configPath}03_VLESS_WS_inbounds.json | jq -c '.[]' | while read -r user; do + local email= + email=$(echo "${user}" | jq -r .email//.name) + + local vlessWSPort=${currentDefaultPort} + if [[ "${coreInstallType}" == "2" ]]; then + vlessWSPort="${singBoxVLESSWSPort}" + fi + echo + local path="${currentPath}ws" + + if [[ ${coreInstallType} == "1" ]]; then + path="/${currentPath}ws" + elif [[ "${coreInstallType}" == "2" ]]; then + path="${singBoxVLESSWSPath}" + fi + + local count= + while read -r line; do + echoContent skyBlue "\n ---> 账号:${email}${count}" + if [[ -n "${line}" ]]; then + defaultBase64Code vlessws "${vlessWSPort}" "${email}${count}" "$(echo "${user}" | jq -r .id//.uuid)" "${line}" "${path}" + count=$((count + 1)) + echo + fi + done < <(echo "${currentCDNAddress}" | tr ',' '\n') + done + fi + # trojan grpc + if echo ${currentInstallProtocolType} | grep -q ",2,"; then + echoContent skyBlue "\n================================ Trojan gRPC TLS [仅CDN推荐] ================================\n" + jq .inbounds[0].settings.clients ${configPath}04_trojan_gRPC_inbounds.json | jq -c '.[]' | while read -r user; do + local email= + email=$(echo "${user}" | jq -r .email) + local count= + while read -r line; do + echoContent skyBlue "\n ---> 账号:${email}${count}" + echo + if [[ -n "${line}" ]]; then + defaultBase64Code trojangrpc "${currentDefaultPort}" "${email}${count}" "$(echo "${user}" | jq -r .password)" "${line}" + count=$((count + 1)) + fi + done < <(echo "${currentCDNAddress}" | tr ',' '\n') + + done + fi + # VMess WS + if echo ${currentInstallProtocolType} | grep -q ",3,"; then + echoContent skyBlue "\n================================ VMess WS TLS [仅CDN推荐] ================================\n" + local path="${currentPath}vws" + if [[ ${coreInstallType} == "1" ]]; then + path="/${currentPath}vws" + elif [[ "${coreInstallType}" == "2" ]]; then + path="${singBoxVMessWSPath}" + fi + jq .inbounds[0].settings.clients//.inbounds[0].users ${configPath}05_VMess_WS_inbounds.json | jq -c '.[]' | while read -r user; do + local email= + email=$(echo "${user}" | jq -r .email//.name) + + local vmessPort=${currentDefaultPort} + if [[ "${coreInstallType}" == "2" ]]; then + vmessPort="${singBoxVMessWSPort}" + fi + + local count= + while read -r line; do + echoContent skyBlue "\n ---> 账号:${email}${count}" + echo + if [[ -n "${line}" ]]; then + defaultBase64Code vmessws "${vmessPort}" "${email}${count}" "$(echo "${user}" | jq -r .id//.uuid)" "${line}" "${path}" + count=$((count + 1)) + fi + done < <(echo "${currentCDNAddress}" | tr ',' '\n') + done + fi + + # trojan tcp + if echo ${currentInstallProtocolType} | grep -q ",4,"; then + echoContent skyBlue "\n================================== Trojan TLS [不推荐] ==================================\n" + jq .inbounds[0].settings.clients//.inbounds[0].users ${configPath}04_trojan_TCP_inbounds.json | jq -c '.[]' | while read -r user; do + local email= + email=$(echo "${user}" | jq -r .email//.name) + echoContent skyBlue "\n ---> 账号:${email}" + + defaultBase64Code trojan "${currentDefaultPort}${singBoxTrojanPort}" "${email}" "$(echo "${user}" | jq -r .password)" + done + fi + # VLESS grpc + if echo ${currentInstallProtocolType} | grep -q ",5,"; then + echoContent skyBlue "\n=============================== VLESS gRPC TLS [仅CDN推荐] ===============================\n" + jq .inbounds[0].settings.clients ${configPath}06_VLESS_gRPC_inbounds.json | jq -c '.[]' | while read -r user; do + + local email= + email=$(echo "${user}" | jq -r .email) + + local count= + while read -r line; do + echoContent skyBlue "\n ---> 账号:${email}${count}" + echo + if [[ -n "${line}" ]]; then + defaultBase64Code vlessgrpc "${currentDefaultPort}" "${email}${count}" "$(echo "${user}" | jq -r .id)" "${line}" + count=$((count + 1)) + fi + done < <(echo "${currentCDNAddress}" | tr ',' '\n') + + done + fi + # hysteria2 + if echo ${currentInstallProtocolType} | grep -q ",6," || [[ -n "${hysteriaPort}" ]]; then + readPortHopping "hysteria2" "${singBoxHysteria2Port}" + echoContent skyBlue "\n================================ Hysteria2 TLS [推荐] ================================\n" + local path="${configPath}" + if [[ "${coreInstallType}" == "1" ]]; then + path="${singBoxConfigPath}" + fi + local hysteria2DefaultPort= + if [[ -n "${hysteria2PortHoppingStart}" && -n "${hysteria2PortHoppingEnd}" ]]; then + hysteria2DefaultPort="${hysteria2PortHopping}" + else + hysteria2DefaultPort=${singBoxHysteria2Port} + fi + + jq -r -c '.inbounds[]|.users[]' "${path}06_hysteria2_inbounds.json" | while read -r user; do + echoContent skyBlue "\n ---> 账号:$(echo "${user}" | jq -r .name)" + echo + defaultBase64Code hysteria "${hysteria2DefaultPort}" "$(echo "${user}" | jq -r .name)" "$(echo "${user}" | jq -r .password)" + done + + fi + + # VLESS reality vision + if echo ${currentInstallProtocolType} | grep -q ",7,"; then + echoContent skyBlue "============================= VLESS reality_vision [推荐] ==============================\n" + jq .inbounds[0].settings.clients//.inbounds[0].users ${configPath}07_VLESS_vision_reality_inbounds.json | jq -c '.[]' | while read -r user; do + local email= + email=$(echo "${user}" | jq -r .email//.name) + + echoContent skyBlue "\n ---> 账号:${email}" + echo + defaultBase64Code vlessReality "${xrayVLESSRealityVisionPort}${singBoxVLESSRealityVisionPort}" "${email}" "$(echo "${user}" | jq -r .id//.uuid)" + done + fi + # VLESS reality gRPC + if echo ${currentInstallProtocolType} | grep -q ",8,"; then + echoContent skyBlue "============================== VLESS reality_gRPC [推荐] ===============================\n" + jq .inbounds[0].settings.clients//.inbounds[0].users ${configPath}08_VLESS_vision_gRPC_inbounds.json | jq -c '.[]' | while read -r user; do + local email= + email=$(echo "${user}" | jq -r .email//.name) + + echoContent skyBlue "\n ---> 账号:${email}" + echo + defaultBase64Code vlessRealityGRPC "${xrayVLESSRealityVisionPort}${singBoxVLESSRealityGRPCPort}" "${email}" "$(echo "${user}" | jq -r .id//.uuid)" + done + fi + # tuic + if echo ${currentInstallProtocolType} | grep -q ",9," || [[ -n "${tuicPort}" ]]; then + echoContent skyBlue "\n================================ Tuic TLS [推荐] ================================\n" + local path="${configPath}" + if [[ "${coreInstallType}" == "1" ]]; then + path="${singBoxConfigPath}" + fi + jq -r -c '.inbounds[].users[]' "${path}09_tuic_inbounds.json" | while read -r user; do + echoContent skyBlue "\n ---> 账号:$(echo "${user}" | jq -r .name)" + echo + defaultBase64Code tuic "${singBoxTuicPort}" "$(echo "${user}" | jq -r .name)" "$(echo "${user}" | jq -r .uuid)_$(echo "${user}" | jq -r .password)" + done + + fi + # naive + if echo ${currentInstallProtocolType} | grep -q ",10," || [[ -n "${singBoxNaivePort}" ]]; then + echoContent skyBlue "\n================================ naive TLS [推荐,不支持ClashMeta] ================================\n" + + jq -r -c '.inbounds[]|.users[]' "${configPath}10_naive_inbounds.json" | while read -r user; do + echoContent skyBlue "\n ---> 账号:$(echo "${user}" | jq -r .username)" + echo + defaultBase64Code naive "${singBoxNaivePort}" "$(echo "${user}" | jq -r .username)" "$(echo "${user}" | jq -r .password)" + done + + fi + # VMess HTTPUpgrade + if echo ${currentInstallProtocolType} | grep -q ",11,"; then + echoContent skyBlue "\n================================ VMess HTTPUpgrade TLS [仅CDN推荐] ================================\n" + local path="${currentPath}vws" + if [[ ${coreInstallType} == "1" ]]; then + path="/${currentPath}vws" + elif [[ "${coreInstallType}" == "2" ]]; then + path="${singBoxVMessHTTPUpgradePath}" + fi + jq .inbounds[0].settings.clients//.inbounds[0].users ${configPath}11_VMess_HTTPUpgrade_inbounds.json | jq -c '.[]' | while read -r user; do + local email= + email=$(echo "${user}" | jq -r .email//.name) + + local vmessHTTPUpgradePort=${currentDefaultPort} + if [[ "${coreInstallType}" == "2" ]]; then + vmessHTTPUpgradePort="${singBoxVMessHTTPUpgradePort}" + fi + + local count= + while read -r line; do + echoContent skyBlue "\n ---> 账号:${email}${count}" + echo + if [[ -n "${line}" ]]; then + defaultBase64Code vmessHTTPUpgrade "${vmessHTTPUpgradePort}" "${email}${count}" "$(echo "${user}" | jq -r .id//.uuid)" "${line}" "${path}" + count=$((count + 1)) + fi + done < <(echo "${currentCDNAddress}" | tr ',' '\n') + done + fi + # VLESS XHTTP + if echo ${currentInstallProtocolType} | grep -q ",12,"; then + echoContent skyBlue "\n================================ VLESS XHTTP TLS [仅CDN推荐] ================================\n" + + jq .inbounds[0].settings.clients//.inbounds[0].users ${configPath}12_VLESS_XHTTP_inbounds.json | jq -c '.[]' | while read -r user; do + local email= + email=$(echo "${user}" | jq -r .email//.name) + echo + local path="${currentPath}xHTTP" + + local count= + while read -r line; do + echoContent skyBlue "\n ---> 账号:${email}${count}" + if [[ -n "${line}" ]]; then + defaultBase64Code vlessXHTTP "${xrayVLESSRealityXHTTPort}" "${email}${count}" "$(echo "${user}" | jq -r .id//.uuid)" "${line}" "${path}" + count=$((count + 1)) + echo + fi + done < <(echo "${currentCDNAddress}" | tr ',' '\n') + done + fi + # AnyTLS + if echo ${currentInstallProtocolType} | grep -q ",13,"; then + echoContent skyBlue "\n================================ AnyTLS ================================\n" + + jq -r -c '.inbounds[]|.users[]' "${configPath}13_anytls_inbounds.json" | while read -r user; do + echoContent skyBlue "\n ---> 账号:$(echo "${user}" | jq -r .name)" + echo + defaultBase64Code anytls "${singBoxAnyTLSPort}" "$(echo "${user}" | jq -r .name)" "$(echo "${user}" | jq -r .password)" + done + + fi +} +# 移除nginx302配置 +removeNginx302() { + local count= + grep -n "return 302" <"${nginxConfigPath}alone.conf" | while read -r line; do + + if ! echo "${line}" | grep -q "request_uri"; then + local removeIndex= + removeIndex=$(echo "${line}" | awk -F "[:]" '{print $1}') + removeIndex=$((removeIndex + count)) + sed -i "${removeIndex}d" ${nginxConfigPath}alone.conf + count=$((count - 1)) + fi + done +} + +# 检查302是否成功 +checkNginx302() { + local domain302Status= + domain302Status=$(curl -s "https://${currentHost}:${currentPort}") + if echo "${domain302Status}" | grep -q "302"; then + # local domain302Result= + # domain302Result=$(curl -L -s "https://${currentHost}:${currentPort}") + # if [[ -n "${domain302Result}" ]]; then + echoContent green " ---> 302重定向设置完毕" + exit 0 + # fi + fi + echoContent red " ---> 302重定向设置失败,请仔细检查是否和示例相同" + backupNginxConfig restoreBackup +} + +# 备份恢复nginx文件 +backupNginxConfig() { + if [[ "$1" == "backup" ]]; then + cp ${nginxConfigPath}alone.conf /etc/v2ray-agent/alone_backup.conf + echoContent green " ---> nginx配置文件备份成功" + fi + + if [[ "$1" == "restoreBackup" ]] && [[ -f "/etc/v2ray-agent/alone_backup.conf" ]]; then + cp /etc/v2ray-agent/alone_backup.conf ${nginxConfigPath}alone.conf + echoContent green " ---> nginx配置文件恢复备份成功" + rm /etc/v2ray-agent/alone_backup.conf + fi + +} +# 添加302配置 +addNginx302() { + + local count=1 + grep -n "location / {" <"${nginxConfigPath}alone.conf" | while read -r line; do + if [[ -n "${line}" ]]; then + local insertIndex= + insertIndex="$(echo "${line}" | awk -F "[:]" '{print $1}')" + insertIndex=$((insertIndex + count)) + sed "${insertIndex}i return 302 '$1';" ${nginxConfigPath}alone.conf >${nginxConfigPath}tmpfile && mv ${nginxConfigPath}tmpfile ${nginxConfigPath}alone.conf + count=$((count + 1)) + else + echoContent red " ---> 302添加失败" + backupNginxConfig restoreBackup + fi + + done +} + +# 更新伪装站 +updateNginxBlog() { + if [[ "${coreInstallType}" == "2" ]]; then + echoContent red "\n ---> 此功能仅支持Xray-core内核" + exit 0 + fi + + echoContent skyBlue "\n进度 $1/${totalProgress} : 更换伪装站点" + + if ! echo "${currentInstallProtocolType}" | grep -q ",0," || [[ -z "${coreInstallType}" ]]; then + echoContent red "\n ---> 由于环境依赖,请先安装Xray-core的VLESS_TCP_TLS_Vision" + exit 0 + fi + echoContent red "==============================================================" + echoContent yellow "# 如需自定义,请手动复制模版文件到 ${nginxStaticPath} \n" + echoContent yellow "1.新手引导" + echoContent yellow "2.游戏网站" + echoContent yellow "3.个人博客01" + echoContent yellow "4.企业站" + echoContent yellow "5.解锁加密的音乐文件模版[https://github.com/ix64/unlock-music]" + echoContent yellow "6.mikutap[https://github.com/HFIProgramming/mikutap]" + echoContent yellow "7.企业站02" + echoContent yellow "8.个人博客02" + echoContent yellow "9.404自动跳转baidu" + echoContent yellow "10.302重定向网站" + echoContent red "==============================================================" + read -r -p "请选择:" selectInstallNginxBlogType + + if [[ "${selectInstallNginxBlogType}" == "10" ]]; then + if [[ "${coreInstallType}" == "2" ]]; then + echoContent red "\n ---> 此功能仅支持Xray-core内核,请等待后续更新" + exit 0 + fi + echoContent red "\n==============================================================" + echoContent yellow "重定向的优先级更高,配置302之后如果更改伪装站点,根路由下伪装站点将不起作用" + echoContent yellow "如想要伪装站点实现作用需删除302重定向配置\n" + echoContent yellow "1.添加" + echoContent yellow "2.删除" + echoContent red "==============================================================" + read -r -p "请选择:" redirectStatus + + if [[ "${redirectStatus}" == "1" ]]; then + backupNginxConfig backup + read -r -p "请输入要重定向的域名,例如 https://www.baidu.com:" redirectDomain + removeNginx302 + addNginx302 "${redirectDomain}" + handleNginx stop + handleNginx start + if [[ -z $(pgrep -f "nginx") ]]; then + backupNginxConfig restoreBackup + handleNginx start + exit 0 + fi + checkNginx302 + exit 0 + fi + if [[ "${redirectStatus}" == "2" ]]; then + removeNginx302 + echoContent green " ---> 移除302重定向成功" + exit 0 + fi + fi + if [[ "${selectInstallNginxBlogType}" =~ ^[1-9]$ ]]; then + rm -rf "${nginxStaticPath}*" + + if [[ "${release}" == "alpine" ]]; then + wget -q -P "${nginxStaticPath}" "https://raw.githubusercontent.com/mack-a/v2ray-agent/master/fodder/blog/unable/html${selectInstallNginxBlogType}.zip" + else + wget -q "${wgetShowProgressStatus}" -P "${nginxStaticPath}" "https://raw.githubusercontent.com/mack-a/v2ray-agent/master/fodder/blog/unable/html${selectInstallNginxBlogType}.zip" + fi + + unzip -o "${nginxStaticPath}html${selectInstallNginxBlogType}.zip" -d "${nginxStaticPath}" >/dev/null + rm -f "${nginxStaticPath}html${selectInstallNginxBlogType}.zip*" + echoContent green " ---> 更换伪站成功" + else + echoContent red " ---> 选择错误,请重新选择" + updateNginxBlog + fi +} + +# 添加新端口 +addCorePort() { + + if [[ "${coreInstallType}" == "2" ]]; then + echoContent red "\n ---> 此功能仅支持Xray-core内核" + exit 0 + fi + + echoContent skyBlue "\n功能 1/${totalProgress} : 添加新端口" + echoContent red "\n==============================================================" + echoContent yellow "# 注意事项\n" + echoContent yellow "支持批量添加" + echoContent yellow "不影响默认端口的使用" + echoContent yellow "查看账号时,只会展示默认端口的账号" + echoContent yellow "不允许有特殊字符,注意逗号的格式" + echoContent yellow "如已安装hysteria,会同时安装hysteria新端口" + echoContent yellow "录入示例:2053,2083,2087\n" + + echoContent yellow "1.查看已添加端口" + echoContent yellow "2.添加端口" + echoContent yellow "3.删除端口" + echoContent red "==============================================================" + read -r -p "请选择:" selectNewPortType + if [[ "${selectNewPortType}" == "1" ]]; then + find ${configPath} -name "*dokodemodoor*" | grep -v "hysteria" | awk -F "[c][o][n][f][/]" '{print $2}' | awk -F "[_]" '{print $4}' | awk -F "[.]" '{print ""NR""":"$1}' + exit 0 + elif [[ "${selectNewPortType}" == "2" ]]; then + read -r -p "请输入端口号:" newPort + read -r -p "请输入默认的端口号,同时会更改订阅端口以及节点端口,[回车]默认443:" defaultPort + + if [[ -n "${defaultPort}" ]]; then + rm -rf "$(find ${configPath}* | grep "default")" + fi + + if [[ -n "${newPort}" ]]; then + + while read -r port; do + rm -rf "$(find ${configPath}* | grep "${port}")" + + local fileName= + local hysteriaFileName= + if [[ -n "${defaultPort}" && "${port}" == "${defaultPort}" ]]; then + fileName="${configPath}02_dokodemodoor_inbounds_${port}_default.json" + else + fileName="${configPath}02_dokodemodoor_inbounds_${port}.json" + fi + + if [[ -n ${hysteriaPort} ]]; then + hysteriaFileName="${configPath}02_dokodemodoor_inbounds_hysteria_${port}.json" + fi + + # 开放端口 + allowPort "${port}" + allowPort "${port}" "udp" + + local settingsPort=443 + if [[ -n "${customPort}" ]]; then + settingsPort=${customPort} + fi + + if [[ -n ${hysteriaFileName} ]]; then + cat <"${hysteriaFileName}" +{ + "inbounds": [ + { + "listen": "0.0.0.0", + "port": ${port}, + "protocol": "dokodemo-door", + "settings": { + "address": "127.0.0.1", + "port": ${hysteriaPort}, + "network": "udp", + "followRedirect": false + }, + "tag": "dokodemo-door-newPort-hysteria-${port}" + } + ] +} +EOF + fi + cat <"${fileName}" +{ + "inbounds": [ + { + "listen": "0.0.0.0", + "port": ${port}, + "protocol": "dokodemo-door", + "settings": { + "address": "127.0.0.1", + "port": ${settingsPort}, + "network": "tcp", + "followRedirect": false + }, + "tag": "dokodemo-door-newPort-${port}" + } + ] +} +EOF + done < <(echo "${newPort}" | tr ',' '\n') + + echoContent green " ---> 添加完毕" + reloadCore + addCorePort + fi + elif [[ "${selectNewPortType}" == "3" ]]; then + find ${configPath} -name "*dokodemodoor*" | grep -v "hysteria" | awk -F "[c][o][n][f][/]" '{print $2}' | awk -F "[_]" '{print $4}' | awk -F "[.]" '{print ""NR""":"$1}' + read -r -p "请输入要删除的端口编号:" portIndex + local dokoConfig + dokoConfig=$(find ${configPath} -name "*dokodemodoor*" | grep -v "hysteria" | awk -F "[c][o][n][f][/]" '{print $2}' | awk -F "[_]" '{print $4}' | awk -F "[.]" '{print ""NR""":"$1}' | grep "${portIndex}:") + if [[ -n "${dokoConfig}" ]]; then + rm "${configPath}02_dokodemodoor_inbounds_$(echo "${dokoConfig}" | awk -F "[:]" '{print $2}').json" + local hysteriaDokodemodoorFilePath= + + hysteriaDokodemodoorFilePath="${configPath}02_dokodemodoor_inbounds_hysteria_$(echo "${dokoConfig}" | awk -F "[:]" '{print $2}').json" + if [[ -f "${hysteriaDokodemodoorFilePath}" ]]; then + rm "${hysteriaDokodemodoorFilePath}" + fi + + reloadCore + addCorePort + else + echoContent yellow "\n ---> 编号输入错误,请重新选择" + addCorePort + fi + fi +} + +# 卸载脚本 +unInstall() { + read -r -p "是否确认卸载安装内容?[y/n]:" unInstallStatus + if [[ "${unInstallStatus}" != "y" ]]; then + echoContent green " ---> 放弃卸载" + menu + exit 0 + fi + checkBTPanel + echoContent yellow " ---> 脚本不会删除acme相关配置,删除请手动执行 [rm -rf /root/.acme.sh]" + handleNginx stop + if [[ -z $(pgrep -f "nginx") ]]; then + echoContent green " ---> 停止Nginx成功" + fi + if [[ "${release}" == "alpine" ]]; then + if [[ "${coreInstallType}" == "1" ]]; then + handleXray stop + rc-update del xray default + rm -rf /etc/init.d/xray + echoContent green " ---> 删除Xray开机自启完成" + fi + if [[ "${coreInstallType}" == "2" || -n "${singBoxConfigPath}" ]]; then + handleSingBox stop + rc-update del sing-box default + rm -rf /etc/init.d/sing-box + echoContent green " ---> 删除sing-box开机自启完成" + fi + else + if [[ "${coreInstallType}" == "1" ]]; then + handleXray stop + rm -rf /etc/systemd/system/xray.service + echoContent green " ---> 删除Xray开机自启完成" + fi + if [[ "${coreInstallType}" == "2" || -n "${singBoxConfigPath}" ]]; then + handleSingBox stop + rm -rf /etc/systemd/system/sing-box.service + echoContent green " ---> 删除sing-box开机自启完成" + fi + fi + + rm -rf /etc/v2ray-agent + rm -rf ${nginxConfigPath}alone.conf + rm -rf ${nginxConfigPath}checkPortOpen.conf >/dev/null 2>&1 + rm -rf "${nginxConfigPath}sing_box_VMess_HTTPUpgrade.conf" >/dev/null 2>&1 + rm -rf ${nginxConfigPath}checkPortOpen.conf >/dev/null 2>&1 + + unInstallSubscribe + + if [[ -d "${nginxStaticPath}" && -f "${nginxStaticPath}/check" ]]; then + rm -rf "${nginxStaticPath}" + echoContent green " ---> 删除伪装网站完成" + fi + + rm -rf /usr/bin/vasma + rm -rf /usr/sbin/vasma + echoContent green " ---> 卸载快捷方式完成" + echoContent green " ---> 卸载v2ray-agent脚本完成" +} + +# CDN节点管理 +manageCDN() { + echoContent skyBlue "\n进度 $1/1 : CDN节点管理" + local setCDNDomain= + + if echo "${currentInstallProtocolType}" | grep -qE ",1,|,2,|,3,|,5,|,11,"; then + echoContent red "==============================================================" + echoContent yellow "# 注意事项" + echoContent yellow "\n教程地址:" + echoContent skyBlue "https://www.v2ray-agent.com/archives/cloudflarezi-xuan-ip" + echoContent red "\n如对Cloudflare优化不了解,请不要使用" + + echoContent yellow "1.CNAME www.digitalocean.com" + echoContent yellow "2.CNAME who.int" + echoContent yellow "3.CNAME blog.hostmonit.com" + echoContent yellow "4.CNAME www.visa.com.hk" + echoContent yellow "5.手动输入[可输入多个,比如: 1.1.1.1,1.1.2.2,cloudflare.com 逗号分隔]" + echoContent yellow "6.移除CDN节点" + echoContent red "==============================================================" + read -r -p "请选择:" selectCDNType + case ${selectCDNType} in + 1) + setCDNDomain="www.digitalocean.com" + ;; + 2) + setCDNDomain="who.int" + ;; + 3) + setCDNDomain="blog.hostmonit.com" + ;; + 4) + setCDNDomain="www.visa.com.hk" + ;; + 5) + read -r -p "请输入想要自定义CDN IP或者域名:" setCDNDomain + ;; + 6) + echo >/etc/v2ray-agent/cdn + echoContent green " ---> 移除成功" + exit 0 + ;; + esac + + if [[ -n "${setCDNDomain}" ]]; then + echo >/etc/v2ray-agent/cdn + echo "${setCDNDomain}" >"/etc/v2ray-agent/cdn" + echoContent green " ---> 修改CDN成功" + subscribe false false + else + echoContent red " ---> 不可以为空,请重新输入" + manageCDN 1 + fi + else + echoContent yellow "\n教程地址:" + echoContent skyBlue "https://www.v2ray-agent.com/archives/cloudflarezi-xuan-ip\n" + echoContent red " ---> 未检测到可以使用的协议,仅支持ws、grpc、HTTPUpgrade相关的协议" + fi +} +# 自定义uuid +customUUID() { + read -r -p "请输入合法的UUID,[回车]随机UUID:" currentCustomUUID + echo + if [[ -z "${currentCustomUUID}" ]]; then + if [[ "${selectInstallType}" == "1" || "${coreInstallType}" == "1" ]]; then + currentCustomUUID=$(${ctlPath} uuid) + elif [[ "${selectInstallType}" == "2" || "${coreInstallType}" == "2" ]]; then + currentCustomUUID=$(${ctlPath} generate uuid) + fi + + echoContent yellow "uuid:${currentCustomUUID}\n" + + else + local checkUUID= + if [[ "${coreInstallType}" == "1" ]]; then + checkUUID=$(jq -r --arg currentUUID "$currentCustomUUID" ".inbounds[0].settings.clients[] | select(.uuid | index(\$currentUUID) != null) | .name" ${configPath}${frontingType}.json) + elif [[ "${coreInstallType}" == "2" ]]; then + checkUUID=$(jq -r --arg currentUUID "$currentCustomUUID" ".inbounds[0].users[] | select(.uuid | index(\$currentUUID) != null) | .name//.username" ${configPath}${frontingType}.json) + fi + + if [[ -n "${checkUUID}" ]]; then + echoContent red " ---> UUID不可重复" + exit 0 + fi + fi +} + +# 自定义email +customUserEmail() { + read -r -p "请输入合法的email,[回车]随机email:" currentCustomEmail + echo + if [[ -z "${currentCustomEmail}" ]]; then + currentCustomEmail="${currentCustomUUID}" + echoContent yellow "email: ${currentCustomEmail}\n" + else + local checkEmail= + if [[ "${coreInstallType}" == "1" ]]; then + local frontingTypeConfig="${frontingType}" + if [[ "${currentInstallProtocolType}" == ",7,8," ]]; then + frontingTypeConfig="07_VLESS_vision_reality_inbounds" + fi + + checkEmail=$(jq -r --arg currentEmail "$currentCustomEmail" ".inbounds[0].settings.clients[] | select(.name | index(\$currentEmail) != null) | .name" ${configPath}${frontingTypeConfig}.json) + elif + [[ "${coreInstallType}" == "2" ]] + then + checkEmail=$(jq -r --arg currentEmail "$currentCustomEmail" ".inbounds[0].users[] | select(.name | index(\$currentEmail) != null) | .name" ${configPath}${frontingType}.json) + fi + + if [[ -n "${checkEmail}" ]]; then + echoContent red " ---> email不可重复" + exit 0 + fi + fi +} + +# 添加用户 +addUser() { + read -r -p "请输入要添加的用户数量:" userNum + echo + if [[ -z ${userNum} || ${userNum} -le 0 ]]; then + echoContent red " ---> 输入有误,请重新输入" + exit 0 + fi + local userConfig= + if [[ "${coreInstallType}" == "1" ]]; then + userConfig=".inbounds[0].settings.clients" + elif [[ "${coreInstallType}" == "2" ]]; then + userConfig=".inbounds[0].users" + fi + + while [[ ${userNum} -gt 0 ]]; do + readConfigHostPathUUID + local users= + ((userNum--)) || true + + customUUID + customUserEmail + + uuid=${currentCustomUUID} + email=${currentCustomEmail} + + # VLESS TCP + if echo "${currentInstallProtocolType}" | grep -q ",0,"; then + local clients= + if [[ "${coreInstallType}" == "1" ]]; then + clients=$(initXrayClients 0 "${uuid}" "${email}") + elif [[ "${coreInstallType}" == "2" ]]; then + clients=$(initSingBoxClients 0 "${uuid}" "${email}") + fi + clients=$(jq -r "${userConfig} = ${clients}" ${configPath}02_VLESS_TCP_inbounds.json) + echo "${clients}" | jq . >${configPath}02_VLESS_TCP_inbounds.json + fi + + # VLESS WS + if echo "${currentInstallProtocolType}" | grep -q ",1,"; then + local clients= + if [[ "${coreInstallType}" == "1" ]]; then + clients=$(initXrayClients 1 "${uuid}" "${email}") + elif [[ "${coreInstallType}" == "2" ]]; then + clients=$(initSingBoxClients 1 "${uuid}" "${email}") + fi + + clients=$(jq -r "${userConfig} = ${clients}" ${configPath}03_VLESS_WS_inbounds.json) + echo "${clients}" | jq . >${configPath}03_VLESS_WS_inbounds.json + fi + + # trojan grpc + if echo "${currentInstallProtocolType}" | grep -q ",2,"; then + local clients= + if [[ "${coreInstallType}" == "1" ]]; then + clients=$(initXrayClients 2 "${uuid}" "${email}") + elif [[ "${coreInstallType}" == "2" ]]; then + clients=$(initSingBoxClients 2 "${uuid}" "${email}") + fi + + clients=$(jq -r "${userConfig} = ${clients}" ${configPath}04_trojan_gRPC_inbounds.json) + echo "${clients}" | jq . >${configPath}04_trojan_gRPC_inbounds.json + fi + # VMess WS + if echo "${currentInstallProtocolType}" | grep -q ",3,"; then + local clients= + if [[ "${coreInstallType}" == "1" ]]; then + clients=$(initXrayClients 3 "${uuid}" "${email}") + elif [[ "${coreInstallType}" == "2" ]]; then + clients=$(initSingBoxClients 3 "${uuid}" "${email}") + fi + + clients=$(jq -r "${userConfig} = ${clients}" ${configPath}05_VMess_WS_inbounds.json) + echo "${clients}" | jq . >${configPath}05_VMess_WS_inbounds.json + fi + # trojan tcp + if echo "${currentInstallProtocolType}" | grep -q ",4,"; then + local clients= + if [[ "${coreInstallType}" == "1" ]]; then + clients=$(initXrayClients 4 "${uuid}" "${email}") + elif [[ "${coreInstallType}" == "2" ]]; then + clients=$(initSingBoxClients 4 "${uuid}" "${email}") + fi + clients=$(jq -r "${userConfig} = ${clients}" ${configPath}04_trojan_TCP_inbounds.json) + echo "${clients}" | jq . >${configPath}04_trojan_TCP_inbounds.json + fi + + # vless grpc + if echo "${currentInstallProtocolType}" | grep -q ",5,"; then + local clients= + if [[ "${coreInstallType}" == "1" ]]; then + clients=$(initXrayClients 5 "${uuid}" "${email}") + elif [[ "${coreInstallType}" == "2" ]]; then + clients=$(initSingBoxClients 5 "${uuid}" "${email}") + fi + clients=$(jq -r "${userConfig} = ${clients}" ${configPath}06_VLESS_gRPC_inbounds.json) + echo "${clients}" | jq . >${configPath}06_VLESS_gRPC_inbounds.json + fi + + # vless reality vision + if echo "${currentInstallProtocolType}" | grep -q ",7,"; then + local clients= + if [[ "${coreInstallType}" == "1" ]]; then + clients=$(initXrayClients 7 "${uuid}" "${email}") + elif [[ "${coreInstallType}" == "2" ]]; then + clients=$(initSingBoxClients 7 "${uuid}" "${email}") + fi + clients=$(jq -r "${userConfig} = ${clients}" ${configPath}07_VLESS_vision_reality_inbounds.json) + echo "${clients}" | jq . >${configPath}07_VLESS_vision_reality_inbounds.json + fi + + # vless reality grpc + if echo "${currentInstallProtocolType}" | grep -q ",8,"; then + local clients= + if [[ "${coreInstallType}" == "1" ]]; then + clients=$(initXrayClients 8 "${uuid}" "${email}") + elif [[ "${coreInstallType}" == "2" ]]; then + clients=$(initSingBoxClients 8 "${uuid}" "${email}") + fi + clients=$(jq -r "${userConfig} = ${clients}" ${configPath}08_VLESS_vision_gRPC_inbounds.json) + echo "${clients}" | jq . >${configPath}08_VLESS_vision_gRPC_inbounds.json + fi + + # hysteria2 + if echo ${currentInstallProtocolType} | grep -q ",6,"; then + local clients= + + if [[ "${coreInstallType}" == "1" ]]; then + clients=$(initXrayClients 6 "${uuid}" "${email}") + elif [[ -n "${singBoxConfigPath}" ]]; then + clients=$(initSingBoxClients 6 "${uuid}" "${email}") + fi + + clients=$(jq -r ".inbounds[0].users = ${clients}" "${singBoxConfigPath}06_hysteria2_inbounds.json") + echo "${clients}" | jq . >"${singBoxConfigPath}06_hysteria2_inbounds.json" + fi + + # tuic + if echo ${currentInstallProtocolType} | grep -q ",9,"; then + local clients= + if [[ "${coreInstallType}" == "1" ]]; then + clients=$(initXrayClients 9 "${uuid}" "${email}") + elif [[ "${coreInstallType}" == "2" ]]; then + clients=$(initSingBoxClients 9 "${uuid}" "${email}") + fi + + clients=$(jq -r ".inbounds[0].users = ${clients}" "${singBoxConfigPath}09_tuic_inbounds.json") + + echo "${clients}" | jq . >"${singBoxConfigPath}09_tuic_inbounds.json" + fi + # naive + if echo ${currentInstallProtocolType} | grep -q ",10,"; then + local clients= + clients=$(initSingBoxClients 10 "${uuid}" "${email}") + clients=$(jq -r ".inbounds[0].users = ${clients}" "${singBoxConfigPath}10_naive_inbounds.json") + + echo "${clients}" | jq . >"${singBoxConfigPath}10_naive_inbounds.json" + fi + # VMess WS + if echo "${currentInstallProtocolType}" | grep -q ",11,"; then + local clients= + if [[ "${coreInstallType}" == "1" ]]; then + clients=$(initXrayClients 11 "${uuid}" "${email}") + elif [[ "${coreInstallType}" == "2" ]]; then + clients=$(initSingBoxClients 11 "${uuid}" "${email}") + fi + + clients=$(jq -r "${userConfig} = ${clients}" ${configPath}11_VMess_HTTPUpgrade_inbounds.json) + echo "${clients}" | jq . >${configPath}11_VMess_HTTPUpgrade_inbounds.json + fi + done + reloadCore + echoContent green " ---> 添加完成" + subscribe false + manageAccount 1 +} +# 移除用户 +removeUser() { + local userConfigType= + if [[ -n "${frontingType}" ]]; then + userConfigType="${frontingType}" + elif [[ -n "${frontingTypeReality}" ]]; then + userConfigType="${frontingTypeReality}" + fi + + local uuid= + if [[ -n "${userConfigType}" ]]; then + if [[ "${coreInstallType}" == "1" ]]; then + jq -r -c .inbounds[0].settings.clients[].email ${configPath}${userConfigType}.json | awk '{print NR""":"$0}' + elif [[ "${coreInstallType}" == "2" ]]; then + jq -r -c .inbounds[0].users[].name//.inbounds[0].users[].username ${configPath}${userConfigType}.json | awk '{print NR""":"$0}' + fi + + read -r -p "请选择要删除的用户编号[仅支持单个删除]:" delUserIndex + if [[ $(jq -r '.inbounds[0].settings.clients|length' ${configPath}${userConfigType}.json) -lt ${delUserIndex} && $(jq -r '.inbounds[0].users|length' ${configPath}${userConfigType}.json) -lt ${delUserIndex} ]]; then + echoContent red " ---> 选择错误" + else + delUserIndex=$((delUserIndex - 1)) + fi + fi + + if [[ -n "${delUserIndex}" ]]; then + + if echo ${currentInstallProtocolType} | grep -q ",0,"; then + local vlessVision + vlessVision=$(jq -r 'del(.inbounds[0].settings.clients['${delUserIndex}']//.inbounds[0].users['${delUserIndex}'])' ${configPath}02_VLESS_TCP_inbounds.json) + echo "${vlessVision}" | jq . >${configPath}02_VLESS_TCP_inbounds.json + fi + if echo ${currentInstallProtocolType} | grep -q ",1,"; then + local vlessWSResult + vlessWSResult=$(jq -r 'del(.inbounds[0].settings.clients['${delUserIndex}'])' ${configPath}03_VLESS_WS_inbounds.json) + echo "${vlessWSResult}" | jq . >${configPath}03_VLESS_WS_inbounds.json + fi + + if echo ${currentInstallProtocolType} | grep -q ",2,"; then + local trojangRPCUsers + trojangRPCUsers=$(jq -r 'del(.inbounds[0].settings.clients['${delUserIndex}'])' ${configPath}04_trojan_gRPC_inbounds.json) + echo "${trojangRPCUsers}" | jq . >${configPath}04_trojan_gRPC_inbounds.json + fi + + if echo ${currentInstallProtocolType} | grep -q ",3,"; then + local vmessWSResult + vmessWSResult=$(jq -r 'del(.inbounds[0].settings.clients['${delUserIndex}']//.inbounds[0].users['${delUserIndex}'])' ${configPath}05_VMess_WS_inbounds.json) + echo "${vmessWSResult}" | jq . >${configPath}05_VMess_WS_inbounds.json + fi + + if echo ${currentInstallProtocolType} | grep -q ",5,"; then + local vlessGRPCResult + vlessGRPCResult=$(jq -r 'del(.inbounds[0].settings.clients['${delUserIndex}']//.inbounds[0].users['${delUserIndex}'])' ${configPath}06_VLESS_gRPC_inbounds.json) + echo "${vlessGRPCResult}" | jq . >${configPath}06_VLESS_gRPC_inbounds.json + fi + + if echo ${currentInstallProtocolType} | grep -q ",4,"; then + local trojanTCPResult + trojanTCPResult=$(jq -r 'del(.inbounds[0].settings.clients['${delUserIndex}']//.inbounds[0].users['${delUserIndex}'])' ${configPath}04_trojan_TCP_inbounds.json) + echo "${trojanTCPResult}" | jq . >${configPath}04_trojan_TCP_inbounds.json + fi + + if echo ${currentInstallProtocolType} | grep -q ",7,"; then + local vlessRealityResult + vlessRealityResult=$(jq -r 'del(.inbounds[0].settings.clients['${delUserIndex}']//.inbounds[0].users['${delUserIndex}'])' ${configPath}07_VLESS_vision_reality_inbounds.json) + echo "${vlessRealityResult}" | jq . >${configPath}07_VLESS_vision_reality_inbounds.json + fi + if echo ${currentInstallProtocolType} | grep -q ",8,"; then + local vlessRealityGRPCResult + vlessRealityGRPCResult=$(jq -r 'del(.inbounds[0].settings.clients['${delUserIndex}']//.inbounds[0].users['${delUserIndex}'])' ${configPath}08_VLESS_vision_gRPC_inbounds.json) + echo "${vlessRealityGRPCResult}" | jq . >${configPath}08_VLESS_vision_gRPC_inbounds.json + fi + + if echo ${currentInstallProtocolType} | grep -q ",6,"; then + local hysteriaResult + hysteriaResult=$(jq -r 'del(.inbounds[0].users['${delUserIndex}']//.inbounds[0].users['${delUserIndex}'])' "${singBoxConfigPath}06_hysteria2_inbounds.json") + echo "${hysteriaResult}" | jq . >"${singBoxConfigPath}06_hysteria2_inbounds.json" + fi + if echo ${currentInstallProtocolType} | grep -q ",9,"; then + local tuicResult + tuicResult=$(jq -r 'del(.inbounds[0].users['${delUserIndex}']//.inbounds[0].users['${delUserIndex}'])' "${singBoxConfigPath}09_tuic_inbounds.json") + echo "${tuicResult}" | jq . >"${singBoxConfigPath}09_tuic_inbounds.json" + fi + if echo ${currentInstallProtocolType} | grep -q ",10,"; then + local naiveResult + naiveResult=$(jq -r 'del(.inbounds[0].users['${delUserIndex}']//.inbounds[0].users['${delUserIndex}'])' "${singBoxConfigPath}10_naive_inbounds.json") + echo "${naiveResult}" | jq . >"${singBoxConfigPath}10_naive_inbounds.json" + fi + # VMess HTTPUpgrade + if echo ${currentInstallProtocolType} | grep -q ",11,"; then + local vmessHTTPUpgradeResult + vmessHTTPUpgradeResult=$(jq -r 'del(.inbounds[0].users['${delUserIndex}']//.inbounds[0].users['${delUserIndex}'])' "${singBoxConfigPath}11_VMess_HTTPUpgrade_inbounds.json") + echo "${vmessHTTPUpgradeResult}" | jq . >"${singBoxConfigPath}11_VMess_HTTPUpgrade_inbounds.json" + echo "${vmessHTTPUpgradeResult}" | jq . >${configPath}11_VMess_HTTPUpgrade_inbounds.json + fi + reloadCore + subscribe false + fi + manageAccount 1 +} +# 更新脚本 +updateV2RayAgent() { + echoContent skyBlue "\n进度 $1/${totalProgress} : 更新v2ray-agent脚本" + rm -rf /etc/v2ray-agent/install.sh + if [[ "${release}" == "alpine" ]]; then + wget -c -q -P /etc/v2ray-agent/ -N --no-check-certificate "https://raw.githubusercontent.com/mack-a/v2ray-agent/master/install.sh" + else + wget -c -q "${wgetShowProgressStatus}" -P /etc/v2ray-agent/ -N --no-check-certificate "https://raw.githubusercontent.com/mack-a/v2ray-agent/master/install.sh" + fi + + sudo chmod 700 /etc/v2ray-agent/install.sh + local version + version=$(grep '当前版本:v' "/etc/v2ray-agent/install.sh" | awk -F "[v]" '{print $2}' | tail -n +2 | head -n 1 | awk -F "[\"]" '{print $1}') + + echoContent green "\n ---> 更新完毕" + echoContent yellow " ---> 请手动执行[vasma]打开脚本" + echoContent green " ---> 当前版本:${version}\n" + echoContent yellow "如更新不成功,请手动执行下面命令\n" + echoContent skyBlue "wget -P /root -N --no-check-certificate https://raw.githubusercontent.com/mack-a/v2ray-agent/master/install.sh && chmod 700 /root/install.sh && /root/install.sh" + echo + exit 0 +} + +# 防火墙 +handleFirewall() { + if systemctl status ufw 2>/dev/null | grep -q "active (exited)" && [[ "$1" == "stop" ]]; then + systemctl stop ufw >/dev/null 2>&1 + systemctl disable ufw >/dev/null 2>&1 + echoContent green " ---> ufw关闭成功" + + fi + + if systemctl status firewalld 2>/dev/null | grep -q "active (running)" && [[ "$1" == "stop" ]]; then + systemctl stop firewalld >/dev/null 2>&1 + systemctl disable firewalld >/dev/null 2>&1 + echoContent green " ---> firewalld关闭成功" + fi +} + +# 安装BBR +bbrInstall() { + echoContent red "\n==============================================================" + echoContent green "BBR、DD脚本用的[ylx2016]的成熟作品,地址[https://github.com/ylx2016/Linux-NetSpeed],请熟知" + echoContent yellow "1.安装脚本【推荐原版BBR+FQ】" + echoContent yellow "2.回退主目录" + echoContent red "==============================================================" + read -r -p "请选择:" installBBRStatus + if [[ "${installBBRStatus}" == "1" ]]; then + wget -N --no-check-certificate "https://raw.githubusercontent.com/ylx2016/Linux-NetSpeed/master/tcp.sh" && chmod +x tcp.sh && ./tcp.sh + else + menu + fi +} + +# 查看、检查日志 +checkLog() { + if [[ "${coreInstallType}" == "2" ]]; then + echoContent red "\n ---> 此功能仅支持Xray-core内核" + exit 0 + fi + if [[ -z "${configPath}" && -z "${realityStatus}" ]]; then + echoContent red " ---> 没有检测到安装目录,请执行脚本安装内容" + exit 0 + fi + local realityLogShow= + local logStatus=false + if grep -q "access" ${configPath}00_log.json; then + logStatus=true + fi + + echoContent skyBlue "\n功能 $1/${totalProgress} : 查看日志" + echoContent red "\n==============================================================" + echoContent yellow "# 建议仅调试时打开access日志\n" + + if [[ "${logStatus}" == "false" ]]; then + echoContent yellow "1.打开access日志" + else + echoContent yellow "1.关闭access日志" + fi + + echoContent yellow "2.监听access日志" + echoContent yellow "3.监听error日志" + echoContent yellow "4.查看证书定时任务日志" + echoContent yellow "5.查看证书安装日志" + echoContent yellow "6.清空日志" + echoContent red "==============================================================" + + read -r -p "请选择:" selectAccessLogType + local configPathLog=${configPath//conf\//} + + case ${selectAccessLogType} in + 1) + if [[ "${logStatus}" == "false" ]]; then + realityLogShow=true + cat <${configPath}00_log.json +{ + "log": { + "access":"${configPathLog}access.log", + "error": "${configPathLog}error.log", + "loglevel": "debug" + } +} +EOF + elif [[ "${logStatus}" == "true" ]]; then + realityLogShow=false + cat <${configPath}00_log.json +{ + "log": { + "error": "${configPathLog}error.log", + "loglevel": "warning" + } +} +EOF + fi + + if [[ -n ${realityStatus} ]]; then + local vlessVisionRealityInbounds + vlessVisionRealityInbounds=$(jq -r ".inbounds[0].streamSettings.realitySettings.show=${realityLogShow}" ${configPath}07_VLESS_vision_reality_inbounds.json) + echo "${vlessVisionRealityInbounds}" | jq . >${configPath}07_VLESS_vision_reality_inbounds.json + fi + reloadCore + checkLog 1 + ;; + 2) + tail -f ${configPathLog}access.log + ;; + 3) + tail -f ${configPathLog}error.log + ;; + 4) + if [[ ! -f "/etc/v2ray-agent/crontab_tls.log" ]]; then + touch /etc/v2ray-agent/crontab_tls.log + fi + tail -n 100 /etc/v2ray-agent/crontab_tls.log + ;; + 5) + tail -n 100 /etc/v2ray-agent/tls/acme.log + ;; + 6) + echo >${configPathLog}access.log + echo >${configPathLog}error.log + ;; + esac +} + +# 脚本快捷方式 +aliasInstall() { + + if [[ -f "$HOME/install.sh" ]] && [[ -d "/etc/v2ray-agent" ]] && grep <"$HOME/install.sh" -q "作者:mack-a"; then + mv "$HOME/install.sh" /etc/v2ray-agent/install.sh + local vasmaType= + if [[ -d "/usr/bin/" ]]; then + if [[ ! -f "/usr/bin/vasma" ]]; then + ln -s /etc/v2ray-agent/install.sh /usr/bin/vasma + chmod 700 /usr/bin/vasma + vasmaType=true + fi + + rm -rf "$HOME/install.sh" + elif [[ -d "/usr/sbin" ]]; then + if [[ ! -f "/usr/sbin/vasma" ]]; then + ln -s /etc/v2ray-agent/install.sh /usr/sbin/vasma + chmod 700 /usr/sbin/vasma + vasmaType=true + fi + rm -rf "$HOME/install.sh" + fi + if [[ "${vasmaType}" == "true" ]]; then + echoContent green "快捷方式创建成功,可执行[vasma]重新打开脚本" + fi + fi +} + +# 检查ipv6、ipv4 +checkIPv6() { + currentIPv6IP=$(curl -s -6 -m 4 http://www.cloudflare.com/cdn-cgi/trace | grep "ip" | cut -d "=" -f 2) + + if [[ -z "${currentIPv6IP}" ]]; then + echoContent red " ---> 不支持ipv6" + exit 0 + fi +} + +# ipv6 分流 +ipv6Routing() { + if [[ -z "${configPath}" ]]; then + echoContent red " ---> 未安装,请使用脚本安装" + menu + exit 0 + fi + + checkIPv6 + echoContent skyBlue "\n功能 1/${totalProgress} : IPv6分流" + echoContent red "\n==============================================================" + echoContent yellow "1.查看已分流域名" + echoContent yellow "2.添加域名" + echoContent yellow "3.设置IPv6全局" + echoContent yellow "4.卸载IPv6分流" + echoContent red "==============================================================" + read -r -p "请选择:" ipv6Status + if [[ "${ipv6Status}" == "1" ]]; then + showIPv6Routing + exit 0 + elif [[ "${ipv6Status}" == "2" ]]; then + echoContent red "==============================================================" + echoContent yellow "# 注意事项\n" + echoContent yellow "# 注意事项" + echoContent yellow "# 使用教程:https://www.v2ray-agent.com/archives/1683226921000 \n" + + read -r -p "请按照上面示例录入域名:" domainList + if [[ "${coreInstallType}" == "1" ]]; then + addInstallRouting IPv6_out outboundTag "${domainList}" + addXrayOutbound IPv6_out + fi + + if [[ -n "${singBoxConfigPath}" ]]; then + addSingBoxRouteRule "IPv6_out" "${domainList}" "IPv6_route" + addSingBoxOutbound 01_direct_outbound + addSingBoxOutbound IPv6_out + addSingBoxOutbound IPv4_out + fi + + echoContent green " ---> 添加完毕" + + elif [[ "${ipv6Status}" == "3" ]]; then + + echoContent red "==============================================================" + echoContent yellow "# 注意事项\n" + echoContent yellow "1.会删除所有设置的分流规则" + echoContent yellow "2.会删除IPv6之外的所有出站规则\n" + read -r -p "是否确认设置?[y/n]:" IPv6OutStatus + + if [[ "${IPv6OutStatus}" == "y" ]]; then + if [[ "${coreInstallType}" == "1" ]]; then + addXrayOutbound IPv6_out + removeXrayOutbound IPv4_out + removeXrayOutbound z_direct_outbound + removeXrayOutbound blackhole_out + removeXrayOutbound wireguard_out_IPv4 + removeXrayOutbound wireguard_out_IPv6 + removeXrayOutbound socks5_outbound + + rm ${configPath}09_routing.json >/dev/null 2>&1 + fi + if [[ -n "${singBoxConfigPath}" ]]; then + + removeSingBoxConfig IPv4_out + + removeSingBoxConfig wireguard_endpoints_IPv4_route + removeSingBoxConfig wireguard_endpoints_IPv6_route + removeSingBoxConfig wireguard_endpoints_IPv4 + removeSingBoxConfig wireguard_endpoints_IPv6 + + removeSingBoxConfig socks5_inbound_route + + removeSingBoxConfig IPv6_route + + removeSingBoxConfig 01_direct_outbound + + addSingBoxOutbound IPv6_out + + fi + + echoContent green " ---> IPv6全局出站设置完毕" + else + + echoContent green " ---> 放弃设置" + exit 0 + fi + + elif [[ "${ipv6Status}" == "4" ]]; then + if [[ "${coreInstallType}" == "1" ]]; then + unInstallRouting IPv6_out outboundTag + + removeXrayOutbound IPv6_out + addXrayOutbound "z_direct_outbound" + fi + + if [[ -n "${singBoxConfigPath}" ]]; then + removeSingBoxConfig IPv6_out + removeSingBoxConfig "IPv6_route" + addSingBoxOutbound "01_direct_outbound" + fi + + echoContent green " ---> IPv6分流卸载成功" + else + echoContent red " ---> 选择错误" + exit 0 + fi + + reloadCore +} + +# ipv6分流规则展示 +showIPv6Routing() { + if [[ "${coreInstallType}" == "1" ]]; then + if [[ -f "${configPath}09_routing.json" ]]; then + echoContent yellow "Xray-core:" + jq -r -c '.routing.rules[]|select (.outboundTag=="IPv6_out")|.domain' ${configPath}09_routing.json | jq -r + elif [[ ! -f "${configPath}09_routing.json" && -f "${configPath}IPv6_out.json" ]]; then + echoContent yellow "Xray-core" + echoContent green " ---> 已设置IPv6全局分流" + else + echoContent yellow " ---> 未安装IPv6分流" + fi + + fi + if [[ -n "${singBoxConfigPath}" ]]; then + if [[ -f "${singBoxConfigPath}IPv6_route.json" ]]; then + echoContent yellow "sing-box" + jq -r -c '.route.rules[]|select (.outbound=="IPv6_out")' "${singBoxConfigPath}IPv6_route.json" | jq -r + elif [[ ! -f "${singBoxConfigPath}IPv6_route.json" && -f "${singBoxConfigPath}IPv6_out.json" ]]; then + echoContent yellow "sing-box" + echoContent green " ---> 已设置IPv6全局分流" + else + echoContent yellow " ---> 未安装IPv6分流" + fi + fi +} +# bt下载管理 +btTools() { + if [[ "${coreInstallType}" == "2" ]]; then + echoContent red "\n ---> 此功能仅支持Xray-core内核,请等待后续更新" + exit 0 + fi + if [[ -z "${configPath}" ]]; then + echoContent red " ---> 未安装,请使用脚本安装" + menu + exit 0 + fi + + echoContent skyBlue "\n功能 1/${totalProgress} : bt下载管理" + echoContent red "\n==============================================================" + + if [[ -f ${configPath}09_routing.json ]] && grep -q bittorrent <${configPath}09_routing.json; then + echoContent yellow "当前状态:已禁止下载BT" + else + echoContent yellow "当前状态:允许下载BT" + fi + + echoContent yellow "1.禁止下载BT" + echoContent yellow "2.允许下载BT" + echoContent red "==============================================================" + read -r -p "请选择:" btStatus + if [[ "${btStatus}" == "1" ]]; then + + if [[ -f "${configPath}09_routing.json" ]]; then + + unInstallRouting blackhole_out outboundTag bittorrent + + routing=$(jq -r '.routing.rules += [{"type":"field","outboundTag":"blackhole_out","protocol":["bittorrent"]}]' ${configPath}09_routing.json) + + echo "${routing}" | jq . >${configPath}09_routing.json + + else + cat <${configPath}09_routing.json +{ + "routing":{ + "domainStrategy": "IPOnDemand", + "rules": [ + { + "type": "field", + "outboundTag": "blackhole_out", + "protocol": [ "bittorrent" ] + } + ] + } +} +EOF + fi + + installSniffing + removeXrayOutbound blackhole_out + addXrayOutbound blackhole_out + + echoContent green " ---> 禁止BT下载" + + elif [[ "${btStatus}" == "2" ]]; then + + unInstallSniffing + + unInstallRouting blackhole_out outboundTag bittorrent + + echoContent green " ---> 允许BT下载" + else + echoContent red " ---> 选择错误" + exit 0 + fi + + reloadCore +} + +# 域名黑名单 +blacklist() { + if [[ -z "${configPath}" ]]; then + echoContent red " ---> 未安装,请使用脚本安装" + menu + exit 0 + fi + + echoContent skyBlue "\n进度 $1/${totalProgress} : 域名黑名单" + echoContent red "\n==============================================================" + echoContent yellow "1.查看已屏蔽域名" + echoContent yellow "2.添加域名" + echoContent yellow "3.屏蔽大陆域名" + echoContent yellow "4.卸载黑名单" + echoContent red "==============================================================" + + read -r -p "请选择:" blacklistStatus + if [[ "${blacklistStatus}" == "1" ]]; then + jq -r -c '.routing.rules[]|select (.outboundTag=="blackhole_out")|.domain' ${configPath}09_routing.json | jq -r + exit 0 + elif [[ "${blacklistStatus}" == "2" ]]; then + echoContent red "==============================================================" + echoContent yellow "# 注意事项\n" + echoContent yellow "1.规则支持预定义域名列表[https://github.com/v2fly/domain-list-community]" + echoContent yellow "2.规则支持自定义域名" + echoContent yellow "3.录入示例:speedtest,facebook,cn,example.com" + echoContent yellow "4.如果域名在预定义域名列表中存在则使用 geosite:xx,如果不存在则默认使用输入的域名" + echoContent yellow "5.添加规则为增量配置,不会删除之前设置的内容\n" + read -r -p "请按照上面示例录入域名:" domainList + if [[ "${coreInstallType}" == "1" ]]; then + addInstallRouting blackhole_out outboundTag "${domainList}" + addXrayOutbound blackhole_out + fi + + if [[ -n "${singBoxConfigPath}" ]]; then + addSingBoxRouteRule "block_domain_outbound" "${domainList}" "block_domain_route" + addSingBoxOutbound "block_domain_outbound" + addSingBoxOutbound "01_direct_outbound" + fi + echoContent green " ---> 添加完毕" + + elif [[ "${blacklistStatus}" == "3" ]]; then + + if [[ "${coreInstallType}" == "1" ]]; then + unInstallRouting blackhole_out outboundTag + + addInstallRouting blackhole_out outboundTag "cn" + + addXrayOutbound blackhole_out + fi + + if [[ -n "${singBoxConfigPath}" ]]; then + + addSingBoxRouteRule "cn_block_outbound" "cn" "cn_block_route" + + addSingBoxRouteRule "01_direct_outbound" "googleapis.com,googleapis.cn,xn--ngstr-lra8j.com,gstatic.com" "cn_01_google_play_route" + + addSingBoxOutbound "cn_block_outbound" + addSingBoxOutbound "01_direct_outbound" + fi + + echoContent green " ---> 屏蔽大陆域名完毕" + + elif [[ "${blacklistStatus}" == "4" ]]; then + if [[ "${coreInstallType}" == "1" ]]; then + unInstallRouting blackhole_out outboundTag + fi + + if [[ -n "${singBoxConfigPath}" ]]; then + removeSingBoxConfig "cn_block_route" + removeSingBoxConfig "cn_block_outbound" + + removeSingBoxConfig "cn_01_google_play_route" + + removeSingBoxConfig "block_domain_route" + removeSingBoxConfig "block_domain_outbound" + fi + echoContent green " ---> 域名黑名单删除完毕" + else + echoContent red " ---> 选择错误" + exit 0 + fi + reloadCore +} +# 添加routing配置 +addInstallRouting() { + + local tag=$1 # warp-socks + local type=$2 # outboundTag/inboundTag + local domain=$3 # 域名 + + if [[ -z "${tag}" || -z "${type}" || -z "${domain}" ]]; then + echoContent red " ---> 参数错误" + exit 0 + fi + + local routingRule= + if [[ ! -f "${configPath}09_routing.json" ]]; then + cat <${configPath}09_routing.json +{ + "routing":{ + "type": "field", + "rules": [ + { + "type": "field", + "domain": [ + ], + "outboundTag": "${tag}" + } + ] + } +} +EOF + fi + local routingRule= + routingRule=$(jq -r ".routing.rules[]|select(.outboundTag==\"${tag}\" and (.protocol == null))" ${configPath}09_routing.json) + + if [[ -z "${routingRule}" ]]; then + routingRule="{\"type\": \"field\",\"domain\": [],\"outboundTag\": \"${tag}\"}" + fi + + while read -r line; do + if echo "${routingRule}" | grep -q "${line}"; then + echoContent yellow " ---> ${line}已存在,跳过" + else + local geositeStatus + geositeStatus=$(curl -s "https://api.github.com/repos/v2fly/domain-list-community/contents/data/${line}" | jq .message) + + if [[ "${geositeStatus}" == "null" ]]; then + routingRule=$(echo "${routingRule}" | jq -r '.domain += ["geosite:'"${line}"'"]') + else + routingRule=$(echo "${routingRule}" | jq -r '.domain += ["domain:'"${line}"'"]') + fi + fi + done < <(echo "${domain}" | tr ',' '\n') + + unInstallRouting "${tag}" "${type}" + if ! grep -q "gstatic.com" ${configPath}09_routing.json && [[ "${tag}" == "blackhole_out" ]]; then + local routing= + routing=$(jq -r ".routing.rules += [{\"type\": \"field\",\"domain\": [\"gstatic.com\"],\"outboundTag\": \"direct\"}]" ${configPath}09_routing.json) + echo "${routing}" | jq . >${configPath}09_routing.json + fi + + routing=$(jq -r ".routing.rules += [${routingRule}]" ${configPath}09_routing.json) + echo "${routing}" | jq . >${configPath}09_routing.json +} +# 根据tag卸载Routing +unInstallRouting() { + local tag=$1 + local type=$2 + local protocol=$3 + + if [[ -f "${configPath}09_routing.json" ]]; then + local routing= + if [[ -n "${protocol}" ]]; then + routing=$(jq -r "del(.routing.rules[] | select(.${type} == \"${tag}\" and (.protocol | index(\"${protocol}\"))))" ${configPath}09_routing.json) + echo "${routing}" | jq . >${configPath}09_routing.json + else + routing=$(jq -r "del(.routing.rules[] | select(.${type} == \"${tag}\" and (.protocol == null )))" ${configPath}09_routing.json) + echo "${routing}" | jq . >${configPath}09_routing.json + fi + fi +} + +# 卸载嗅探 +unInstallSniffing() { + + find ${configPath} -name "*inbounds.json*" | awk -F "[c][o][n][f][/]" '{print $2}' | while read -r inbound; do + if grep -q "destOverride" <"${configPath}${inbound}"; then + sniffing=$(jq -r 'del(.inbounds[0].sniffing)' "${configPath}${inbound}") + echo "${sniffing}" | jq . >"${configPath}${inbound}" + fi + done + +} + +# 安装嗅探 +installSniffing() { + readInstallType + if [[ "${coreInstallType}" == "1" ]]; then + if [[ -f "${configPath}02_VLESS_TCP_inbounds.json" ]]; then + if ! grep -q "destOverride" <"${configPath}02_VLESS_TCP_inbounds.json"; then + sniffing=$(jq -r '.inbounds[0].sniffing = {"enabled":true,"destOverride":["http","tls","quic"]}' "${configPath}02_VLESS_TCP_inbounds.json") + echo "${sniffing}" | jq . >"${configPath}02_VLESS_TCP_inbounds.json" + fi + fi + fi +} + +# 读取第三方warp配置 +readConfigWarpReg() { + if [[ ! -f "/etc/v2ray-agent/warp/config" ]]; then + /etc/v2ray-agent/warp/warp-reg >/etc/v2ray-agent/warp/config + fi + + secretKeyWarpReg=$(grep <"/etc/v2ray-agent/warp/config" private_key | awk '{print $2}') + + addressWarpReg=$(grep <"/etc/v2ray-agent/warp/config" v6 | awk '{print $2}') + + publicKeyWarpReg=$(grep <"/etc/v2ray-agent/warp/config" public_key | awk '{print $2}') + + reservedWarpReg=$(grep <"/etc/v2ray-agent/warp/config" reserved | awk -F "[:]" '{print $2}') + +} +# 安装warp-reg工具 +installWarpReg() { + if [[ ! -f "/etc/v2ray-agent/warp/warp-reg" ]]; then + echo + echoContent yellow "# 注意事项" + echoContent yellow "# 依赖第三方程序,请熟知其中风险" + echoContent yellow "# 项目地址:https://github.com/badafans/warp-reg \n" + + read -r -p "warp-reg未安装,是否安装 ?[y/n]:" installWarpRegStatus + + if [[ "${installWarpRegStatus}" == "y" ]]; then + + curl -sLo /etc/v2ray-agent/warp/warp-reg "https://github.com/badafans/warp-reg/releases/download/v1.0/${warpRegCoreCPUVendor}" + chmod 655 /etc/v2ray-agent/warp/warp-reg + + else + echoContent yellow " ---> 放弃安装" + exit 0 + fi + fi +} + +# 展示warp分流域名 +showWireGuardDomain() { + local type=$1 + # xray + if [[ "${coreInstallType}" == "1" ]]; then + if [[ -f "${configPath}09_routing.json" ]]; then + echoContent yellow "Xray-core" + jq -r -c '.routing.rules[]|select (.outboundTag=="wireguard_out_'"${type}"'")|.domain' ${configPath}09_routing.json | jq -r + elif [[ ! -f "${configPath}09_routing.json" && -f "${configPath}wireguard_out_${type}.json" ]]; then + echoContent yellow "Xray-core" + echoContent green " ---> 已设置warp ${type}全局分流" + else + echoContent yellow " ---> 未安装warp ${type}分流" + fi + fi + + # sing-box + if [[ -n "${singBoxConfigPath}" ]]; then + if [[ -f "${singBoxConfigPath}wireguard_endpoints_${type}_route.json" ]]; then + echoContent yellow "sing-box" + jq -r -c '.route.rules[]' "${singBoxConfigPath}wireguard_endpoints_${type}_route.json" | jq -r + elif [[ ! -f "${singBoxConfigPath}wireguard_endpoints_${type}_route.json" && -f "${singBoxConfigPath}wireguard_endpoints_${type}.json" ]]; then + echoContent yellow "sing-box" + echoContent green " ---> 已设置warp ${type}全局分流" + else + echoContent yellow " ---> 未安装warp ${type}分流" + fi + fi + +} + +# 添加WireGuard分流 +addWireGuardRoute() { + local type=$1 + local tag=$2 + local domainList=$3 + # xray + if [[ "${coreInstallType}" == "1" ]]; then + + addInstallRouting "wireguard_out_${type}" "${tag}" "${domainList}" + addXrayOutbound "wireguard_out_${type}" + fi + # sing-box + if [[ -n "${singBoxConfigPath}" ]]; then + + # rule + addSingBoxRouteRule "wireguard_endpoints_${type}" "${domainList}" "wireguard_endpoints_${type}_route" + # addSingBoxOutbound "wireguard_out_${type}" "wireguard_out" + if [[ -n "${domainList}" ]]; then + addSingBoxOutbound "01_direct_outbound" + fi + + # outbound + addSingBoxWireGuardEndpoints "${type}" + fi +} + +# 卸载wireGuard +unInstallWireGuard() { + local type=$1 + if [[ "${coreInstallType}" == "1" ]]; then + + if [[ "${type}" == "IPv4" ]]; then + if [[ ! -f "${configPath}wireguard_out_IPv6.json" ]]; then + rm -rf /etc/v2ray-agent/warp/config >/dev/null 2>&1 + fi + elif [[ "${type}" == "IPv6" ]]; then + if [[ ! -f "${configPath}wireguard_out_IPv4.json" ]]; then + rm -rf /etc/v2ray-agent/warp/config >/dev/null 2>&1 + fi + fi + fi + + if [[ -n "${singBoxConfigPath}" ]]; then + if [[ ! -f "${singBoxConfigPath}wireguard_endpoints_IPv6_route.json" && ! -f "${singBoxConfigPath}wireguard_endpoints_IPv4_route.json" ]]; then + rm "${singBoxConfigPath}wireguard_outbound.json" >/dev/null 2>&1 + rm -rf /etc/v2ray-agent/warp/config >/dev/null 2>&1 + fi + fi +} +# 移除WireGuard分流 +removeWireGuardRoute() { + local type=$1 + if [[ "${coreInstallType}" == "1" ]]; then + + unInstallRouting wireguard_out_"${type}" outboundTag + + removeXrayOutbound "wireguard_out_${type}" + if [[ ! -f "${configPath}IPv4_out.json" ]]; then + addXrayOutbound IPv4_out + fi + fi + + # sing-box + if [[ -n "${singBoxConfigPath}" ]]; then + removeSingBoxRouteRule "wireguard_endpoints_${type}" + fi + + unInstallWireGuard "${type}" +} +# warp分流-第三方IPv4 +warpRoutingReg() { + local type=$2 + echoContent skyBlue "\n进度 $1/${totalProgress} : WARP分流[第三方]" + echoContent red "==============================================================" + + echoContent yellow "1.查看已分流域名" + echoContent yellow "2.添加域名" + echoContent yellow "3.设置WARP全局" + echoContent yellow "4.卸载WARP分流" + echoContent red "==============================================================" + read -r -p "请选择:" warpStatus + installWarpReg + readConfigWarpReg + local address= + if [[ ${type} == "IPv4" ]]; then + address="172.16.0.2/32" + elif [[ ${type} == "IPv6" ]]; then + address="${addressWarpReg}/128" + else + echoContent red " ---> IP获取失败,退出安装" + fi + + if [[ "${warpStatus}" == "1" ]]; then + showWireGuardDomain "${type}" + exit 0 + elif [[ "${warpStatus}" == "2" ]]; then + echoContent yellow "# 注意事项" + echoContent yellow "# 支持sing-box、Xray-core" + echoContent yellow "# 使用教程:https://www.v2ray-agent.com/archives/1683226921000 \n" + + read -r -p "请按照上面示例录入域名:" domainList + addWireGuardRoute "${type}" outboundTag "${domainList}" + echoContent green " ---> 添加完毕" + + elif [[ "${warpStatus}" == "3" ]]; then + + echoContent red "==============================================================" + echoContent yellow "# 注意事项\n" + echoContent yellow "1.会删除所有设置的分流规则" + echoContent yellow "2.会删除除WARP[第三方]之外的所有出站规则\n" + read -r -p "是否确认设置?[y/n]:" warpOutStatus + + if [[ "${warpOutStatus}" == "y" ]]; then + readConfigWarpReg + if [[ "${coreInstallType}" == "1" ]]; then + addXrayOutbound "wireguard_out_${type}" + if [[ "${type}" == "IPv4" ]]; then + removeXrayOutbound "wireguard_out_IPv6" + elif [[ "${type}" == "IPv6" ]]; then + removeXrayOutbound "wireguard_out_IPv4" + fi + + removeXrayOutbound IPv4_out + removeXrayOutbound IPv6_out + removeXrayOutbound z_direct_outbound + removeXrayOutbound blackhole_out + removeXrayOutbound socks5_outbound + + rm ${configPath}09_routing.json >/dev/null 2>&1 + fi + + if [[ -n "${singBoxConfigPath}" ]]; then + + removeSingBoxConfig IPv4_out + removeSingBoxConfig IPv6_out + removeSingBoxConfig 01_direct_outbound + + # 删除所有分流规则 + removeSingBoxConfig wireguard_endpoints_IPv4_route + removeSingBoxConfig wireguard_endpoints_IPv6_route + + removeSingBoxConfig IPv6_route + removeSingBoxConfig socks5_inbound_route + + addSingBoxWireGuardEndpoints "${type}" + addWireGuardRoute "${type}" outboundTag "" + if [[ "${type}" == "IPv4" ]]; then + removeSingBoxConfig wireguard_endpoints_IPv6 + else + removeSingBoxConfig wireguard_endpoints_IPv4 + fi + + # outbound + # addSingBoxOutbound "wireguard_out_${type}" "wireguard_out" + + fi + + echoContent green " ---> WARP全局出站设置完毕" + else + echoContent green " ---> 放弃设置" + exit 0 + fi + + elif [[ "${warpStatus}" == "4" ]]; then + if [[ "${coreInstallType}" == "1" ]]; then + unInstallRouting "wireguard_out_${type}" outboundTag + + removeXrayOutbound "wireguard_out_${type}" + addXrayOutbound "z_direct_outbound" + fi + + if [[ -n "${singBoxConfigPath}" ]]; then + removeSingBoxConfig "wireguard_endpoints_${type}_route" + + removeSingBoxConfig "wireguard_endpoints_${type}" + addSingBoxOutbound "01_direct_outbound" + fi + + echoContent green " ---> 卸载WARP ${type}分流完毕" + else + + echoContent red " ---> 选择错误" + exit 0 + fi + reloadCore +} + +# 分流工具 +routingToolsMenu() { + echoContent skyBlue "\n功能 1/${totalProgress} : 分流工具" + echoContent red "\n==============================================================" + echoContent yellow "# 注意事项" + echoContent yellow "# 用于服务端的流量分流,可用于解锁ChatGPT、流媒体等相关内容\n" + + echoContent yellow "1.WARP分流【第三方 IPv4】" + echoContent yellow "2.WARP分流【第三方 IPv6】" + echoContent yellow "3.IPv6分流" + echoContent yellow "4.Socks5分流【替换任意门分流】" + echoContent yellow "5.DNS分流" + # echoContent yellow "6.VMess+WS+TLS分流" + echoContent yellow "7.SNI反向代理分流" + + read -r -p "请选择:" selectType + + case ${selectType} in + 1) + warpRoutingReg 1 IPv4 + ;; + 2) + warpRoutingReg 1 IPv6 + ;; + 3) + ipv6Routing 1 + ;; + 4) + socks5Routing + ;; + 5) + dnsRouting 1 + ;; + # 6) + # if [[ -n "${singBoxConfigPath}" ]]; then + # echoContent red "\n ---> 此功能不支持Hysteria2、Tuic" + # fi + # vmessWSRouting 1 + # ;; + 7) + if [[ -n "${singBoxConfigPath}" ]]; then + echoContent red "\n ---> 此功能不支持Hysteria2、Tuic" + fi + sniRouting 1 + ;; + esac + +} + +# VMess+WS+TLS 分流 +vmessWSRouting() { + echoContent skyBlue "\n功能 1/${totalProgress} : VMess+WS+TLS 分流" + echoContent red "\n==============================================================" + echoContent yellow "# 注意事项" + echoContent yellow "# 使用教程:https://www.v2ray-agent.com/archives/1683226921000 \n" + + echoContent yellow "1.添加出站" + echoContent yellow "2.卸载" + read -r -p "请选择:" selectType + + case ${selectType} in + 1) + setVMessWSRoutingOutbounds + ;; + 2) + removeVMessWSRouting + ;; + esac +} +# Socks5分流 +socks5Routing() { + if [[ -z "${coreInstallType}" ]]; then + echoContent red " ---> 未安装任意协议,请使用 1.安装 或者 2.任意组合安装 进行安装后使用" + exit 0 + fi + echoContent skyBlue "\n功能 1/${totalProgress} : Socks5分流" + echoContent red "\n==============================================================" + echoContent red "# 注意事项" + echoContent yellow "# 流量明文访问" + + echoContent yellow "# 仅限正常网络环境下设备间流量转发,禁止用于代理访问。" + echoContent yellow "# 使用教程:https://www.v2ray-agent.com/archives/1683226921000#heading-5 \n" + + echoContent yellow "1.Socks5出站" + echoContent yellow "2.Socks5入站" + echoContent yellow "3.卸载" + read -r -p "请选择:" selectType + + case ${selectType} in + 1) + socks5OutboundRoutingMenu + ;; + 2) + socks5InboundRoutingMenu + ;; + 3) + removeSocks5Routing + ;; + esac +} +# Socks5入站菜单 +socks5InboundRoutingMenu() { + readInstallType + echoContent skyBlue "\n功能 1/1 : Socks5入站" + echoContent red "\n==============================================================" + + echoContent yellow "1.安装Socks5入站" + echoContent yellow "2.查看分流规则" + echoContent yellow "3.添加分流规则" + echoContent yellow "4.查看入站配置" + read -r -p "请选择:" selectType + case ${selectType} in + 1) + totalProgress=1 + installSingBox 1 + installSingBoxService 1 + setSocks5Inbound + setSocks5InboundRouting + reloadCore + socks5InboundRoutingMenu + ;; + 2) + showSingBoxRoutingRules socks5_inbound_route + socks5InboundRoutingMenu + ;; + 3) + setSocks5InboundRouting addRules + reloadCore + socks5InboundRoutingMenu + ;; + 4) + if [[ -f "${singBoxConfigPath}20_socks5_inbounds.json" ]]; then + echoContent yellow "\n ---> 下列内容需要配置到其他机器的出站,请不要进行代理行为\n" + echoContent green " 端口:$(jq .inbounds[0].listen_port ${singBoxConfigPath}20_socks5_inbounds.json)" + echoContent green " 用户名称:$(jq -r .inbounds[0].users[0].username ${singBoxConfigPath}20_socks5_inbounds.json)" + echoContent green " 用户密码:$(jq -r .inbounds[0].users[0].password ${singBoxConfigPath}20_socks5_inbounds.json)" + else + echoContent red " ---> 未安装相应功能" + socks5InboundRoutingMenu + fi + ;; + esac + +} + +# Socks5出站菜单 +socks5OutboundRoutingMenu() { + echoContent skyBlue "\n功能 1/1 : Socks5出站" + echoContent red "\n==============================================================" + + echoContent yellow "1.安装Socks5出站" + echoContent yellow "2.设置Socks5全局转发" + echoContent yellow "3.查看分流规则" + echoContent yellow "4.添加分流规则" + read -r -p "请选择:" selectType + case ${selectType} in + 1) + setSocks5Outbound + setSocks5OutboundRouting + reloadCore + socks5OutboundRoutingMenu + ;; + 2) + setSocks5Outbound + setSocks5OutboundRoutingAll + reloadCore + socks5OutboundRoutingMenu + ;; + 3) + showSingBoxRoutingRules socks5_outbound_route + showXrayRoutingRules socks5_outbound + socks5OutboundRoutingMenu + ;; + 4) + setSocks5OutboundRouting addRules + reloadCore + socks5OutboundRoutingMenu + ;; + esac + +} + +# socks5全局 +setSocks5OutboundRoutingAll() { + + echoContent red "==============================================================" + echoContent yellow "# 注意事项\n" + echoContent yellow "1.会删除所有已经设置的分流规则,包括其他分流(warp、IPv6等)" + echoContent yellow "2.会删除Socks5之外的所有出站规则\n" + read -r -p "是否确认设置?[y/n]:" socksOutStatus + + if [[ "${socksOutStatus}" == "y" ]]; then + if [[ "${coreInstallType}" == "1" ]]; then + removeXrayOutbound IPv4_out + removeXrayOutbound IPv6_out + removeXrayOutbound z_direct_outbound + removeXrayOutbound blackhole_out + removeXrayOutbound wireguard_out_IPv4 + removeXrayOutbound wireguard_out_IPv6 + + rm ${configPath}09_routing.json >/dev/null 2>&1 + fi + if [[ -n "${singBoxConfigPath}" ]]; then + + removeSingBoxConfig IPv4_out + removeSingBoxConfig IPv6_out + + removeSingBoxConfig wireguard_endpoints_IPv4_route + removeSingBoxConfig wireguard_endpoints_IPv6_route + removeSingBoxConfig wireguard_endpoints_IPv4 + removeSingBoxConfig wireguard_endpoints_IPv6 + + removeSingBoxConfig socks5_outbound_route + removeSingBoxConfig 01_direct_outbound + fi + + echoContent green " ---> Socks5全局出站设置完毕" + fi +} +# socks5 分流规则 +showSingBoxRoutingRules() { + if [[ -n "${singBoxConfigPath}" ]]; then + if [[ -f "${singBoxConfigPath}$1.json" ]]; then + jq .route.rules "${singBoxConfigPath}$1.json" + elif [[ "$1" == "socks5_outbound_route" && -f "${singBoxConfigPath}socks5_outbound.json" ]]; then + echoContent yellow "已安装 sing-box socks5全局出站分流" + echoContent yellow "\n出站分流配置:" + echoContent skyBlue "$(jq .outbounds[0] ${singBoxConfigPath}socks5_outbound.json)" + elif [[ "$1" == "socks5_inbound_route" && -f "${singBoxConfigPath}20_socks5_inbounds.json" ]]; then + echoContent yellow "已安装 sing-box socks5全局入站分流" + echoContent yellow "\n出站分流配置:" + echoContent skyBlue "$(jq .outbounds[0] ${singBoxConfigPath}socks5_outbound.json)" + fi + fi +} + +# xray内核分流规则 +showXrayRoutingRules() { + if [[ "${coreInstallType}" == "1" ]]; then + if [[ -f "${configPath}09_routing.json" ]]; then + jq ".routing.rules[]|select(.outboundTag==\"$1\")" "${configPath}09_routing.json" + + echoContent yellow "\n已安装 xray-core socks5全局出站分流" + echoContent yellow "\n出站分流配置:" + echoContent skyBlue "$(jq .outbounds[0].settings.servers[0] ${configPath}socks5_outbound.json)" + + elif [[ "$1" == "socks5_outbound" && -f "${configPath}socks5_outbound.json" ]]; then + echoContent yellow "\n已安装 xray-core socks5全局出站分流" + echoContent yellow "\n出站分流配置:" + echoContent skyBlue "$(jq .outbounds[0].settings.servers[0] ${configPath}socks5_outbound.json)" + fi + fi +} + +# 卸载Socks5分流 +removeSocks5Routing() { + echoContent skyBlue "\n功能 1/1 : 卸载Socks5分流" + echoContent red "\n==============================================================" + + echoContent yellow "1.卸载Socks5出站" + echoContent yellow "2.卸载Socks5入站" + echoContent yellow "3.卸载全部" + read -r -p "请选择:" unInstallSocks5RoutingStatus + if [[ "${unInstallSocks5RoutingStatus}" == "1" ]]; then + if [[ "${coreInstallType}" == "1" ]]; then + removeXrayOutbound socks5_outbound + unInstallRouting socks5_outbound outboundTag + addXrayOutbound z_direct_outbound + fi + + if [[ -n "${singBoxConfigPath}" ]]; then + removeSingBoxConfig socks5_outbound + removeSingBoxConfig socks5_outbound_route + addSingBoxOutbound 01_direct_outbound + fi + + elif [[ "${unInstallSocks5RoutingStatus}" == "2" ]]; then + + removeSingBoxConfig 20_socks5_inbounds + removeSingBoxConfig socks5_inbound_route + + handleSingBox stop + elif [[ "${unInstallSocks5RoutingStatus}" == "3" ]]; then + if [[ "${coreInstallType}" == "1" ]]; then + removeXrayOutbound socks5_outbound + unInstallRouting socks5_outbound outboundTag + addXrayOutbound z_direct_outbound + fi + + if [[ -n "${singBoxConfigPath}" ]]; then + removeSingBoxConfig socks5_outbound + removeSingBoxConfig socks5_outbound_route + removeSingBoxConfig 20_socks5_inbounds + removeSingBoxConfig socks5_inbound_route + addSingBoxOutbound 01_direct_outbound + fi + + handleSingBox stop + else + echoContent red " ---> 选择错误" + exit 0 + fi + echoContent green " ---> 卸载完毕" + reloadCore +} +# Socks5入站 +setSocks5Inbound() { + + echoContent yellow "\n==================== 配置 Socks5 入站(解锁机、落地机) =====================\n" + echoContent skyBlue "\n开始配置Socks5协议入站端口" + echo + mapfile -t result < <(initSingBoxPort "${singBoxSocks5Port}") + echoContent green "\n ---> 入站Socks5端口:${result[-1]}" + echoContent green "\n ---> 此端口需要配置到其他机器出站,请不要进行代理行为" + + echoContent yellow "\n请输入自定义UUID[需合法],[回车]随机UUID" + read -r -p 'UUID:' socks5RoutingUUID + if [[ -z "${socks5RoutingUUID}" ]]; then + if [[ "${coreInstallType}" == "1" ]]; then + socks5RoutingUUID=$(/etc/v2ray-agent/xray/xray uuid) + elif [[ -n "${singBoxConfigPath}" ]]; then + socks5RoutingUUID=$(/etc/v2ray-agent/sing-box/sing-box generate uuid) + fi + fi + echo + echoContent green "用户名称:${socks5RoutingUUID}" + echoContent green "用户密码:${socks5RoutingUUID}" + + echoContent yellow "\n请选择分流域名DNS解析类型" + echoContent yellow "# 注意事项:需要保证vps支持相应的DNS解析" + echoContent yellow "1.IPv4[回车默认]" + echoContent yellow "2.IPv6" + + read -r -p 'IP类型:' socks5InboundDomainStrategyStatus + local domainStrategy= + if [[ -z "${socks5InboundDomainStrategyStatus}" || "${socks5InboundDomainStrategyStatus}" == "1" ]]; then + domainStrategy="ipv4_only" + elif [[ "${socks5InboundDomainStrategyStatus}" == "2" ]]; then + domainStrategy="ipv6_only" + else + echoContent red " ---> 选择类型错误" + exit 0 + fi + cat </etc/v2ray-agent/sing-box/conf/config/20_socks5_inbounds.json +{ + "inbounds":[ + { + "type": "socks", + "listen":"::", + "listen_port":${result[-1]}, + "tag":"socks5_inbound", + "users":[ + { + "username": "${socks5RoutingUUID}", + "password": "${socks5RoutingUUID}" + } + ], + "domain_strategy":"${domainStrategy}" + } + ] +} +EOF + +} + +# 初始化sing-box rule配置 +initSingBoxRules() { + local domainRules=[] + local ruleSet=[] + while read -r line; do + local geositeStatus + geositeStatus=$(curl -s "https://api.github.com/repos/SagerNet/sing-geosite/contents/geosite-${line}.srs?ref=rule-set" | jq .message) + + if [[ "${geositeStatus}" == "null" ]]; then + ruleSet=$(echo "${ruleSet}" | jq -r ". += [{\"tag\":\"${line}_$2\",\"type\":\"remote\",\"format\":\"binary\",\"url\":\"https://raw.githubusercontent.com/SagerNet/sing-geosite/rule-set/geosite-${line}.srs\",\"download_detour\":\"01_direct_outbound\"}]") + else + domainRules=$(echo "${domainRules}" | jq -r ". += [\"^([a-zA-Z0-9_-]+\\\.)*${line//./\\\\.}\"]") + fi + done < <(echo "$1" | tr ',' '\n' | grep -v '^$' | sort -n | uniq | paste -sd ',' | tr ',' '\n') + echo "{ \"domainRules\":${domainRules},\"ruleSet\":${ruleSet}}" +} + +# socks5 inbound routing规则 +setSocks5InboundRouting() { + + singBoxConfigPath=/etc/v2ray-agent/sing-box/conf/config/ + + if [[ "$1" == "addRules" && ! -f "${singBoxConfigPath}socks5_inbound_route.json" && ! -f "${configPath}09_routing.json" ]]; then + echoContent red " ---> 请安装入站分流后再添加分流规则" + echoContent red " ---> 如已选择允许所有网站,请重新安装分流后设置规则" + exit 0 + fi + local socks5InboundRoutingIPs= + if [[ "$1" == "addRules" ]]; then + socks5InboundRoutingIPs=$(jq .route.rules[0].source_ip_cidr "${singBoxConfigPath}socks5_inbound_route.json") + else + echoContent red "==============================================================" + echoContent skyBlue "请输入允许访问的IP地址,多个IP英文逗号隔开。例如:1.1.1.1,2.2.2.2\n" + read -r -p "IP:" socks5InboundRoutingIPs + + if [[ -z "${socks5InboundRoutingIPs}" ]]; then + echoContent red " ---> IP不可为空" + exit 0 + fi + socks5InboundRoutingIPs=$(echo "\"${socks5InboundRoutingIPs}"\" | jq -c '.|split(",")') + fi + + echoContent red "==============================================================" + echoContent skyBlue "请输入要分流的域名\n" + echoContent yellow "支持Xray-core geosite匹配,支持sing-box1.8+ rule_set匹配\n" + echoContent yellow "非增量添加,会替换原有规则\n" + echoContent yellow "当输入的规则匹配到geosite或者rule_set后会使用相应的规则\n" + echoContent yellow "如无法匹配则,则使用domain精确匹配\n" + + read -r -p "是否允许所有网站?请选择[y/n]:" socks5InboundRoutingDomainStatus + if [[ "${socks5InboundRoutingDomainStatus}" == "y" ]]; then + addSingBoxRouteRule "01_direct_outbound" "" "socks5_inbound_route" + local route= + route=$(jq ".route.rules[0].inbound = [\"socks5_inbound\"]" "${singBoxConfigPath}socks5_inbound_route.json") + route=$(echo "${route}" | jq ".route.rules[0].source_ip_cidr=${socks5InboundRoutingIPs}") + echo "${route}" | jq . >"${singBoxConfigPath}socks5_inbound_route.json" + + addSingBoxOutbound block + addSingBoxOutbound "01_direct_outbound" + else + echoContent yellow "录入示例:netflix,openai,v2ray-agent.com\n" + read -r -p "域名:" socks5InboundRoutingDomain + if [[ -z "${socks5InboundRoutingDomain}" ]]; then + echoContent red " ---> 域名不可为空" + exit 0 + fi + addSingBoxRouteRule "01_direct_outbound" "${socks5InboundRoutingDomain}" "socks5_inbound_route" + local route= + route=$(jq ".route.rules[0].inbound = [\"socks5_inbound\"]" "${singBoxConfigPath}socks5_inbound_route.json") + route=$(echo "${route}" | jq ".route.rules[0].source_ip_cidr=${socks5InboundRoutingIPs}") + echo "${route}" | jq . >"${singBoxConfigPath}socks5_inbound_route.json" + + addSingBoxOutbound block + addSingBoxOutbound "01_direct_outbound" + fi + +} + +# socks5 出站 +setSocks5Outbound() { + + echoContent yellow "\n==================== 配置 Socks5 出站(转发机、代理机) =====================\n" + echo + read -r -p "请输入落地机IP地址:" socks5RoutingOutboundIP + if [[ -z "${socks5RoutingOutboundIP}" ]]; then + echoContent red " ---> IP不可为空" + exit 0 + fi + echo + read -r -p "请输入落地机端口:" socks5RoutingOutboundPort + if [[ -z "${socks5RoutingOutboundPort}" ]]; then + echoContent red " ---> 端口不可为空" + exit 0 + fi + echo + read -r -p "请输入用户名:" socks5RoutingOutboundUserName + if [[ -z "${socks5RoutingOutboundUserName}" ]]; then + echoContent red " ---> 用户名不可为空" + exit 0 + fi + echo + read -r -p "请输入用户密码:" socks5RoutingOutboundPassword + if [[ -z "${socks5RoutingOutboundPassword}" ]]; then + echoContent red " ---> 用户密码不可为空" + exit 0 + fi + echo + if [[ -n "${singBoxConfigPath}" ]]; then + cat <"${singBoxConfigPath}socks5_outbound.json" +{ + "outbounds":[ + { + "type": "socks", + "tag":"socks5_outbound", + "server": "${socks5RoutingOutboundIP}", + "server_port": ${socks5RoutingOutboundPort}, + "version": "5", + "username":"${socks5RoutingOutboundUserName}", + "password":"${socks5RoutingOutboundPassword}" + } + ] +} +EOF + fi + if [[ "${coreInstallType}" == "1" ]]; then + addXrayOutbound socks5_outbound + fi +} + +# socks5 outbound routing规则 +setSocks5OutboundRouting() { + + if [[ "$1" == "addRules" && ! -f "${singBoxConfigPath}socks5_outbound_route.json" && ! -f "${configPath}09_routing.json" ]]; then + echoContent red " ---> 请安装出站分流后再添加分流规则" + exit 0 + fi + + echoContent red "==============================================================" + echoContent skyBlue "请输入要分流的域名\n" + echoContent yellow "支持Xray-core geosite匹配,支持sing-box1.8+ rule_set匹配\n" + echoContent yellow "非增量添加,会替换原有规则\n" + echoContent yellow "当输入的规则匹配到geosite或者rule_set后会使用相应的规则\n" + echoContent yellow "如无法匹配则,则使用domain精确匹配\n" + echoContent yellow "录入示例:netflix,openai,v2ray-agent.com\n" + read -r -p "域名:" socks5RoutingOutboundDomain + if [[ -z "${socks5RoutingOutboundDomain}" ]]; then + echoContent red " ---> IP不可为空" + exit 0 + fi + addSingBoxRouteRule "socks5_outbound" "${socks5RoutingOutboundDomain}" "socks5_outbound_route" + addSingBoxOutbound "01_direct_outbound" + + if [[ "${coreInstallType}" == "1" ]]; then + + unInstallRouting "socks5_outbound" "outboundTag" + local domainRules=[] + while read -r line; do + if echo "${routingRule}" | grep -q "${line}"; then + echoContent yellow " ---> ${line}已存在,跳过" + else + local geositeStatus + geositeStatus=$(curl -s "https://api.github.com/repos/v2fly/domain-list-community/contents/data/${line}" | jq .message) + + if [[ "${geositeStatus}" == "null" ]]; then + domainRules=$(echo "${domainRules}" | jq -r ". += [\"geosite:${line}\"]") + else + domainRules=$(echo "${domainRules}" | jq -r ". += [\"domain:${line}\"]") + fi + fi + done < <(echo "${socks5RoutingOutboundDomain}" | tr ',' '\n') + if [[ ! -f "${configPath}09_routing.json" ]]; then + cat <${configPath}09_routing.json +{ + "routing":{ + "rules": [] + } +} +EOF + fi + routing=$(jq -r ".routing.rules += [{\"type\": \"field\",\"domain\": ${domainRules},\"outboundTag\": \"socks5_outbound\"}]" ${configPath}09_routing.json) + echo "${routing}" | jq . >${configPath}09_routing.json + fi +} + +# 设置VMess+WS+TLS【仅出站】 +setVMessWSRoutingOutbounds() { + read -r -p "请输入VMess+WS+TLS的地址:" setVMessWSTLSAddress + echoContent red "==============================================================" + echoContent yellow "录入示例:netflix,openai\n" + read -r -p "请按照上面示例录入域名:" domainList + + if [[ -z ${domainList} ]]; then + echoContent red " ---> 域名不可为空" + setVMessWSRoutingOutbounds + fi + + if [[ -n "${setVMessWSTLSAddress}" ]]; then + removeXrayOutbound VMess-out + + echo + read -r -p "请输入VMess+WS+TLS的端口:" setVMessWSTLSPort + echo + if [[ -z "${setVMessWSTLSPort}" ]]; then + echoContent red " ---> 端口不可为空" + fi + + read -r -p "请输入VMess+WS+TLS的UUID:" setVMessWSTLSUUID + echo + if [[ -z "${setVMessWSTLSUUID}" ]]; then + echoContent red " ---> UUID不可为空" + fi + + read -r -p "请输入VMess+WS+TLS的Path路径:" setVMessWSTLSPath + echo + if [[ -z "${setVMessWSTLSPath}" ]]; then + echoContent red " ---> 路径不可为空" + elif ! echo "${setVMessWSTLSPath}" | grep -q "/"; then + setVMessWSTLSPath="/${setVMessWSTLSPath}" + fi + addXrayOutbound "VMess-out" + addInstallRouting VMess-out outboundTag "${domainList}" + reloadCore + echoContent green " ---> 添加分流成功" + exit 0 + fi + echoContent red " ---> 地址不可为空" + setVMessWSRoutingOutbounds +} + +# 移除VMess+WS+TLS分流 +removeVMessWSRouting() { + + removeXrayOutbound VMess-out + unInstallRouting VMess-out outboundTag + + reloadCore + echoContent green " ---> 卸载成功" +} + +# 重启核心 +reloadCore() { + readInstallType + + if [[ "${coreInstallType}" == "1" ]]; then + handleXray stop + handleXray start + fi + if echo "${currentInstallProtocolType}" | grep -q ",20," || [[ "${coreInstallType}" == "2" || -n "${singBoxConfigPath}" ]]; then + handleSingBox stop + handleSingBox start + fi +} + +# dns分流 +dnsRouting() { + + if [[ -z "${configPath}" ]]; then + echoContent red " ---> 未安装,请使用脚本安装" + menu + exit 0 + fi + echoContent skyBlue "\n功能 1/${totalProgress} : DNS分流" + echoContent red "\n==============================================================" + echoContent yellow "# 注意事项" + echoContent yellow "# 使用教程:https://www.v2ray-agent.com/archives/1683226921000 \n" + + echoContent yellow "1.添加" + echoContent yellow "2.卸载" + read -r -p "请选择:" selectType + + case ${selectType} in + 1) + setUnlockDNS + ;; + 2) + removeUnlockDNS + ;; + esac +} + +# SNI反向代理分流 +sniRouting() { + + if [[ -z "${configPath}" ]]; then + echoContent red " ---> 未安装,请使用脚本安装" + menu + exit 0 + fi + echoContent skyBlue "\n功能 1/${totalProgress} : SNI反向代理分流" + echoContent red "\n==============================================================" + echoContent yellow "# 注意事项" + echoContent yellow "# 使用教程:https://www.v2ray-agent.com/archives/1683226921000 \n" + + echoContent yellow "1.添加" + echoContent yellow "2.卸载" + read -r -p "请选择:" selectType + + case ${selectType} in + 1) + setUnlockSNI + ;; + 2) + removeUnlockSNI + ;; + esac +} +# 设置SNI分流 +setUnlockSNI() { + read -r -p "请输入分流的SNI IP:" setSNIP + if [[ -n ${setSNIP} ]]; then + echoContent red "==============================================================" + echoContent yellow "录入示例:netflix,disney,hulu" + read -r -p "请按照上面示例录入域名:" domainList + + if [[ -n "${domainList}" ]]; then + local hosts={} + while read -r domain; do + hosts=$(echo "${hosts}" | jq -r ".\"geosite:${domain}\"=\"${setSNIP}\"") + done < <(echo "${domainList}" | tr ',' '\n') + cat <${configPath}11_dns.json +{ + "dns": { + "hosts":${hosts}, + "servers": [ + "8.8.8.8", + "1.1.1.1" + ] + } +} +EOF + echoContent red " ---> SNI反向代理分流成功" + reloadCore + else + echoContent red " ---> 域名不可为空" + fi + + else + + echoContent red " ---> SNI IP不可为空" + fi + exit 0 +} + +# 添加xray dns 配置 +addXrayDNSConfig() { + local ip=$1 + local domainList=$2 + local domains=[] + while read -r line; do + local geositeStatus + geositeStatus=$(curl -s "https://api.github.com/repos/v2fly/domain-list-community/contents/data/${line}" | jq .message) + + if [[ "${geositeStatus}" == "null" ]]; then + domains=$(echo "${domains}" | jq -r '. += ["geosite:'"${line}"'"]') + else + domains=$(echo "${domains}" | jq -r '. += ["domain:'"${line}"'"]') + fi + done < <(echo "${domainList}" | tr ',' '\n') + + if [[ "${coreInstallType}" == "1" ]]; then + + cat <${configPath}11_dns.json +{ + "dns": { + "servers": [ + { + "address": "${ip}", + "port": 53, + "domains": ${domains} + }, + "localhost" + ] + } +} +EOF + fi +} + +# 添加sing-box dns配置 +addSingBoxDNSConfig() { + local ip=$1 + local domainList=$2 + + local rules= + rules=$(initSingBoxRules "${domainList}" "dns") + # domain精确匹配规则 + local domainRules= + domainRules=$(echo "${rules}" | jq .domainRules) + + # ruleSet规则集 + local ruleSet= + ruleSet=$(echo "${rules}" | jq .ruleSet) + + # ruleSet规则tag + local ruleSetTag=[] + if [[ "$(echo "${ruleSet}" | jq '.|length')" != "0" ]]; then + ruleSetTag=$(echo "${ruleSet}" | jq '.|map(.tag)') + fi + if [[ -n "${singBoxConfigPath}" ]]; then + cat <"${singBoxConfigPath}dns.json" +{ + "dns": { + "servers": [ + { + "tag": "local", + "address": "local" + }, + { + "tag": "dnsRouting", + "address": "${ip}" + } + ], + "rules": [ + { + "rule_set": ${ruleSetTag}, + "domain_regex": ${domainRules}, + "server":"dnsRouting" + } + ] + }, + "route":{ + "rule_set":${ruleSet} + } +} +EOF + fi +} +# 设置dns +setUnlockDNS() { + read -r -p "请输入分流的DNS:" setDNS + if [[ -n ${setDNS} ]]; then + echoContent red "==============================================================" + echoContent yellow "录入示例:netflix,disney,hulu" + read -r -p "请按照上面示例录入域名:" domainList + + if [[ "${coreInstallType}" == "1" ]]; then + addXrayDNSConfig "${setDNS}" "${domainList}" + fi + + if [[ -n "${singBoxConfigPath}" ]]; then + addSingBoxOutbound 01_direct_outbound + addSingBoxDNSConfig "${setDNS}" "${domainList}" + fi + + reloadCore + + echoContent yellow "\n ---> 如还无法观看可以尝试以下两种方案" + echoContent yellow " 1.重启vps" + echoContent yellow " 2.卸载dns解锁后,修改本地的[/etc/resolv.conf]DNS设置并重启vps\n" + else + echoContent red " ---> dns不可为空" + fi + exit 0 +} + +# 移除 DNS分流 +removeUnlockDNS() { + if [[ "${coreInstallType}" == "1" && -f "${configPath}11_dns.json" ]]; then + cat <${configPath}11_dns.json +{ + "dns": { + "servers": [ + "localhost" + ] + } +} +EOF + fi + + if [[ "${coreInstallType}" == "2" && -f "${singBoxConfigPath}dns.json" ]]; then + cat <${singBoxConfigPath}dns.json +{ + "dns": { + "servers":[ + { + "address":"local" + } + ] + } +} +EOF + fi + + reloadCore + + echoContent green " ---> 卸载成功" + + exit 0 +} + +# 移除SNI分流 +removeUnlockSNI() { + cat <${configPath}11_dns.json +{ + "dns": { + "servers": [ + "localhost" + ] + } +} +EOF + reloadCore + + echoContent green " ---> 卸载成功" + + exit 0 +} + +# sing-box 个性化安装 +customSingBoxInstall() { + echoContent skyBlue "\n========================个性化安装============================" + echoContent yellow "0.VLESS+Vision+TCP" + echoContent yellow "1.VLESS+TLS+WS[仅CDN推荐]" + echoContent yellow "3.VMess+TLS+WS[仅CDN推荐]" + echoContent yellow "4.Trojan+TLS[不推荐]" + echoContent yellow "6.Hysteria2" + echoContent yellow "7.VLESS+Reality+Vision" + echoContent yellow "8.VLESS+Reality+gRPC" + echoContent yellow "9.Tuic" + echoContent yellow "10.Naive" + echoContent yellow "11.VMess+TLS+HTTPUpgrade" + echoContent yellow "13.anytls" + + read -r -p "请选择[多选],[例如:1,2,3]:" selectCustomInstallType + echoContent skyBlue "--------------------------------------------------------------" + if echo "${selectCustomInstallType}" | grep -q ","; then + echoContent red " ---> 请使用英文逗号分隔" + exit 0 + fi + if [[ "${selectCustomInstallType}" != "10" ]] && [[ "${selectCustomInstallType}" != "11" ]] && [[ "${selectCustomInstallType}" != "13" ]] && ((${#selectCustomInstallType} >= 2)) && ! echo "${selectCustomInstallType}" | grep -q ","; then + echoContent red " ---> 多选请使用英文逗号分隔" + exit 0 + fi + if [[ "${selectCustomInstallType: -1}" != "," ]]; then + selectCustomInstallType="${selectCustomInstallType}," + fi + if [[ "${selectCustomInstallType:0:1}" != "," ]]; then + selectCustomInstallType=",${selectCustomInstallType}," + fi + + if [[ "${selectCustomInstallType//,/}" =~ ^[0-9]+$ ]]; then + readLastInstallationConfig + unInstallSubscribe + totalProgress=9 + installTools 1 + # 申请tls + if echo "${selectCustomInstallType}" | grep -q -E ",0,|,1,|,3,|,4,|,6,|,9,|,10,|,11,|,13,"; then + initTLSNginxConfig 2 + installTLS 3 + handleNginx stop + fi + + installSingBox 4 + installSingBoxService 5 + initSingBoxConfig custom 6 + cleanUp xrayDel + installCronTLS 7 + handleSingBox stop + handleSingBox start + handleNginx stop + handleNginx start + # 生成账号 + checkGFWStatue 8 + showAccounts 9 + else + echoContent red " ---> 输入不合法" + customSingBoxInstall + fi +} + +# Xray-core个性化安装 +customXrayInstall() { + echoContent skyBlue "\n========================个性化安装============================" + echoContent yellow "VLESS前置,默认安装0,无域名安装Reality只选择7即可" + echoContent yellow "0.VLESS+TLS_Vision+TCP[推荐]" + echoContent yellow "1.VLESS+TLS+WS[仅CDN推荐]" + # echoContent yellow "2.Trojan+TLS+gRPC[仅CDN推荐]" + echoContent yellow "3.VMess+TLS+WS[仅CDN推荐]" + echoContent yellow "4.Trojan+TLS[不推荐]" + echoContent yellow "5.VLESS+TLS+gRPC[仅CDN推荐]" + echoContent yellow "7.VLESS+Reality+uTLS+Vision[推荐]" + # echoContent yellow "8.VLESS+Reality+gRPC" + echoContent yellow "12.VLESS+XHTTP+TLS" + read -r -p "请选择[多选],[例如:1,2,3]:" selectCustomInstallType + echoContent skyBlue "--------------------------------------------------------------" + if echo "${selectCustomInstallType}" | grep -q ","; then + echoContent red " ---> 请使用英文逗号分隔" + exit 0 + fi + if [[ "${selectCustomInstallType}" != "12" ]] && ((${#selectCustomInstallType} >= 2)) && ! echo "${selectCustomInstallType}" | grep -q ","; then + echoContent red " ---> 多选请使用英文逗号分隔" + exit 0 + fi + + if [[ "${selectCustomInstallType}" == "7" ]]; then + selectCustomInstallType=",${selectCustomInstallType}," + else + if ! echo "${selectCustomInstallType}" | grep -q "0,"; then + selectCustomInstallType=",0,${selectCustomInstallType}," + else + selectCustomInstallType=",${selectCustomInstallType}," + fi + fi + + if [[ "${selectCustomInstallType:0:1}" != "," ]]; then + selectCustomInstallType=",${selectCustomInstallType}," + fi + if [[ "${selectCustomInstallType//,/}" =~ ^[0-7]+$ ]]; then + readLastInstallationConfig + unInstallSubscribe + checkBTPanel + check1Panel + totalProgress=12 + installTools 1 + if [[ -n "${btDomain}" ]]; then + echoContent skyBlue "\n进度 3/${totalProgress} : 检测到宝塔面板/1Panel,跳过申请TLS步骤" + handleXray stop + if [[ "${selectCustomInstallType}" != ",7," ]]; then + customPortFunction + fi + else + # 申请tls + if [[ "${selectCustomInstallType}" != ",7," ]]; then + initTLSNginxConfig 2 + handleXray stop + installTLS 3 + else + echoContent skyBlue "\n进度 2/${totalProgress} : 检测到仅安装Reality,跳过TLS证书步骤" + fi + fi + + handleNginx stop + # 随机path + if echo "${selectCustomInstallType}" | grep -qE ",1,|,2,|,3,|,5,|,12,"; then + randomPathFunction 4 + fi + if [[ -n "${btDomain}" ]]; then + echoContent skyBlue "\n进度 6/${totalProgress} : 检测到宝塔面板/1Panel,跳过伪装网站" + else + nginxBlog 6 + fi + if [[ "${selectCustomInstallType}" != ",7," ]]; then + updateRedirectNginxConf + handleNginx start + fi + + # 安装Xray + installXray 7 false + installXrayService 8 + initXrayConfig custom 9 + cleanUp singBoxDel + if [[ "${selectCustomInstallType}" != ",7," ]]; then + installCronTLS 10 + fi + + handleXray stop + handleXray start + # 生成账号 + checkGFWStatue 11 + showAccounts 12 + else + echoContent red " ---> 输入不合法" + customXrayInstall + fi +} + +# 选择核心安装sing-box、xray-core +selectCoreInstall() { + echoContent skyBlue "\n功能 1/${totalProgress} : 选择核心安装" + echoContent red "\n==============================================================" + echoContent yellow "1.Xray-core" + echoContent yellow "2.sing-box" + echoContent red "==============================================================" + read -r -p "请选择:" selectCoreType + case ${selectCoreType} in + 1) + if [[ "${selectInstallType}" == "2" ]]; then + customXrayInstall + else + xrayCoreInstall + fi + ;; + 2) + if [[ "${selectInstallType}" == "2" ]]; then + customSingBoxInstall + else + singBoxInstall + fi + ;; + *) + echoContent red ' ---> 选择错误,重新选择' + selectCoreInstall + ;; + esac +} + +# xray-core 安装 +xrayCoreInstall() { + readLastInstallationConfig + unInstallSubscribe + checkBTPanel + check1Panel + selectCustomInstallType= + totalProgress=12 + installTools 2 + if [[ -n "${btDomain}" ]]; then + echoContent skyBlue "\n进度 3/${totalProgress} : 检测到宝塔面板/1Panel,跳过申请TLS步骤" + handleXray stop + customPortFunction + else + # 申请tls + initTLSNginxConfig 3 + handleXray stop + installTLS 4 + fi + + handleNginx stop + randomPathFunction 5 + + # 安装Xray + installXray 6 false + installXrayService 7 + initXrayConfig all 8 + cleanUp singBoxDel + installCronTLS 9 + if [[ -n "${btDomain}" ]]; then + echoContent skyBlue "\n进度 11/${totalProgress} : 检测到宝塔面板/1Panel,跳过伪装网站" + else + nginxBlog 10 + fi + updateRedirectNginxConf + handleXray stop + sleep 2 + handleXray start + + handleNginx start + # 生成账号 + checkGFWStatue 11 + showAccounts 12 +} + +# sing-box 全部安装 +singBoxInstall() { + readLastInstallationConfig + unInstallSubscribe + checkBTPanel + check1Panel + selectCustomInstallType= + totalProgress=8 + installTools 2 + + if [[ -n "${btDomain}" ]]; then + echoContent skyBlue "\n进度 3/${totalProgress} : 检测到宝塔面板/1Panel,跳过申请TLS步骤" + handleXray stop + customPortFunction + else + # 申请tls + initTLSNginxConfig 3 + handleXray stop + installTLS 4 + fi + + handleNginx stop + + installSingBox 5 + installSingBoxService 6 + initSingBoxConfig all 7 + + cleanUp xrayDel + installCronTLS 8 + + handleSingBox stop + handleSingBox start + handleNginx stop + handleNginx start + # 生成账号 + showAccounts 9 +} + +# 核心管理 +coreVersionManageMenu() { + + if [[ -z "${coreInstallType}" ]]; then + echoContent red "\n ---> 没有检测到安装目录,请执行脚本安装内容" + menu + exit 0 + fi + echoContent skyBlue "\n功能 1/1 : 请选择核心" + echoContent red "\n==============================================================" + echoContent yellow "1.Xray-core" + echoContent yellow "2.sing-box" + echoContent red "==============================================================" + read -r -p "请输入:" selectCore + + if [[ "${selectCore}" == "1" ]]; then + xrayVersionManageMenu 1 + elif [[ "${selectCore}" == "2" ]]; then + singBoxVersionManageMenu 1 + fi +} +# 定时任务检查 +cronFunction() { + if [[ "${cronName}" == "RenewTLS" ]]; then + renewalTLS + exit 0 + elif [[ "${cronName}" == "UpdateGeo" ]]; then + updateGeoSite >>/etc/v2ray-agent/crontab_updateGeoSite.log + echoContent green " ---> geo更新日期:$(date "+%F %H:%M:%S")" >>/etc/v2ray-agent/crontab_updateGeoSite.log + exit 0 + fi +} +# 账号管理 +manageAccount() { + echoContent skyBlue "\n功能 1/${totalProgress} : 账号管理" + if [[ -z "${configPath}" ]]; then + echoContent red " ---> 未安装" + exit 0 + fi + + echoContent red "\n==============================================================" + echoContent yellow "# 添加单个用户时可自定义email和uuid" + echoContent yellow "# 如安装了Hysteria或者Tuic,账号会同时添加到相应的类型下面\n" + echoContent yellow "1.查看账号" + echoContent yellow "2.查看订阅" + echoContent yellow "3.管理其他订阅" + echoContent yellow "4.添加用户" + echoContent yellow "5.删除用户" + echoContent red "==============================================================" + read -r -p "请输入:" manageAccountStatus + if [[ "${manageAccountStatus}" == "1" ]]; then + showAccounts 1 + elif [[ "${manageAccountStatus}" == "2" ]]; then + subscribe + elif [[ "${manageAccountStatus}" == "3" ]]; then + addSubscribeMenu 1 + elif [[ "${manageAccountStatus}" == "4" ]]; then + addUser + elif [[ "${manageAccountStatus}" == "5" ]]; then + removeUser + else + echoContent red " ---> 选择错误" + fi +} + +# 安装订阅 +installSubscribe() { + readNginxSubscribe + local nginxSubscribeListen= + local nginxSubscribeSSL= + local serverName= + local SSLType= + local listenIPv6= + if [[ -z "${subscribePort}" ]]; then + + nginxVersion=$(nginx -v 2>&1) + + if echo "${nginxVersion}" | grep -q "not found" || [[ -z "${nginxVersion}" ]]; then + echoContent yellow "未检测到nginx,无法使用订阅服务\n" + read -r -p "是否安装[y/n]?" installNginxStatus + if [[ "${installNginxStatus}" == "y" ]]; then + installNginxTools + else + echoContent red " ---> 放弃安装nginx\n" + exit 0 + fi + fi + echoContent yellow "开始配置订阅,请输入订阅的端口\n" + + mapfile -t result < <(initSingBoxPort "${subscribePort}") + echo + echoContent yellow " ---> 开始配置订阅的伪装站点\n" + nginxBlog + echo + local httpSubscribeStatus= + + if ! echo "${selectCustomInstallType}" | grep -qE ",0,|,1,|,2,|,3,|,4,|,5,|,6,|,9,|,10,|,11,|,13," && ! echo "${currentInstallProtocolType}" | grep -qE ",0,|,1,|,2,|,3,|,4,|,5,|,6,|,9,|,10,|,11,|,13," && [[ -z "${domain}" ]]; then + httpSubscribeStatus=true + fi + + if [[ "${httpSubscribeStatus}" == "true" ]]; then + + echoContent yellow "未发现tls证书,使用无加密订阅,可能被运营商拦截,请注意风险。" + echo + read -r -p "是否使用http订阅[y/n]?" addNginxSubscribeStatus + echo + if [[ "${addNginxSubscribeStatus}" != "y" ]]; then + echoContent yellow " ---> 退出安装" + exit + fi + else + local subscribeServerName= + if [[ -n "${currentHost}" ]]; then + subscribeServerName="${currentHost}" + else + subscribeServerName="${domain}" + fi + + SSLType="ssl" + serverName="server_name ${subscribeServerName};" + nginxSubscribeSSL="ssl_certificate /etc/v2ray-agent/tls/${subscribeServerName}.crt;ssl_certificate_key /etc/v2ray-agent/tls/${subscribeServerName}.key;" + fi + if [[ -n "$(curl --connect-timeout 2 -s -6 http://www.cloudflare.com/cdn-cgi/trace | grep "ip" | cut -d "=" -f 2)" ]]; then + listenIPv6="listen [::]:${result[-1]} ${SSLType};" + fi + if echo "${nginxVersion}" | grep -q "1.25" && [[ $(echo "${nginxVersion}" | awk -F "[.]" '{print $3}') -gt 0 ]] || [[ $(echo "${nginxVersion}" | awk -F "[.]" '{print $2}') -gt 25 ]]; then + nginxSubscribeListen="listen ${result[-1]} ${SSLType} so_keepalive=on;http2 on;${listenIPv6}" + else + nginxSubscribeListen="listen ${result[-1]} ${SSLType} so_keepalive=on;${listenIPv6}" + fi + + cat <${nginxConfigPath}subscribe.conf +server { + ${nginxSubscribeListen} + ${serverName} + ${nginxSubscribeSSL} + ssl_protocols TLSv1.2 TLSv1.3; + ssl_ciphers TLS13_AES_128_GCM_SHA256:TLS13_AES_256_GCM_SHA384:TLS13_CHACHA20_POLY1305_SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305; + ssl_prefer_server_ciphers on; + + resolver 1.1.1.1 valid=60s; + resolver_timeout 2s; + client_max_body_size 100m; + root ${nginxStaticPath}; + location ~ ^/s/(clashMeta|default|clashMetaProfiles|sing-box|sing-box_profiles)/(.*) { + default_type 'text/plain; charset=utf-8'; + alias /etc/v2ray-agent/subscribe/\$1/\$2; + } + location / { + } +} +EOF + bootStartup nginx + handleNginx stop + handleNginx start + fi + if [[ -z $(pgrep -f "nginx") ]]; then + handleNginx start + fi +} +# 卸载订阅 +unInstallSubscribe() { + rm -rf ${nginxConfigPath}subscribe.conf >/dev/null 2>&1 +} + +# 添加订阅 +addSubscribeMenu() { + echoContent skyBlue "\n===================== 添加其他机器订阅 =======================" + echoContent yellow "1.添加" + echoContent yellow "2.移除" + echoContent red "==============================================================" + read -r -p "请选择:" addSubscribeStatus + if [[ "${addSubscribeStatus}" == "1" ]]; then + addOtherSubscribe + elif [[ "${addSubscribeStatus}" == "2" ]]; then + if [[ ! -f "/etc/v2ray-agent/subscribe_remote/remoteSubscribeUrl" ]]; then + echoContent green " ---> 未安装其他订阅" + exit 0 + fi + grep -v '^$' "/etc/v2ray-agent/subscribe_remote/remoteSubscribeUrl" | awk '{print NR""":"$0}' + read -r -p "请选择要删除的订阅编号[仅支持单个删除]:" delSubscribeIndex + if [[ -z "${delSubscribeIndex}" ]]; then + echoContent green " ---> 不可以为空" + exit 0 + fi + + sed -i "$((delSubscribeIndex))d" "/etc/v2ray-agent/subscribe_remote/remoteSubscribeUrl" >/dev/null 2>&1 + + echoContent green " ---> 其他机器订阅删除成功" + subscribe + fi +} +# 添加其他机器clashMeta订阅 +addOtherSubscribe() { + echoContent yellow "#注意事项:" + echoContent yellow "请仔细阅读以下文章: https://www.v2ray-agent.com/archives/1681804748677" + echoContent skyBlue "录入示例:www.v2ray-agent.com:443:vps1\n" + read -r -p "请输入域名 端口 机器别名:" remoteSubscribeUrl + if [[ -z "${remoteSubscribeUrl}" ]]; then + echoContent red " ---> 不可为空" + addOtherSubscribe + elif ! echo "${remoteSubscribeUrl}" | grep -q ":"; then + echoContent red " ---> 规则不合法" + else + + if [[ -f "/etc/v2ray-agent/subscribe_remote/remoteSubscribeUrl" ]] && grep -q "${remoteSubscribeUrl}" /etc/v2ray-agent/subscribe_remote/remoteSubscribeUrl; then + echoContent red " ---> 此订阅已添加" + exit 0 + fi + echo + read -r -p "是否是HTTP订阅?[y/n]" httpSubscribeStatus + if [[ "${httpSubscribeStatus}" == "y" ]]; then + remoteSubscribeUrl="${remoteSubscribeUrl}:http" + fi + echo "${remoteSubscribeUrl}" >>/etc/v2ray-agent/subscribe_remote/remoteSubscribeUrl + subscribe + fi +} +# clashMeta配置文件 +clashMetaConfig() { + local url=$1 + local id=$2 + cat <"/etc/v2ray-agent/subscribe/clashMetaProfiles/${id}" +log-level: debug +mode: rule +ipv6: true +mixed-port: 7890 +allow-lan: true +bind-address: "*" +lan-allowed-ips: + - 0.0.0.0/0 + - ::/0 +find-process-mode: strict +external-controller: 0.0.0.0:9090 + +geox-url: + geoip: "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.dat" + geosite: "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat" + mmdb: "https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.metadb" +geo-auto-update: true +geo-update-interval: 24 + +external-controller-cors: + allow-private-network: true + +global-client-fingerprint: chrome + +profile: + store-selected: true + store-fake-ip: true + +sniffer: + enable: true + override-destination: false + sniff: + QUIC: + ports: [ 443 ] + TLS: + ports: [ 443 ] + HTTP: + ports: [80] + + +dns: + enable: true + prefer-h3: false + listen: 0.0.0.0:1053 + ipv6: true + enhanced-mode: fake-ip + fake-ip-range: 198.18.0.1/16 + fake-ip-filter: + - '*.lan' + - '*.local' + - 'dns.google' + - "localhost.ptlogin2.qq.com" + use-hosts: true + nameserver: + - https://1.1.1.1/dns-query + - https://8.8.8.8/dns-query + - 1.1.1.1 + - 8.8.8.8 + proxy-server-nameserver: + - https://223.5.5.5/dns-query + - https://1.12.12.12/dns-query + nameserver-policy: + "geosite:cn,private": + - https://doh.pub/dns-query + - https://dns.alidns.com/dns-query + +proxy-providers: + ${subscribeSalt}_provider: + type: http + path: ./${subscribeSalt}_provider.yaml + url: ${url} + interval: 3600 + proxy: DIRECT + health-check: + enable: true + url: https://cp.cloudflare.com/generate_204 + interval: 300 + +proxy-groups: + - name: 手动切换 + type: select + use: + - ${subscribeSalt}_provider + proxies: null + - name: 自动选择 + type: url-test + url: http://www.gstatic.com/generate_204 + interval: 36000 + tolerance: 50 + use: + - ${subscribeSalt}_provider + proxies: null + + - name: 全球代理 + type: select + use: + - ${subscribeSalt}_provider + proxies: + - 手动切换 + - 自动选择 + + - name: 流媒体 + type: select + use: + - ${subscribeSalt}_provider + proxies: + - 手动切换 + - 自动选择 + - DIRECT + - name: DNS_Proxy + type: select + use: + - ${subscribeSalt}_provider + proxies: + - 自动选择 + - DIRECT + + - name: Telegram + type: select + use: + - ${subscribeSalt}_provider + proxies: + - 手动切换 + - 自动选择 + - name: Google + type: select + use: + - ${subscribeSalt}_provider + proxies: + - 手动切换 + - 自动选择 + - DIRECT + - name: YouTube + type: select + use: + - ${subscribeSalt}_provider + proxies: + - 手动切换 + - 自动选择 + - name: Netflix + type: select + use: + - ${subscribeSalt}_provider + proxies: + - 流媒体 + - 手动切换 + - 自动选择 + - name: Spotify + type: select + use: + - ${subscribeSalt}_provider + proxies: + - 流媒体 + - 手动切换 + - 自动选择 + - DIRECT + - name: HBO + type: select + use: + - ${subscribeSalt}_provider + proxies: + - 流媒体 + - 手动切换 + - 自动选择 + - name: Bing + type: select + use: + - ${subscribeSalt}_provider + proxies: + - 自动选择 + - name: OpenAI + type: select + use: + - ${subscribeSalt}_provider + proxies: + - 自动选择 + - 手动切换 + - name: ClaudeAI + type: select + use: + - ${subscribeSalt}_provider + proxies: + - 自动选择 + - 手动切换 + - name: Disney + type: select + use: + - ${subscribeSalt}_provider + proxies: + - 流媒体 + - 手动切换 + - 自动选择 + - name: GitHub + type: select + use: + - ${subscribeSalt}_provider + proxies: + - 手动切换 + - 自动选择 + - DIRECT + + - name: 国内媒体 + type: select + use: + - ${subscribeSalt}_provider + proxies: + - DIRECT + - name: 本地直连 + type: select + use: + - ${subscribeSalt}_provider + proxies: + - DIRECT + - 自动选择 + - name: 漏网之鱼 + type: select + use: + - ${subscribeSalt}_provider + proxies: + - DIRECT + - 手动切换 + - 自动选择 +rule-providers: + lan: + type: http + behavior: classical + interval: 86400 + url: https://gh-proxy.com/https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Clash/Lan/Lan.yaml + path: ./Rules/lan.yaml + reject: + type: http + behavior: domain + url: https://gh-proxy.com/https://raw.githubusercontent.com/Loyalsoldier/clash-rules/release/reject.txt + path: ./ruleset/reject.yaml + interval: 86400 + proxy: + type: http + behavior: domain + url: https://gh-proxy.com/https://raw.githubusercontent.com/Loyalsoldier/clash-rules/release/proxy.txt + path: ./ruleset/proxy.yaml + interval: 86400 + direct: + type: http + behavior: domain + url: https://gh-proxy.com/https://raw.githubusercontent.com/Loyalsoldier/clash-rules/release/direct.txt + path: ./ruleset/direct.yaml + interval: 86400 + private: + type: http + behavior: domain + url: https://gh-proxy.com/https://raw.githubusercontent.com/Loyalsoldier/clash-rules/release/private.txt + path: ./ruleset/private.yaml + interval: 86400 + gfw: + type: http + behavior: domain + url: https://gh-proxy.com/https://raw.githubusercontent.com/Loyalsoldier/clash-rules/release/gfw.txt + path: ./ruleset/gfw.yaml + interval: 86400 + greatfire: + type: http + behavior: domain + url: https://gh-proxy.com/https://raw.githubusercontent.com/Loyalsoldier/clash-rules/release/greatfire.txt + path: ./ruleset/greatfire.yaml + interval: 86400 + tld-not-cn: + type: http + behavior: domain + url: https://gh-proxy.com/https://raw.githubusercontent.com/Loyalsoldier/clash-rules/release/tld-not-cn.txt + path: ./ruleset/tld-not-cn.yaml + interval: 86400 + telegramcidr: + type: http + behavior: ipcidr + url: https://gh-proxy.com/https://raw.githubusercontent.com/Loyalsoldier/clash-rules/release/telegramcidr.txt + path: ./ruleset/telegramcidr.yaml + interval: 86400 + applications: + type: http + behavior: classical + url: https://gh-proxy.com/https://raw.githubusercontent.com/Loyalsoldier/clash-rules/release/applications.txt + path: ./ruleset/applications.yaml + interval: 86400 + Disney: + type: http + behavior: classical + url: https://gh-proxy.com/https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Clash/Disney/Disney.yaml + path: ./ruleset/disney.yaml + interval: 86400 + Netflix: + type: http + behavior: classical + url: https://gh-proxy.com/https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Clash/Netflix/Netflix.yaml + path: ./ruleset/netflix.yaml + interval: 86400 + YouTube: + type: http + behavior: classical + url: https://gh-proxy.com/https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Clash/YouTube/YouTube.yaml + path: ./ruleset/youtube.yaml + interval: 86400 + HBO: + type: http + behavior: classical + url: https://gh-proxy.com/https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Clash/HBO/HBO.yaml + path: ./ruleset/hbo.yaml + interval: 86400 + OpenAI: + type: http + behavior: classical + url: https://gh-proxy.com/https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Clash/OpenAI/OpenAI.yaml + path: ./ruleset/openai.yaml + interval: 86400 + ClaudeAI: + type: http + behavior: classical + url: https://gh-proxy.com/https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Clash/Claude/Claude.yaml + path: ./ruleset/claudeai.yaml + interval: 86400 + Bing: + type: http + behavior: classical + url: https://gh-proxy.com/https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Clash/Bing/Bing.yaml + path: ./ruleset/bing.yaml + interval: 86400 + Google: + type: http + behavior: classical + url: https://gh-proxy.com/https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Clash/Google/Google.yaml + path: ./ruleset/google.yaml + interval: 86400 + GitHub: + type: http + behavior: classical + url: https://gh-proxy.com/https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Clash/GitHub/GitHub.yaml + path: ./ruleset/github.yaml + interval: 86400 + Spotify: + type: http + behavior: classical + url: https://gh-proxy.com/https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Clash/Spotify/Spotify.yaml + path: ./ruleset/spotify.yaml + interval: 86400 + ChinaMaxDomain: + type: http + behavior: domain + interval: 86400 + url: https://gh-proxy.com/https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Clash/ChinaMax/ChinaMax_Domain.yaml + path: ./Rules/ChinaMaxDomain.yaml + ChinaMaxIPNoIPv6: + type: http + behavior: ipcidr + interval: 86400 + url: https://gh-proxy.com/https://raw.githubusercontent.com/blackmatrix7/ios_rule_script/master/rule/Clash/ChinaMax/ChinaMax_IP_No_IPv6.yaml + path: ./Rules/ChinaMaxIPNoIPv6.yaml +rules: + - RULE-SET,YouTube,YouTube,no-resolve + - RULE-SET,Google,Google,no-resolve + - RULE-SET,GitHub,GitHub + - RULE-SET,telegramcidr,Telegram,no-resolve + - RULE-SET,Spotify,Spotify,no-resolve + - RULE-SET,Netflix,Netflix + - RULE-SET,HBO,HBO + - RULE-SET,Bing,Bing + - RULE-SET,OpenAI,OpenAI + - RULE-SET,ClaudeAI,ClaudeAI + - RULE-SET,Disney,Disney + - RULE-SET,proxy,全球代理 + - RULE-SET,gfw,全球代理 + - RULE-SET,applications,本地直连 + - RULE-SET,ChinaMaxDomain,本地直连 + - RULE-SET,ChinaMaxIPNoIPv6,本地直连,no-resolve + - RULE-SET,lan,本地直连,no-resolve + - GEOIP,CN,本地直连 + - MATCH,漏网之鱼 +EOF + +} +# 随机salt +initRandomSalt() { + local chars="abcdefghijklmnopqrtuxyz" + local initCustomPath= + for i in {1..10}; do + echo "${i}" >/dev/null + initCustomPath+="${chars:RANDOM%${#chars}:1}" + done + echo "${initCustomPath}" +} +# 订阅 +subscribe() { + readInstallProtocolType + installSubscribe + + readNginxSubscribe + local renewSalt=$1 + local showStatus=$2 + if [[ "${coreInstallType}" == "1" || "${coreInstallType}" == "2" ]]; then + + echoContent skyBlue "-------------------------备注---------------------------------" + echoContent yellow "# 查看订阅会重新生成本地账号的订阅" + echoContent red "# 需要手动输入md5加密的salt值,如果不了解使用随机即可" + echoContent yellow "# 不影响已添加的远程订阅的内容\n" + + if [[ -f "/etc/v2ray-agent/subscribe_local/subscribeSalt" && -n $(cat "/etc/v2ray-agent/subscribe_local/subscribeSalt") ]]; then + if [[ -z "${renewSalt}" ]]; then + read -r -p "读取到上次安装设置的Salt,是否使用上次生成的Salt ?[y/n]:" historySaltStatus + if [[ "${historySaltStatus}" == "y" ]]; then + subscribeSalt=$(cat /etc/v2ray-agent/subscribe_local/subscribeSalt) + else + read -r -p "请输入salt值, [回车]使用随机:" subscribeSalt + fi + else + subscribeSalt=$(cat /etc/v2ray-agent/subscribe_local/subscribeSalt) + fi + else + read -r -p "请输入salt值, [回车]使用随机:" subscribeSalt + showStatus= + fi + + if [[ -z "${subscribeSalt}" ]]; then + subscribeSalt=$(initRandomSalt) + fi + echoContent yellow "\n ---> Salt: ${subscribeSalt}" + + echo "${subscribeSalt}" >/etc/v2ray-agent/subscribe_local/subscribeSalt + + rm -rf /etc/v2ray-agent/subscribe/default/* + rm -rf /etc/v2ray-agent/subscribe/clashMeta/* + rm -rf /etc/v2ray-agent/subscribe_local/default/* + rm -rf /etc/v2ray-agent/subscribe_local/clashMeta/* + rm -rf /etc/v2ray-agent/subscribe_local/sing-box/* + showAccounts >/dev/null + if [[ -n $(ls /etc/v2ray-agent/subscribe_local/default/) ]]; then + if [[ -f "/etc/v2ray-agent/subscribe_remote/remoteSubscribeUrl" && -n $(cat "/etc/v2ray-agent/subscribe_remote/remoteSubscribeUrl") ]]; then + if [[ -z "${renewSalt}" ]]; then + read -r -p "读取到其他订阅,是否更新?[y/n]" updateOtherSubscribeStatus + else + updateOtherSubscribeStatus=y + fi + fi + local subscribePortLocal="${subscribePort}" + find /etc/v2ray-agent/subscribe_local/default/* | while read -r email; do + email=$(echo "${email}" | awk -F "[d][e][f][a][u][l][t][/]" '{print $2}') + + local emailMd5= + emailMd5=$(echo -n "${email}${subscribeSalt}"$'\n' | md5sum | awk '{print $1}') + + cat "/etc/v2ray-agent/subscribe_local/default/${email}" >>"/etc/v2ray-agent/subscribe/default/${emailMd5}" + if [[ "${updateOtherSubscribeStatus}" == "y" ]]; then + updateRemoteSubscribe "${emailMd5}" "${email}" + fi + local base64Result + base64Result=$(base64 -w 0 "/etc/v2ray-agent/subscribe/default/${emailMd5}") + echo "${base64Result}" >"/etc/v2ray-agent/subscribe/default/${emailMd5}" + echoContent yellow "--------------------------------------------------------------" + local currentDomain=${currentHost} + + if [[ -n "${currentDefaultPort}" && "${currentDefaultPort}" != "443" ]]; then + currentDomain="${currentHost}:${currentDefaultPort}" + fi + if [[ -n "${subscribePortLocal}" ]]; then + if [[ "${subscribeType}" == "http" ]]; then + currentDomain="$(getPublicIP):${subscribePort}" + else + currentDomain="${currentHost}:${subscribePort}" + fi + fi + if [[ -z "${showStatus}" ]]; then + echoContent skyBlue "\n----------默认订阅----------\n" + echoContent green "email:${email}\n" + echoContent yellow "url:${subscribeType}://${currentDomain}/s/default/${emailMd5}\n" + echoContent yellow "在线二维码:https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=${subscribeType}://${currentDomain}/s/default/${emailMd5}\n" + if [[ "${release}" != "alpine" ]]; then + echo "${subscribeType}://${currentDomain}/s/default/${emailMd5}" | qrencode -s 10 -m 1 -t UTF8 + fi + + # clashMeta + if [[ -f "/etc/v2ray-agent/subscribe_local/clashMeta/${email}" ]]; then + + cat "/etc/v2ray-agent/subscribe_local/clashMeta/${email}" >>"/etc/v2ray-agent/subscribe/clashMeta/${emailMd5}" + + sed -i '1i\proxies:' "/etc/v2ray-agent/subscribe/clashMeta/${emailMd5}" + + local clashProxyUrl="${subscribeType}://${currentDomain}/s/clashMeta/${emailMd5}" + clashMetaConfig "${clashProxyUrl}" "${emailMd5}" + echoContent skyBlue "\n----------clashMeta订阅----------\n" + echoContent yellow "url:${subscribeType}://${currentDomain}/s/clashMetaProfiles/${emailMd5}\n" + echoContent yellow "在线二维码:https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=${subscribeType}://${currentDomain}/s/clashMetaProfiles/${emailMd5}\n" + if [[ "${release}" != "alpine" ]]; then + echo "${subscribeType}://${currentDomain}/s/clashMetaProfiles/${emailMd5}" | qrencode -s 10 -m 1 -t UTF8 + fi + + fi + # sing-box + if [[ -f "/etc/v2ray-agent/subscribe_local/sing-box/${email}" ]]; then + cp "/etc/v2ray-agent/subscribe_local/sing-box/${email}" "/etc/v2ray-agent/subscribe/sing-box_profiles/${emailMd5}" + + echoContent skyBlue " ---> 下载 sing-box 通用配置文件" + if [[ "${release}" == "alpine" ]]; then + wget -O "/etc/v2ray-agent/subscribe/sing-box/${emailMd5}" -q "https://raw.githubusercontent.com/mack-a/v2ray-agent/master/documents/sing-box.json" + else + wget -O "/etc/v2ray-agent/subscribe/sing-box/${emailMd5}" -q "${wgetShowProgressStatus}" "https://raw.githubusercontent.com/mack-a/v2ray-agent/master/documents/sing-box.json" + fi + + jq ".outbounds=$(jq ".outbounds|map(if has(\"outbounds\") then .outbounds += $(jq ".|map(.tag)" "/etc/v2ray-agent/subscribe_local/sing-box/${email}") else . end)" "/etc/v2ray-agent/subscribe/sing-box/${emailMd5}")" "/etc/v2ray-agent/subscribe/sing-box/${emailMd5}" >"/etc/v2ray-agent/subscribe/sing-box/${emailMd5}_tmp" && mv "/etc/v2ray-agent/subscribe/sing-box/${emailMd5}_tmp" "/etc/v2ray-agent/subscribe/sing-box/${emailMd5}" + jq ".outbounds += $(jq '.' "/etc/v2ray-agent/subscribe_local/sing-box/${email}")" "/etc/v2ray-agent/subscribe/sing-box/${emailMd5}" >"/etc/v2ray-agent/subscribe/sing-box/${emailMd5}_tmp" && mv "/etc/v2ray-agent/subscribe/sing-box/${emailMd5}_tmp" "/etc/v2ray-agent/subscribe/sing-box/${emailMd5}" + + echoContent skyBlue "\n----------sing-box订阅----------\n" + echoContent yellow "url:${subscribeType}://${currentDomain}/s/sing-box/${emailMd5}\n" + echoContent yellow "在线二维码:https://api.qrserver.com/v1/create-qr-code/?size=400x400&data=${subscribeType}://${currentDomain}/s/sing-box/${emailMd5}\n" + if [[ "${release}" != "alpine" ]]; then + echo "${subscribeType}://${currentDomain}/s/sing-box/${emailMd5}" | qrencode -s 10 -m 1 -t UTF8 + fi + + fi + + echoContent skyBlue "--------------------------------------------------------------" + else + echoContent green " ---> email:${email},订阅已更新,请使用客户端重新拉取" + fi + + done + fi + else + echoContent red " ---> 未安装伪装站点,无法使用订阅服务" + fi +} + +# 更新远程订阅 +updateRemoteSubscribe() { + + local emailMD5=$1 + local email=$2 + while read -r line; do + local subscribeType= + subscribeType="https" + + local serverAlias= + serverAlias=$(echo "${line}" | awk -F "[:]" '{print $3}') + + local remoteUrl= + remoteUrl=$(echo "${line}" | awk -F "[:]" '{print $1":"$2}') + + local subscribeTypeRemote= + subscribeTypeRemote=$(echo "${line}" | awk -F "[:]" '{print $4}') + + if [[ -n "${subscribeTypeRemote}" ]]; then + subscribeType="${subscribeTypeRemote}" + fi + local clashMetaProxies= + + clashMetaProxies=$(curl -s "${subscribeType}://${remoteUrl}/s/clashMeta/${emailMD5}" | sed '/proxies:/d' | sed "s/\"${email}/\"${email}_${serverAlias}/g") + + if ! echo "${clashMetaProxies}" | grep -q "nginx" && [[ -n "${clashMetaProxies}" ]]; then + echo "${clashMetaProxies}" >>"/etc/v2ray-agent/subscribe/clashMeta/${emailMD5}" + echoContent green " ---> clashMeta订阅 ${remoteUrl}:${email} 更新成功" + else + echoContent red " ---> clashMeta订阅 ${remoteUrl}:${email}不存在" + fi + + local default= + default=$(curl -s "${subscribeType}://${remoteUrl}/s/default/${emailMD5}") + + if ! echo "${default}" | grep -q "nginx" && [[ -n "${default}" ]]; then + default=$(echo "${default}" | base64 -d | sed "s/#${email}/#${email}_${serverAlias}/g") + echo "${default}" >>"/etc/v2ray-agent/subscribe/default/${emailMD5}" + + echoContent green " ---> 通用订阅 ${remoteUrl}:${email} 更新成功" + else + echoContent red " ---> 通用订阅 ${remoteUrl}:${email} 不存在" + fi + + local singBoxSubscribe= + singBoxSubscribe=$(curl -s "${subscribeType}://${remoteUrl}/s/sing-box_profiles/${emailMD5}") + + if ! echo "${singBoxSubscribe}" | grep -q "nginx" && [[ -n "${singBoxSubscribe}" ]]; then + singBoxSubscribe=${singBoxSubscribe//tag\": \"${email}/tag\": \"${email}_${serverAlias}} + singBoxSubscribe=$(jq ". +=${singBoxSubscribe}" "/etc/v2ray-agent/subscribe_local/sing-box/${email}") + echo "${singBoxSubscribe}" | jq . >"/etc/v2ray-agent/subscribe_local/sing-box/${email}" + + echoContent green " ---> 通用订阅 ${remoteUrl}:${email} 更新成功" + else + echoContent red " ---> 通用订阅 ${remoteUrl}:${email} 不存在" + fi + + done < <(grep -v '^$' <"/etc/v2ray-agent/subscribe_remote/remoteSubscribeUrl") +} + +# 切换alpn +switchAlpn() { + echoContent skyBlue "\n功能 1/${totalProgress} : 切换alpn" + if [[ -z ${currentAlpn} ]]; then + echoContent red " ---> 无法读取alpn,请检查是否安装" + exit 0 + fi + + echoContent red "\n==============================================================" + echoContent green "当前alpn首位为:${currentAlpn}" + echoContent yellow " 1.当http/1.1首位时,trojan可用,gRPC部分客户端可用【客户端支持手动选择alpn的可用】" + echoContent yellow " 2.当h2首位时,gRPC可用,trojan部分客户端可用【客户端支持手动选择alpn的可用】" + echoContent yellow " 3.如客户端不支持手动更换alpn,建议使用此功能更改服务端alpn顺序,来使用相应的协议" + echoContent red "==============================================================" + + if [[ "${currentAlpn}" == "http/1.1" ]]; then + echoContent yellow "1.切换alpn h2 首位" + elif [[ "${currentAlpn}" == "h2" ]]; then + echoContent yellow "1.切换alpn http/1.1 首位" + else + echoContent red '不符合' + fi + + echoContent red "==============================================================" + + read -r -p "请选择:" selectSwitchAlpnType + if [[ "${selectSwitchAlpnType}" == "1" && "${currentAlpn}" == "http/1.1" ]]; then + + local frontingTypeJSON + frontingTypeJSON=$(jq -r ".inbounds[0].streamSettings.tlsSettings.alpn = [\"h2\",\"http/1.1\"]" ${configPath}${frontingType}.json) + echo "${frontingTypeJSON}" | jq . >${configPath}${frontingType}.json + + elif [[ "${selectSwitchAlpnType}" == "1" && "${currentAlpn}" == "h2" ]]; then + local frontingTypeJSON + frontingTypeJSON=$(jq -r ".inbounds[0].streamSettings.tlsSettings.alpn =[\"http/1.1\",\"h2\"]" ${configPath}${frontingType}.json) + echo "${frontingTypeJSON}" | jq . >${configPath}${frontingType}.json + else + echoContent red " ---> 选择错误" + exit 0 + fi + reloadCore +} + +# 初始化realityKey +initRealityKey() { + echoContent skyBlue "\n生成Reality key\n" + if [[ -n "${currentRealityPublicKey}" && -z "${lastInstallationConfig}" ]]; then + read -r -p "读取到上次安装记录,是否使用上次安装时的PublicKey/PrivateKey ?[y/n]:" historyKeyStatus + if [[ "${historyKeyStatus}" == "y" ]]; then + realityPrivateKey=${currentRealityPrivateKey} + realityPublicKey=${currentRealityPublicKey} + fi + elif [[ -n "${currentRealityPublicKey}" && -n "${lastInstallationConfig}" ]]; then + realityPrivateKey=${currentRealityPrivateKey} + realityPublicKey=${currentRealityPublicKey} + fi + if [[ -z "${realityPrivateKey}" ]]; then + if [[ "${selectCoreType}" == "2" || "${coreInstallType}" == "2" ]]; then + realityX25519Key=$(/etc/v2ray-agent/sing-box/sing-box generate reality-keypair) + realityPrivateKey=$(echo "${realityX25519Key}" | head -1 | awk '{print $2}') + realityPublicKey=$(echo "${realityX25519Key}" | tail -n 1 | awk '{print $2}') + echo "publicKey:${realityPublicKey}" >/etc/v2ray-agent/sing-box/conf/config/reality_key + else + read -r -p "请输入Private Key[回车自动生成]:" historyPrivateKey + if [[ -n "${historyPrivateKey}" ]]; then + realityX25519Key=$(/etc/v2ray-agent/xray/xray x25519 -i "${historyPrivateKey}") + else + realityX25519Key=$(/etc/v2ray-agent/xray/xray x25519) + fi + realityPrivateKey=$(echo "${realityX25519Key}" | grep "PrivateKey" | awk '{print $2}') + realityPublicKey=$(echo "${realityX25519Key}" | grep "Password" | awk '{print $2}') + if [[ -z "${realityPrivateKey}" ]]; then + echoContent red "输入的Private Key不合法" + initRealityKey + else + echoContent green "\n privateKey:${realityPrivateKey}" + echoContent green "\n publicKey:${realityPublicKey}" + fi + fi + fi +} +# 初始化 mldsa65Seed +initRealityMldsa65() { + echoContent skyBlue "\n生成Reality mldsa65\n" + if /etc/v2ray-agent/xray/xray tls ping "${realityServerName}:${realityDomainPort}" 2>/dev/null | grep -q "X25519MLKEM768"; then + length=$(/etc/v2ray-agent/xray/xray tls ping "${realityServerName}:${realityDomainPort}" | grep "Certificate chain's total length:" | awk '{print $5}' | head -1) + + if [ "$length" -gt 3500 ]; then + if [[ -n "${currentRealityMldsa65}" && -z "${lastInstallationConfig}" ]]; then + read -r -p "读取到上次安装记录,是否使用上次安装时的Seed/Verify ?[y/n]:" historyMldsa65Status + if [[ "${historyMldsa65Status}" == "y" ]]; then + realityMldsa65Seed=${currentRealityMldsa65Seed} + realityMldsa65Verify=${currentRealityMldsa65Verify} + fi + elif [[ -n "${currentRealityMldsa65Seed}" && -n "${lastInstallationConfig}" ]]; then + realityMldsa65Seed=${currentRealityMldsa65Seed} + realityMldsa65Verify=${currentRealityMldsa65Verify} + fi + if [[ -z "${realityMldsa65Seed}" ]]; then + # if [[ "${selectCoreType}" == "2" || "${coreInstallType}" == "2" ]]; then + # realityX25519Key=$(/etc/v2ray-agent/sing-box/sing-box generate reality-keypair) + # realityPrivateKey=$(echo "${realityX25519Key}" | head -1 | awk '{print $2}') + # realityPublicKey=$(echo "${realityX25519Key}" | tail -n 1 | awk '{print $2}') + # echo "publicKey:${realityPublicKey}" >/etc/v2ray-agent/sing-box/conf/config/reality_key + # else + realityMldsa65=$(/etc/v2ray-agent/xray/xray mldsa65) + realityMldsa65Seed=$(echo "${realityMldsa65}" | head -1 | awk '{print $2}') + realityMldsa65Verify=$(echo "${realityMldsa65}" | tail -n 1 | awk '{print $2}') + # fi + fi + # echoContent green "\n Seed:${realityMldsa65Seed}" + # echoContent green "\n Verify:${realityMldsa65Verify}" + else + echoContent green " 目标域名支持X25519MLKEM768,但是证书的长度不足,忽略ML-DSA-65。" + fi + else + echoContent green " 目标域名不支持X25519MLKEM768,忽略ML-DSA-65。" + fi +} +# 检查reality域名是否符合 +checkRealityDest() { + local traceResult= + traceResult=$(curl -s "https://$(echo "${realityDestDomain}" | cut -d ':' -f 1)/cdn-cgi/trace" | grep "visit_scheme=https") + if [[ -n "${traceResult}" ]]; then + echoContent red "\n ---> 检测到使用的域名,托管在cloudflare并开启了代理,使用此类型域名可能导致VPS流量被其他人使用[不建议使用]\n" + read -r -p "是否继续 ?[y/n]" setRealityDestStatus + if [[ "${setRealityDestStatus}" != 'y' ]]; then + exit 0 + fi + echoContent yellow "\n ---> 忽略风险,继续使用" + fi +} + +# 初始化客户端可用的ServersName +initRealityClientServersName() { + local realityDestDomainList="gateway.icloud.com,itunes.apple.com,swdist.apple.com,swcdn.apple.com,updates.cdn-apple.com,mensura.cdn-apple.com,osxapps.itunes.apple.com,aod.itunes.apple.com,download-installer.cdn.mozilla.net,addons.mozilla.org,s0.awsstatic.com,d1.awsstatic.com,images-na.ssl-images-amazon.com,m.media-amazon.com,player.live-video.net,one-piece.com,lol.secure.dyn.riotcdn.net,www.lovelive-anime.jp,www.swift.com,academy.nvidia.com,www.cisco.com,www.asus.com,www.samsung.com,www.amd.com,cdn-dynmedia-1.microsoft.com,software.download.prss.microsoft.com,dl.google.com,www.google-analytics.com" + if [[ -n "${realityServerName}" && -z "${lastInstallationConfig}" ]]; then + if echo ${realityDestDomainList} | grep -q "${realityServerName}"; then + read -r -p "读取到上次安装设置的Reality域名,是否使用?[y/n]:" realityServerNameStatus + if [[ "${realityServerNameStatus}" != "y" ]]; then + realityServerName= + realityDomainPort= + fi + else + realityServerName= + realityDomainPort= + fi + elif [[ -n "${realityServerName}" && -z "${lastInstallationConfig}" ]]; then + realityServerName= + realityDomainPort= + fi + + if [[ -z "${realityServerName}" ]]; then + if [[ -n "${domain}" ]]; then + echo + read -r -p "是否使用 ${domain} 此域名作为Reality目标域名 ?[y/n]:" realityServerNameCurrentDomainStatus + if [[ "${realityServerNameCurrentDomainStatus}" == "y" ]]; then + realityServerName="${domain}" + if [[ "${selectCoreType}" == "1" ]]; then + if [[ -z "${subscribePort}" ]]; then + echo + installSubscribe + readNginxSubscribe + realityDomainPort="${subscribePort}" + else + realityDomainPort="${subscribePort}" + fi + fi + if [[ "${selectCoreType}" == "2" ]]; then + if [[ -z "${subscribePort}" ]]; then + echo + installSubscribe + readNginxSubscribe + realityDomainPort="${subscribePort}" + else + realityDomainPort="${subscribePort}" + fi + fi + fi + fi + if [[ -z "${realityServerName}" ]]; then + realityDomainPort=443 + echoContent skyBlue "\n================ 配置客户端可用的serverNames ===============\n" + echoContent yellow "#注意事项" + echoContent green "Reality目标可用域名列表:https://www.v2ray-agent.com/archives/1689439383686#heading-3\n" + echoContent yellow "录入示例:addons.mozilla.org:443\n" + read -r -p "请输入目标域名,[回车]随机域名,默认端口443:" realityServerName + if [[ -z "${realityServerName}" ]]; then + randomNum=$(randomNum 1 27) + realityServerName=$(echo "${realityDestDomainList}" | awk -F ',' -v randomNum="$randomNum" '{print $randomNum}') + fi + if echo "${realityServerName}" | grep -q ":"; then + realityDomainPort=$(echo "${realityServerName}" | awk -F "[:]" '{print $2}') + realityServerName=$(echo "${realityServerName}" | awk -F "[:]" '{print $1}') + fi + fi + fi + + echoContent yellow "\n ---> 客户端可用域名: ${realityServerName}:${realityDomainPort}\n" +} +# 初始化reality端口 +initXrayRealityPort() { + if [[ -n "${xrayVLESSRealityPort}" && -z "${lastInstallationConfig}" ]]; then + read -r -p "读取到上次安装记录,是否使用上次安装时的端口 ?[y/n]:" historyRealityPortStatus + if [[ "${historyRealityPortStatus}" == "y" ]]; then + realityPort=${xrayVLESSRealityPort} + fi + elif [[ -n "${xrayVLESSRealityPort}" && -n "${lastInstallationConfig}" ]]; then + realityPort=${xrayVLESSRealityPort} + fi + + if [[ -z "${realityPort}" ]]; then + # if [[ -n "${port}" ]]; then + # read -r -p "是否使用TLS+Vision端口 ?[y/n]:" realityPortTLSVisionStatus + # if [[ "${realityPortTLSVisionStatus}" == "y" ]]; then + # realityPort=${port} + # fi + # fi + # if [[ -z "${realityPort}" ]]; then + echoContent yellow "请输入端口[回车随机10000-30000]" + + read -r -p "端口:" realityPort + if [[ -z "${realityPort}" ]]; then + realityPort=$((RANDOM % 20001 + 10000)) + fi + # fi + if [[ -n "${realityPort}" && "${xrayVLESSRealityPort}" == "${realityPort}" ]]; then + handleXray stop + else + checkPort "${realityPort}" + fi + fi + if [[ -z "${realityPort}" ]]; then + initXrayRealityPort + else + allowPort "${realityPort}" + echoContent yellow "\n ---> 端口: ${realityPort}" + fi + +} +# 初始化XHTTP端口 +initXrayXHTTPort() { + if [[ -n "${xrayVLESSRealityXHTTPort}" && -z "${lastInstallationConfig}" ]]; then + read -r -p "读取到上次安装记录,是否使用上次安装时的端口 ?[y/n]:" historyXHTTPortStatus + if [[ "${historyXHTTPortStatus}" == "y" ]]; then + xHTTPort=${xrayVLESSRealityXHTTPort} + fi + elif [[ -n "${xrayVLESSRealityXHTTPort}" && -n "${lastInstallationConfig}" ]]; then + xHTTPort=${xrayVLESSRealityXHTTPort} + fi + + if [[ -z "${xHTTPort}" ]]; then + + echoContent yellow "请输入端口[回车随机10000-30000]" + read -r -p "端口:" xHTTPort + if [[ -z "${xHTTPort}" ]]; then + xHTTPort=$((RANDOM % 20001 + 10000)) + fi + if [[ -n "${xHTTPort}" && "${xrayVLESSRealityXHTTPort}" == "${xHTTPort}" ]]; then + handleXray stop + else + checkPort "${xHTTPort}" + fi + fi + if [[ -z "${xHTTPort}" ]]; then + initXrayXHTTPort + else + allowPort "${xHTTPort}" + allowPort "${xHTTPort}" "udp" + echoContent yellow "\n ---> 端口: ${xHTTPort}" + fi +} + +# reality管理 +manageReality() { + readInstallProtocolType + readConfigHostPathUUID + readCustomPort + readSingBoxConfig + + if ! echo "${currentInstallProtocolType}" | grep -q -E "7,|8," || [[ -z "${coreInstallType}" ]]; then + echoContent red "\n ---> 请先安装Reality协议,参考教程 https://www.v2ray-agent.com/archives/1680104902581#heading-11" + exit 0 + fi + + if [[ "${coreInstallType}" == "1" ]]; then + selectCustomInstallType=",7," + initXrayConfig custom 1 true + elif [[ "${coreInstallType}" == "2" ]]; then + if echo "${currentInstallProtocolType}" | grep -q ",7,"; then + selectCustomInstallType=",7," + fi + if echo "${currentInstallProtocolType}" | grep -q ",8,"; then + selectCustomInstallType="${selectCustomInstallType},8," + fi + initSingBoxConfig custom 1 true + fi + + reloadCore + subscribe false +} + +# 安装reality scanner +installRealityScanner() { + if [[ ! -f "/etc/v2ray-agent/xray/reality_scan/RealiTLScanner-linux-64" ]]; then + version=$(curl -s https://api.github.com/repos/XTLS/RealiTLScanner/releases?per_page=1 | jq -r '.[]|.tag_name') + wget -c -q -P /etc/v2ray-agent/xray/reality_scan/ "https://github.com/XTLS/RealiTLScanner/releases/download/${version}/RealiTLScanner-linux-64" + chmod 655 /etc/v2ray-agent/xray/reality_scan/RealiTLScanner-linux-64 + fi +} +# reality scanner +realityScanner() { + echoContent skyBlue "\n进度 1/1 : 扫描Reality域名" + echoContent red "\n==============================================================" + echoContent yellow "# 注意事项" + echoContent yellow "扫描完成后,请自行检查扫描网站结果内容是否合规,需个人承担风险" + echoContent red "某些IDC不允许扫描操作,比如搬瓦工,其中风险请自行承担\n" + echoContent yellow "1.扫描IPv4" + echoContent yellow "2.扫描IPv6" + echoContent red "==============================================================" + read -r -p "请选择:" realityScannerStatus + local type= + if [[ "${realityScannerStatus}" == "1" ]]; then + type=4 + elif [[ "${realityScannerStatus}" == "2" ]]; then + type=6 + fi + + read -r -p "某些IDC不允许扫描操作,比如搬瓦工,其中风险请自行承担,是否继续?[y/n]:" scanStatus + + if [[ "${scanStatus}" != "y" ]]; then + exit 0 + fi + + publicIP=$(getPublicIP "${type}") + echoContent yellow "IP:${publicIP}" + if [[ -z "${publicIP}" ]]; then + echoContent red " ---> 无法获取IP" + exit 0 + fi + + read -r -p "IP是否正确?[y/n]:" ipStatus + if [[ "${ipStatus}" == "y" ]]; then + echoContent yellow "结果存储在 /etc/v2ray-agent/xray/reality_scan/result.log 文件中\n" + /etc/v2ray-agent/xray/reality_scan/RealiTLScanner-linux-64 -addr "${publicIP}" | tee /etc/v2ray-agent/xray/reality_scan/result.log + else + echoContent red " ---> 无法读取正确IP" + fi +} +# hysteria管理 +manageHysteria() { + echoContent skyBlue "\n进度 1/1 : Hysteria2 管理" + echoContent red "\n==============================================================" + local hysteria2Status= + if [[ -n "${singBoxConfigPath}" ]] && [[ -f "/etc/v2ray-agent/sing-box/conf/config/06_hysteria2_inbounds.json" ]]; then + echoContent yellow "依赖第三方sing-box\n" + echoContent yellow "1.重新安装" + echoContent yellow "2.卸载" + echoContent yellow "3.端口跳跃管理" + hysteria2Status=true + else + echoContent yellow "依赖sing-box内核\n" + echoContent yellow "1.安装" + fi + + echoContent red "==============================================================" + read -r -p "请选择:" installHysteria2Status + if [[ "${installHysteria2Status}" == "1" ]]; then + singBoxHysteria2Install + elif [[ "${installHysteria2Status}" == "2" && "${hysteria2Status}" == "true" ]]; then + unInstallSingBox hysteria2 + elif [[ "${installHysteria2Status}" == "3" && "${hysteria2Status}" == "true" ]]; then + portHoppingMenu hysteria2 + fi +} + +# tuic管理 +manageTuic() { + echoContent skyBlue "\n进度 1/1 : Tuic管理" + echoContent red "\n==============================================================" + local tuicStatus= + if [[ -n "${singBoxConfigPath}" ]] && [[ -f "/etc/v2ray-agent/sing-box/conf/config/09_tuic_inbounds.json" ]]; then + echoContent yellow "依赖sing-box内核\n" + echoContent yellow "1.重新安装" + echoContent yellow "2.卸载" + echoContent yellow "3.端口跳跃管理" + tuicStatus=true + else + echoContent yellow "依赖sing-box内核\n" + echoContent yellow "1.安装" + fi + + echoContent red "==============================================================" + read -r -p "请选择:" installTuicStatus + if [[ "${installTuicStatus}" == "1" ]]; then + singBoxTuicInstall + elif [[ "${installTuicStatus}" == "2" && "${tuicStatus}" == "true" ]]; then + unInstallSingBox tuic + elif [[ "${installTuicStatus}" == "3" && "${tuicStatus}" == "true" ]]; then + portHoppingMenu tuic + fi +} +# sing-box log日志 +singBoxLog() { + cat </etc/v2ray-agent/sing-box/conf/config/log.json +{ + "log": { + "disabled": $1, + "level": "debug", + "output": "/etc/v2ray-agent/sing-box/conf/box.log", + "timestamp": true + } +} +EOF + + handleSingBox stop + handleSingBox start +} + +# sing-box 版本管理 +singBoxVersionManageMenu() { + echoContent skyBlue "\n进度 $1/${totalProgress} : sing-box 版本管理" + if [[ -z "${singBoxConfigPath}" ]]; then + echoContent red " ---> 没有检测到安装程序,请执行脚本安装内容" + menu + exit 0 + fi + echoContent red "\n==============================================================" + echoContent yellow "1.升级 sing-box" + echoContent yellow "2.关闭 sing-box" + echoContent yellow "3.打开 sing-box" + echoContent yellow "4.重启 sing-box" + echoContent yellow "==============================================================" + local logStatus= + if [[ -n "${singBoxConfigPath}" && -f "${singBoxConfigPath}log.json" && "$(jq -r .log.disabled "${singBoxConfigPath}log.json")" == "false" ]]; then + echoContent yellow "5.关闭日志" + logStatus=true + else + echoContent yellow "5.启用日志" + logStatus=false + fi + + echoContent yellow "6.查看日志" + echoContent red "==============================================================" + + read -r -p "请选择:" selectSingBoxType + if [[ ! -f "${singBoxConfigPath}../box.log" ]]; then + touch "${singBoxConfigPath}../box.log" >/dev/null 2>&1 + fi + if [[ "${selectSingBoxType}" == "1" ]]; then + installSingBox 1 + handleSingBox stop + handleSingBox start + elif [[ "${selectSingBoxType}" == "2" ]]; then + handleSingBox stop + elif [[ "${selectSingBoxType}" == "3" ]]; then + handleSingBox start + elif [[ "${selectSingBoxType}" == "4" ]]; then + handleSingBox stop + handleSingBox start + elif [[ "${selectSingBoxType}" == "5" ]]; then + singBoxLog ${logStatus} + if [[ "${logStatus}" == "false" ]]; then + tail -f "${singBoxConfigPath}../box.log" + fi + elif [[ "${selectSingBoxType}" == "6" ]]; then + tail -f "${singBoxConfigPath}../box.log" + fi +} + +# 主菜单 +menu() { + cd "$HOME" || exit + echoContent red "\n==============================================================" + echoContent green "作者:mack-a" + echoContent green "当前版本:v3.4.30" + echoContent green "Github:https://github.com/mack-a/v2ray-agent" + echoContent green "描述:八合一共存脚本\c" + showInstallStatus + checkWgetShowProgress + echoContent red "\n=========================== 推广区============================" + echoContent red " " + echoContent green "VPS选购攻略:https://www.v2ray-agent.com/archives/1679975663984" + echoContent green "年付10美金低价VPS AS4837:https://www.v2ray-agent.com/archives/racknerdtao-can-zheng-li-nian-fu-10mei-yuan" + echoContent green "优质常驻套餐DMIT CN2-GIA:https://www.v2ray-agent.com/archives/186cee7b-9459-4e57-b9b2-b07a4f36931c" + echoContent green "VPS探针:https://ping.v2ray-agent.com/" + echoContent red "==============================================================" + if [[ -n "${coreInstallType}" ]]; then + echoContent yellow "1.重新安装" + else + echoContent yellow "1.安装" + fi + + echoContent yellow "2.任意组合安装" + echoContent yellow "4.Hysteria2管理" + echoContent yellow "5.REALITY管理" + echoContent yellow "6.Tuic管理" + + echoContent skyBlue "-------------------------工具管理-----------------------------" + echoContent yellow "7.用户管理" + echoContent yellow "8.伪装站管理" + echoContent yellow "9.证书管理" + echoContent yellow "10.CDN节点管理" + echoContent yellow "11.分流工具" + echoContent yellow "12.添加新端口" + echoContent yellow "13.BT下载管理" + echoContent yellow "15.域名黑名单" + echoContent skyBlue "-------------------------版本管理-----------------------------" + echoContent yellow "16.core管理" + echoContent yellow "17.更新脚本" + echoContent yellow "18.安装BBR、DD脚本" + echoContent skyBlue "-------------------------脚本管理-----------------------------" + echoContent yellow "20.卸载脚本" + echoContent red "==============================================================" + mkdirTools + aliasInstall + read -r -p "请选择:" selectInstallType + case ${selectInstallType} in + 1) + selectCoreInstall + ;; + 2) + selectCoreInstall + ;; + # 3) + # initXrayFrontingConfig 1 + # ;; + 4) + manageHysteria + ;; + 5) + manageReality 1 + ;; + 6) + manageTuic + ;; + 7) + manageAccount 1 + ;; + 8) + updateNginxBlog 1 + ;; + 9) + renewalTLS 1 + ;; + 10) + manageCDN 1 + ;; + 11) + routingToolsMenu 1 + ;; + 12) + addCorePort 1 + ;; + 13) + btTools 1 + ;; + 14) + switchAlpn 1 + ;; + 15) + blacklist 1 + ;; + 16) + coreVersionManageMenu 1 + ;; + 17) + updateV2RayAgent 1 + ;; + 18) + bbrInstall + ;; + 20) + unInstall 1 + ;; + esac +} +cronFunction +menu