Files
script/ss2022-manager.sh
2025-11-06 20:40:12 +08:00

1400 lines
41 KiB
Bash
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/bin/bash
# Shadowsocks 管理脚本 - 集成安装、卸载和管理功能
# 专门支持 shadowsocks-libev 协议
# 使用方法: chmod +x shadowsocks-manager.sh && ./shadowsocks-manager.sh
# 使用方法: chmod +x ss2022-manager.sh && ./ss2022-manager.sh
# 全局变量
SS_PATH="/usr/local/bin"
CONFIG_PATH="/etc/shadowsocks-libev"
SS_PORT="" # Shadowsocks 端口
LOG_PATH="/var/log/shadowsocks"
LOG_FILE="/var/log/shadowsocks-manager.log"
SCRIPT_VERSION="1.0.0"
SS_PASSWORD=""
SS_METHOD="aes-128-gcm" # 默认使用 AEAD 加密方法
CONFIG_BACKUP=""
SERVER_IP=""
# 颜色定义
RED='\033[0;31m'
GREEN='\033[0;32m'
YELLOW='\033[0;33m'
BLUE='\033[0;34m'
CYAN='\033[0;36m'
NC='\033[0m' # No Color
# 进度条函数
show_progress() {
local pid=$1
local delay=0.1
local spinstr='|/-\'
local temp
local count=0
local start_time=$(date +%s)
echo -n " "
while ps -p $pid > /dev/null; do
temp=${spinstr#?}
printf "\r[%c] %s 已进行 %ds" "$spinstr" "$2" "$(($(date +%s) - start_time))"
spinstr=$temp${spinstr%"$temp"}
sleep $delay
count=$((count + 1))
done
printf "\r\033[K"
}
# 进度条显示函数 - 适用于apt操作
apt_progress() {
local cmd="$1"
local msg="$2"
local logfile="$LOG_FILE.tmp"
echo -e "${CYAN}开始 $msg...${NC}"
touch "$logfile"
($cmd 2>&1 | tee -a "$LOG_FILE" > "$logfile") &
local pid=$!
# 显示进度条
local start_time=$(date +%s)
local dots=""
local status=""
local progress=0
local last_line=""
local delay=0.2
while ps -p $pid > /dev/null; do
# 读取最后一行日志
if [[ -f "$logfile" ]]; then
last_line=$(tail -n 1 "$logfile")
# 尝试从输出中提取进度信息
if [[ $last_line == *%* ]]; then
status="${last_line%%(*}"
progress="${last_line#*(}"
progress="${progress%%)*}"
printf "\r\033[K${CYAN}[$msg]${NC} %s %s " "$status" "$progress"
else
dots="${dots}."
if [[ ${#dots} -gt 5 ]]; then dots="."; fi
elapsed=$(($(date +%s) - start_time))
printf "\r\033[K${CYAN}[$msg]${NC} 进行中%s (%ds)" "$dots" "$elapsed"
fi
fi
sleep $delay
done
if wait $pid; then
printf "\r\033[K${GREEN}[$msg]${NC} 完成! 用时: %ds\n" "$(($(date +%s) - start_time))"
rm -f "$logfile"
return 0
else
printf "\r\033[K${RED}[$msg]${NC} 失败! 查看日志: $LOG_FILE\n"
rm -f "$logfile"
return 1
fi
}
# 日志函数
log_info() {
echo -e "${GREEN}[INFO]${NC} $1" | tee -a "$LOG_FILE"
}
log_warn() {
echo -e "${YELLOW}[WARN]${NC} $1" | tee -a "$LOG_FILE"
}
log_error() {
echo -e "${RED}[ERROR]${NC} $1" >&2 | tee -a "$LOG_FILE"
}
log_debug() {
echo -e "${BLUE}[DEBUG]${NC} $1" | tee -a "$LOG_FILE"
}
# 检查是否有 root 权限
check_root() {
if [[ $EUID -ne 0 ]]; then
log_error "此脚本需要 root 权限运行"
exit 1
fi
}
# 检查系统环境
check_system() {
# 显示系统信息
echo "系统信息:"
echo "------------------------"
if [ -f /etc/os-release ]; then
cat /etc/os-release | grep "PRETTY_NAME" | cut -d= -f2- | tr -d '"'
fi
echo "内核版本: $(uname -r)"
echo "架构: $(uname -m)"
echo "------------------------"
# 检查是否为 Debian/Ubuntu 系统
if [[ ! -f /etc/debian_version && ! -f /etc/lsb-release ]]; then
log_warn "未检测到 Debian/Ubuntu 系统,脚本可能无法正常工作"
read -rp "是否继续? [y/N] " response
if [[ ! "$response" =~ ^[yY]$ ]]; then
exit 1
fi
fi
# 检查网络连接
echo -n "检查网络连接... "
if ping -c 1 -W 2 github.com &>/dev/null; then
echo -e "${GREEN}连接正常${NC}"
else
echo -e "${YELLOW}无法连接到 GitHub${NC}"
log_warn "无法连接到 GitHub请检查网络连接"
read -rp "是否继续? [y/N] " response
if [[ ! "$response" =~ ^[yY]$ ]]; then
exit 1
fi
fi
}
# 备份函数
backup_config() {
if [[ -d "$CONFIG_PATH" ]]; then
CONFIG_BACKUP="${CONFIG_PATH}_backup_$(date +%Y%m%d%H%M%S)"
log_info "备份现有配置到 $CONFIG_BACKUP"
cp -r "$CONFIG_PATH" "$CONFIG_BACKUP" || log_warn "配置备份失败"
# 备份 Shadowsocks 信息文件
if [[ -f "/root/shadowsocks_info.txt" ]]; then
cp "/root/shadowsocks_info.txt" "${CONFIG_BACKUP}/shadowsocks_info.txt.bak" || log_warn "Shadowsocks 信息文件备份失败"
fi
else
log_warn "找不到配置目录,跳过备份"
fi
}
# 显示帮助信息
show_help() {
echo "Shadowsocks 管理脚本 v${SCRIPT_VERSION}"
echo "用法: $0 [选项]"
echo ""
echo "选项:"
echo " -h, --help 显示此帮助信息"
echo " -i, --install 直接运行安装"
echo " -u, --uninstall 直接运行卸载"
echo " -s, --status 查看 Shadowsocks 状态"
echo " -up, --update 更新 Shadowsocks"
echo ""
echo "无参数运行脚本将显示交互式菜单"
}
# 菜单函数
show_menu() {
clear
echo "=================================================="
echo -e "${CYAN}Shadowsocks 管理脚本 v${SCRIPT_VERSION}${NC}"
echo -e "${CYAN}(shadowsocks-libev)${NC}"
echo "=================================================="
echo -e "1) ${GREEN}安装 Shadowsocks${NC}"
echo -e "2) ${RED}卸载 Shadowsocks${NC}"
echo -e "3) ${YELLOW}更新 Shadowsocks${NC}"
echo -e "4) ${BLUE}查看 Shadowsocks 状态${NC}"
echo -e "5) ${CYAN}查看 Shadowsocks 配置信息${NC}"
echo -e "6) ${GREEN}重启 Shadowsocks 服务${NC}"
echo -e "7) ${YELLOW}修改服务器配置${NC}"
echo -e "0) ${RED}退出${NC}"
echo "=================================================="
echo ""
read -rp "请输入选项 [0-7]: " choice
case $choice in
1) install_shadowsocks ;;
2) uninstall_shadowsocks ;;
3) update_shadowsocks ;;
4) check_status ;;
5) show_config ;;
6) restart_service ;;
7) modify_config ;;
0) exit 0 ;;
*) log_error "无效选项" && sleep 2 && show_menu ;;
esac
}
# 安装依赖
install_dependencies() {
log_info "安装必要依赖"
# 更新软件包列表
apt_progress "apt-get update" "更新软件包列表" || {
log_error "更新软件包列表失败"
return 1
}
# 安装必要工具
local deps=(curl wget jq)
for dep in "${deps[@]}"; do
if ! command -v "$dep" &>/dev/null; then
apt_progress "apt-get install -y $dep" "安装 $dep" || {
log_error "安装 $dep 失败"
return 1
}
else
log_info "$dep 已安装,跳过"
fi
done
# 检查安装结果
local all_installed=true
for dep in "${deps[@]}"; do
if ! command -v "$dep" &>/dev/null; then
log_error "$dep 安装失败"
all_installed=false
fi
done
if [ "$all_installed" = true ]; then
log_info "所有依赖安装完成"
return 0
else
log_error "部分依赖安装失败"
return 1
fi
}
# 安装 shadowsocks-libev
install_shadowsocks_libev() {
log_info "开始安装 shadowsocks-libev"
# 检查是否已安装
if command -v ss-server &>/dev/null; then
log_info "检测到已安装的 shadowsocks-libev将进行更新"
# 停止服务
if systemctl is-active shadowsocks-libev &>/dev/null; then
echo -n "停止 Shadowsocks 服务... "
if systemctl stop shadowsocks-libev &>/dev/null; then
echo -e "${GREEN}成功${NC}"
else
echo -e "${RED}失败${NC}"
log_warn "无法停止 Shadowsocks 服务"
fi
fi
fi
# 添加 shadowsocks-libev 官方仓库(如果是 Ubuntu
if command -v lsb_release &>/dev/null && [[ $(lsb_release -si) == "Ubuntu" ]]; then
local ubuntu_version=$(lsb_release -sr)
if [[ $(echo "$ubuntu_version >= 16.04" | bc) -eq 1 ]]; then
log_info "添加 shadowsocks-libev 官方 PPA"
apt_progress "add-apt-repository -y ppa:max-c-lv/shadowsocks-libev" "添加 PPA 仓库"
apt_progress "apt-get update" "更新软件包列表"
fi
fi
# 安装 shadowsocks-libev
apt_progress "apt-get install -y shadowsocks-libev" "安装 shadowsocks-libev" || {
log_error "安装 shadowsocks-libev 失败"
return 1
}
# 验证安装
if command -v ss-server &>/dev/null; then
log_info "shadowsocks-libev 安装成功"
return 0
else
log_error "shadowsocks-libev 安装失败"
return 1
fi
}
# 生成随机配置值
generate_random_values() {
log_info "生成随机配置值"
# 询问用户是否指定端口
if [[ -z "$SS_PORT" ]]; then
read -rp "是否指定端口? [y/N] " specify_port
if [[ "$specify_port" =~ ^[yY]$ ]]; then
# 用户选择指定端口
while true; do
read -rp "请输入端口号 (1-65535): " SS_PORT
# 验证端口是否为有效数字
if ! [[ "$SS_PORT" =~ ^[0-9]+$ ]] || [ "$SS_PORT" -lt 1 ] || [ "$SS_PORT" -gt 65535 ]; then
log_error "无效的端口号请输入1-65535之间的数字"
continue
fi
# 检查端口是否被占用
if ss -tuln | grep -q ":$SS_PORT "; then
log_warn "端口 $SS_PORT 已被占用,请选择其他端口"
continue
fi
log_info "将使用指定端口: $SS_PORT"
break
done
else
# 用户选择随机端口
local attempts=0
while [[ "$attempts" -lt 10 ]]; do
SS_PORT=$(shuf -i 10000-60000 -n 1)
# 检查端口是否被占用
if ! ss -tuln | grep -q ":$SS_PORT "; then
log_info "生成随机端口: $SS_PORT"
break
fi
attempts=$((attempts + 1))
done
if [[ "$attempts" -eq 10 ]]; then
log_warn "无法找到未占用的端口,使用随机端口: $SS_PORT"
fi
fi
fi
# 生成密码
if [[ -z "$SS_PASSWORD" ]]; then
read -rp "是否指定密码? [y/N] " specify_password
if [[ "$specify_password" =~ ^[yY]$ ]]; then
while true; do
read -rp "请输入密码 (至少8位): " SS_PASSWORD
if [[ ${#SS_PASSWORD} -lt 8 ]]; then
log_error "密码至少需要8位"
continue
fi
log_info "将使用指定密码"
break
done
else
# 生成随机密码
SS_PASSWORD=$(openssl rand -base64 16)
log_info "生成随机密码: $SS_PASSWORD"
fi
fi
# 选择加密方法
echo "选择加密方法:"
echo "1) chacha20-ietf-poly1305"
echo "2) aes-256-gcm"
echo "3) aes-192-gcm"
echo "4) aes-128-gcm (默认)"
read -rp "请选择加密方法 [1-4, 默认4]: " method_choice
case $method_choice in
1) SS_METHOD="chacha20-ietf-poly1305" ;;
2) SS_METHOD="aes-256-gcm" ;;
3) SS_METHOD="aes-192-gcm" ;;
*) SS_METHOD="aes-128-gcm" ;;
esac
log_info "选择的加密方法: $SS_METHOD"
# 获取服务器IP
get_server_ip
}
# 获取服务器IP
get_server_ip() {
log_info "获取服务器IP地址"
if [[ -n "$SERVER_IP" ]]; then
log_info "使用已设置的IP: $SERVER_IP"
return 0
fi
# 尝试首选方法获取公网IP
SERVER_IP=$(curl -s -m 5 https://api.ipify.org 2>/dev/null)
# 验证获取到的IP是否为有效IPv4地址
if [[ -n "$SERVER_IP" && "$SERVER_IP" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
log_info "成功获取公网IP: $SERVER_IP"
return 0
fi
# 如果第一个方法失败,尝试备用方法
local backup_services=("https://ifconfig.me" "https://ip.sb" "https://ipinfo.io/ip")
for service in "${backup_services[@]}"; do
SERVER_IP=$(curl -s -m 3 "$service" 2>/dev/null)
if [[ -n "$SERVER_IP" && "$SERVER_IP" =~ ^([0-9]{1,3}\.){3}[0-9]{1,3}$ ]]; then
log_info "成功从 $service 获取公网IP: $SERVER_IP"
return 0
fi
done
# 如果所有公网IP获取方式都失败则使用本地IP
if command -v hostname &>/dev/null; then
SERVER_IP=$(hostname -I 2>/dev/null | awk '{print $1}')
fi
# 如果hostname命令失败尝试使用ip命令
if [[ -z "$SERVER_IP" && -x "$(command -v ip)" ]]; then
SERVER_IP=$(ip -4 addr show scope global | grep -oP '(?<=inet\s)\d+(\.\d+){3}' | head -n 1)
fi
if [[ -n "$SERVER_IP" ]]; then
log_warn "无法获取公网IP使用本地IP: $SERVER_IP"
return 0
else
log_error "无法获取任何有效IP地址将使用127.0.0.1作为占位符"
SERVER_IP="127.0.0.1"
return 1
fi
}
# 配置 Shadowsocks
configure_shadowsocks() {
log_info "配置 Shadowsocks"
# 生成随机值
generate_random_values
# 创建配置目录
mkdir -p "$CONFIG_PATH"
mkdir -p "$LOG_PATH"
log_info "创建 Shadowsocks 配置文件"
# 创建主配置文件
cat > "$CONFIG_PATH/config.json" << EOF
{
"server": "0.0.0.0",
"server_port": $SS_PORT,
"password": "$SS_PASSWORD",
"method": "$SS_METHOD",
"timeout": 300,
"fast_open": true,
"workers": 1,
"prefer_ipv6": false,
"no_delay": true,
"reuse_port": true
}
EOF
# 创建客户端配置示例
cat > "$CONFIG_PATH/client_config.json" << EOF
{
"server": "$SERVER_IP",
"server_port": $SS_PORT,
"local_address": "127.0.0.1",
"local_port": 1080,
"password": "$SS_PASSWORD",
"method": "$SS_METHOD",
"timeout": 300,
"fast_open": true
}
EOF
if [[ -f "$CONFIG_PATH/config.json" ]]; then
log_info "配置文件创建成功"
else
log_error "配置文件创建失败"
return 1
fi
return 0
}
# 配置防火墙
configure_firewall() {
log_info "配置防火墙"
# 检测和关闭旧端口
local old_ports=()
# 从备份中查找旧端口
if [[ -n "$CONFIG_BACKUP" && -f "$CONFIG_BACKUP/config.json" ]]; then
log_info "检测旧配置中的端口"
if command -v jq &>/dev/null; then
local detected_port=$(jq '.server_port' "$CONFIG_BACKUP/config.json" 2>/dev/null)
if [[ "$detected_port" != "null" && -n "$detected_port" ]]; then
old_ports+=("$detected_port")
log_info "检测到旧端口: $detected_port"
fi
else
local detected_port=$(grep -o '"server_port": [0-9]*' "$CONFIG_BACKUP/config.json" | awk '{print $2}')
if [[ -n "$detected_port" ]]; then
old_ports+=("$detected_port")
log_info "检测到旧端口: $detected_port"
fi
fi
fi
# 检查是否安装了 ufw
if command -v ufw &>/dev/null; then
# 检查ufw是否启用
local ufw_status=$(ufw status | grep -o "Status: active" 2>/dev/null)
if [[ -z "$ufw_status" ]]; then
log_warn "UFW 防火墙未启用,可能需要手动配置防火墙规则"
log_info "您可以运行 'sudo ufw enable' 启用 UFW 防火墙"
return 0
fi
# 关闭旧端口
if [[ ${#old_ports[@]} -gt 0 ]]; then
log_info "开始关闭旧端口"
for port in "${old_ports[@]}"; do
if [[ "$port" -eq "$SS_PORT" ]]; then
continue # 跳过当前端口
fi
echo -n "关闭 UFW 防火墙端口 $port... "
if ufw delete allow "$port" &>/dev/null; then
echo -e "${GREEN}完成${NC}"
else
echo -e "${RED}失败${NC}"
fi
done
fi
# 开放新端口
echo -n "配置 UFW 防火墙开放端口 $SS_PORT... "
if ufw allow "$SS_PORT" &>/dev/null; then
echo -e "${GREEN}完成${NC}"
log_info "已在 UFW 防火墙开放端口: $SS_PORT"
else
echo -e "${RED}失败${NC}"
log_warn "无法开放端口 $SS_PORT"
fi
else
log_warn "未检测到 UFW 防火墙,跳过防火墙配置"
fi
return 0
}
# 生成客户端信息
generate_client_info() {
log_info "生成客户端信息"
# 确保IP地址已获取
if [[ -z "$SERVER_IP" ]]; then
get_server_ip
fi
# 生成 Shadowsocks URI
local userinfo="${SS_METHOD}:${SS_PASSWORD}"
local userinfo_encoded=$(echo -n "$userinfo" | base64 -w 0)
local share_uri="ss://${userinfo_encoded}@${SERVER_IP}:${SS_PORT}#Shadowsocks-Server"
# 保存客户端信息
cat > /root/shadowsocks_info.txt << EOF
========================= Shadowsocks 配置信息 =========================
服务器地址: ${SERVER_IP}
端口: ${SS_PORT}
密码: ${SS_PASSWORD}
加密方法: ${SS_METHOD}
==================================================================
分享链接 (SIP002 格式):
${share_uri}
二维码链接:
https://api.qrserver.com/v1/create-qr-code/?size=300x300&data=${share_uri}
==================================================================
客户端配置文件: ${CONFIG_PATH}/client_config.json
支持的客户端:
- Android: shadowsocks-android
- Windows: shadowsocks-windows
- macOS: ShadowsocksX-NG
- iOS: Shadowrocket, Quantumult
- Linux: shadowsocks-libev
==================================================================
服务控制:
启动: systemctl start shadowsocks-libev
停止: systemctl stop shadowsocks-libev
重启: systemctl restart shadowsocks-libev
状态: systemctl status shadowsocks-libev
配置文件: ${CONFIG_PATH}/config.json
日志文件: /var/log/shadowsocks/shadowsocks.log
==================================================================
EOF
log_info "客户端信息已保存到 /root/shadowsocks_info.txt"
# 打印信息
cat /root/shadowsocks_info.txt
return 0
}
# 创建系统服务
create_service() {
log_info "创建 Shadowsocks 系统服务"
# 检查并清理现有服务
if [[ -f /etc/systemd/system/shadowsocks-libev.service ]]; then
log_info "检测到现有服务文件,清理现有服务"
systemctl stop shadowsocks-libev &>/dev/null
systemctl disable shadowsocks-libev &>/dev/null
# 确保进程完全停止
pkill -f ss-server &>/dev/null
sleep 2
# 删除旧服务文件
rm -f /etc/systemd/system/shadowsocks-libev.service
systemctl daemon-reload
log_info "现有服务已清理"
fi
# 创建服务文件
cat > /etc/systemd/system/shadowsocks-libev.service << EOF
[Unit]
Description=Shadowsocks-libev Default Server Service
Documentation=man:shadowsocks-libev(8)
After=network-online.target
[Service]
Type=simple
User=root
LimitNOFILE=32768
ExecStart=/usr/bin/ss-server -c $CONFIG_PATH/config.json -u -v
ExecReload=/bin/kill -s HUP \$MAINPID
ExecStop=/bin/kill -s TERM \$MAINPID
TimeoutStopSec=10
KillMode=mixed
Restart=on-failure
RestartSec=5s
[Install]
WantedBy=multi-user.target
EOF
# 重新加载 systemd 配置并启用服务
systemctl daemon-reload
echo -n "启用 Shadowsocks 服务... "
if systemctl enable shadowsocks-libev &>/dev/null; then
echo -e "${GREEN}成功${NC}"
else
echo -e "${RED}失败${NC}"
log_error "无法启用 Shadowsocks 服务"
return 1
fi
echo -n "启动 Shadowsocks 服务... "
if systemctl start shadowsocks-libev; then
echo -e "${GREEN}成功${NC}"
else
echo -e "${RED}失败${NC}"
log_error "无法启动 Shadowsocks 服务"
return 1
fi
# 等待服务启动并验证配置
echo -n "验证服务配置... "
sleep 3
# 检查服务是否真正运行
if systemctl is-active shadowsocks-libev &>/dev/null; then
echo -e "${GREEN}服务运行正常${NC}"
# 验证端口监听
local config_port=""
if command -v jq &>/dev/null; then
config_port=$(jq -r '.server_port' "$CONFIG_PATH/config.json" 2>/dev/null)
else
config_port=$(grep -o '"server_port": [0-9]*' "$CONFIG_PATH/config.json" | awk '{print $2}' 2>/dev/null)
fi
if [[ -n "$config_port" ]]; then
local retry_count=0
while [[ $retry_count -lt 10 ]]; do
if command -v netstat &>/dev/null; then
if netstat -tuln | grep -q ":$config_port "; then
log_info "端口 $config_port 监听正常"
break
fi
elif command -v ss &>/dev/null; then
if ss -tuln | grep -q ":$config_port "; then
log_info "端口 $config_port 监听正常"
break
fi
fi
sleep 1
retry_count=$((retry_count + 1))
done
if [[ $retry_count -eq 10 ]]; then
log_warn "警告: 端口 $config_port 可能未正确监听,请检查配置"
fi
fi
else
echo -e "${RED}服务启动异常${NC}"
log_error "服务启动后状态异常,请检查日志"
systemctl status shadowsocks-libev --no-pager -l
return 1
fi
return 0
}
# 安装完整流程
install_shadowsocks() {
clear
echo "=================================================="
echo -e "${GREEN}开始安装 Shadowsocks${NC}"
echo "=================================================="
# 检查是否为 root
check_root
# 检查系统环境
check_system
# 备份现有配置
backup_config
# 安装依赖
install_dependencies || {
log_error "安装依赖失败,退出安装"
return 1
}
# 安装 shadowsocks-libev
install_shadowsocks_libev || {
log_error "安装 shadowsocks-libev 失败,退出安装"
return 1
}
# 配置 Shadowsocks
configure_shadowsocks || {
log_error "配置 Shadowsocks 失败,退出安装"
return 1
}
# 创建系统服务
create_service || {
log_error "创建系统服务失败,但会继续安装过程"
}
# 配置防火墙
configure_firewall
# 生成客户端信息
generate_client_info
echo ""
echo "=================================================="
echo -e "${GREEN}Shadowsocks 安装完成!${NC}"
echo "=================================================="
# 最终验证
echo -e "\n${BLUE}最终验证结果:${NC}"
# 检查服务状态
if systemctl is-active shadowsocks-libev &>/dev/null; then
echo -e "服务状态: ${GREEN}运行中${NC}"
else
echo -e "服务状态: ${RED}未运行${NC}"
fi
# 检查端口监听
if [[ -f "$CONFIG_PATH/config.json" ]]; then
local current_port=""
if command -v jq &>/dev/null; then
current_port=$(jq -r '.server_port' "$CONFIG_PATH/config.json" 2>/dev/null)
else
current_port=$(grep -o '"server_port": [0-9]*' "$CONFIG_PATH/config.json" | awk '{print $2}' 2>/dev/null)
fi
if [[ -n "$current_port" ]]; then
if command -v netstat &>/dev/null; then
if netstat -tuln | grep -q ":$current_port "; then
echo -e "端口状态: ${GREEN}$current_port 正常监听${NC}"
else
echo -e "端口状态: ${RED}$current_port 未监听${NC}"
fi
elif command -v ss &>/dev/null; then
if ss -tuln | grep -q ":$current_port "; then
echo -e "端口状态: ${GREEN}$current_port 正常监听${NC}"
else
echo -e "端口状态: ${RED}$current_port 未监听${NC}"
fi
fi
fi
fi
echo -e "\n${YELLOW}重要提醒:${NC}"
echo "1. 客户端信息已保存到 /root/shadowsocks_info.txt"
echo "2. 如果使用云服务器,请在控制台开放对应端口"
echo "3. 可以运行脚本选择'4'查看详细状态"
read -rp "按回车键返回主菜单..." temp
show_menu
}
# 停止 Shadowsocks 服务
stop_shadowsocks_service() {
log_info "停止 Shadowsocks 服务"
if systemctl is-active shadowsocks-libev &>/dev/null; then
echo -n "正在停止 Shadowsocks 服务... "
if systemctl stop shadowsocks-libev &>/dev/null; then
echo -e "${GREEN}成功${NC}"
else
echo -e "${RED}失败${NC}"
log_warn "无法停止 Shadowsocks 服务,将尝试继续卸载"
fi
else
log_info "Shadowsocks 服务未运行"
fi
echo -n "禁用 Shadowsocks 服务自启动... "
if systemctl disable shadowsocks-libev &>/dev/null; then
echo -e "${GREEN}成功${NC}"
else
echo -e "${YELLOW}失败${NC}"
log_warn "无法禁用 Shadowsocks 服务自启动"
fi
}
# 关闭防火墙端口
close_firewall_port() {
log_info "尝试关闭之前开放的防火墙端口"
# 检查是否安装了 ufw
if ! command -v ufw &>/dev/null; then
log_warn "未检测到 UFW 防火墙,跳过防火墙配置关闭"
return 0
fi
# 尝试从配置文件中读取端口
local ports=()
if [[ -f "$CONFIG_PATH/config.json" ]]; then
if command -v jq &>/dev/null; then
local port=$(jq '.server_port' "$CONFIG_PATH/config.json" 2>/dev/null)
if [[ "$port" != "null" && -n "$port" ]]; then
ports+=("$port")
fi
else
local port=$(grep -o '"server_port": [0-9]*' "$CONFIG_PATH/config.json" | awk '{print $2}')
if [[ -n "$port" ]]; then
ports+=("$port")
fi
fi
fi
# 如果找到了端口,关闭防火墙规则
if [[ ${#ports[@]} -gt 0 ]]; then
for port in "${ports[@]}"; do
echo -n "关闭 UFW 防火墙端口 $port... "
if ufw delete allow "$port" &>/dev/null; then
echo -e "${GREEN}完成${NC}"
else
echo -e "${RED}失败${NC}"
fi
done
fi
}
# 删除 Shadowsocks 文件
remove_shadowsocks_files() {
log_info "删除 Shadowsocks 文件"
# 删除配置目录
echo -n "删除配置目录... "
if [[ -d "$CONFIG_PATH" ]]; then
if rm -rf "$CONFIG_PATH"; then
echo -e "${GREEN}成功${NC}"
else
echo -e "${RED}失败${NC}"
fi
else
echo -e "${YELLOW}配置目录不存在${NC}"
fi
# 删除日志目录
echo -n "删除日志目录... "
if [[ -d "$LOG_PATH" ]]; then
if rm -rf "$LOG_PATH"; then
echo -e "${GREEN}成功${NC}"
else
echo -e "${RED}失败${NC}"
fi
else
echo -e "${YELLOW}日志目录不存在${NC}"
fi
# 删除服务文件
echo -n "删除服务文件... "
if rm -f /etc/systemd/system/shadowsocks-libev.service; then
echo -e "${GREEN}成功${NC}"
systemctl daemon-reload
else
echo -e "${YELLOW}服务文件不存在${NC}"
fi
# 卸载 shadowsocks-libev 软件包
echo -n "卸载 shadowsocks-libev... "
if apt-get remove -y shadowsocks-libev &>/dev/null; then
echo -e "${GREEN}成功${NC}"
else
echo -e "${YELLOW}卸载失败或软件包未安装${NC}"
fi
return 0
}
# 完整卸载流程
uninstall_shadowsocks() {
clear
echo "=================================================="
echo -e "${RED}开始卸载 Shadowsocks${NC}"
echo "=================================================="
# 确认卸载
echo -e "${YELLOW}警告: 这将卸载 Shadowsocks 并删除相关文件${NC}"
read -rp "是否继续? [y/N] " confirm
if [[ ! "$confirm" =~ ^[yY]$ ]]; then
log_info "卸载已取消"
read -rp "按回车键返回主菜单..." temp
show_menu
return 0
fi
# 检查 root 权限
check_root
# 备份配置
backup_config
# 停止服务
stop_shadowsocks_service
# 关闭防火墙端口
close_firewall_port
# 删除文件
remove_shadowsocks_files
echo ""
log_info "Shadowsocks 卸载完成"
read -rp "按回车键返回主菜单..." temp
show_menu
}
# 检查 Shadowsocks 状态
check_status() {
clear
echo "=================================================="
echo -e "${BLUE}Shadowsocks 状态检查${NC}"
echo "=================================================="
# 检查是否安装
if ! command -v ss-server &>/dev/null; then
echo -e "${RED}Shadowsocks 未安装${NC}"
read -rp "按回车键返回主菜单..." temp
show_menu
return 0
fi
# 检查版本
echo -n "Shadowsocks 版本: "
ss-server -h 2>&1 | grep -o 'shadowsocks-libev [0-9]\+\.[0-9]\+\.[0-9]\+' || echo "shadowsocks-libev $(dpkg -l shadowsocks-libev 2>/dev/null | grep '^ii' | awk '{print $3}' || echo '未知版本')"
# 检查服务状态
echo -n "服务状态: "
if systemctl is-active shadowsocks-libev &>/dev/null; then
echo -e "${GREEN}运行中${NC}"
else
echo -e "${RED}未运行${NC}"
fi
echo -n "自启动状态: "
if systemctl is-enabled shadowsocks-libev &>/dev/null; then
echo -e "${GREEN}已启用${NC}"
else
echo -e "${RED}未启用${NC}"
fi
# 检查端口
if [[ -f "$CONFIG_PATH/config.json" ]]; then
local current_port=""
if command -v jq &>/dev/null; then
current_port=$(jq '.server_port' "$CONFIG_PATH/config.json" 2>/dev/null)
else
current_port=$(grep -o '"server_port": [0-9]*' "$CONFIG_PATH/config.json" | awk '{print $2}')
fi
if [[ -n "$current_port" && "$current_port" != "null" ]]; then
echo -n "端口 $current_port 状态: "
if command -v netstat &>/dev/null; then
if netstat -tuln | grep -q ":$current_port "; then
echo -e "${GREEN}已开放${NC}"
else
echo -e "${RED}未开放${NC}"
fi
elif command -v ss &>/dev/null; then
if ss -tuln | grep -q ":$current_port "; then
echo -e "${GREEN}已开放${NC}"
else
echo -e "${RED}未开放${NC}"
fi
else
echo -e "${YELLOW}无法检查${NC}"
fi
fi
fi
echo -e "\n最近的日志:"
if systemctl status shadowsocks-libev &>/dev/null; then
systemctl status shadowsocks-libev --no-pager -l
else
echo "无法获取服务日志"
fi
read -rp "按回车键返回主菜单..." temp
show_menu
}
# 显示配置信息
show_config() {
clear
echo "=================================================="
echo -e "${CYAN}Shadowsocks 配置信息${NC}"
echo "=================================================="
# 检查是否已安装
if ! command -v ss-server &>/dev/null; then
echo -e "${RED}Shadowsocks 未安装${NC}"
read -rp "按回车键返回主菜单..." temp
show_menu
return 0
fi
# 显示配置文件内容
if [[ -f "$CONFIG_PATH/config.json" ]]; then
echo "服务器配置:"
if command -v jq &>/dev/null; then
jq . "$CONFIG_PATH/config.json"
else
cat "$CONFIG_PATH/config.json"
fi
else
echo -e "${RED}找不到服务器配置文件${NC}"
fi
echo -e "\n"
# 显示客户端信息
if [[ -f "/root/shadowsocks_info.txt" ]]; then
echo "客户端信息:"
cat /root/shadowsocks_info.txt
else
echo -e "${RED}找不到客户端信息文件${NC}"
fi
read -rp "按回车键返回主菜单..." temp
show_menu
}
# 重启 Shadowsocks 服务
restart_service() {
clear
echo "=================================================="
echo -e "${GREEN}重启 Shadowsocks 服务${NC}"
echo "=================================================="
# 检查是否已安装
if ! command -v ss-server &>/dev/null; then
echo -e "${RED}Shadowsocks 未安装${NC}"
read -rp "按回车键返回主菜单..." temp
show_menu
return 0
fi
echo -n "重启 Shadowsocks 服务... "
if systemctl restart shadowsocks-libev; then
echo -e "${GREEN}成功${NC}"
log_info "Shadowsocks 服务已重启"
else
echo -e "${RED}失败${NC}"
log_error "无法重启 Shadowsocks 服务"
fi
# 等待服务启动
echo -n "等待服务启动... "
sleep 3
echo -e "${GREEN}完成${NC}"
# 检查服务状态
echo -n "Shadowsocks 服务状态: "
if systemctl is-active shadowsocks-libev &>/dev/null; then
echo -e "${GREEN}运行中${NC}"
else
echo -e "${RED}未运行${NC}"
fi
# 验证端口监听
if [[ -f "$CONFIG_PATH/config.json" ]]; then
local current_port=""
if command -v jq &>/dev/null; then
current_port=$(jq -r '.server_port' "$CONFIG_PATH/config.json" 2>/dev/null)
else
current_port=$(grep -o '"server_port": [0-9]*' "$CONFIG_PATH/config.json" | awk '{print $2}' 2>/dev/null)
fi
if [[ -n "$current_port" ]]; then
echo -n "端口 $current_port 监听状态: "
if command -v netstat &>/dev/null; then
if netstat -tuln | grep -q ":$current_port "; then
echo -e "${GREEN}正常${NC}"
else
echo -e "${RED}未监听${NC}"
fi
elif command -v ss &>/dev/null; then
if ss -tuln | grep -q ":$current_port "; then
echo -e "${GREEN}正常${NC}"
else
echo -e "${RED}未监听${NC}"
fi
fi
fi
fi
read -rp "按回车键返回主菜单..." temp
show_menu
}
# 修改配置
modify_config() {
clear
echo "=================================================="
echo -e "${YELLOW}修改 Shadowsocks 配置${NC}"
echo "=================================================="
# 检查是否已安装
if ! command -v ss-server &>/dev/null; then
echo -e "${RED}Shadowsocks 未安装${NC}"
read -rp "按回车键返回主菜单..." temp
show_menu
return 0
fi
# 读取当前配置
if [[ -f "$CONFIG_PATH/config.json" ]]; then
if command -v jq &>/dev/null; then
SS_PORT=$(jq -r '.server_port' "$CONFIG_PATH/config.json")
SS_PASSWORD=$(jq -r '.password' "$CONFIG_PATH/config.json")
SS_METHOD=$(jq -r '.method' "$CONFIG_PATH/config.json")
else
SS_PORT=$(grep -o '"server_port": [0-9]*' "$CONFIG_PATH/config.json" | awk '{print $2}')
SS_PASSWORD=$(grep -o '"password": "[^"]*"' "$CONFIG_PATH/config.json" | cut -d'"' -f4)
SS_METHOD=$(grep -o '"method": "[^"]*"' "$CONFIG_PATH/config.json" | cut -d'"' -f4)
fi
fi
echo "当前配置:"
echo "端口: $SS_PORT"
echo "密码: $SS_PASSWORD"
echo "加密方法: $SS_METHOD"
echo ""
echo "1) 修改端口"
echo "2) 修改密码"
echo "3) 修改加密方法"
echo "4) 重新生成所有配置"
echo "0) 返回主菜单"
echo ""
read -rp "请选择 [0-4]: " modify_choice
local config_changed=false
case $modify_choice in
1)
read -rp "请输入新端口 (当前: $SS_PORT): " new_port
if [[ "$new_port" =~ ^[0-9]+$ ]] && [[ "$new_port" -ge 1 ]] && [[ "$new_port" -le 65535 ]]; then
SS_PORT="$new_port"
config_changed=true
else
log_error "无效端口"
fi
;;
2)
read -rp "请输入新密码 (当前: $SS_PASSWORD): " new_password
if [[ ${#new_password} -ge 8 ]]; then
SS_PASSWORD="$new_password"
config_changed=true
else
log_error "密码至少需要8位"
fi
;;
3)
echo "选择新的加密方法:"
echo "1) chacha20-ietf-poly1305"
echo "2) aes-256-gcm"
echo "3) aes-192-gcm"
echo "4) aes-128-gcm (默认)"
read -rp "请选择 [1-4, 默认4]: " method_choice
case $method_choice in
1) SS_METHOD="chacha20-ietf-poly1305"; config_changed=true ;;
2) SS_METHOD="aes-256-gcm"; config_changed=true ;;
3) SS_METHOD="aes-192-gcm"; config_changed=true ;;
4) SS_METHOD="aes-128-gcm"; config_changed=true ;;
"") SS_METHOD="aes-128-gcm"; config_changed=true ;; # 默认选择
*) log_error "无效选择" ;;
esac
;;
4)
SS_PORT=""
SS_PASSWORD=""
configure_shadowsocks
create_service
configure_firewall
generate_client_info
log_info "配置已重新生成"
read -rp "按回车键返回主菜单..." temp
show_menu
return 0
;;
0)
show_menu
return 0
;;
*)
log_error "无效选择"
read -rp "按回车键重试..." temp
modify_config
return 0
;;
esac
if [[ "$config_changed" == true ]]; then
# 更新配置文件
cat > "$CONFIG_PATH/config.json" << EOF
{
"server": "0.0.0.0",
"server_port": $SS_PORT,
"password": "$SS_PASSWORD",
"method": "$SS_METHOD",
"timeout": 300,
"fast_open": true,
"workers": 1,
"prefer_ipv6": false,
"no_delay": true,
"reuse_port": true
}
EOF
# 重启服务
systemctl restart shadowsocks-libev
# 重新配置防火墙
configure_firewall
# 重新生成客户端信息
generate_client_info
log_info "配置已更新并重启服务"
fi
read -rp "按回车键返回主菜单..." temp
show_menu
}
# 更新 Shadowsocks
update_shadowsocks() {
clear
echo "=================================================="
echo -e "${YELLOW}更新 Shadowsocks${NC}"
echo "=================================================="
# 检查是否已安装
if ! command -v ss-server &>/dev/null; then
echo -e "${RED}Shadowsocks 未安装,请先安装${NC}"
read -rp "按回车键返回主菜单..." temp
show_menu
return 0
fi
# 备份配置
backup_config
# 停止服务
echo -n "停止 Shadowsocks 服务... "
if systemctl stop shadowsocks-libev &>/dev/null; then
echo -e "${GREEN}成功${NC}"
else
echo -e "${RED}失败${NC}"
log_warn "无法停止 Shadowsocks 服务"
fi
# 更新软件包
apt_progress "apt-get update" "更新软件包列表"
apt_progress "apt-get upgrade -y shadowsocks-libev" "更新 shadowsocks-libev"
# 启动服务
echo -n "启动 Shadowsocks 服务... "
if systemctl start shadowsocks-libev; then
echo -e "${GREEN}成功${NC}"
else
echo -e "${RED}失败${NC}"
log_error "启动 Shadowsocks 服务失败"
fi
log_info "Shadowsocks 更新完成"
read -rp "按回车键返回主菜单..." temp
show_menu
}
# 主函数
main() {
# 创建日志目录
mkdir -p "$(dirname "$LOG_FILE")"
# 处理命令行参数
if [[ $# -gt 0 ]]; then
case "$1" in
-h|--help)
show_help
exit 0
;;
-i|--install)
check_root
install_shadowsocks
exit 0
;;
-u|--uninstall)
check_root
uninstall_shadowsocks
exit 0
;;
-s|--status)
check_root
check_status
exit 0
;;
-up|--update)
check_root
update_shadowsocks
exit 0
;;
*)
log_error "未知参数: $1"
show_help
exit 1
;;
esac
fi
# 无参数则显示菜单
check_root
show_menu
}
# 执行主函数
main "$@"