commit a054c198359e7d16e4cb39aa33ab05d83498f497 Author: Olia Lisa Date: Mon Dec 29 15:56:22 2025 +0800 init diff --git a/bin/gen_self_tls.sh b/bin/gen_self_tls.sh new file mode 100644 index 0000000..15c0ead --- /dev/null +++ b/bin/gen_self_tls.sh @@ -0,0 +1,25 @@ +#!/bin/bash + +# 生成自签名证书 +gen_self_tls() { + local domain="${1:-bing.com}" + local script_dir="$(dirname "$(realpath "$0")")" + local key_dir="$script_dir/../tls" + local config_file="$script_dir/../config.json" + + # 导入utils文件夹中的jq_util.sh脚本 + source "$script_dir/utils/jq_util.sh" + + # 生成自签名证书 + openssl req -x509 -nodes -newkey ec:<(openssl ecparam -name prime256v1) \ + -keyout "$key_dir/server.key" \ + -out "$key_dir/server.crt" \ + -subj "/CN=$domain" \ + -days 36500 + + # 更新config.json文件中的域名信息 + modify_json_file "$config_file" "inbounds[0].tls.server_name" "https://$domain" +} + +# 调用函数,传入第一个参数作为域名 +gen_self_tls "${1:-bing.com}" \ No newline at end of file diff --git a/bin/print_share_link.sh b/bin/print_share_link.sh new file mode 100644 index 0000000..33ceba9 --- /dev/null +++ b/bin/print_share_link.sh @@ -0,0 +1,96 @@ +#!/bin/bash + +print_reality_share_link(){ + local script_dir=$(cd "$(dirname "$0")"; pwd) # 脚本文件绝对路径 + local config_dir=$(readlink -f "$script_dir/../conf") # 配置文件绝对路径 + + # 获取本机IP + local ipv4=$(curl -4 -sSL --connect-timeout 3 --retry 2 ip.sb || echo "null") + + local uuid=$(jq -r '.inbounds[0].settings.clients[0].id' $config_dir/config.json) + local port=$(jq -r '.inbounds[0].port' $config_dir/config.json) + local dest=$(jq -r '.inbounds[0].streamSettings.realitySettings.dest' $config_dir/config.json) + local sni=$(echo $dest | awk -F ':' '{print $1}') + local network="tcp" + local public_key=$(cat $config_dir/key.txt | grep "Password" | awk -F ': ' '{print $2}') + + # 打印配置信息 + echo -e "\033[32m" # config info with green color + echo "ipv4: $ipv4" + echo "port: $port" + echo "uuid: $uuid" + echo "dest: $dest" + echo "sni: $sni" + echo "publicKey/password: $public_key" + echo "network: $network" + if [ "$ipv4" != "null" ]; then + sub_ipv4="vless://$uuid@$ipv4:$port?encryption=none&security=reality&type=$network&sni=$sni&fp=chrome&pbk=$public_key&flow=xtls-rprx-vision#我的最新vless机场" + echo "" + echo "IPV4 分享连接: $sub_ipv4" + echo "" + + # 如果qrencode安装了,则打印二维码 + if command -v qrencode &> /dev/null; then + echo -e "IPV4 分享二维码:\n$(echo "$sub_ipv4" | qrencode -o - -t UTF8)" + fi + fi + + echo -e "\033[0m" +} + + +print_xhttp_reality_share_link(){ + local script_dir=$(cd "$(dirname "$0")"; pwd) # 脚本文件绝对路径 + local config_dir=$(readlink -f "$script_dir/../conf") # xray配置文件绝对路径 + + local ipv4=$(curl -4 -sSL --connect-timeout 3 --retry 2 ip.sb || echo "null") # 本机IP + local uuid=$(jq -r '.inbounds[0].settings.clients[0].id' $config_dir/config.json) + local port=$(jq -r '.inbounds[0].port' $config_dir/config.json) + local dest=$(jq -r '.inbounds[0].streamSettings.realitySettings.dest' $config_dir/config.json) + local sni=$(echo $dest | awk -F ':' '{print $1}') + local network=$(jq -r '.inbounds[0].streamSettings.network' $config_dir/config.json) + local xhttp_path=$(jq -r ".inbounds[0].streamSettings.xhttpSettings.path" $config_dir/config.json) + local public_key=$(cat $config_dir/key.txt | grep "Password" | awk -F ': ' '{print $2}') + + + # 打印配置信息 + echo -e "\033[32m" # config info with green color + echo "ipv4: $ipv4" + echo "port: $port" + echo "uuid: $uuid" + echo "dest: $dest" + echo "sni: $sni" + echo "publicKey/password: $public_key" + echo "network: $network" + echo "xhttp_path: $xhttp_path" + if [ "$ipv4" != "null" ]; then + sub_ipv4="vless://$uuid@$ipv4:$port?encryption=none&security=reality&type=$network&sni=$sni&fp=chrome&pbk=$public_key&path=$xhttp_path&mode=auto#${ipv4}-my_xray_xhttp_reality" + echo "" + echo "ipv4 分享连接: $sub_ipv4" + echo "" + # 如果qrencode安装了,则打印二维码 + if command -v qrencode &> /dev/null; then + echo -e "ipv4 分享二维码:\n$(echo "$sub_ipv4" | qrencode -o - -t UTF8)" + fi + fi + + echo -e "\033[0m" +} + +print_share_link(){ + local script_dir=$(cd "$(dirname "$0")"; pwd) # 脚本文件绝对路径 + local config_dir=$(readlink -f "$script_dir/../conf") # xray配置文件绝对路径 + + # 检查 jq 是否安装 + source $script_dir/utils/base.sh + check_jq + + local xhttp_path=$(jq -r ".inbounds[0].streamSettings.xhttpSettings.path" $config_dir/config.json) + if [ -z $xhttp_path ] || [ "$xhttp_path" = "null" ];then + print_reality_share_link + else + print_xhttp_reality_share_link + fi +} + +print_share_link diff --git a/bin/update_docker_images.sh b/bin/update_docker_images.sh new file mode 100644 index 0000000..7cbdb9d --- /dev/null +++ b/bin/update_docker_images.sh @@ -0,0 +1,27 @@ +#!/bin/bash + + +update_docker_images(){ + script_dir=$(cd "$(dirname "$0")"; pwd) # 脚本文件绝对路径 + docker_compose_file="$script_dir/../docker-compose.yml" # docker-compose.yml文件路径 + + # 检查是否存在 docker-compose.yml 文件 + if [ ! -f $docker_compose_file ]; then + echo "Error: docker-compose.yml 文件不存在." + exit 1 + fi + + echo "正在关闭容器.." + docker-compose -f $docker_compose_file down + + echo "正在更新镜像.." + docker-compose -f $docker_compose_file pull + + echo "正在删除未使用的镜像..." + docker image prune -f + + echo "正在启动容器.." + docker-compose -f $docker_compose_file up -d +} + +update_docker_images diff --git a/bin/update_port.sh b/bin/update_port.sh new file mode 100644 index 0000000..6e08b86 --- /dev/null +++ b/bin/update_port.sh @@ -0,0 +1,57 @@ +#!/bin/bash + +# 查找空闲端口 +find_free_port() { + local start="${1:-10001}" + local max="${2:-65535}" + + # 收集当前监听端口(TCP + UDP) + local used_ports raw + if command -v ss >/dev/null 2>&1; then + raw=$(ss -lntu 2>/dev/null || true) + used_ports=$(printf "%s\n" "$raw" | awk '{print $5}' | sed -n '2,$p' | sed -E 's/.*[:]//g' | sed '/^$/d') + elif command -v netstat >/dev/null 2>&1; then + raw=$(netstat -lntu 2>/dev/null || true) + used_ports=$(printf "%s\n" "$raw" | awk '{print $4}' | sed -n '2,$p' | sed -E 's/.*[:]//g' | sed '/^$/d') + elif command -v lsof >/dev/null 2>&1; then + raw=$(lsof -i -P -n 2>/dev/null || true) + used_ports=$(printf "%s\n" "$raw" | awk '/LISTEN/ {print $9}' | sed -E 's/.*[:]//g' | sed '/^$/d') + else + echo "Error: neither ss, netstat nor lsof is available to check listening ports." >&2 + return 2 + fi + + # 用关联数组记录已占用端口(需要 bash 4+) + declare -A used_map + local p + for p in $used_ports; do + # 过滤掉非数字 + if [[ $p =~ ^[0-9]+$ ]]; then + used_map["$p"]=1 + fi + done + + # 从 start 到 max 逐个检查 + for ((port = start; port <= max; port++)); do + if [[ -z "${used_map[$port]}" ]]; then + echo "$port" + return 0 + fi + done + + # 没找到可用端口 + echo "Error: No available port found in range $start-$max" >&2 + return 1 +} + +update_port(){ + local script_dir=$(cd "$(dirname "$0")"; pwd) + local config_dir="$script_dir/../" + source "$script_dir/utils/jq_util.sh" + + local port=$(find_free_port) + modify_json_file "$config_dir/config.json" ".inbounds[0].listen_port" "$port" + echo "设置端口成功" +} + +update_port \ No newline at end of file diff --git a/bin/update_uuid.sh b/bin/update_uuid.sh new file mode 100644 index 0000000..9d602fb --- /dev/null +++ b/bin/update_uuid.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +update_uuid(){ + + script_dir=$(cd "$(dirname "$0")"; pwd) # 脚本文件绝对路径 + config_dir="$script_dir/../conf" + + source $script_dir/utils/jq_util.sh + + # 生成 UUID + uuid=$(docker run --rm teddysun/xray:latest xray uuid) + + #修改 UUID + modify_json_file "$config_dir/config.json" ".inbounds[0].settings.clients[0].id" "$uuid" + + echo "设置UUID成功." +} + +update_uuid diff --git a/bin/utils/base.sh b/bin/utils/base.sh new file mode 100644 index 0000000..e5a6e4c --- /dev/null +++ b/bin/utils/base.sh @@ -0,0 +1,86 @@ +#!/bin/bash + +red(){ + echo -e "\033[31m$1\033[0m" +} + +green(){ + echo -e "\033[32m$1\033[0m" +} + + +# 获取系统包管理器 +get_package_manager(){ + if command -v apt-get &> /dev/null; then + echo "apt-get" + elif command -v yum &> /dev/null; then + echo "yum" + else + echo "未知的系统包管理器" + return 1 # 返回错误状态码 + fi +} + +# 检查 jq 是否已安装 +check_jq() { + if command -v jq >/dev/null 2>&1; then + return 0 + else + echo "jq 未安装" + return 1 + fi +} + +# 检查 Docker 是否已安装 +check_docker() { + if command -v docker >/dev/null 2>&1; then + return 0 + else + echo "Docker 未安装" + return 1 + fi +} + +# 检查 docker-compose 是否已安装 +check_docker_compose() { + if command -v docker-compose >/dev/null 2>&1; then + return 0 + elif docker compose version >/dev/null 2>&1; then ## Docker 内置 compose 已安装 + return 0 + else + echo "docker-compose 未安装" + return 1 + fi +} + +# 安装jq +install_jq(){ + local package_manager=$(get_package_manager) # 获取包管理器 + if [[ -z "$package_manager" ]]; then + echo "无法识别系统包管理器" + return 1 # 无法识别包管理器 + fi + + if [[ "$package_manager" == "apt-get" ]]; then + sudo apt-get install -y jq + elif [[ "$package_manager" == "yum" ]]; then + sudo yum install -y jq + else + echo "未知的系统包管理器" + return 1 # 未知的包管理器 + fi +} + +# 安装docker +install_docker(){ + echo 安装docker... + curl -fsSL https://get.docker.com | bash -s docker +} + +# 安装docker-compose +install_docker_compose(){ + echo 安装docker-compose... + wget -O /usr/local/bin/docker-compose https://github.com/docker/compose/releases/download/v2.29.0/docker-compose-linux-x86_64 + chmod +x /usr/local/bin/docker-compose +} + diff --git a/bin/utils/jq_util.sh b/bin/utils/jq_util.sh new file mode 100644 index 0000000..c2b8370 --- /dev/null +++ b/bin/utils/jq_util.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# 修改json文件的属性值 +# 案例1:modify_json_file "/foo/bar.json" "person.name" "张三" +# 案例2:modify_json_file "/foo/bar.json" "servers" '["a.com", "b.com"]' +# 该函数能自动处理不同数据类型,包括数组、对象和数字等。 +function modify_json_file() { + local json_file=$1 # json文件路径 + local key=$2 # 要修改的key + local value=$3 # 要修改的value + + #如果key的值是.开头, 则去掉.号 + if [[ ${key} == "."* ]]; then + key=${key:1} + fi + + # 检查值是否为有效的 JSON 格式(如数组、对象或数字)。 + # 如果是,则直接将值传递给 jq,无需引号。 + if echo "${value}" | jq . >/dev/null 2>&1; then + jq ".${key} = ${value}" "${json_file}" > "${json_file}.tmp" + else + # 否则,将值视为字符串,并添加引号。 + jq ".${key} = \"${value}\"" "${json_file}" > "${json_file}.tmp" + fi + + mv ${json_file}.tmp ${json_file} +} diff --git a/config.json b/config.json new file mode 100644 index 0000000..8304f2e --- /dev/null +++ b/config.json @@ -0,0 +1,53 @@ +{ + "log": { + "level": "info", + "timestamp": true + }, + "inbounds": [ + { + "type": "tuic", + "tag": "tuic-in", + "listen": "::", + "listen_port": 8443, + "users": [ + { + "name": "user1", + "uuid": "5baf12ee-6922-4249-8ff8-7c7e26127e0d", + "password": "9tK!69jP7BpB" + } + ], + "congestion_control": "bbr", + "auth_timeout": "3s", + "zero_rtt_handshake": false, + "heartbeat": "10s", + "tls": { + "enabled": true, + "server_name": "bing.com", + "alpn": [ + "h3" + ], + "certificate_path": "./tls/server.pem", + "key_path": "./tls/server.pem" + } + } + ], + "outbounds": [ + { + "type": "direct", + "tag": "direct" + }, + { + "type": "block", + "tag": "block" + } + ], + "route": { + "rules": [ + { + "geoip": "private", + "outbound": "block" + } + ], + "final": "direct" + } +} \ No newline at end of file