2277 lines
68 KiB
Bash
2277 lines
68 KiB
Bash
#!/bin/bash
|
|
|
|
# Hysteria2 配置管理脚本
|
|
# 版本: 1.1.0
|
|
# 作者: Hysteria2 Manager
|
|
|
|
# 颜色定义
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
PURPLE='\033[0;35m'
|
|
CYAN='\033[0;36m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# 错误处理函数
|
|
error_exit() {
|
|
echo -e "${RED}错误: $1${NC}" >&2
|
|
exit "${2:-1}"
|
|
}
|
|
|
|
# 脚本目录处理 - 改进符号链接检测
|
|
get_script_dir() {
|
|
local source="${BASH_SOURCE[0]}"
|
|
local dir
|
|
|
|
# 处理符号链接
|
|
while [[ -L "$source" ]]; do
|
|
dir="$(cd -P "$(dirname "$source")" && pwd)"
|
|
source="$(readlink "$source")"
|
|
[[ $source != /* ]] && source="$dir/$source"
|
|
done
|
|
|
|
dir="$(cd -P "$(dirname "$source")" && pwd)"
|
|
|
|
# 如果脚本在 /usr/local/bin 中运行,假设安装在 /opt/s-hy2
|
|
if [[ "$dir" == "/usr/local/bin" ]]; then
|
|
dir="/opt/s-hy2"
|
|
fi
|
|
|
|
echo "$dir"
|
|
}
|
|
|
|
SCRIPT_DIR="$(get_script_dir)"
|
|
SCRIPTS_DIR="$SCRIPT_DIR/scripts"
|
|
TEMPLATES_DIR="$SCRIPT_DIR/templates"
|
|
|
|
# 配置文件路径
|
|
CONFIG_PATH="/etc/hysteria/config.yaml"
|
|
SERVER_DOMAIN_CONFIG="/etc/hysteria/server-domain.conf"
|
|
SERVICE_NAME="hysteria-server.service"
|
|
|
|
# 日志记录函数
|
|
log_info() {
|
|
echo -e "${BLUE}[INFO]${NC} $1"
|
|
}
|
|
|
|
log_warn() {
|
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
log_success() {
|
|
echo -e "${GREEN}[SUCCESS]${NC} $1"
|
|
}
|
|
|
|
# 检查是否为 root 用户
|
|
check_root() {
|
|
if [[ $EUID -ne 0 ]]; then
|
|
error_exit "此脚本需要 root 权限运行,请使用 sudo 运行此脚本"
|
|
fi
|
|
}
|
|
|
|
# 检查脚本文件完整性
|
|
check_script_integrity() {
|
|
local missing_scripts=()
|
|
|
|
# 检查必需的脚本文件
|
|
local required_scripts=(
|
|
"install.sh"
|
|
"config.sh"
|
|
"service.sh"
|
|
"domain-test.sh"
|
|
"node-info.sh"
|
|
)
|
|
|
|
for script in "${required_scripts[@]}"; do
|
|
if [[ ! -f "$SCRIPTS_DIR/$script" ]]; then
|
|
missing_scripts+=("$script")
|
|
fi
|
|
done
|
|
|
|
if [[ ${#missing_scripts[@]} -gt 0 ]]; then
|
|
log_warn "检测到缺失的脚本文件:"
|
|
for script in "${missing_scripts[@]}"; do
|
|
echo " - $script"
|
|
done
|
|
echo ""
|
|
echo "这可能影响某些功能的正常使用"
|
|
echo ""
|
|
fi
|
|
}
|
|
|
|
# 打印标题
|
|
print_header() {
|
|
clear
|
|
echo -e "${CYAN}================================================${NC}"
|
|
echo -e "${CYAN} Hysteria2 配置管理脚本 v1.1.0${NC}"
|
|
echo -e "${CYAN}================================================${NC}"
|
|
echo ""
|
|
}
|
|
|
|
# 打印菜单
|
|
print_menu() {
|
|
echo -e "${YELLOW}请选择操作:${NC}"
|
|
echo ""
|
|
echo -e "${GREEN} 1.${NC} 安装 Hysteria2"
|
|
echo -e "${GREEN} 2.${NC} 快速配置"
|
|
echo -e "${GREEN} 3.${NC} 手动配置"
|
|
echo -e "${GREEN} 4.${NC} 修改配置"
|
|
echo -e "${GREEN} 5.${NC} 域名管理"
|
|
echo -e "${GREEN} 6.${NC} 证书管理"
|
|
echo -e "${GREEN} 7.${NC} 服务管理"
|
|
echo -e "${GREEN} 8.${NC} 订阅链接"
|
|
echo -e "${GREEN} 9.${NC} 卸载服务"
|
|
echo -e "${GREEN}10.${NC} 关于脚本"
|
|
echo -e "${RED} 0.${NC} 退出"
|
|
echo ""
|
|
echo -n -e "${BLUE}请输入选项 [0-10]: ${NC}"
|
|
}
|
|
|
|
# 检查 Hysteria2 是否已安装
|
|
check_hysteria_installed() {
|
|
command -v hysteria &> /dev/null
|
|
}
|
|
|
|
# 检查服务状态
|
|
check_service_status() {
|
|
if systemctl is-active --quiet "$SERVICE_NAME"; then
|
|
echo -e "${GREEN}✅ 运行中${NC}"
|
|
return 0
|
|
elif systemctl is-enabled --quiet "$SERVICE_NAME"; then
|
|
echo -e "${YELLOW}⏸️ 已启用但未运行${NC}"
|
|
return 1
|
|
else
|
|
echo -e "${RED}❌ 未启用${NC}"
|
|
return 2
|
|
fi
|
|
}
|
|
|
|
# 显示系统信息
|
|
show_system_info() {
|
|
local server_ip
|
|
server_ip=$(get_server_ip)
|
|
local server_domain
|
|
server_domain=$(get_server_domain)
|
|
|
|
echo -e "${CYAN}系统信息:${NC}"
|
|
echo "服务器IP: ${server_ip:-未知}"
|
|
if [[ -n "$server_domain" ]]; then
|
|
echo "服务器域名: $server_domain"
|
|
fi
|
|
echo "系统: $(get_system_info)"
|
|
echo ""
|
|
}
|
|
|
|
# 获取系统信息
|
|
get_system_info() {
|
|
if [[ -f /etc/os-release ]]; then
|
|
source /etc/os-release
|
|
echo "${PRETTY_NAME:-$NAME $VERSION_ID}"
|
|
else
|
|
echo "未知系统"
|
|
fi
|
|
}
|
|
|
|
# 显示当前状态
|
|
show_status() {
|
|
show_system_info
|
|
|
|
echo -e "${CYAN}Hysteria2 状态:${NC}"
|
|
if check_hysteria_installed; then
|
|
echo -e "程序状态: ${GREEN}✅ 已安装${NC}"
|
|
echo -n "服务状态: "
|
|
check_service_status
|
|
if [[ -f "$CONFIG_PATH" ]]; then
|
|
echo -e "配置文件: ${GREEN}✅ 存在${NC}"
|
|
else
|
|
echo -e "配置文件: ${RED}❌ 不存在${NC}"
|
|
fi
|
|
else
|
|
echo -e "程序状态: ${RED}❌ 未安装${NC}"
|
|
fi
|
|
echo ""
|
|
}
|
|
|
|
# 安全地执行脚本
|
|
safe_source_script() {
|
|
local script_path="$1"
|
|
local script_name="$2"
|
|
|
|
if [[ -f "$script_path" ]]; then
|
|
log_info "加载 $script_name..."
|
|
# shellcheck source=/dev/null
|
|
source "$script_path" || {
|
|
log_error "$script_name 加载失败"
|
|
return 1
|
|
}
|
|
return 0
|
|
else
|
|
log_error "$script_name 不存在: $script_path"
|
|
echo ""
|
|
echo "可能的解决方案:"
|
|
echo "1. 重新运行安装脚本"
|
|
echo "2. 检查脚本文件是否完整"
|
|
echo ""
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# 等待用户确认
|
|
wait_for_user() {
|
|
echo ""
|
|
read -p "按回车键继续..." -r
|
|
}
|
|
|
|
# 安装 Hysteria2
|
|
install_hysteria() {
|
|
log_info "准备安装 Hysteria2..."
|
|
|
|
if safe_source_script "$SCRIPTS_DIR/install.sh" "安装脚本"; then
|
|
install_hysteria2
|
|
fi
|
|
wait_for_user
|
|
}
|
|
|
|
# 一键快速配置
|
|
quick_config() {
|
|
log_info "准备执行一键快速配置..."
|
|
|
|
if ! check_hysteria_installed; then
|
|
log_error "Hysteria2 未安装,请先安装"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
if safe_source_script "$SCRIPTS_DIR/config.sh" "配置脚本"; then
|
|
quick_setup_hysteria
|
|
fi
|
|
}
|
|
|
|
# 手动配置
|
|
manual_config() {
|
|
log_info "准备执行手动配置..."
|
|
|
|
if ! check_hysteria_installed; then
|
|
log_error "Hysteria2 未安装,请先安装"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
if safe_source_script "$SCRIPTS_DIR/config.sh" "配置脚本"; then
|
|
generate_hysteria_config
|
|
fi
|
|
}
|
|
|
|
# 管理服务
|
|
manage_service() {
|
|
log_info "准备进入服务管理..."
|
|
|
|
if ! check_hysteria_installed; then
|
|
log_error "Hysteria2 未安装,请先安装"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
if safe_source_script "$SCRIPTS_DIR/service.sh" "服务管理脚本"; then
|
|
manage_hysteria_service
|
|
fi
|
|
}
|
|
|
|
|
|
# 域名管理 - 重构版本,分离ACME域名和伪装域名
|
|
domain_management() {
|
|
while true; do
|
|
clear
|
|
echo -e "${CYAN}=== 域名管理 ===${NC}"
|
|
echo ""
|
|
|
|
# 显示当前域名配置状态
|
|
echo -e "${YELLOW}当前域名配置状态:${NC}"
|
|
|
|
# 检查ACME域名
|
|
if [[ -f "$SERVER_DOMAIN_CONFIG" ]]; then
|
|
local acme_domain
|
|
acme_domain=$(cat "$SERVER_DOMAIN_CONFIG")
|
|
echo -e "ACME域名: ${GREEN}$acme_domain${NC}"
|
|
else
|
|
echo -e "ACME域名: ${YELLOW}未配置${NC}"
|
|
fi
|
|
|
|
# 检查伪装域名
|
|
local masquerade_domain=""
|
|
if [[ -f "$CONFIG_PATH" ]]; then
|
|
masquerade_domain=$(grep -A 3 "masquerade:" "$CONFIG_PATH" 2>/dev/null | grep "url:" | awk '{print $2}' | sed 's|https\?://||' | sed 's|/.*||')
|
|
fi
|
|
|
|
if [[ -n "$masquerade_domain" ]]; then
|
|
echo -e "伪装域名: ${GREEN}$masquerade_domain${NC}"
|
|
else
|
|
echo -e "伪装域名: ${YELLOW}未配置${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${YELLOW}域名管理选项:${NC}"
|
|
echo -e "${GREEN}1.${NC} ACME域名管理"
|
|
echo -e "${GREEN}2.${NC} 伪装域名管理"
|
|
echo -e "${GREEN}3.${NC} 测试域名连通性"
|
|
echo -e "${RED}0.${NC} 返回主菜单"
|
|
echo ""
|
|
echo -n -e "${BLUE}请选择操作 [0-3]: ${NC}"
|
|
read -r choice
|
|
|
|
case $choice in
|
|
1) acme_domain_management ;;
|
|
2) masquerade_domain_management ;;
|
|
3) test_domain_connectivity ;;
|
|
0) break ;;
|
|
*)
|
|
log_error "无效选项"
|
|
sleep 1
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# ACME域名管理
|
|
acme_domain_management() {
|
|
while true; do
|
|
clear
|
|
echo -e "${CYAN}=== ACME域名管理 ===${NC}"
|
|
echo ""
|
|
echo -e "${BLUE}ACME域名用于申请SSL证书,需要域名解析到本服务器${NC}"
|
|
echo ""
|
|
|
|
# 显示当前配置
|
|
if [[ -f "$SERVER_DOMAIN_CONFIG" ]]; then
|
|
local current_domain
|
|
current_domain=$(cat "$SERVER_DOMAIN_CONFIG")
|
|
echo -e "${GREEN}当前ACME域名: $current_domain${NC}"
|
|
else
|
|
echo -e "${YELLOW}当前未配置ACME域名${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${YELLOW}ACME域名选项:${NC}"
|
|
echo -e "${GREEN}1.${NC} 设置ACME域名"
|
|
echo -e "${GREEN}2.${NC} 验证域名解析"
|
|
echo -e "${GREEN}3.${NC} 删除ACME域名配置"
|
|
echo -e "${RED}0.${NC} 返回上级菜单"
|
|
echo ""
|
|
echo -n -e "${BLUE}请选择操作 [0-3]: ${NC}"
|
|
read -r choice
|
|
|
|
case $choice in
|
|
1) set_server_domain ;;
|
|
2) verify_domain_resolution ;;
|
|
3) remove_server_domain ;;
|
|
0) break ;;
|
|
*)
|
|
log_error "无效选项"
|
|
sleep 1
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# 伪装域名管理
|
|
masquerade_domain_management() {
|
|
while true; do
|
|
clear
|
|
echo -e "${CYAN}=== 伪装域名管理 ===${NC}"
|
|
echo ""
|
|
echo -e "${BLUE}伪装域名用于TLS握手,提高连接的隐蔽性${NC}"
|
|
echo ""
|
|
|
|
# 显示当前伪装域名配置
|
|
local current_masquerade=""
|
|
if [[ -f "$CONFIG_PATH" ]]; then
|
|
current_masquerade=$(grep -A 3 "masquerade:" "$CONFIG_PATH" 2>/dev/null | grep "url:" | awk '{print $2}')
|
|
fi
|
|
|
|
if [[ -n "$current_masquerade" ]]; then
|
|
echo -e "${GREEN}当前伪装域名: $current_masquerade${NC}"
|
|
else
|
|
echo -e "${YELLOW}当前未配置伪装域名${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${YELLOW}伪装域名选项:${NC}"
|
|
echo -e "${GREEN}1.${NC} 手动设置伪装域名"
|
|
echo -e "${GREEN}2.${NC} 自动测试选择最佳伪装域名"
|
|
echo -e "${GREEN}3.${NC} 删除伪装域名配置"
|
|
echo -e "${RED}0.${NC} 返回上级菜单"
|
|
echo ""
|
|
echo -n -e "${BLUE}请选择操作 [0-3]: ${NC}"
|
|
read -r choice
|
|
|
|
case $choice in
|
|
1) set_masquerade_domain ;;
|
|
2) auto_select_masquerade_domain ;;
|
|
3) remove_masquerade_domain ;;
|
|
0) break ;;
|
|
*)
|
|
log_error "无效选项"
|
|
sleep 1
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# 测试域名连通性
|
|
test_domain_connectivity() {
|
|
echo ""
|
|
echo -e "${BLUE}测试域名连通性${NC}"
|
|
echo ""
|
|
|
|
# 测试ACME域名
|
|
if [[ -f "$SERVER_DOMAIN_CONFIG" ]]; then
|
|
local acme_domain
|
|
acme_domain=$(cat "$SERVER_DOMAIN_CONFIG")
|
|
echo -e "${YELLOW}测试ACME域名: $acme_domain${NC}"
|
|
verify_domain_resolution
|
|
fi
|
|
|
|
# 测试伪装域名
|
|
if [[ -f "$CONFIG_PATH" ]]; then
|
|
local masquerade_url
|
|
masquerade_url=$(grep -A 3 "masquerade:" "$CONFIG_PATH" 2>/dev/null | grep "url:" | awk '{print $2}')
|
|
if [[ -n "$masquerade_url" ]]; then
|
|
echo ""
|
|
echo -e "${YELLOW}测试伪装域名连通性: $masquerade_url${NC}"
|
|
test_masquerade_connectivity "$masquerade_url"
|
|
fi
|
|
fi
|
|
|
|
wait_for_user
|
|
}
|
|
|
|
# 设置伪装域名
|
|
set_masquerade_domain() {
|
|
echo ""
|
|
echo -e "${BLUE}设置伪装域名${NC}"
|
|
echo "请输入伪装域名 URL (例如: https://www.bing.com):"
|
|
echo -n -e "${YELLOW}伪装URL: ${NC}"
|
|
read -r masquerade_url
|
|
|
|
if [[ -z "$masquerade_url" ]]; then
|
|
log_error "伪装URL不能为空"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
# 验证URL格式
|
|
if [[ ! "$masquerade_url" =~ ^https?:// ]]; then
|
|
masquerade_url="https://$masquerade_url"
|
|
fi
|
|
|
|
# 备份配置文件
|
|
if [[ -f "$CONFIG_PATH" ]]; then
|
|
cp "$CONFIG_PATH" "$CONFIG_PATH.bak"
|
|
|
|
# 更新或添加伪装域名配置
|
|
if grep -q "masquerade:" "$CONFIG_PATH"; then
|
|
# 更新现有配置
|
|
sed -i "/masquerade:/,/url:/s|url:.*|url: $masquerade_url|" "$CONFIG_PATH"
|
|
else
|
|
# 添加新配置
|
|
echo "" >> "$CONFIG_PATH"
|
|
echo "masquerade:" >> "$CONFIG_PATH"
|
|
echo " type: proxy" >> "$CONFIG_PATH"
|
|
echo " url: $masquerade_url" >> "$CONFIG_PATH"
|
|
fi
|
|
|
|
log_success "伪装域名已设置: $masquerade_url"
|
|
|
|
echo ""
|
|
echo -n -e "${YELLOW}是否重启服务以应用更改? [Y/n]: ${NC}"
|
|
read -r restart
|
|
if [[ ! $restart =~ ^[Nn]$ ]]; then
|
|
systemctl restart "$SERVICE_NAME"
|
|
log_success "服务已重启"
|
|
fi
|
|
else
|
|
log_error "配置文件不存在"
|
|
fi
|
|
|
|
wait_for_user
|
|
}
|
|
|
|
# 自动选择最佳伪装域名
|
|
auto_select_masquerade_domain() {
|
|
echo ""
|
|
echo -e "${BLUE}自动测试并选择最佳伪装域名${NC}"
|
|
|
|
if safe_source_script "$SCRIPTS_DIR/domain-test.sh" "域名测试脚本"; then
|
|
test_masquerade_domains
|
|
fi
|
|
}
|
|
|
|
# 删除伪装域名配置
|
|
remove_masquerade_domain() {
|
|
echo ""
|
|
echo -e "${YELLOW}删除伪装域名配置${NC}"
|
|
|
|
if [[ ! -f "$CONFIG_PATH" ]]; then
|
|
log_warn "配置文件不存在"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
if ! grep -q "masquerade:" "$CONFIG_PATH"; then
|
|
log_warn "未配置伪装域名"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
local current_masquerade
|
|
current_masquerade=$(grep -A 3 "masquerade:" "$CONFIG_PATH" | grep "url:" | awk '{print $2}')
|
|
echo "当前伪装域名: $current_masquerade"
|
|
echo ""
|
|
echo -n -e "${RED}确定要删除伪装域名配置吗? [y/N]: ${NC}"
|
|
read -r confirm
|
|
|
|
if [[ $confirm =~ ^[Yy]$ ]]; then
|
|
cp "$CONFIG_PATH" "$CONFIG_PATH.bak"
|
|
# 删除masquerade配置块
|
|
sed -i '/masquerade:/,/url:/d' "$CONFIG_PATH"
|
|
log_success "伪装域名配置已删除"
|
|
|
|
echo ""
|
|
echo -n -e "${YELLOW}是否重启服务以应用更改? [Y/n]: ${NC}"
|
|
read -r restart
|
|
if [[ ! $restart =~ ^[Nn]$ ]]; then
|
|
systemctl restart "$SERVICE_NAME"
|
|
log_success "服务已重启"
|
|
fi
|
|
else
|
|
echo -e "${BLUE}取消删除${NC}"
|
|
fi
|
|
|
|
wait_for_user
|
|
}
|
|
|
|
# 测试伪装域名连通性
|
|
test_masquerade_connectivity() {
|
|
local url="$1"
|
|
local domain
|
|
domain=$(echo "$url" | sed 's|https\?://||' | sed 's|/.*||')
|
|
|
|
echo "正在测试 $url..."
|
|
|
|
# 测试HTTP连接
|
|
local http_code
|
|
http_code=$(curl -s -o /dev/null -w "%{http_code}" --connect-timeout 5 --max-time 10 "$url" 2>/dev/null)
|
|
|
|
if [[ "$http_code" =~ ^[23] ]]; then
|
|
echo -e "${GREEN}✅ HTTP连接测试成功 (状态码: $http_code)${NC}"
|
|
else
|
|
echo -e "${YELLOW}⚠️ HTTP连接测试异常 (状态码: $http_code)${NC}"
|
|
fi
|
|
|
|
# 测试DNS解析
|
|
if command -v dig &> /dev/null; then
|
|
local ip
|
|
ip=$(dig +short "$domain" A 2>/dev/null | head -1)
|
|
if [[ -n "$ip" && "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
|
|
echo -e "${GREEN}✅ DNS解析成功: $ip${NC}"
|
|
else
|
|
echo -e "${RED}❌ DNS解析失败${NC}"
|
|
fi
|
|
fi
|
|
}
|
|
|
|
# 证书管理
|
|
certificate_management() {
|
|
while true; do
|
|
clear
|
|
echo -e "${CYAN}=== 证书管理 ===${NC}"
|
|
echo ""
|
|
|
|
# 检查当前证书状态
|
|
show_certificate_status
|
|
|
|
echo ""
|
|
echo -e "${YELLOW}证书管理选项:${NC}"
|
|
echo -e "${GREEN}1.${NC} 生成自签名证书"
|
|
echo -e "${GREEN}2.${NC} 上传自定义证书"
|
|
echo -e "${GREEN}3.${NC} 查看证书信息"
|
|
echo -e "${GREEN}4.${NC} 删除证书文件"
|
|
echo -e "${GREEN}5.${NC} 证书文件路径管理"
|
|
echo -e "${RED}0.${NC} 返回主菜单"
|
|
echo ""
|
|
echo -n -e "${BLUE}请选择操作 [0-5]: ${NC}"
|
|
read -r choice
|
|
|
|
case $choice in
|
|
1) generate_self_signed_cert ;;
|
|
2) upload_custom_cert ;;
|
|
3) show_certificate_info ;;
|
|
4) remove_certificate_files ;;
|
|
5) manage_certificate_paths ;;
|
|
0) break ;;
|
|
*)
|
|
log_error "无效选项"
|
|
sleep 1
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# 显示证书状态
|
|
show_certificate_status() {
|
|
echo -e "${YELLOW}当前证书状态:${NC}"
|
|
|
|
# 检查配置文件中的证书配置
|
|
if [[ -f "$CONFIG_PATH" ]]; then
|
|
if grep -q "^tls:" "$CONFIG_PATH"; then
|
|
local cert_file
|
|
local key_file
|
|
cert_file=$(grep -A 5 "^tls:" "$CONFIG_PATH" | grep "cert:" | awk '{print $2}')
|
|
key_file=$(grep -A 5 "^tls:" "$CONFIG_PATH" | grep "key:" | awk '{print $2}')
|
|
|
|
echo -e "证书模式: ${GREEN}手动证书${NC}"
|
|
echo -e "证书文件: ${cert_file:-未设置}"
|
|
echo -e "密钥文件: ${key_file:-未设置}"
|
|
|
|
# 检查文件是否存在
|
|
if [[ -n "$cert_file" && -f "$cert_file" ]]; then
|
|
echo -e "证书文件状态: ${GREEN}存在${NC}"
|
|
else
|
|
echo -e "证书文件状态: ${RED}不存在${NC}"
|
|
fi
|
|
|
|
if [[ -n "$key_file" && -f "$key_file" ]]; then
|
|
echo -e "密钥文件状态: ${GREEN}存在${NC}"
|
|
else
|
|
echo -e "密钥文件状态: ${RED}不存在${NC}"
|
|
fi
|
|
elif grep -q "^acme:" "$CONFIG_PATH"; then
|
|
echo -e "证书模式: ${GREEN}ACME自动证书${NC}"
|
|
local domains
|
|
domains=$(grep -A 5 "^acme:" "$CONFIG_PATH" | grep "domains:" -A 5 | grep -E "^\s*-" | sed 's/^\s*-\s*//' | tr '\n' ' ')
|
|
echo -e "ACME域名: ${domains:-未设置}"
|
|
else
|
|
echo -e "证书模式: ${YELLOW}未配置${NC}"
|
|
fi
|
|
else
|
|
echo -e "证书模式: ${RED}配置文件不存在${NC}"
|
|
fi
|
|
}
|
|
|
|
# 生成自签名证书
|
|
generate_self_signed_cert() {
|
|
echo ""
|
|
echo -e "${BLUE}生成自签名证书${NC}"
|
|
echo ""
|
|
|
|
# 获取域名
|
|
echo -n -e "${YELLOW}请输入证书域名 (留空使用服务器IP): ${NC}"
|
|
read -r cert_domain
|
|
|
|
if [[ -z "$cert_domain" ]]; then
|
|
cert_domain=$(get_server_ip)
|
|
echo "使用服务器IP: $cert_domain"
|
|
fi
|
|
|
|
# 设置证书文件路径
|
|
local cert_dir="/etc/hysteria"
|
|
local cert_file="$cert_dir/server.crt"
|
|
local key_file="$cert_dir/server.key"
|
|
|
|
# 创建目录
|
|
mkdir -p "$cert_dir"
|
|
|
|
echo "正在生成自签名证书..."
|
|
|
|
# 生成私钥和证书
|
|
if openssl req -x509 -nodes -newkey rsa:2048 -keyout "$key_file" -out "$cert_file" -days 365 \
|
|
-subj "/C=US/ST=State/L=City/O=Organization/CN=$cert_domain" 2>/dev/null; then
|
|
|
|
# 设置权限
|
|
chmod 600 "$key_file"
|
|
chmod 644 "$cert_file"
|
|
chown hysteria:hysteria "$cert_file" "$key_file" 2>/dev/null || true
|
|
|
|
log_success "自签名证书生成成功"
|
|
echo "证书文件: $cert_file"
|
|
echo "密钥文件: $key_file"
|
|
echo "域名: $cert_domain"
|
|
|
|
# 询问是否更新配置文件
|
|
echo ""
|
|
echo -n -e "${YELLOW}是否更新配置文件使用新证书? [Y/n]: ${NC}"
|
|
read -r update_config
|
|
if [[ ! $update_config =~ ^[Nn]$ ]]; then
|
|
update_tls_config "$cert_file" "$key_file"
|
|
fi
|
|
else
|
|
log_error "自签名证书生成失败"
|
|
fi
|
|
|
|
wait_for_user
|
|
}
|
|
|
|
# 上传自定义证书
|
|
upload_custom_cert() {
|
|
echo ""
|
|
echo -e "${BLUE}上传自定义证书${NC}"
|
|
echo ""
|
|
echo "请提供证书文件路径:"
|
|
echo ""
|
|
|
|
echo -n -e "${YELLOW}证书文件路径 (.crt/.pem): ${NC}"
|
|
read -r cert_path
|
|
|
|
echo -n -e "${YELLOW}私钥文件路径 (.key): ${NC}"
|
|
read -r key_path
|
|
|
|
# 验证文件存在
|
|
if [[ ! -f "$cert_path" ]]; then
|
|
log_error "证书文件不存在: $cert_path"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
if [[ ! -f "$key_path" ]]; then
|
|
log_error "私钥文件不存在: $key_path"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
# 验证证书文件格式
|
|
if ! openssl x509 -in "$cert_path" -text -noout &>/dev/null; then
|
|
log_error "无效的证书文件格式"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
if ! openssl rsa -in "$key_path" -check &>/dev/null && ! openssl ec -in "$key_path" -check &>/dev/null; then
|
|
log_error "无效的私钥文件格式"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
# 复制到标准位置
|
|
local cert_dir="/etc/hysteria"
|
|
local new_cert_file="$cert_dir/custom.crt"
|
|
local new_key_file="$cert_dir/custom.key"
|
|
|
|
mkdir -p "$cert_dir"
|
|
|
|
if cp "$cert_path" "$new_cert_file" && cp "$key_path" "$new_key_file"; then
|
|
# 设置权限
|
|
chmod 600 "$new_key_file"
|
|
chmod 644 "$new_cert_file"
|
|
chown hysteria:hysteria "$new_cert_file" "$new_key_file" 2>/dev/null || true
|
|
|
|
log_success "证书文件上传成功"
|
|
echo "新证书文件: $new_cert_file"
|
|
echo "新私钥文件: $new_key_file"
|
|
|
|
# 显示证书信息
|
|
echo ""
|
|
echo -e "${CYAN}证书信息:${NC}"
|
|
openssl x509 -in "$new_cert_file" -text -noout | grep -E "(Subject:|Issuer:|Not Before|Not After)"
|
|
|
|
# 询问是否更新配置文件
|
|
echo ""
|
|
echo -n -e "${YELLOW}是否更新配置文件使用新证书? [Y/n]: ${NC}"
|
|
read -r update_config
|
|
if [[ ! $update_config =~ ^[Nn]$ ]]; then
|
|
update_tls_config "$new_cert_file" "$new_key_file"
|
|
fi
|
|
else
|
|
log_error "证书文件复制失败"
|
|
fi
|
|
|
|
wait_for_user
|
|
}
|
|
|
|
# 更新TLS配置
|
|
update_tls_config() {
|
|
local cert_file="$1"
|
|
local key_file="$2"
|
|
|
|
if [[ ! -f "$CONFIG_PATH" ]]; then
|
|
log_error "配置文件不存在"
|
|
return 1
|
|
fi
|
|
|
|
# 备份配置文件
|
|
cp "$CONFIG_PATH" "$CONFIG_PATH.bak"
|
|
|
|
# 删除现有的ACME配置
|
|
sed -i '/^acme:/,/^[[:alpha:]]/{ /^acme:/d; /^[[:alpha:]]/!d; }' "$CONFIG_PATH"
|
|
|
|
# 添加或更新TLS配置
|
|
if grep -q "^tls:" "$CONFIG_PATH"; then
|
|
# 更新现有TLS配置
|
|
sed -i "/^tls:/,/^[[:alpha:]]/ {
|
|
/cert:/c\\ cert: $cert_file
|
|
/key:/c\\ key: $key_file
|
|
}" "$CONFIG_PATH"
|
|
else
|
|
# 添加新的TLS配置
|
|
echo "" >> "$CONFIG_PATH"
|
|
echo "tls:" >> "$CONFIG_PATH"
|
|
echo " cert: $cert_file" >> "$CONFIG_PATH"
|
|
echo " key: $key_file" >> "$CONFIG_PATH"
|
|
fi
|
|
|
|
log_success "配置文件已更新"
|
|
|
|
# 询问是否重启服务
|
|
echo -n -e "${YELLOW}是否重启服务以应用新证书? [Y/n]: ${NC}"
|
|
read -r restart
|
|
if [[ ! $restart =~ ^[Nn]$ ]]; then
|
|
systemctl restart "$SERVICE_NAME"
|
|
log_success "服务已重启"
|
|
fi
|
|
}
|
|
|
|
# 查看证书信息
|
|
show_certificate_info() {
|
|
echo ""
|
|
echo -e "${BLUE}证书详细信息${NC}"
|
|
echo ""
|
|
|
|
if [[ ! -f "$CONFIG_PATH" ]]; then
|
|
log_error "配置文件不存在"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
# 获取证书文件路径
|
|
local cert_file
|
|
if grep -q "^tls:" "$CONFIG_PATH"; then
|
|
cert_file=$(grep -A 5 "^tls:" "$CONFIG_PATH" | grep "cert:" | awk '{print $2}')
|
|
else
|
|
log_warn "未配置手动证书,检查ACME证书..."
|
|
# 查找ACME证书
|
|
local acme_dir="/var/lib/hysteria"
|
|
if [[ -d "$acme_dir" ]]; then
|
|
cert_file=$(find "$acme_dir" -name "*.crt" | head -1)
|
|
fi
|
|
fi
|
|
|
|
if [[ -z "$cert_file" || ! -f "$cert_file" ]]; then
|
|
log_error "未找到证书文件"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
echo -e "${CYAN}证书文件: $cert_file${NC}"
|
|
echo ""
|
|
|
|
# 显示证书详细信息
|
|
echo -e "${YELLOW}证书基本信息:${NC}"
|
|
openssl x509 -in "$cert_file" -text -noout | grep -A 1 "Subject:"
|
|
openssl x509 -in "$cert_file" -text -noout | grep -A 1 "Issuer:"
|
|
|
|
echo ""
|
|
echo -e "${YELLOW}有效期:${NC}"
|
|
openssl x509 -in "$cert_file" -text -noout | grep -E "Not (Before|After)"
|
|
|
|
echo ""
|
|
echo -e "${YELLOW}主体备用名称 (SAN):${NC}"
|
|
openssl x509 -in "$cert_file" -text -noout | grep -A 5 "Subject Alternative Name:" || echo "无"
|
|
|
|
echo ""
|
|
echo -e "${YELLOW}证书指纹:${NC}"
|
|
echo -n "MD5: "
|
|
openssl x509 -in "$cert_file" -noout -fingerprint -md5 | cut -d'=' -f2
|
|
echo -n "SHA1: "
|
|
openssl x509 -in "$cert_file" -noout -fingerprint -sha1 | cut -d'=' -f2
|
|
echo -n "SHA256: "
|
|
openssl x509 -in "$cert_file" -noout -fingerprint -sha256 | cut -d'=' -f2
|
|
|
|
wait_for_user
|
|
}
|
|
|
|
# 删除证书文件
|
|
remove_certificate_files() {
|
|
echo ""
|
|
echo -e "${YELLOW}删除证书文件${NC}"
|
|
echo ""
|
|
|
|
# 列出可删除的证书文件
|
|
local cert_files=()
|
|
if [[ -f "/etc/hysteria/server.crt" ]]; then
|
|
cert_files+=("/etc/hysteria/server.crt和server.key (自签名证书)")
|
|
fi
|
|
if [[ -f "/etc/hysteria/custom.crt" ]]; then
|
|
cert_files+=("/etc/hysteria/custom.crt和custom.key (自定义证书)")
|
|
fi
|
|
|
|
if [[ ${#cert_files[@]} -eq 0 ]]; then
|
|
log_warn "未找到可删除的证书文件"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
echo -e "${YELLOW}找到以下证书文件:${NC}"
|
|
for i in "${!cert_files[@]}"; do
|
|
echo "$((i+1)). ${cert_files[i]}"
|
|
done
|
|
echo "0. 取消"
|
|
echo ""
|
|
echo -n -e "${BLUE}请选择要删除的证书 [0-${#cert_files[@]}]: ${NC}"
|
|
read -r choice
|
|
|
|
if [[ "$choice" == "0" ]]; then
|
|
echo -e "${BLUE}取消删除${NC}"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
if [[ ! "$choice" =~ ^[0-9]+$ ]] || [[ "$choice" -lt 1 ]] || [[ "$choice" -gt ${#cert_files[@]} ]]; then
|
|
log_error "无效选择"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
local selected_cert="${cert_files[$((choice-1))]}"
|
|
echo ""
|
|
echo -e "${RED}警告: 将删除 $selected_cert${NC}"
|
|
echo -n -e "${YELLOW}确定要删除吗? [y/N]: ${NC}"
|
|
read -r confirm
|
|
|
|
if [[ $confirm =~ ^[Yy]$ ]]; then
|
|
case $choice in
|
|
1)
|
|
if [[ -f "/etc/hysteria/server.crt" ]]; then
|
|
rm -f /etc/hysteria/server.crt /etc/hysteria/server.key
|
|
log_success "自签名证书已删除"
|
|
fi
|
|
;;
|
|
2)
|
|
if [[ -f "/etc/hysteria/custom.crt" ]]; then
|
|
rm -f /etc/hysteria/custom.crt /etc/hysteria/custom.key
|
|
log_success "自定义证书已删除"
|
|
fi
|
|
;;
|
|
esac
|
|
else
|
|
echo -e "${BLUE}取消删除${NC}"
|
|
fi
|
|
|
|
wait_for_user
|
|
}
|
|
|
|
# 证书文件路径管理
|
|
manage_certificate_paths() {
|
|
echo ""
|
|
echo -e "${BLUE}证书文件路径管理${NC}"
|
|
echo ""
|
|
|
|
if [[ ! -f "$CONFIG_PATH" ]]; then
|
|
log_error "配置文件不存在"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
# 显示当前配置
|
|
if grep -q "^tls:" "$CONFIG_PATH"; then
|
|
local current_cert
|
|
local current_key
|
|
current_cert=$(grep -A 5 "^tls:" "$CONFIG_PATH" | grep "cert:" | awk '{print $2}')
|
|
current_key=$(grep -A 5 "^tls:" "$CONFIG_PATH" | grep "key:" | awk '{print $2}')
|
|
|
|
echo -e "${YELLOW}当前证书配置:${NC}"
|
|
echo "证书文件: $current_cert"
|
|
echo "私钥文件: $current_key"
|
|
else
|
|
echo -e "${YELLOW}当前未配置手动证书${NC}"
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${YELLOW}路径管理选项:${NC}"
|
|
echo "1. 修改证书文件路径"
|
|
echo "2. 修改私钥文件路径"
|
|
echo "3. 同时修改证书和私钥路径"
|
|
echo "0. 返回"
|
|
echo ""
|
|
echo -n -e "${BLUE}请选择操作 [0-3]: ${NC}"
|
|
read -r choice
|
|
|
|
case $choice in
|
|
1)
|
|
echo -n -e "${YELLOW}输入新的证书文件路径: ${NC}"
|
|
read -r new_cert
|
|
if [[ -f "$new_cert" ]]; then
|
|
local current_key
|
|
current_key=$(grep -A 5 "^tls:" "$CONFIG_PATH" | grep "key:" | awk '{print $2}')
|
|
update_tls_config "$new_cert" "$current_key"
|
|
else
|
|
log_error "证书文件不存在"
|
|
fi
|
|
;;
|
|
2)
|
|
echo -n -e "${YELLOW}输入新的私钥文件路径: ${NC}"
|
|
read -r new_key
|
|
if [[ -f "$new_key" ]]; then
|
|
local current_cert
|
|
current_cert=$(grep -A 5 "^tls:" "$CONFIG_PATH" | grep "cert:" | awk '{print $2}')
|
|
update_tls_config "$current_cert" "$new_key"
|
|
else
|
|
log_error "私钥文件不存在"
|
|
fi
|
|
;;
|
|
3)
|
|
echo -n -e "${YELLOW}输入新的证书文件路径: ${NC}"
|
|
read -r new_cert
|
|
echo -n -e "${YELLOW}输入新的私钥文件路径: ${NC}"
|
|
read -r new_key
|
|
|
|
if [[ -f "$new_cert" && -f "$new_key" ]]; then
|
|
update_tls_config "$new_cert" "$new_key"
|
|
else
|
|
log_error "文件不存在,请检查路径"
|
|
fi
|
|
;;
|
|
0)
|
|
return
|
|
;;
|
|
*)
|
|
log_error "无效选择"
|
|
;;
|
|
esac
|
|
|
|
wait_for_user
|
|
}
|
|
|
|
# 验证域名格式
|
|
validate_domain() {
|
|
local domain="$1"
|
|
[[ $domain =~ ^[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?)*$ ]]
|
|
}
|
|
|
|
# 设置服务器域名
|
|
set_server_domain() {
|
|
echo ""
|
|
echo -e "${BLUE}设置服务器域名${NC}"
|
|
echo "请输入解析到此服务器的域名 (例如: example.com):"
|
|
echo -n -e "${YELLOW}域名: ${NC}"
|
|
read -r domain
|
|
|
|
if [[ -z "$domain" ]]; then
|
|
log_error "域名不能为空"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
if ! validate_domain "$domain"; then
|
|
log_error "域名格式不正确"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
# 创建目录(如果不存在)
|
|
mkdir -p "$(dirname "$SERVER_DOMAIN_CONFIG")"
|
|
|
|
# 保存域名配置
|
|
echo "$domain" > "$SERVER_DOMAIN_CONFIG"
|
|
log_success "服务器域名已设置: $domain"
|
|
|
|
# 询问是否立即验证
|
|
echo ""
|
|
echo -n -e "${YELLOW}是否立即验证域名解析? [Y/n]: ${NC}"
|
|
read -r verify
|
|
if [[ ! $verify =~ ^[Nn]$ ]]; then
|
|
verify_domain_resolution
|
|
fi
|
|
|
|
wait_for_user
|
|
}
|
|
|
|
# 验证域名解析 - 改进版本
|
|
verify_domain_resolution() {
|
|
echo ""
|
|
echo -e "${BLUE}验证域名解析${NC}"
|
|
|
|
if [[ ! -f "$SERVER_DOMAIN_CONFIG" ]]; then
|
|
log_error "未配置服务器域名"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
local domain
|
|
domain=$(cat "$SERVER_DOMAIN_CONFIG")
|
|
local server_ip
|
|
server_ip=$(get_server_ip)
|
|
|
|
echo "正在验证域名: $domain"
|
|
echo "服务器IP: $server_ip"
|
|
echo ""
|
|
|
|
# 使用多种方法解析域名
|
|
local resolved_ips=()
|
|
local dns_tools=("dig" "nslookup" "host")
|
|
|
|
for tool in "${dns_tools[@]}"; do
|
|
if command -v "$tool" &> /dev/null; then
|
|
local result
|
|
case $tool in
|
|
dig)
|
|
result=$(dig +short "$domain" A | head -5)
|
|
;;
|
|
nslookup)
|
|
result=$(nslookup "$domain" 2>/dev/null | grep "Address:" | tail -n +2 | awk '{print $2}' | head -5)
|
|
;;
|
|
host)
|
|
result=$(host "$domain" 2>/dev/null | grep "has address" | awk '{print $4}' | head -5)
|
|
;;
|
|
esac
|
|
|
|
if [[ -n "$result" ]]; then
|
|
echo "使用 $tool 解析结果:"
|
|
echo "$result" | while read -r ip; do
|
|
if [[ -n "$ip" && "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
|
|
if [[ "$ip" == "$server_ip" ]]; then
|
|
echo -e " ${GREEN}✅ $ip (匹配)${NC}"
|
|
else
|
|
echo -e " ${YELLOW}⚠️ $ip (不匹配)${NC}"
|
|
fi
|
|
resolved_ips+=("$ip")
|
|
fi
|
|
done
|
|
break
|
|
fi
|
|
fi
|
|
done
|
|
|
|
if [[ ${#resolved_ips[@]} -eq 0 ]]; then
|
|
log_error "无法解析域名,可能原因:"
|
|
echo "1. 域名DNS设置未生效"
|
|
echo "2. 网络连接问题"
|
|
echo "3. DNS服务器问题"
|
|
fi
|
|
|
|
wait_for_user
|
|
}
|
|
|
|
# 删除服务器域名配置
|
|
remove_server_domain() {
|
|
echo ""
|
|
echo -e "${YELLOW}删除服务器域名配置${NC}"
|
|
|
|
if [[ ! -f "$SERVER_DOMAIN_CONFIG" ]]; then
|
|
log_warn "未配置服务器域名"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
local domain
|
|
domain=$(cat "$SERVER_DOMAIN_CONFIG")
|
|
echo "当前配置域名: $domain"
|
|
echo ""
|
|
echo -n -e "${RED}确定要删除域名配置吗? [y/N]: ${NC}"
|
|
read -r confirm
|
|
|
|
if [[ $confirm =~ ^[Yy]$ ]]; then
|
|
rm -f "$SERVER_DOMAIN_CONFIG"
|
|
log_success "域名配置已删除"
|
|
else
|
|
echo -e "${BLUE}取消删除${NC}"
|
|
fi
|
|
|
|
wait_for_user
|
|
}
|
|
|
|
# 获取服务器IP - 改进版本
|
|
get_server_ip() {
|
|
local ip=""
|
|
local timeout=5
|
|
|
|
# IP获取服务列表(按可靠性排序)
|
|
local ip_services=(
|
|
"ipv4.icanhazip.com"
|
|
"ifconfig.me/ip"
|
|
"ip.sb"
|
|
"checkip.amazonaws.com"
|
|
"ipinfo.io/ip"
|
|
"httpbin.org/ip"
|
|
)
|
|
|
|
for service in "${ip_services[@]}"; do
|
|
ip=$(curl -s --connect-timeout "$timeout" --max-time "$timeout" "https://$service" 2>/dev/null)
|
|
# 验证IP格式
|
|
if [[ -n "$ip" && "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
|
|
# 验证IP有效性
|
|
IFS='.' read -ra ADDR <<< "$ip"
|
|
local valid=true
|
|
for octet in "${ADDR[@]}"; do
|
|
if [[ $octet -gt 255 || $octet -lt 0 ]]; then
|
|
valid=false
|
|
break
|
|
fi
|
|
done
|
|
if $valid; then
|
|
echo "$ip"
|
|
return
|
|
fi
|
|
fi
|
|
done
|
|
|
|
# 如果无法获取公网IP,尝试获取本地IP
|
|
ip=$(ip route get 8.8.8.8 2>/dev/null | grep -oP 'src \K\S+' | head -1)
|
|
echo "${ip:-127.0.0.1}"
|
|
}
|
|
|
|
# 获取配置的服务器域名
|
|
get_server_domain() {
|
|
if [[ -f "$SERVER_DOMAIN_CONFIG" ]]; then
|
|
cat "$SERVER_DOMAIN_CONFIG"
|
|
fi
|
|
}
|
|
|
|
# 配置管理
|
|
config_management() {
|
|
while true; do
|
|
clear
|
|
echo -e "${CYAN}=== 配置管理 ===${NC}"
|
|
echo ""
|
|
|
|
if [[ ! -f "$CONFIG_PATH" ]]; then
|
|
echo -e "${YELLOW}未找到配置文件${NC}"
|
|
echo ""
|
|
echo -e "${GREEN}1.${NC} 返回主菜单"
|
|
echo -n -e "${BLUE}请选择: ${NC}"
|
|
read -r choice
|
|
break
|
|
fi
|
|
|
|
echo -e "${YELLOW}配置管理选项:${NC}"
|
|
echo -e "${GREEN}1.${NC} 查看当前配置"
|
|
echo -e "${GREEN}2.${NC} 修改认证密码"
|
|
echo -e "${GREEN}3.${NC} 修改端口设置"
|
|
echo -e "${GREEN}4.${NC} 修改混淆设置"
|
|
echo -e "${GREEN}5.${NC} 端口跳跃配置"
|
|
echo -e "${GREEN}6.${NC} 打开配置文件编辑"
|
|
echo -e "${RED}0.${NC} 返回主菜单"
|
|
echo ""
|
|
echo -n -e "${BLUE}请选择操作 [0-6]: ${NC}"
|
|
read -r choice
|
|
|
|
case $choice in
|
|
1) view_current_config ;;
|
|
2) modify_auth_password ;;
|
|
3) modify_port_settings ;;
|
|
4) modify_obfs_settings ;;
|
|
5) manage_port_hopping ;;
|
|
6) edit_config_file ;;
|
|
0) break ;;
|
|
*)
|
|
log_error "无效选项"
|
|
sleep 1
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# 订阅链接
|
|
show_node_info() {
|
|
log_info "准备显示订阅链接..."
|
|
|
|
if ! check_hysteria_installed; then
|
|
log_error "Hysteria2 未安装,请先安装"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
if safe_source_script "$SCRIPTS_DIR/node-info.sh" "节点信息脚本"; then
|
|
display_node_info
|
|
fi
|
|
}
|
|
|
|
# 查看当前配置
|
|
view_current_config() {
|
|
echo ""
|
|
echo -e "${BLUE}当前配置文件内容:${NC}"
|
|
echo -e "${CYAN}================================${NC}"
|
|
cat "$CONFIG_PATH"
|
|
echo -e "${CYAN}================================${NC}"
|
|
wait_for_user
|
|
}
|
|
|
|
# 修改认证密码
|
|
modify_auth_password() {
|
|
echo ""
|
|
echo -e "${BLUE}修改认证密码${NC}"
|
|
|
|
# 获取当前密码
|
|
local current_password
|
|
current_password=$(grep -E "^\s*password:" "$CONFIG_PATH" | awk '{print $2}' | tr -d '"' || echo "未设置")
|
|
echo "当前密码: $current_password"
|
|
echo ""
|
|
|
|
echo -n -e "${YELLOW}输入新密码 (回车生成随机密码): ${NC}"
|
|
read -r new_password
|
|
|
|
if [[ -z "$new_password" ]]; then
|
|
new_password=$(openssl rand -base64 12 | tr -d "=+/")
|
|
echo "生成的随机密码: $new_password"
|
|
fi
|
|
|
|
# 备份配置文件
|
|
cp "$CONFIG_PATH" "$CONFIG_PATH.bak"
|
|
|
|
# 修改密码
|
|
sed -i "s/password:.*/password: \"$new_password\"/" "$CONFIG_PATH"
|
|
|
|
log_success "认证密码已更新"
|
|
echo ""
|
|
echo -n -e "${YELLOW}是否重启服务以应用更改? [Y/n]: ${NC}"
|
|
read -r restart
|
|
if [[ ! $restart =~ ^[Nn]$ ]]; then
|
|
systemctl restart "$SERVICE_NAME"
|
|
log_success "服务已重启"
|
|
fi
|
|
|
|
wait_for_user
|
|
}
|
|
|
|
# 修改端口设置
|
|
modify_port_settings() {
|
|
echo ""
|
|
echo -e "${BLUE}修改端口设置${NC}"
|
|
|
|
# 获取当前端口
|
|
local current_port
|
|
current_port=$(grep -E "^\s*listen:" "$CONFIG_PATH" | awk -F':' '{print $3}' | tr -d ' ' || echo "443")
|
|
echo "当前端口: $current_port"
|
|
echo ""
|
|
|
|
echo -n -e "${YELLOW}输入新端口 [1-65535]: ${NC}"
|
|
read -r new_port
|
|
|
|
if [[ ! "$new_port" =~ ^[0-9]+$ ]] || [[ "$new_port" -lt 1 ]] || [[ "$new_port" -gt 65535 ]]; then
|
|
log_error "端口必须是 1-65535 之间的数字"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
# 检查端口是否被占用
|
|
if ss -tuln | grep -q ":$new_port "; then
|
|
log_warn "端口 $new_port 似乎已被占用,请确认"
|
|
echo -n -e "${YELLOW}是否继续? [y/N]: ${NC}"
|
|
read -r continue
|
|
if [[ ! $continue =~ ^[Yy]$ ]]; then
|
|
return
|
|
fi
|
|
fi
|
|
|
|
# 备份配置文件
|
|
cp "$CONFIG_PATH" "$CONFIG_PATH.bak"
|
|
|
|
# 修改端口
|
|
sed -i "s/:$current_port/:$new_port/g" "$CONFIG_PATH"
|
|
|
|
log_success "端口已更新为: $new_port"
|
|
|
|
# 检查端口跳跃配置并询问是否更新
|
|
if safe_source_script "$SCRIPTS_DIR/config.sh" "配置脚本"; then
|
|
if check_port_hopping_status; then
|
|
echo ""
|
|
echo -e "${YELLOW}检测到已配置端口跳跃${NC}"
|
|
local hopping_info=$(get_port_hopping_info)
|
|
if [[ -n "$hopping_info" ]]; then
|
|
echo " $hopping_info"
|
|
fi
|
|
echo ""
|
|
echo -n -e "${YELLOW}是否更新端口跳跃的目标端口为 $new_port? [Y/n]: ${NC}"
|
|
read -r update_hopping
|
|
if [[ ! $update_hopping =~ ^[Nn]$ ]]; then
|
|
# 获取当前端口跳跃配置
|
|
if [[ -f "/etc/hysteria/port-hopping.conf" ]]; then
|
|
source /etc/hysteria/port-hopping.conf 2>/dev/null
|
|
if [[ -n "$START_PORT" && -n "$END_PORT" ]]; then
|
|
echo -e "${BLUE}正在更新端口跳跃配置...${NC}"
|
|
clear_port_hopping_rules
|
|
if add_port_hopping_rules "$START_PORT" "$END_PORT" "$new_port"; then
|
|
log_success "端口跳跃配置已更新为: $START_PORT-$END_PORT -> $new_port"
|
|
else
|
|
log_error "端口跳跃配置更新失败"
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
echo -n -e "${YELLOW}是否重启服务以应用更改? [Y/n]: ${NC}"
|
|
read -r restart
|
|
if [[ ! $restart =~ ^[Nn]$ ]]; then
|
|
systemctl restart "$SERVICE_NAME"
|
|
log_success "服务已重启"
|
|
fi
|
|
|
|
wait_for_user
|
|
}
|
|
|
|
# 修改混淆设置
|
|
modify_obfs_settings() {
|
|
echo ""
|
|
echo -e "${BLUE}修改混淆设置${NC}"
|
|
|
|
# 检查当前混淆配置
|
|
local current_obfs
|
|
current_obfs=$(grep -E "^\s*type: salamander" "$CONFIG_PATH" && echo "启用" || echo "禁用")
|
|
echo "当前混淆状态: $current_obfs"
|
|
|
|
if [[ "$current_obfs" == "启用" ]]; then
|
|
local current_obfs_password
|
|
current_obfs_password=$(grep -A1 "type: salamander" "$CONFIG_PATH" | grep "password:" | awk '{print $2}' | tr -d '"')
|
|
echo "当前混淆密码: $current_obfs_password"
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${YELLOW}混淆选项:${NC}"
|
|
echo "1. 启用混淆"
|
|
echo "2. 禁用混淆"
|
|
echo "3. 修改混淆密码"
|
|
echo "0. 返回"
|
|
echo ""
|
|
echo -n -e "${BLUE}请选择: ${NC}"
|
|
read -r obfs_choice
|
|
|
|
case $obfs_choice in
|
|
1|2|3)
|
|
# 备份配置文件
|
|
cp "$CONFIG_PATH" "$CONFIG_PATH.bak"
|
|
|
|
case $obfs_choice in
|
|
1)
|
|
echo -n -e "${YELLOW}输入混淆密码 (回车生成随机密码): ${NC}"
|
|
read -r obfs_password
|
|
if [[ -z "$obfs_password" ]]; then
|
|
obfs_password=$(openssl rand -base64 12 | tr -d "=+/")
|
|
echo "生成的随机密码: $obfs_password"
|
|
fi
|
|
|
|
# 添加混淆配置
|
|
if ! grep -q "obfs:" "$CONFIG_PATH"; then
|
|
sed -i "/listen:/a\\
|
|
obfs:\\
|
|
type: salamander\\
|
|
password: \"$obfs_password\"" "$CONFIG_PATH"
|
|
else
|
|
sed -i "/obfs:/,+2c\\
|
|
obfs:\\
|
|
type: salamander\\
|
|
password: \"$obfs_password\"" "$CONFIG_PATH"
|
|
fi
|
|
log_success "混淆已启用"
|
|
;;
|
|
2)
|
|
# 删除混淆配置
|
|
sed -i '/obfs:/,+2d' "$CONFIG_PATH"
|
|
log_success "混淆已禁用"
|
|
;;
|
|
3)
|
|
if [[ "$current_obfs" == "禁用" ]]; then
|
|
log_error "当前未启用混淆"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
echo -n -e "${YELLOW}输入新的混淆密码: ${NC}"
|
|
read -r new_obfs_password
|
|
|
|
if [[ -z "$new_obfs_password" ]]; then
|
|
log_error "混淆密码不能为空"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
# 修改混淆密码
|
|
sed -i "/obfs:/,+2s/password:.*/password: \"$new_obfs_password\"/" "$CONFIG_PATH"
|
|
log_success "混淆密码已更新"
|
|
;;
|
|
esac
|
|
|
|
echo ""
|
|
echo -n -e "${YELLOW}是否重启服务以应用更改? [Y/n]: ${NC}"
|
|
read -r restart
|
|
if [[ ! $restart =~ ^[Nn]$ ]]; then
|
|
systemctl restart "$SERVICE_NAME"
|
|
log_success "服务已重启"
|
|
fi
|
|
;;
|
|
0)
|
|
return
|
|
;;
|
|
*)
|
|
log_error "无效选择"
|
|
;;
|
|
esac
|
|
|
|
wait_for_user
|
|
}
|
|
|
|
# 端口跳跃配置管理
|
|
manage_port_hopping() {
|
|
while true; do
|
|
clear
|
|
echo -e "${CYAN}=== 端口跳跃配置管理 ===${NC}"
|
|
echo ""
|
|
|
|
# 显示当前端口跳跃状态
|
|
echo -e "${YELLOW}当前端口跳跃状态:${NC}"
|
|
if safe_source_script "$SCRIPTS_DIR/config.sh" "配置脚本"; then
|
|
local current_port=$(get_current_listen_port)
|
|
echo -e "监听端口: ${GREEN}$current_port${NC}"
|
|
|
|
if check_port_hopping_status; then
|
|
local hopping_info=$(get_port_hopping_info)
|
|
echo -e "端口跳跃: ${GREEN}✅ 已启用${NC}"
|
|
if [[ -n "$hopping_info" ]]; then
|
|
echo " $hopping_info"
|
|
fi
|
|
else
|
|
echo -e "端口跳跃: ${YELLOW}❌ 未启用${NC}"
|
|
fi
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${YELLOW}端口跳跃管理选项:${NC}"
|
|
echo -e "${GREEN}1.${NC} 启用端口跳跃"
|
|
echo -e "${GREEN}2.${NC} 修改端口跳跃配置"
|
|
echo -e "${GREEN}3.${NC} 禁用端口跳跃"
|
|
echo -e "${GREEN}4.${NC} 查看端口跳跃详情"
|
|
echo -e "${RED}0.${NC} 返回上级菜单"
|
|
echo ""
|
|
echo -n -e "${BLUE}请选择操作 [0-4]: ${NC}"
|
|
read -r choice
|
|
|
|
case $choice in
|
|
1) enable_port_hopping ;;
|
|
2) modify_port_hopping ;;
|
|
3) disable_port_hopping ;;
|
|
4) show_port_hopping_details ;;
|
|
0) break ;;
|
|
*)
|
|
log_error "无效选项"
|
|
sleep 1
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# 启用端口跳跃
|
|
enable_port_hopping() {
|
|
echo ""
|
|
echo -e "${BLUE}启用端口跳跃${NC}"
|
|
|
|
if safe_source_script "$SCRIPTS_DIR/config.sh" "配置脚本"; then
|
|
if check_port_hopping_status; then
|
|
log_warn "端口跳跃已启用"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${BLUE}配置端口跳跃范围:${NC}"
|
|
echo -n -e "${BLUE}起始端口 (默认 20000): ${NC}"
|
|
read -r start_port
|
|
start_port=${start_port:-20000}
|
|
|
|
echo -n -e "${BLUE}结束端口 (默认 50000): ${NC}"
|
|
read -r end_port
|
|
end_port=${end_port:-50000}
|
|
|
|
# 验证端口范围
|
|
if [[ ! "$start_port" =~ ^[0-9]+$ ]] || [[ ! "$end_port" =~ ^[0-9]+$ ]] ||
|
|
[[ "$start_port" -lt 1 ]] || [[ "$end_port" -gt 65535 ]] ||
|
|
[[ "$start_port" -ge "$end_port" ]]; then
|
|
log_error "端口范围无效"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
local current_port=$(get_current_listen_port)
|
|
if add_port_hopping_rules "$start_port" "$end_port" "$current_port"; then
|
|
log_success "端口跳跃启用成功"
|
|
echo "配置: $start_port-$end_port -> $current_port"
|
|
else
|
|
log_error "端口跳跃启用失败"
|
|
fi
|
|
fi
|
|
|
|
wait_for_user
|
|
}
|
|
|
|
# 修改端口跳跃配置
|
|
modify_port_hopping() {
|
|
echo ""
|
|
echo -e "${BLUE}修改端口跳跃配置${NC}"
|
|
|
|
if safe_source_script "$SCRIPTS_DIR/config.sh" "配置脚本"; then
|
|
if ! check_port_hopping_status; then
|
|
log_warn "端口跳跃未启用,请先启用"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
# 获取当前配置
|
|
local current_info=""
|
|
if [[ -f "/etc/hysteria/port-hopping.conf" ]]; then
|
|
source /etc/hysteria/port-hopping.conf 2>/dev/null
|
|
if [[ -n "$START_PORT" && -n "$END_PORT" && -n "$TARGET_PORT" ]]; then
|
|
current_info="当前配置: $START_PORT-$END_PORT -> $TARGET_PORT"
|
|
fi
|
|
fi
|
|
|
|
if [[ -n "$current_info" ]]; then
|
|
echo "$current_info"
|
|
fi
|
|
|
|
echo ""
|
|
echo -e "${BLUE}输入新的端口跳跃范围:${NC}"
|
|
echo -n -e "${BLUE}起始端口 (当前 ${START_PORT:-20000}): ${NC}"
|
|
read -r new_start_port
|
|
new_start_port=${new_start_port:-${START_PORT:-20000}}
|
|
|
|
echo -n -e "${BLUE}结束端口 (当前 ${END_PORT:-50000}): ${NC}"
|
|
read -r new_end_port
|
|
new_end_port=${new_end_port:-${END_PORT:-50000}}
|
|
|
|
echo -n -e "${BLUE}目标端口 (当前 ${TARGET_PORT:-$(get_current_listen_port)}): ${NC}"
|
|
read -r new_target_port
|
|
new_target_port=${new_target_port:-${TARGET_PORT:-$(get_current_listen_port)}}
|
|
|
|
# 验证端口范围
|
|
if [[ ! "$new_start_port" =~ ^[0-9]+$ ]] || [[ ! "$new_end_port" =~ ^[0-9]+$ ]] || [[ ! "$new_target_port" =~ ^[0-9]+$ ]] ||
|
|
[[ "$new_start_port" -lt 1 ]] || [[ "$new_end_port" -gt 65535 ]] || [[ "$new_target_port" -lt 1 ]] || [[ "$new_target_port" -gt 65535 ]] ||
|
|
[[ "$new_start_port" -ge "$new_end_port" ]]; then
|
|
log_error "端口范围无效"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
# 清除旧规则并添加新规则
|
|
echo -e "${BLUE}正在更新端口跳跃配置...${NC}"
|
|
clear_port_hopping_rules
|
|
|
|
if add_port_hopping_rules "$new_start_port" "$new_end_port" "$new_target_port"; then
|
|
log_success "端口跳跃配置修改成功"
|
|
echo "新配置: $new_start_port-$new_end_port -> $new_target_port"
|
|
else
|
|
log_error "端口跳跃配置修改失败"
|
|
fi
|
|
fi
|
|
|
|
wait_for_user
|
|
}
|
|
|
|
# 禁用端口跳跃
|
|
disable_port_hopping() {
|
|
echo ""
|
|
echo -e "${BLUE}禁用端口跳跃${NC}"
|
|
|
|
if safe_source_script "$SCRIPTS_DIR/config.sh" "配置脚本"; then
|
|
if ! check_port_hopping_status; then
|
|
log_warn "端口跳跃未启用"
|
|
wait_for_user
|
|
return
|
|
fi
|
|
|
|
echo -n -e "${YELLOW}确定要禁用端口跳跃吗? [y/N]: ${NC}"
|
|
read -r confirm
|
|
if [[ $confirm =~ ^[Yy]$ ]]; then
|
|
clear_port_hopping_rules
|
|
log_success "端口跳跃已禁用"
|
|
else
|
|
echo -e "${BLUE}取消禁用${NC}"
|
|
fi
|
|
fi
|
|
|
|
wait_for_user
|
|
}
|
|
|
|
# 显示端口跳跃详情
|
|
show_port_hopping_details() {
|
|
echo ""
|
|
echo -e "${BLUE}端口跳跃详细信息${NC}"
|
|
echo ""
|
|
|
|
if safe_source_script "$SCRIPTS_DIR/config.sh" "配置脚本"; then
|
|
local current_port=$(get_current_listen_port)
|
|
echo -e "${YELLOW}当前监听端口:${NC} $current_port"
|
|
|
|
if check_port_hopping_status; then
|
|
echo -e "${YELLOW}端口跳跃状态:${NC} ${GREEN}已启用${NC}"
|
|
|
|
# 显示配置文件信息
|
|
if [[ -f "/etc/hysteria/port-hopping.conf" ]]; then
|
|
echo -e "${YELLOW}配置文件:${NC} /etc/hysteria/port-hopping.conf"
|
|
echo ""
|
|
echo -e "${CYAN}配置内容:${NC}"
|
|
cat /etc/hysteria/port-hopping.conf
|
|
echo ""
|
|
fi
|
|
|
|
# 显示当前iptables规则
|
|
echo -e "${YELLOW}当前 iptables 规则:${NC}"
|
|
local rules=$(iptables -t nat -L PREROUTING -n --line-numbers 2>/dev/null | grep "REDIRECT.*--to-ports $current_port")
|
|
if [[ -n "$rules" ]]; then
|
|
echo "$rules"
|
|
else
|
|
echo "未找到相关规则"
|
|
fi
|
|
else
|
|
echo -e "${YELLOW}端口跳跃状态:${NC} ${YELLOW}未启用${NC}"
|
|
fi
|
|
fi
|
|
|
|
wait_for_user
|
|
}
|
|
|
|
# 编辑配置文件
|
|
edit_config_file() {
|
|
echo ""
|
|
echo -e "${BLUE}打开配置文件编辑${NC}"
|
|
echo "配置文件路径: $CONFIG_PATH"
|
|
echo ""
|
|
echo -e "${YELLOW}编辑器选项:${NC}"
|
|
echo "1. 使用 nano (推荐新手)"
|
|
echo "2. 使用 vim"
|
|
echo "3. 使用系统默认编辑器"
|
|
echo "0. 返回"
|
|
echo ""
|
|
echo -n -e "${BLUE}请选择编辑器: ${NC}"
|
|
read -r editor_choice
|
|
|
|
# 备份配置文件
|
|
cp "$CONFIG_PATH" "$CONFIG_PATH.bak"
|
|
log_info "已备份配置文件"
|
|
|
|
case $editor_choice in
|
|
1)
|
|
if command -v nano &> /dev/null; then
|
|
nano "$CONFIG_PATH"
|
|
else
|
|
log_error "nano 未安装,使用系统默认编辑器"
|
|
${EDITOR:-vi} "$CONFIG_PATH"
|
|
fi
|
|
;;
|
|
2)
|
|
if command -v vim &> /dev/null; then
|
|
vim "$CONFIG_PATH"
|
|
else
|
|
log_error "vim 未安装,使用系统默认编辑器"
|
|
${EDITOR:-vi} "$CONFIG_PATH"
|
|
fi
|
|
;;
|
|
3)
|
|
${EDITOR:-vi} "$CONFIG_PATH"
|
|
;;
|
|
0)
|
|
return
|
|
;;
|
|
*)
|
|
log_error "无效选择,使用系统默认编辑器"
|
|
${EDITOR:-vi} "$CONFIG_PATH"
|
|
;;
|
|
esac
|
|
|
|
echo ""
|
|
echo -n -e "${YELLOW}配置已修改,是否重启服务以应用更改? [Y/n]: ${NC}"
|
|
read -r restart
|
|
if [[ ! $restart =~ ^[Nn]$ ]]; then
|
|
if systemctl restart "$SERVICE_NAME"; then
|
|
log_success "服务已重启"
|
|
else
|
|
log_error "服务重启失败,请检查配置文件语法"
|
|
echo -n -e "${YELLOW}是否恢复备份配置? [Y/n]: ${NC}"
|
|
read -r restore
|
|
if [[ ! $restore =~ ^[Nn]$ ]]; then
|
|
cp "$CONFIG_PATH.bak" "$CONFIG_PATH"
|
|
systemctl restart "$SERVICE_NAME"
|
|
log_info "已恢复备份配置"
|
|
fi
|
|
fi
|
|
fi
|
|
|
|
wait_for_user
|
|
}
|
|
|
|
# 卸载服务
|
|
uninstall_hysteria() {
|
|
clear
|
|
echo -e "${CYAN}=== Hysteria2 卸载向导 ===${NC}"
|
|
echo ""
|
|
|
|
echo -e "${YELLOW}卸载选项:${NC}"
|
|
echo -e "${GREEN}1.${NC} 卸载hy2及其相关配置文件"
|
|
echo -e "${GREEN}2.${NC} 卸载删除所有脚本相关的程序和依赖和插件,但是保留脚本"
|
|
echo -e "${GREEN}3.${NC} 完全卸载,删除所有程序,依赖插件和配置文件,包括脚本"
|
|
echo -e "${RED}0.${NC} 取消"
|
|
echo ""
|
|
echo -e "${CYAN}说明:${NC}"
|
|
echo "选项1: 卸载 Hysteria2 程序和配置文件"
|
|
echo "选项2: 卸载所有相关依赖(包括订阅链接依赖),保留管理脚本"
|
|
echo "选项3: 完全清理所有内容,包括管理脚本本身"
|
|
echo ""
|
|
echo -n -e "${BLUE}请选择卸载方式 [0-3]: ${NC}"
|
|
read -r uninstall_choice
|
|
|
|
case $uninstall_choice in
|
|
1) uninstall_hy2_and_config ;;
|
|
2) uninstall_all_dependencies ;;
|
|
3) uninstall_everything ;;
|
|
0)
|
|
echo -e "${BLUE}取消卸载${NC}"
|
|
;;
|
|
*)
|
|
log_error "无效选择"
|
|
;;
|
|
esac
|
|
wait_for_user
|
|
}
|
|
|
|
# 选项1: 卸载hy2及其相关配置文件
|
|
uninstall_hy2_and_config() {
|
|
echo ""
|
|
echo -e "${BLUE}卸载 Hysteria2 程序和配置文件${NC}"
|
|
echo ""
|
|
|
|
echo -e "${YELLOW}此操作将删除:${NC}"
|
|
echo "• Hysteria2 程序文件"
|
|
echo "• 系统服务"
|
|
echo "• 配置文件和证书"
|
|
echo "• 用户账户"
|
|
echo "• 端口跳跃规则"
|
|
echo ""
|
|
echo -n -e "${YELLOW}确定要卸载吗? [y/N]: ${NC}"
|
|
read -r confirm
|
|
if [[ ! $confirm =~ ^[Yy]$ ]]; then
|
|
echo -e "${BLUE}取消卸载${NC}"
|
|
return
|
|
fi
|
|
|
|
log_info "开始卸载 Hysteria2..."
|
|
|
|
# 1. 清理端口跳跃规则
|
|
log_info "步骤 1/5: 清理端口跳跃规则..."
|
|
cleanup_port_hopping
|
|
|
|
# 2. 停止并禁用服务
|
|
log_info "步骤 2/5: 停止并禁用服务..."
|
|
if systemctl is-active --quiet hysteria-server.service; then
|
|
systemctl stop hysteria-server.service
|
|
log_info "已停止服务"
|
|
fi
|
|
if systemctl is-enabled --quiet hysteria-server.service 2>/dev/null; then
|
|
systemctl disable hysteria-server.service 2>/dev/null
|
|
log_info "已禁用服务"
|
|
fi
|
|
|
|
# 3. 卸载 Hysteria2 程序
|
|
log_info "步骤 3/5: 卸载 Hysteria2 程序..."
|
|
if check_hysteria_installed; then
|
|
if bash <(curl -fsSL https://get.hy2.sh/) --remove 2>/dev/null; then
|
|
log_info "Hysteria2 程序卸载成功"
|
|
else
|
|
log_warn "程序卸载失败,继续清理"
|
|
fi
|
|
else
|
|
log_info "Hysteria2 未安装,跳过程序卸载"
|
|
fi
|
|
|
|
# 4. 删除配置文件和证书
|
|
log_info "步骤 4/5: 删除配置文件和证书..."
|
|
if [[ -d "/etc/hysteria" ]]; then
|
|
rm -rf /etc/hysteria
|
|
log_info "已删除 /etc/hysteria 目录"
|
|
fi
|
|
|
|
# 5. 清理用户账户和系统残留
|
|
log_info "步骤 5/5: 清理用户账户和系统残留..."
|
|
if id "hysteria" &>/dev/null; then
|
|
userdel -r hysteria 2>/dev/null && log_info "已删除 hysteria 用户"
|
|
fi
|
|
|
|
# 清理 systemd 残留文件
|
|
rm -f /etc/systemd/system/multi-user.target.wants/hysteria-server.service 2>/dev/null
|
|
rm -f /etc/systemd/system/multi-user.target.wants/hysteria-server@*.service 2>/dev/null
|
|
systemctl daemon-reload
|
|
|
|
echo ""
|
|
log_success "Hysteria2 程序和配置文件卸载完成!"
|
|
}
|
|
|
|
# 选项2: 卸载删除所有脚本相关的程序和依赖和插件,但是保留脚本
|
|
uninstall_all_dependencies() {
|
|
echo ""
|
|
echo -e "${BLUE}卸载所有依赖和插件 (保留管理脚本)${NC}"
|
|
echo ""
|
|
|
|
echo -e "${YELLOW}此操作将删除:${NC}"
|
|
echo "• Hysteria2 程序和配置"
|
|
echo "• nginx (订阅链接依赖)"
|
|
echo "• 订阅文件 (/var/www/html/sub/)"
|
|
echo "• 端口跳跃规则"
|
|
echo "• 系统用户账户"
|
|
echo ""
|
|
echo -e "${GREEN}保留内容:${NC}"
|
|
echo "• 管理脚本 (s-hy2)"
|
|
echo ""
|
|
echo -n -e "${YELLOW}确定要卸载所有依赖吗? [y/N]: ${NC}"
|
|
read -r confirm
|
|
if [[ ! $confirm =~ ^[Yy]$ ]]; then
|
|
echo -e "${BLUE}取消卸载${NC}"
|
|
return
|
|
fi
|
|
|
|
log_info "开始卸载所有依赖..."
|
|
|
|
# 1. 先执行基本的 hy2 卸载
|
|
log_info "步骤 1/4: 卸载 Hysteria2..."
|
|
# 清理端口跳跃规则
|
|
cleanup_port_hopping
|
|
|
|
# 停止并禁用服务
|
|
if systemctl is-active --quiet hysteria-server.service; then
|
|
systemctl stop hysteria-server.service
|
|
fi
|
|
if systemctl is-enabled --quiet hysteria-server.service 2>/dev/null; then
|
|
systemctl disable hysteria-server.service 2>/dev/null
|
|
fi
|
|
|
|
# 卸载程序
|
|
if check_hysteria_installed; then
|
|
bash <(curl -fsSL https://get.hy2.sh/) --remove 2>/dev/null || log_warn "程序卸载失败"
|
|
fi
|
|
|
|
# 删除配置
|
|
rm -rf /etc/hysteria 2>/dev/null
|
|
|
|
# 删除用户
|
|
if id "hysteria" &>/dev/null; then
|
|
userdel -r hysteria 2>/dev/null
|
|
fi
|
|
|
|
# 2. 卸载 nginx (订阅链接依赖)
|
|
log_info "步骤 2/4: 卸载 nginx..."
|
|
if command -v nginx &>/dev/null; then
|
|
systemctl stop nginx 2>/dev/null
|
|
systemctl disable nginx 2>/dev/null
|
|
|
|
if command -v apt &>/dev/null; then
|
|
apt remove -y nginx nginx-common nginx-core 2>/dev/null
|
|
apt autoremove -y 2>/dev/null
|
|
elif command -v yum &>/dev/null; then
|
|
yum remove -y nginx 2>/dev/null
|
|
elif command -v dnf &>/dev/null; then
|
|
dnf remove -y nginx 2>/dev/null
|
|
fi
|
|
log_info "已卸载 nginx"
|
|
else
|
|
log_info "nginx 未安装,跳过"
|
|
fi
|
|
|
|
# 3. 删除订阅文件
|
|
log_info "步骤 3/4: 删除订阅文件..."
|
|
if [[ -d "/var/www/html/sub" ]]; then
|
|
rm -rf /var/www/html/sub
|
|
log_info "已删除订阅文件目录"
|
|
fi
|
|
|
|
# 清理可能的web根目录 (如果为空)
|
|
if [[ -d "/var/www/html" && -z "$(ls -A /var/www/html 2>/dev/null)" ]]; then
|
|
rmdir /var/www/html 2>/dev/null
|
|
fi
|
|
if [[ -d "/var/www" && -z "$(ls -A /var/www 2>/dev/null)" ]]; then
|
|
rmdir /var/www 2>/dev/null
|
|
fi
|
|
|
|
# 4. 清理系统残留
|
|
log_info "步骤 4/4: 清理系统残留..."
|
|
rm -f /etc/systemd/system/multi-user.target.wants/hysteria-server.service 2>/dev/null
|
|
rm -f /etc/systemd/system/multi-user.target.wants/hysteria-server@*.service 2>/dev/null
|
|
systemctl daemon-reload
|
|
|
|
echo ""
|
|
log_success "所有依赖和插件卸载完成!"
|
|
echo ""
|
|
echo -e "${GREEN}管理脚本已保留,可以使用 's-hy2' 重新安装${NC}"
|
|
}
|
|
|
|
# 选项3: 完全卸载,删除所有程序,依赖插件和配置文件,包括脚本
|
|
uninstall_everything() {
|
|
echo ""
|
|
echo -e "${RED}完全卸载 - 删除所有内容${NC}"
|
|
echo ""
|
|
|
|
echo -e "${RED}警告: 此操作将删除:${NC}"
|
|
echo "• Hysteria2 程序和配置"
|
|
echo "• nginx 及订阅文件"
|
|
echo "• 管理脚本 (s-hy2)"
|
|
echo "• 所有相关目录和文件"
|
|
echo "• 端口跳跃规则"
|
|
echo "• 系统用户账户"
|
|
echo ""
|
|
echo -e "${YELLOW}此操作不可逆!请输入 'YES' 确认完全卸载: ${NC}"
|
|
read -r confirm
|
|
if [[ "$confirm" != "YES" ]]; then
|
|
echo -e "${BLUE}取消卸载${NC}"
|
|
return
|
|
fi
|
|
|
|
log_info "开始完全卸载..."
|
|
|
|
# 1. 清理端口跳跃配置
|
|
log_info "步骤 1/7: 清理端口跳跃配置..."
|
|
cleanup_port_hopping
|
|
|
|
# 2. 停止并禁用服务
|
|
log_info "步骤 2/7: 停止并禁用服务..."
|
|
if systemctl is-active --quiet hysteria-server.service; then
|
|
systemctl stop hysteria-server.service
|
|
fi
|
|
if systemctl is-enabled --quiet hysteria-server.service 2>/dev/null; then
|
|
systemctl disable hysteria-server.service 2>/dev/null
|
|
fi
|
|
|
|
# 3. 卸载 Hysteria2 程序
|
|
log_info "步骤 3/7: 卸载 Hysteria2 程序..."
|
|
if check_hysteria_installed; then
|
|
bash <(curl -fsSL https://get.hy2.sh/) --remove 2>/dev/null || log_warn "程序卸载失败,继续清理"
|
|
fi
|
|
|
|
# 4. 卸载 nginx 和清理订阅文件
|
|
log_info "步骤 4/7: 卸载 nginx 和清理订阅文件..."
|
|
if command -v nginx &>/dev/null; then
|
|
systemctl stop nginx 2>/dev/null
|
|
systemctl disable nginx 2>/dev/null
|
|
|
|
if command -v apt &>/dev/null; then
|
|
apt remove -y nginx nginx-common nginx-core 2>/dev/null
|
|
apt autoremove -y 2>/dev/null
|
|
elif command -v yum &>/dev/null; then
|
|
yum remove -y nginx 2>/dev/null
|
|
elif command -v dnf &>/dev/null; then
|
|
dnf remove -y nginx 2>/dev/null
|
|
fi
|
|
fi
|
|
|
|
# 删除web目录
|
|
rm -rf /var/www 2>/dev/null
|
|
|
|
# 5. 删除配置文件和证书
|
|
log_info "步骤 5/7: 删除配置文件和证书..."
|
|
rm -rf /etc/hysteria 2>/dev/null
|
|
|
|
# 6. 清理系统残留
|
|
log_info "步骤 6/7: 清理系统残留..."
|
|
if id "hysteria" &>/dev/null; then
|
|
userdel -r hysteria 2>/dev/null
|
|
fi
|
|
|
|
# 清理 iptables 规则残留
|
|
iptables -t nat -L PREROUTING --line-numbers 2>/dev/null | grep "REDIRECT.*443" | awk '{print $1}' | tac | while read -r line; do
|
|
iptables -t nat -D PREROUTING "$line" 2>/dev/null
|
|
done
|
|
|
|
# 清理 systemd 残留
|
|
rm -f /etc/systemd/system/multi-user.target.wants/hysteria-server.service 2>/dev/null
|
|
rm -f /etc/systemd/system/multi-user.target.wants/hysteria-server@*.service 2>/dev/null
|
|
systemctl daemon-reload
|
|
|
|
# 7. 删除管理脚本
|
|
log_info "步骤 7/7: 删除管理脚本..."
|
|
rm -f /usr/local/bin/hy2-manager 2>/dev/null
|
|
rm -f /usr/local/bin/s-hy2 2>/dev/null
|
|
|
|
# 删除安装目录
|
|
if [[ -d "/opt/s-hy2" ]]; then
|
|
rm -rf /opt/s-hy2
|
|
fi
|
|
|
|
# 删除桌面快捷方式
|
|
if [[ -n "$SUDO_USER" ]]; then
|
|
rm -f "/home/$SUDO_USER/Desktop/S-Hy2-Manager.desktop" 2>/dev/null
|
|
fi
|
|
|
|
echo ""
|
|
log_success "完全卸载完成!"
|
|
echo -e "${BLUE}系统已完全清理,感谢使用 S-Hy2 管理脚本${NC}"
|
|
echo ""
|
|
echo -e "${YELLOW}重新安装:${NC}"
|
|
echo "curl -fsSL https://raw.githubusercontent.com/sindricn/s-hy2/main/quick-install.sh | sudo bash"
|
|
echo ""
|
|
|
|
# 由于脚本本身已被删除,这里直接退出
|
|
exit 0
|
|
}
|
|
|
|
# 清理端口跳跃配置
|
|
cleanup_port_hopping() {
|
|
if [[ -f "/etc/hysteria/port-hopping.conf" ]]; then
|
|
# shellcheck source=/dev/null
|
|
source "/etc/hysteria/port-hopping.conf" 2>/dev/null
|
|
if [[ -n "$INTERFACE" && -n "$START_PORT" && -n "$END_PORT" && -n "$TARGET_PORT" ]]; then
|
|
iptables -t nat -D PREROUTING -i "$INTERFACE" -p udp --dport "$START_PORT:$END_PORT" -j REDIRECT --to-ports "$TARGET_PORT" 2>/dev/null
|
|
log_info "已清理端口跳跃规则"
|
|
fi
|
|
fi
|
|
|
|
# 清理其他可能的端口跳跃规则
|
|
local rules_cleared=0
|
|
# 清理所有REDIRECT规则(不仅仅限于443端口)
|
|
while IFS= read -r line_num; do
|
|
if [[ -n "$line_num" ]]; then
|
|
if iptables -t nat -D PREROUTING "$line_num" 2>/dev/null; then
|
|
((rules_cleared++))
|
|
fi
|
|
fi
|
|
done < <(iptables -t nat -L PREROUTING --line-numbers 2>/dev/null | grep "REDIRECT.*--to-ports" | awk '{print $1}' | tac)
|
|
|
|
if [[ $rules_cleared -gt 0 ]]; then
|
|
log_info "清理了 $rules_cleared 条端口跳跃规则"
|
|
fi
|
|
|
|
# 删除配置文件
|
|
rm -f "/etc/hysteria/port-hopping.conf" 2>/dev/null
|
|
}
|
|
|
|
# 关于脚本 - 增强版本
|
|
about_script() {
|
|
clear
|
|
echo -e "${CYAN}=== 关于 Hysteria2 配置管理脚本 ===${NC}"
|
|
echo ""
|
|
echo -e "${YELLOW}基本信息:${NC}"
|
|
echo "脚本名称: S-Hy2 Manager"
|
|
echo "版本: 1.1.0"
|
|
echo "功能: 简化 Hysteria2 的安装、配置和管理"
|
|
echo ""
|
|
echo -e "${YELLOW}主要功能:${NC}"
|
|
echo "✓ 一键安装/卸载 Hysteria2"
|
|
echo "✓ 智能配置生成 (ACME/自签名证书)"
|
|
echo "✓ 配置管理 (密码、端口、混淆等)"
|
|
echo "✓ 域名管理 (ACME域名和伪装域名)"
|
|
echo "✓ 证书管理 (生成、上传、查看)"
|
|
echo "✓ 端口跳跃配置"
|
|
echo "✓ 服务管理和监控"
|
|
echo "✓ 订阅链接和节点信息生成"
|
|
echo ""
|
|
echo -e "${YELLOW}系统兼容性:${NC}"
|
|
echo "• Ubuntu 18.04+ / Debian 9+"
|
|
echo "• CentOS 7+ / RHEL 7+ / Fedora"
|
|
echo "• 支持 systemd 的 Linux 发行版"
|
|
echo ""
|
|
echo -e "${YELLOW}脚本信息:${NC}"
|
|
echo "安装位置: $SCRIPT_DIR"
|
|
echo "配置目录: /etc/hysteria/"
|
|
echo "日志查看: journalctl -u hysteria-server"
|
|
echo ""
|
|
echo -e "${YELLOW}获取支持:${NC}"
|
|
echo "• GitHub: https://github.com/sindricn/s-hy2"
|
|
echo "• Issues: 在 GitHub 仓库提交问题"
|
|
echo ""
|
|
wait_for_user
|
|
}
|
|
|
|
# 输入验证函数
|
|
validate_input() {
|
|
local input="$1"
|
|
local min="$2"
|
|
local max="$3"
|
|
|
|
if [[ "$input" =~ ^[0-9]+$ ]] && [[ "$input" -ge "$min" ]] && [[ "$input" -le "$max" ]]; then
|
|
return 0
|
|
else
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
# 主循环
|
|
main() {
|
|
# 检查基本要求
|
|
check_root
|
|
check_script_integrity
|
|
|
|
# 设置错误处理
|
|
trap 'echo -e "\n${RED}脚本被中断${NC}"; exit 130' INT
|
|
trap 'echo -e "\n${RED}脚本执行错误${NC}"; exit 1' ERR
|
|
|
|
while true; do
|
|
print_header
|
|
show_status
|
|
print_menu
|
|
|
|
read -r choice
|
|
|
|
# 输入验证
|
|
if ! validate_input "$choice" 0 10; then
|
|
log_error "请输入 0-10 之间的数字"
|
|
sleep 2
|
|
continue
|
|
fi
|
|
|
|
case $choice in
|
|
1) install_hysteria ;;
|
|
2) quick_config ;;
|
|
3) manual_config ;;
|
|
4) config_management ;;
|
|
5) domain_management ;;
|
|
6) certificate_management ;;
|
|
7) manage_service ;;
|
|
8) show_node_info ;;
|
|
9) uninstall_hysteria ;;
|
|
10) about_script ;;
|
|
0)
|
|
echo -e "${GREEN}感谢使用 Hysteria2 配置管理脚本!${NC}"
|
|
exit 0
|
|
;;
|
|
esac
|
|
done
|
|
}
|
|
|
|
# 检查依赖
|
|
check_dependencies() {
|
|
local missing_deps=()
|
|
local required_cmds=("curl" "systemctl" "iptables")
|
|
|
|
for cmd in "${required_cmds[@]}"; do
|
|
if ! command -v "$cmd" &> /dev/null; then
|
|
missing_deps+=("$cmd")
|
|
fi
|
|
done
|
|
|
|
if [[ ${#missing_deps[@]} -gt 0 ]]; then
|
|
log_warn "缺少必要的依赖:"
|
|
printf ' • %s\n' "${missing_deps[@]}"
|
|
echo ""
|
|
echo "请安装缺少的依赖后重新运行脚本"
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
# 脚本初始化
|
|
init_script() {
|
|
# 设置严格模式(但允许某些命令失败)
|
|
set -o pipefail
|
|
|
|
# 检查依赖
|
|
check_dependencies
|
|
|
|
# 检查脚本目录权限
|
|
if [[ ! -r "$SCRIPT_DIR" ]]; then
|
|
error_exit "无法访问脚本目录: $SCRIPT_DIR"
|
|
fi
|
|
}
|
|
|
|
# 运行主程序
|
|
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
|
init_script
|
|
main "$@"
|
|
fi
|