Files
proxy-installer/scripts/reality.sh
2026-04-27 21:56:29 +08:00

439 lines
16 KiB
Bash
Executable File

#!/bin/bash
# =================================================================
# Xray (xtls-rprx-vision Reality) 服务器端管理脚本
# =================================================================
# 定义输出颜色
GREEN='\033[0;32m'
RED='\033[0;31m'
NC='\033[0m' # 无颜色
XRAY_CONFIG_FILE="/usr/local/etc/xray/config.json"
XRAY_KEYS_FILE="/usr/local/etc/xray/reality.keys"
# --- 通用函数 ---
check_root() {
if [ "$(id -u)" -ne 0 ]; then
echo -e "${RED}错误:此脚本需要以 root 权限运行。${NC}" >&2
exit 1
fi
}
# --- 函数: 确保 jq 已安装 ---
ensure_jq() {
if ! command -v jq &> /dev/null; then
echo "--> 检测到依赖工具 jq 未安装,正在尝试自动安装..."
if command -v apt-get &> /dev/null; then
apt-get update >/dev/null && apt-get install -y jq
elif command -v yum &> /dev/null; then
yum install -y jq
else
echo -e "${RED}无法自动安装 jq。请手动安装 (sudo apt install jq / sudo yum install jq) 后再试。${NC}"
return 1
fi
if ! command -v jq &> /dev/null; then
echo -e "${RED}jq 安装失败,请检查包管理器或手动安装。${NC}"
return 1
fi
echo " jq 安装成功。"
fi
return 0
}
# --- 函数: 检查 Xray 安装和运行状态 ---
check_xray_status() {
if [ -f /usr/local/bin/xray ]; then
echo -e "${GREEN}Xray 核心: 已安装${NC}"
else
echo -e "${RED}Xray 核心: 未安装${NC}"
fi
if [ -f "${XRAY_CONFIG_FILE}" ]; then
echo -e "${GREEN}配置状态 : 已配置${NC}"
if systemctl is-active --quiet xray; then
echo -e "${GREEN}服务状态 : 运行中${NC}"
else
echo -e "${RED}服务状态 : 未运行${NC}"
fi
else
echo -e "${RED}配置状态 : 未配置${NC}"
fi
}
# --- 函数: 生成 Reality 配置 ---
generate_reality_simple_config() {
echo "--> 正在配置 Reality 配置..."
read -p "请输入监听端口 (例如 443, 留空随机): " PORT
[ -z "$PORT" ] && PORT=$((RANDOM % 55536 + 10000))
read -p "请输入伪装域名 (留空则默认为 icloud.com): " SNI_DOMAIN
[ -z "$SNI_DOMAIN" ] && SNI_DOMAIN="icloud.com"
local sni_domain_cleaned=$(echo "${SNI_DOMAIN}" | cut -d':' -f1)
echo "--> 正在生成 UUID 和 Reality 密钥对..."
local uuid=$(/usr/local/bin/xray uuid)
local key_pair=$(/usr/local/bin/xray x25519)
local private_key=$(echo "$key_pair" | grep 'PrivateKey' | awk '{print $2}')
local public_key=$(echo "$key_pair" | grep 'Password' | awk '{print $2}')
echo "PrivateKey: ${private_key}" > "${XRAY_KEYS_FILE}"
echo "PublicKey: ${public_key}" >> "${XRAY_KEYS_FILE}"
echo "--> 正在创建配置文件 ${XRAY_CONFIG_FILE}..."
cat > "${XRAY_CONFIG_FILE}" <<EOF
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"port": ${PORT},
"protocol": "vless",
"settings": {
"clients": [
{
"id": "${uuid}",
"flow": "xtls-rprx-vision"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"dest": "${sni_domain_cleaned}:443",
"serverNames": [ "${sni_domain_cleaned}" ],
"privateKey": "${private_key}",
"shortIds": [ "", "0123456789abcdef" ]
}
},
"sniffing": {
"enabled": true,
"destOverride": [ "http", "tls", "quic" ],
"routeOnly": true
}
}
],
"outbounds": [
{
"protocol": "freedom",
"tag": "direct"
}
]
}
EOF
}
# --- 函数: 生成 Reality (防偷) 配置 ---
generate_reality_dokodemo_config() {
echo "--> 正在配置 Reality (防偷)..."
read -p "请输入外部监听端口 (例如 443, 留空随机): " EXT_PORT
[ -z "$EXT_PORT" ] && EXT_PORT=$((RANDOM % 55536 + 10000))
read -p "请输入内部 VLESS 端口 (留空随机): " INT_PORT
[ -z "$INT_PORT" ] && INT_PORT=$((RANDOM % 55536 + 10000))
read -p "请输入伪装域名 (留空则默认为 icloud.com): " SNI_DOMAIN
[ -z "$SNI_DOMAIN" ] && SNI_DOMAIN="icloud.com"
local sni_domain_cleaned=$(echo "${SNI_DOMAIN}" | cut -d':' -f1)
echo "--> 正在生成 UUID 和 Reality 密钥对..."
local uuid=$(/usr/local/bin/xray uuid)
local key_pair=$(/usr/local/bin/xray x25519)
local private_key=$(echo "$key_pair" | grep 'PrivateKey' | awk '{print $2}')
local public_key=$(echo "$key_pair" | grep 'Password' | awk '{print $2}')
echo "PrivateKey: ${private_key}" > "${XRAY_KEYS_FILE}"
echo "PublicKey: ${public_key}" >> "${XRAY_KEYS_FILE}"
echo "--> 正在创建配置文件 ${XRAY_CONFIG_FILE}..."
cat > "${XRAY_CONFIG_FILE}" <<EOF
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"tag": "dokodemo-in",
"port": ${EXT_PORT},
"protocol": "dokodemo-door",
"settings": {
"address": "127.0.0.1",
"port": ${INT_PORT},
"network": "tcp"
},
"sniffing": {
"enabled": true,
"destOverride": [ "tls" ],
"routeOnly": true
}
},
{
"listen": "127.0.0.1",
"port": ${INT_PORT},
"protocol": "vless",
"settings": {
"clients": [
{
"id": "${uuid}",
"flow": "xtls-rprx-vision"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"dest": "${sni_domain_cleaned}:443",
"serverNames": [ "${sni_domain_cleaned}" ],
"privateKey": "${private_key}",
"shortIds": [ "", "0123456789abcdef" ]
}
},
"sniffing": {
"enabled": true,
"destOverride": [ "http", "tls", "quic" ],
"routeOnly": true
}
}
],
"outbounds": [
{ "protocol": "freedom", "tag": "direct" },
{ "protocol": "blackhole", "tag": "block" }
],
"routing": {
"rules": [
{
"inboundTag": [ "dokodemo-in" ],
"domain": [ "${sni_domain_cleaned}" ],
"outboundTag": "direct"
},
{
"inboundTag": [ "dokodemo-in" ],
"outboundTag": "block"
}
]
}
}
EOF
}
# --- 函数: 安装 Xray 核心 ---
install_xray_core() {
if [ -f /usr/local/bin/xray ]; then
echo -e "${GREEN}Xray 核心已安装,无需重复操作。${NC}"
return 0
fi
echo "--> 正在使用官方脚本安装 Xray 核心..."
bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ install -u root
if [ ! -f /usr/local/bin/xray ]; then
echo -e "${RED}Xray 核心安装失败。${NC}"
return 1
else
echo -e "${GREEN}Xray 核心安装成功。${NC}"
echo "--> 正在清理旧的配置文件..."
systemctl stop xray >/dev/null 2>&1
rm -f "${XRAY_CONFIG_FILE}"
rm -f "${XRAY_KEYS_FILE}"
echo " 旧配置已清理。"
return 0
fi
}
# --- 函数: (包装器) 配置 Reality ---
configure_reality_simple() {
if ! [ -f /usr/local/bin/xray ]; then
echo "--> Xray 核心未安装,正在自动安装..."
install_xray_core || return
fi
if [ -f "${XRAY_CONFIG_FILE}" ]; then
read -p "检测到现有配置,继续将覆盖它。确定吗?[y/N]: " confirm
if [[ ! "$confirm" =~ ^[yY] ]]; then echo "操作已取消。"; return; fi
fi
generate_reality_simple_config
echo "--> 正在测试配置并启动 Xray..."
/usr/local/bin/xray -test -config "${XRAY_CONFIG_FILE}"
if [ $? -ne 0 ]; then
echo -e "${RED}配置文件测试失败,请检查。配置未应用。${NC}"; return; fi
systemctl restart xray
systemctl enable xray > /dev/null 2>&1
echo -e "${GREEN}🎉 Xray Reality 配置成功!${NC}"
view_config_xray
}
# --- 函数: (包装器) 配置 Reality (防偷) ---
configure_reality_dokodemo() {
if ! [ -f /usr/local/bin/xray ]; then
echo "--> Xray 核心未安装,正在自动安装..."
install_xray_core || return
fi
if [ -f "${XRAY_CONFIG_FILE}" ]; then
read -p "检测到现有配置,继续将覆盖它。确定吗?[y/N]: " confirm
if [[ ! "$confirm" =~ ^[yY] ]]; then echo "操作已取消。"; return; fi
fi
generate_reality_dokodemo_config
echo "--> 正在测试配置并启动 Xray..."
/usr/local/bin/xray -test -config "${XRAY_CONFIG_FILE}"
if [ $? -ne 0 ]; then
echo -e "${RED}配置文件测试失败,请检查。配置未应用。${NC}"; return; fi
systemctl restart xray
systemctl enable xray > /dev/null 2>&1
echo -e "${GREEN}🎉 Xray Reality (防偷) 配置成功!${NC}"
view_config_xray
}
# --- 函数: 卸载 Xray ---
uninstall_xray() {
if [ ! -f /usr/local/bin/xray ]; then
echo -e "${RED}Xray 未安装。${NC}"; return; fi
read -p "警告:确定要卸载 Xray 吗?这将删除所有数据。[y/N]: " confirm
if [[ ! "$confirm" =~ ^[yY]([eE][sS])?$ ]]; then
echo "卸载操作已取消。"; return; fi
systemctl stop xray
bash -c "$(curl -L https://github.com/XTLS/Xray-install/raw/main/install-release.sh)" @ remove --purge
rm -f "${XRAY_KEYS_FILE}"
echo -e "${GREEN}Xray 已成功卸载。${NC}"
}
# --- 函数: 查看 Xray 配置 ---
view_config_xray() {
if [ ! -f "${XRAY_CONFIG_FILE}" ]; then
echo -e "${RED}Xray 未配置。请先选择一个配置方案。${NC}"; return; fi
ensure_jq || return
if ! jq . "${XRAY_CONFIG_FILE}" >/dev/null 2>&1; then
echo -e "${RED}错误:配置文件 ${XRAY_CONFIG_FILE} 格式无效。${NC}"; return; fi
local ip_address=$(curl -s https://ipv4.icanhazip.com || echo "<您的服务器IP>")
local public_key="<未找到>"
if [ -f "${XRAY_KEYS_FILE}" ]; then
public_key=$(grep 'PublicKey' "${XRAY_KEYS_FILE}" | awk '{print $2}')
fi
echo "------------------------------------------"
echo " Xray (Reality) 当前配置信息"
echo "------------------------------------------"
# 通过 tag 判断配置类型
if jq -e '.inbounds[] | select(.tag=="dokodemo-in")' "${XRAY_CONFIG_FILE}" >/dev/null 2>&1; then
# 防偷配置
local ext_port=$(jq '.inbounds[] | select(.tag=="dokodemo-in") | .port' "${XRAY_CONFIG_FILE}")
local vless_inbound=$(jq '.inbounds[] | select(.protocol=="vless")' "${XRAY_CONFIG_FILE}")
local uuid=$(echo "$vless_inbound" | jq -r '.settings.clients[0].id')
local flow=$(echo "$vless_inbound" | jq -r '.settings.clients[0].flow')
local sni=$(echo "$vless_inbound" | jq -r '.streamSettings.realitySettings.serverNames[0]')
local short_id=$(echo "$vless_inbound" | jq -r '.streamSettings.realitySettings.shortIds[0]')
echo -e "配置类型 : ${GREEN}Reality (防偷)${NC}"
echo -e "监听地址 : ${GREEN}${ip_address}${NC}"
echo -e "外部端口 : ${GREEN}${ext_port}${NC}"
echo -e "UUID : ${GREEN}${uuid}${NC}"
echo -e "Flow : ${GREEN}${flow}${NC}"
echo -e "伪装域名 : ${GREEN}${sni}${NC}"
echo -e "Short ID : ${GREEN}${short_id}${NC}"
echo -e "公钥 (pbk) : ${GREEN}${public_key}${NC}"
echo "------------------------------------------"
echo "VLESS 分享链接:"
local vless_link="vless://${uuid}@${ip_address}:${ext_port}?encryption=none&flow=${flow}&security=reality&sni=${sni}&fp=random&pbk=${public_key}&sid=${short_id}&allowInsecure=1&type=tcp&headerType=none#VPS_防偷"
echo -e "${GREEN}${vless_link}${NC}"
else
# Reality 配置
local vless_inbound=$(jq '.inbounds[] | select(.protocol=="vless")' "${XRAY_CONFIG_FILE}")
local port=$(echo "$vless_inbound" | jq -r '.port')
local uuid=$(echo "$vless_inbound" | jq -r '.settings.clients[0].id')
local flow=$(echo "$vless_inbound" | jq -r '.settings.clients[0].flow')
local sni=$(echo "$vless_inbound" | jq -r '.streamSettings.realitySettings.serverNames[0]')
local short_id=$(echo "$vless_inbound" | jq -r '.streamSettings.realitySettings.shortIds[0]')
echo -e "配置类型 : ${GREEN}Reality 配置${NC}"
echo -e "监听地址 : ${GREEN}${ip_address}${NC}"
echo -e "端口 : ${GREEN}${port}${NC}"
echo -e "UUID : ${GREEN}${uuid}${NC}"
echo -e "Flow : ${GREEN}${flow}${NC}"
echo -e "伪装域名 : ${GREEN}${sni}${NC}"
echo -e "Short ID : ${GREEN}${short_id}${NC}"
echo -e "公钥 (pbk) : ${GREEN}${public_key}${NC}"
echo "------------------------------------------"
echo "VLESS 分享链接:"
local vless_link="vless://${uuid}@${ip_address}:${port}?encryption=none&flow=${flow}&security=reality&sni=${sni}&fp=random&pbk=${public_key}&sid=${short_id}&allowInsecure=1&type=tcp&headerType=none#VPS_Reality"
echo -e "${GREEN}${vless_link}${NC}"
fi
echo "------------------------------------------"
}
# --- 函数: Xray 服务管理 ---
manage_xray_service() {
if ! systemctl list-units --type=service | grep -q "xray.service"; then
echo -e "${RED}Xray 服务未安装。${NC}"; return; fi
case $1 in
start) systemctl start xray && echo -e "${GREEN}服务启动成功。${NC}" || echo -e "${RED}服务启动失败。${NC}" ;;
stop) systemctl stop xray && echo -e "${GREEN}服务已停止。${NC}" || echo -e "${RED}服务停止失败。${NC}" ;;
restart) systemctl restart xray && echo -e "${GREEN}服务重启成功。${NC}" || echo -e "${RED}服务重启失败。${NC}" ;;
status) systemctl status xray ;;
esac
}
# --- 函数: Xray 主菜单 ---
xray_menu() {
while true; do
clear
echo "=================================================="
echo " XTLS-RPRX-VISION Reality 管理脚本"
echo "=================================================="
check_xray_status
echo "--------------------------------------------------"
echo "1. 安装 Xray 核心"
echo "2. 配置 Reality"
echo "3. 配置 Reality (防偷)"
echo "4. 卸载 Xray"
echo "5. 查看 Xray 配置"
echo "6. 启动 Xray"
echo "7. 停止 Xray"
echo "8. 重启 Xray"
echo "9. 查看运行状态"
echo "0. 退出脚本"
echo "=================================================="
read -p "请输入选项 [0-9]: " choice
case $choice in
1) install_xray_core ;;
2) configure_reality_simple ;;
3) configure_reality_dokodemo ;;
4) uninstall_xray ;;
5) view_config_xray ;;
6) manage_xray_service start ;;
7) manage_xray_service stop ;;
8) manage_xray_service restart ;;
9) manage_xray_service status ;;
0) break ;;
*) echo -e "${RED}无效选项,请重试。${NC}" ;;
esac
[ "$choice" != "0" ] && [ "$choice" != "9" ] && read -p "按 Enter 键返回..."
done
}
# --- 脚本入口 ---
check_root
xray_menu
echo "脚本已退出。"